Matrici e vettori in MQL5

MetaQuotes | 14 gennaio, 2023

Le raccolte di dati ordinati, in cui tutti gli elementi hanno lo stesso tipo, vengono generalmente gestite tramite Array, in cui ogni elemento può essere accessibile tramite il suo indice. Gli array sono ampiamente utilizzati nella risoluzione di vari problemi di algebra lineare, nelle attività di modellazione matematica, nell'apprendimento automatico, ecc. In termini generali, la soluzione di tali problemi si basa su operazioni matematiche che utilizzano matrici e vettori, con i quali è possibile scrivere in modo compatto trasformazioni molto complesse sotto forma di semplici formule. La programmazione di tali operazioni richiede una buona conoscenza della matematica insieme alla capacità di scrivere complessi cicli annidati. Il debug e la correzione dei bug in tali programmi possono essere piuttosto impegnativi. 

Utilizzando tipi di dati speciali 'matrix' e 'vector', è possibile creare il codice, il quale è molto vicino alla notazione matematica evitando la necessità di creare cicli annidati o di tenere conto della corretta indicizzazione degli array nei calcoli. In questo articolo vedremo come creare, inizializzare e utilizzare gli oggetti matrix e vector in MQL5.


Tipo "vector"

Vector è un array monodimensionale di tipo double. Sui vettori sono definite le seguenti operazioni: addizione e moltiplicazione, nonché la norma per ottenere la lunghezza o il modulo del vettore. In programmazione, i vettori sono solitamente rappresentati da array di elementi omogenei, sui quali non possono essere definite operazioni vettoriali regolari, cioè gli array non possono essere sommati o moltiplicati, e non hanno norma.

In matematica, i vettori possono essere rappresentati come vettori riga, cioè un array costituito da una riga e n colonne, e come vettori stringa, cioè una matrice composta da una colonna e n righe. In MQL5, il tipo 'vettore' non ha sottotipi di riga e colonna, quindi il programmatore deve capire quale tipo di vettore viene utilizzato in una particolare operazione.

Utilizzare i seguenti metodi predefiniti per creare e inizializzare i vettori.

Metodi
Analogo NumPy
Descrizione
void vector.Init( ulong dimensione);   Crea un vettore della lunghezza specificata, in cui i valori non sono definiti
static vector vector::Ones(ulong dimensione);
 ones
Crea un vettore della lunghezza specificata, riempito con uno
static vector vector::Zeros(ulong dimensione);
 zeros
Crea un vettore della lunghezza specificata, riempito con zeri
static vector vector::Full(ulong dimensione,double valore);
 full
Crea un vettore della lunghezza specificata, riempito con il valore specificato
operator=

Restituisce una copia del vettore
void vector.Resize(const vettore v);
  Ridimensiona un vettore aggiungendo nuovi valori alla fine 


Esempi di creazione di vettori:

void OnStart()
 {
//--- vector initialization examples
  vector v;
  v.Init(7);
  Print("v = ", v); 

  vector v1=vector::Ones(5);
  Print("v1 = ", v1);

  vector v2=vector::Zeros(3);
  Print("v2 = ", v2);

  vector v3=vector::Full(6, 2.5);
  Print("v3 = ", v3);

  vector v4{1, 2, 3};
  Print("v4 = ", v4);  
  v4.Resize(5);
  Print("after Resize(5) v4 = ", v4);  
  
  vector v5=v4;
  Print("v5 = ", v5);  
  v4.Fill(7);
  Print("v4 = ", v4, "   v5 =",v5);  
   
 }
 
 
/*
Execution result

v = [4,5,6,8,10,12,12]
v1 = [1,1,1,1,1]
v2 = [0,0,0]
v3 = [2.5,2.5,2.5,2.5,2.5,2.5]
v4 = [1,2,3]
after Resize(5) v4 = [1,2,3,7,7]
v5 = [1,2,3,7,7]
v4 = [7,7,7,7,7]   v5 =[1,2,3,7,7]

*/ 

Il metodo Init() può essere utilizzato non solo per allocare memoria per il vettore, ma anche per inizializzare elementi vettoriali con valori utilizzando una funzione. In questo caso, la dimensione del vettore viene passata a Init come primo parametro e il nome della funzione viene passato come secondo. Se la funzione contiene parametri, questi parametri devono essere specificati subito dopo il nome della funzione, separati da una virgola.

La funzione stessa deve contenere un riferimento al vettore che le viene passato come primo parametro. Il vettore non deve essere passato durante la chiamata Init. Osserviamo l'operazione del metodo usando la funzione Arange come esempio. Questa funzione simula numpy.arange.

void OnStart()
  {
//---
   vector v;
   v.Init(7,Arange,10,0,0.5); // 3 parameters are passed with Arange call
   Print("v = ", v);
   Print("v.size = ",v.Size());
  }
//+------------------------------------------------------------------+
//|  Values are generated within the half-open interval [start, stop)|
//+------------------------------------------------------------------+
void Arange(vector& v, double stop, double start = 0, double step = 1) // the function has 4 parameters
  {
   if(start >= stop)
     {
      PrintFormat("%s wrong parameters! start=%G  stop=%G", __FILE__,start, stop);
      return;
     }
//---
   int size = (int)((stop - start) / step);
   v.Resize(size);
   double value = start;
   for(ulong i = 0; i < v.Size(); i++)
     {
      v[i] = value;
      value += step;
     }
  }
  
/*
Execution result

v = [0,0.5,1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5]
v.size = 20

*/

La funzione Arange ha due parametri opzionali, "start" e "stop". Quindi, un'altra possibile chiamata di Init(7,Arange,10) e il relativo risultato sono i seguenti:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   vector v;
   v.Init(7,Arange,10);
   Print("v = ", v);
   Print("v.size = ",v.Size());
  }
...

/*

v = [0,1,2,3,4,5,6,7,8,9]
v.size = 10

*/


Operazioni con vettori

Le normali operazioni di addizione, sottrazione, moltiplicazione e divisione utilizzando uno scalare possono essere eseguite sui vettori.

//+------------------------------------------------------------------+
//|                                              vector2_article.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
 {
//---
  vector v= {1, 2, 3, 4, 5};
  Print("Examples without saving vector changes");
  Print("v = ", v);
  Print("v+5 = ", v+5);
  Print("v-Pi= ", v-M_PI);
  Print("v*2.0= ", v*2);
  Print("v/3.0= ", v/3.0);

  Print("Save all vector changes");
  Print("v = ", v);
  Print("v+5 = ", v=v+5);
  Print("v-Pi= ", v=v-M_PI);
  Print("v*2.0= ", v= v*2);
  Print("v/3.0= ", v= v/3.0);
 }
/*
Execution result
   
Examples without saving vector changes
v = [1,2,3,4,5]
v+5 = [6,7,8,9,10]
v-Pi= [-2.141592653589793,-1.141592653589793,-0.1415926535897931,0.8584073464102069,1.858407346410207]
v*2.0= [2,4,6,8,10]
v/3.0= [0.3333333333333333,0.6666666666666666,1,1.333333333333333,1.666666666666667]
Save all vector changes
v = [1,2,3,4,5]
v+5 = [6,7,8,9,10]
v-Pi= [2.858407346410207,3.858407346410207,4.858407346410207,5.858407346410207,6.858407346410207]
v*2.0= [5.716814692820414,7.716814692820414,9.716814692820414,11.71681469282041,13.71681469282041]
v/3.0= [1.905604897606805,2.572271564273471,3.238938230940138,3.905604897606805,4.572271564273471]

*/
//+------------------------------------------------------------------+

I vettori supportano a livello di elementi operazioni di addizione, sottrazione, moltiplicazione e divisione di due vettori della stessa dimensione.

void OnStart()
  {
//---
   vector a = {1, 2, 3};
   vector b = {2, 4, 6};
   Print("a + b = ", a + b);
   Print("a - b = ", a - b);
   Print("a * b = ", a * b);
   Print("b / a = ", b / a);
  }

/*
Execution result

a + b = [3,6,9]
a - b = [-1,-2,-3]
a * b = [2,8,18]
b / a = [2,2,2]

*/

Per questo tipo di dati sono definite quattro operazioni di prodotto.

void OnStart()
 {
//---
  vector a={1, 2, 3};
  vector b={4, 5, 6};
  Print("a = ", a);
  Print("b = ", b);
  Print("1) a.Dot(b) = ", a.Dot(b));
  Print("2) a.MatMul(b) = ", a.MatMul(b));
  Print("3) a.Kron(b) = ", a.Kron(b));
  Print("4) a.Outer(b) = \n", a.Outer(b));
 }
/*
Execution result

a = [1,2,3]
b = [4,5,6]
1) a.Dot(b) = 32.0
2) a.MatMul(b) = 32.0
3) a.Kron(b) = [[4,5,6,8,10,12,12,15,18]]
4) a.Outer(b) = 
[[4,5,6]
 [8,10,12]
 [12,15,18]]

*/

Come puoi vedere dall'esempio, il metodo Outer restituisce una matrice nella quale il numero di righe e colonne corrisponde alle dimensioni dei vettori moltiplicati. Dot e MatMul funzionano nello stesso modo.


Norma vettoriale

La norma del vettore e della matrice rappresenta la lunghezza del vettore (magnitudine) e il valore assoluto. Tre sono i possibili modi per calcolare la norma di un vettore e sono elencati in ENUM_VECTOR_NORM.
void OnStart()
 {
//---
  struct str_vector_norm
   {
    ENUM_VECTOR_NORM  norm;
    int               value;
   };
  str_vector_norm vector_norm[]=
   {
     {VECTOR_NORM_INF,       0},
     {VECTOR_NORM_MINUS_INF, 0},
     {VECTOR_NORM_P,         0},
     {VECTOR_NORM_P,         1},
     {VECTOR_NORM_P,         2},
     {VECTOR_NORM_P,         3},
     {VECTOR_NORM_P,         4},
     {VECTOR_NORM_P,         5},
     {VECTOR_NORM_P,         6},
     {VECTOR_NORM_P,         7},
     {VECTOR_NORM_P,        -1},
     {VECTOR_NORM_P,        -2},
     {VECTOR_NORM_P,        -3},
     {VECTOR_NORM_P,        -4},
     {VECTOR_NORM_P,        -5},
     {VECTOR_NORM_P,        -6},
     {VECTOR_NORM_P,        -7}
   };
  vector v{1, 2, 3, 4, 5, 6, 7};
  double norm;
  Print("v = ", v);
//---
  for(int i=0; i<ArraySize(vector_norm); i++)
   {
    switch(vector_norm[i].norm)
     {
      case VECTOR_NORM_INF :
        norm=v.Norm(VECTOR_NORM_INF);
        Print("v.Norm(VECTOR_NORM_INF) = ", norm);
        break;
      case VECTOR_NORM_MINUS_INF :
        norm=v.Norm(VECTOR_NORM_MINUS_INF);
        Print("v.Norm(VECTOR_NORM_MINUS_INF) = ", norm);
        break;
      case VECTOR_NORM_P :
        norm=v.Norm(VECTOR_NORM_P, vector_norm[i].value);
        PrintFormat("v.Norm(VECTOR_NORM_P,%d) = %G", vector_norm[i].value, norm);
     }
   }
 }
/*

v = [1,2,3,4,5,6,7]
v.Norm(VECTOR_NORM_INF) = 7.0
v.Norm(VECTOR_NORM_MINUS_INF) = 1.0
v.Norm(VECTOR_NORM_P,0) = 7
v.Norm(VECTOR_NORM_P,1) = 28
v.Norm(VECTOR_NORM_P,2) = 11.8322
v.Norm(VECTOR_NORM_P,3) = 9.22087
v.Norm(VECTOR_NORM_P,4) = 8.2693
v.Norm(VECTOR_NORM_P,5) = 7.80735
v.Norm(VECTOR_NORM_P,6) = 7.5473
v.Norm(VECTOR_NORM_P,7) = 7.38704
v.Norm(VECTOR_NORM_P,-1) = 0.385675
v.Norm(VECTOR_NORM_P,-2) = 0.813305
v.Norm(VECTOR_NORM_P,-3) = 0.942818
v.Norm(VECTOR_NORM_P,-4) = 0.980594
v.Norm(VECTOR_NORM_P,-5) = 0.992789
v.Norm(VECTOR_NORM_P,-6) = 0.99714
v.Norm(VECTOR_NORM_P,-7) = 0.998813

*/

Usando la norma, puoi misurare la distanza tra due vettori:

void OnStart()
 {
//---
   vector a{1,2,3};
   vector b{2,3,4};
   double distance=(b-a).Norm(VECTOR_NORM_P,2);
   Print("a = ",a);
   Print("b = ",b);
   Print("|a-b| = ",distance);   
 }
/*
Execution result

a = [1,2,3]
b = [2,3,4]
|a-b| = 1.7320508075688772

*/


Tipo "matrix"

Il vettore è uno speciale contenitore di una matrice, che in realtà è un array bidimensionale di tipo double. Pertanto, una matrice può essere considerata come un array di vettori della stessa dimensione. Il numero di righe della matrice corrisponde al numero di vettori, mentre il numero di colonne è uguale alla lunghezza del vettore. 

Le operazioni di addizione e moltiplicazione sono disponibili anche per le matrici. I linguaggi di programmazione convenzionali utilizzano gli array per rappresentare le matrici. Tuttavia, gli array regolari non possono essere aggiunti o moltiplicati tra loro e non hanno la norma. La matematica considera molti tipi diversi di matrici. Ad esempio, matrici identità, simmetriche, antisimmetriche, matrici triangolari superiori e inferiori e altri tipi.

Una matrice può essere creata e inizializzata utilizzando metodi integrati simili ai metodi vettoriali.

Metodo

Metodo analogo in NumPy

Descrizione

void static matrix.Eye(const int righe, const int colonne, const int ndiag=0)

eye

Costruisce una matrice con numeri uno su una diagonale specificata e zeri altrove

void matrix.Identity()

identity

Riempie una matrice con numeri uno sulla diagonale principale e zeri altrove

void static matix.Ones(const int righe, const int colonne)

ones

Costruisce una nuova matrice in base al numero di righe e colonne, riempita con numeri uno

void static matix.Zeros(const int righe, const int colonne)

zeros

Costruisce una nuova matrice in base al numero di righe e colonne, riempita con zeri

void static matrix.Tri(const int righe, const int colonne, const int ndiag=0)
tri  Costruisce una matrice con numeri uno su una diagonale specificata e sotto di essa e zeri altrove 
void matrix.Diag(const vettore v, const int ndiag=0)  diag  Estrae una diagonale o costruisce una matrice diagonale 

void matrix.Full(const int righe, const int colonne, const valore scalare)

full

Costruisce una nuova matrice in base al numero di righe e colonne, riempita con un valore scalare

void matrix.Fill(const valore scalare)     Riempie la matrice con il valore specificato


Esempi di costruzione e riempimento di matrici:

void OnStart()
 {
//---
  matrix m{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
  Print("m = \n", m);
  matrix ones=matrix::Ones(4, 4);
  Print("ones = \n", ones);
  matrix zeros=matrix::Zeros(4, 4);
  Print("zeros = \n", zeros);
  matrix eye=matrix::Eye(4, 4);
  Print("eye = \n", eye);

  matrix identity(4, 5);
  Print("matrix_identity\n", identity);
  identity.Identity();
  Print("matrix_identity\n", identity);

  matrix tri=matrix::Tri(3, 4);
  Print("tri = \n", tri);
  Print("tri.Transpose() = \n", tri.Transpose()); // transpose the matrix

  matrix diag(5, 5);
  Print("diag = \n", diag);
  vector d{1, 2, 3, 4, 5};
  diag.Diag(d);
  Print("diag = \n", diag); // insert values from the vector into the matrix diagonal

  matrix fill(5, 5);
  fill.Fill(10);
  Print("fill = \n", fill);

  matrix full =matrix::Full(5, 5, 100);
  Print("full = \n", full);


  matrix init(5, 7);
  Print("init = \n", init);
  m.Init(4, 6);
  Print("init = \n", init);
  
  matrix resize=matrix::Full(2, 2, 5);  
  resize.Resize(5,5);
  Print("resize = \n", resize);  
 }
/*
Execution result

m =
[[1,2,3]
[4,5,6]
[7,8,9]]
ones =
[[1,1,1,1]
[1,1,1,1]
[1,1,1,1]
[1,1,1,1]]
zeros =
[[0,0,0,0]
[0,0,0,0]
[0,0,0,0]
[0,0,0,0]]
eye =
[[1,0,0,0]
[0,1,0,0]
[0,0,1,0]
[0,0,0,1]]
matrix_identity
[[1,0,0,0,0]
[0,1,0,0,0]
[0,0,1,0,0]
[0,0,0,1,0]]
matrix_identity
[[1,0,0,0,0]
[0,1,0,0,0]
[0,0,1,0,0]
[0,0,0,1,0]]
tri =
[[1,0,0,0]
[1,1,0,0]
[1,1,1,0]]
tri.Transpose() =
[[1,1,1]
[0,1,1]
[0,0,1]
[0,0,0]]
diag =
[[0,0,0,0,0]
[0,0,0,0,0]
[0,0,0,0,0]
[0,0,0,0,0]
[0,0,0,0,0]]
diag =
[[1,0,0,0,0]
[0,2,0,0,0]
[0,0,3,0,0]
[0,0,0,4,0]
[0,0,0,0,5]]
fill =
[[10,10,10,10,10]
[10,10,10,10,10]
[10,10,10,10,10]
[10,10,10,10,10]
[10,10,10,10,10]]
full =
[[100,100,100,100,100]
[100,100,100,100,100]
[100,100,100,100,100]
[100,100,100,100,100]
[100,100,100,100,100]]
resize = 
[[5,5,0,0,0]
 [5,5,0,0,0]
 [0,0,0,0,0]
 [0,0,0,0,0]
 [0,0,0,0,0]]

*/

L'esempio seguente mostra come utilizzare le funzioni personalizzate durante il riempimento delle matrici:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
 {
//---
  matrix random(4, 5, MatrixRandom);
  Print("random = \n",random);
  
  matrix init(3, 6, MatrixSetValues);  
  Print("init = \n", init);
  
 }
//+------------------------------------------------------------------+
//| Fills the matrix with random values                              |
//+------------------------------------------------------------------+
void MatrixRandom(matrix& m)
 {
  for(ulong r=0; r<m.Rows(); r++)
   {
    for(ulong c=0; c<m.Cols(); c++)
     {
      m[r][c]=double(MathRand())/32767.;
     }
   }
 }
//+------------------------------------------------------------------+
//| Fills the matrix with powers of a number                         |
//+------------------------------------------------------------------+
void MatrixSetValues(matrix& m, double initial=1)
 {
  double value=initial;
  for(ulong r=0; r<m.Rows(); r++)
   {
    for(ulong c=0; c<m.Cols(); c++)
     {
      m[r][c]=value;
      value*=2;
     }
   }
 } 
 
/*
Execution result

random = 
[[0.4200262459181494,0.5014496292001098,0.7520371105075229,0.652058473464156,0.08783227027191992]
 [0.5991088595233008,0.4311960203863643,0.8718832972197638,0.1350138859218116,0.901882992034669]
 [0.4964445936460463,0.8354747154148991,0.5258339182714317,0.6055482650227363,0.5952940458388012]
 [0.3959166234321116,0.8146916104617451,0.2053590502639851,0.2657551805169835,0.3672292245246742]]
init = 
[[1,2,4,8,16,32]
 [64,128,256,512,1024,2048]
 [4096,8192,16384,32768,65536,131072]]

*/ 

Una matrice può essere costruita senza inizializzazione del valore in due modi: 

//--- create a matrix of a given 'rows x cols' size
  matrix m(3, 3);

// ------ equivalent
  matrix m;
  m.Resize(3, 3);


Norma matriciale

I nove modi per calcolare una norma matriciale sono elencati in ENUM_MATRIX_NORM.

void OnStart()
  {
//---
   ENUM_MATRIX_NORM matrix_norm[]= {MATRIX_NORM_FROBENIUS,
                                    MATRIX_NORM_SPECTRAL,
                                    MATRIX_NORM_NUCLEAR,
                                    MATRIX_NORM_INF,
                                    MATRIX_NORM_MINUS_INF,
                                    MATRIX_NORM_P1,
                                    MATRIX_NORM_MINUS_P1,
                                    MATRIX_NORM_P2,
                                    MATRIX_NORM_MINUS_P2
                                   };
   matrix m{{1,2,3},{4,5,6},{7,8,9}};
   Print("matrix m:\n",m);
//--- compute the norm using all ways
   double norm;
   for(int i=0; i<ArraySize(matrix_norm); i++)
     {
      norm=m.Norm(matrix_norm[i]);
      PrintFormat("%d. Norm(%s) = %.6f",i+1, EnumToString(matrix_norm[i]),norm);
     }
//---
   return;
  }

/*
Execution result

matrix m:
[[1,2,3]
[4,5,6]
[7,8,9]]
1. Norm(MATRIX_NORM_FROBENIUS) = 16.881943
2. Norm(MATRIX_NORM_SPECTRAL) = 14.790157
3. Norm(MATRIX_NORM_NUCLEAR) = 17.916473
4. Norm(MATRIX_NORM_INF) = 24.000000
5. Norm(MATRIX_NORM_MINUS_INF) = 6.000000
6. Norm(MATRIX_NORM_P1) = 18.000000
7. Norm(MATRIX_NORM_MINUS_P1) = 12.000000
8. Norm(MATRIX_NORM_P2) = 16.848103
9. Norm(MATRIX_NORM_MINUS_P2) = 0.000000

*/


Operazioni con matrici e vettori

Le matrici forniscono metodi speciali per risolvere problemi matematici:

Decomposizione della matrice:

Metodo

Metodo analogo in NumPy

Descrizione
bool matrix.Cholesky(matrice& L) cholesky Calcola la decomposizione di Cholesky
bool matrix.QR(matrice& Q, matrice& R) qr

Calcola la decomposizione QR

bool matrix.SVD(matrice& U, matrice& V, vettore& valori_singolari)

svd

Calcola la decomposizione SVD

bool matrix.Eig(matrice& autovettori, vettore& autovalori)

eig

Calcola gli autovalori e i giusti autovettori di una matrice quadrata

bool matrix.EigVals(vettore& autovalori)

eigvals

Calcola gli autovalori di una matrice generale

bool matrix.LU(matrice& L, matrice& U)

 

Implementa una decomposizione LU di una matrice: il prodotto di una matrice triangolare inferiore e di una matrice triangolare superiore

bool matrice.LUP(matrice& L, matrice& U, matrice& P)

 

Implementa una decomposizione LUP con pivoting parziale, che è una fattorizzazione LU con una permutazione di righe: PA = LU


Prodotto di matrici e vettori

MatMul() è definito per calcolare il prodotto matriciale di matrici e vettori. Questo metodo è spesso utilizzato per risolvere vari problemi matematici. Le seguenti due opzioni sono possibili quando si moltiplicano una matrice e un vettore:

Se la lunghezza del vettore non è uguale al numero di colonne nella matrice, verrà generato un errore di esecuzione critico.

Per moltiplicare due matrici, la loro forma dovrebbe essere la seguente: A[M,N] * B[N,K] = C[M,K], cioè il numero di colonne della matrice di sinistra deve essere uguale al numero di righe della matrice di destra. Se le dimensioni non sono coerenti, il risultato è una matrice vuota. Esaminiamo tutte le varianti di prodotto di matrice con esempi.

void OnStart()
  {
//--- initialize matrices
   matrix m35, m52;
   m35.Init(3,5,Arange);
   m52.Init(5,2,Arange);
//---
   Print("1. Product of horizontal vector v[3] and matrix m[3,5]");
   vector v3 = {1,2,3};
   Print("On the left v3 = ",v3);
   Print("On the right m35 = \n",m35);
   Print("v3.MatMul(m35) = horizontal vector v[5] \n",v3.MatMul(m35));
//--- show that this is really a horizontal vector
   Print("\n2. Product of matrix m[1,3] and matrix m[3,5]");
   matrix m13;
   m13.Init(1,3,Arange,1);
   Print("On the left m13 = \n",m13);
   Print("On the right m35 = \n",m35);
   Print("m13.MatMul(m35) = matrix m[1,5] \n",m13.MatMul(m35));

   Print("\n3. Product of matrix m[3,5] and vertical vector v[5]");
   vector v5 = {1,2,3,4,5};
   Print("On the left m35 = \n",m35);
   Print("On the right v5 = ",v5);
   Print("m35.MatMul(v5) = vertical vector v[3] \n",m35.MatMul(v5));
//--- show that this is really a vertical vector
   Print("\n4. Product of matrix m[3,5] and matrix m[5,1]");
   matrix m51;
   m51.Init(5,1,Arange,1);
   Print("On the left m35 = \n",m35);
   Print("On the right m51 = \n",m51);
   Print("m35.MatMul(m51) = matrix v[3] \n",m35.MatMul(m51));

   Print("\n5. Product of matrix m[3,5] and matrix m[5,2]");
   Print("On the left m35 = \n",m35);
   Print("On the right m52 = \n",m52);
   Print("m35.MatMul(m52) = matrix m[3,2] \n",m35.MatMul(m52));

   Print("\n6. Product of horizontal vector v[5] and matrix m[5,2]");
   Print("On the left v5 = \n",v5);
   Print("On the right m52 = \n",m52);
   Print("v5.MatMul(m52) = horizontal vector v[2] \n",v5.MatMul(m52));

   Print("\n7. Outer() product of horizontal vector v[5] and vertical vector v[3]");
   Print("On the left v5 = \n",v5);
   Print("On the right v3 = \n",v3);
   Print("v5.Outer(v3) = matrix m[5,3] \n",v5.Outer(v3));
//--- show that the product of matrices generates the same result
   Print("\n8. Outer() product of the matrix m[1,5] and matrix m[3,1]");
   matrix m15,m31;
   m15.Init(1,5,Arange,1);
   m31.Init(3,1,Arange,1);
   Print("On the left m[1,5] = \n",m15);
   Print("On the right m31 = \n",m31);
   Print("m15.Outer(m31) = matrix m[5,3] \n",m15.Outer(m31));
  }
//+------------------------------------------------------------------+
//|  Fill the matrix with increasing values                          |
//+------------------------------------------------------------------+
void Arange(matrix & m, double start = 0, double step = 1) // the function has three parameters
  {
//---
   ulong cols = m.Cols();
   ulong rows = m.Rows();
   double value = start;
   for(ulong r = 0; r < rows; r++)
     {
      for(ulong c = 0; c < cols; c++)
        {
         m[r][c] = value;
         value += step;
        }
     }
//---     
  }
/*
Execution result

1. Product of horizontal vector v[3] and matrix m[3,5]
On the left v3 = [1,2,3]
On the right m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
v3.MatMul(m35) = horizontal vector v[5]
[40,46,52,58,64]

2. Product of matrix m[1,3] and matrix m[3,5]
On the left m13 =
[[1,2,3]]
On the right m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
m13.MatMul(m35) = matrix m[1,5]
[[40,46,52,58,64]]

3. Product of matrix m[3,5] and vertical vector v[5]
On the left m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
On the right v5 = [1,2,3,4,5]
m35.MatMul(v5) = vertical vector v[3]
[40,115,190]

4. Product of matrix m[3,5] and matrix m[5,1]
On the left m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
On the right m51 =
[[1]
 [2]
 [3]
 [4]
 [5]]
m35.MatMul(m51) = matrix v[3]
[[40]
 [115]
 [190]]

5. Product of matrix m[3,5] and matrix m[5,2]
On the left m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
On the right m52 =
[[0,1]
 [2,3]
 [4,5]
 [6,7]
 [8,9]]
m35.MatMul(m52) = matrix m[3,2]
[[60,70]
 [160,195]
 [260,320]]

6. The product of horizontal vector v[5] and matrix m[5,2]
On the left v5 =
[1,2,3,4,5]
On the right m52 =
[[0,1]
 [2,3]
 [4,5]
 [6,7]
 [8,9]]
v5.MatMul(m52) = horizontal vector v[2]
[80,95]

7. Outer() product of horizontal vector v[5] and vertical vector v[3]
On the left v5 =
[1,2,3,4,5]
On the right v3 =
[1,2,3]
v5.Outer(v3) = matrix m[5,3]
[[1,2,3]
 [2,4,6]
 [3,6,9]
 [4,8,12]
 [5,10,15]]

8. Outer() product of the matrix m[1,5] and matrix m[3,1]
On the left m[1,5] =
[[1,2,3,4,5]]
On the right m31 =
[[1]
 [2]
 [3]]
m15.Outer(m31) = matrix m[5,3]
[[1,2,3]
 [2,4,6]
 [3,6,9]
 [4,8,12]
 [5,10,15]]

*/

Per una migliore comprensione di come i tipi matrix e vector sono disposti , questi esempi mostrano come è possibile utilizzare le matrici al posto dei vettori. Significa che i vettori possono essere rappresentati come matrici.


Numeri complessi di tipo 'complex'

Alcuni problemi matematici richiedono l'uso del nuovo tipo di dati "numeri complex". Il tipo complex è una struttura:
struct complex
  {
   double             real;   // real part
   double             imag;   // imaginary part
  };
Il tipo 'complex' può essere passato per valore, come un parametro per le funzioni MQL5 (a differenza delle strutture ordinarie, che sono passate solo per riferimento). Per le funzioni importate da DLL, il tipo "complex" deve essere passato solo per riferimento.

Il suffisso 'i' è usato per descrivere costanti complex:
complex square(complex c)
  {
   return(c*c);
  }
  
void OnStart()
  {
   Print(square(1+2i));  // a constant is passed as a parameter
  }

// will print "(-3,4)" - a string representation of a complex number
Per i numeri complex sono disponibili solo operazioni semplici: =, +, -, *, /, +=, -=, *=, /=, ==, !=.

Presto verrà aggiunto il supporto per ulteriori funzioni matematiche, consentendo il calcolo del valore assoluto, seno, coseno e altri.