/// <summary> /// Calculates the infinity norm of the vector. /// </summary> /// <returns>The maximum absolute value.</returns> public override double InfinityNorm() { return(CommonParallel.Aggregate(0, _storage.ValueCount, i => Math.Abs(_storage.Values[i]), Math.Max, 0d)); }
/// <summary> /// Calculates the infinity norm of the vector. /// </summary> /// <returns>The maximum absolute value.</returns> public override double InfinityNorm() { return(CommonParallel.Aggregate(0, _storage.ValueCount, i => _storage.Values[i].Magnitude, Math.Max, 0f)); }
/// <summary> /// Calculates the infinity norm of the vector. /// </summary> /// <returns>The maximum absolute value.</returns> public override double InfinityNorm() { return(CommonParallel.Aggregate(0, Count, i => Math.Abs(At(i)), Math.Max, 0d)); }
/// <summary> /// Calculates the infinity norm of the vector. /// </summary> /// <returns>The maximum absolute value.</returns> public override double InfinityNorm() { return(CommonParallel.Aggregate(_values, (i, v) => Math.Abs(v), Math.Max, 0f)); }
/// <summary> /// Calculates the infinity norm of the vector. /// </summary> /// <returns>The maximum absolute value.</returns> public override double InfinityNorm() { return(CommonParallel.Aggregate(_values, (i, v) => v.Magnitude, Math.Max, 0d)); }
void MapSubMatrixIndexedToUnchecked <TU>(DenseColumnMajorMatrixStorage <TU> target, Func <int, int, T, TU> f, int sourceRowIndex, int targetRowIndex, int rowCount, int sourceColumnIndex, int targetColumnIndex, int columnCount, Zeros zeros, ExistingData existingData) where TU : struct, IEquatable <TU>, IFormattable { var processZeros = zeros == Zeros.Include || !Zero.Equals(f(0, 1, Zero)); if (existingData == ExistingData.Clear && !processZeros) { target.Clear(targetRowIndex, rowCount, targetColumnIndex, columnCount); } if (processZeros) { CommonParallel.For(0, columnCount, Math.Max(4096 / rowCount, 32), (a, b) => { int sourceColumn = sourceColumnIndex + a; int targetColumn = targetColumnIndex + a; for (int j = a; j < b; j++) { int targetIndex = targetRowIndex + (j + targetColumnIndex) * target.RowCount; int sourceRow = sourceRowIndex; int targetRow = targetRowIndex; for (int i = 0; i < rowCount; i++) { target.Data[targetIndex++] = f(targetRow++, targetColumn, sourceRow++ == sourceColumn ? Data[sourceColumn] : Zero); } sourceColumn++; targetColumn++; } }); } else { if (sourceRowIndex > sourceColumnIndex && sourceColumnIndex + columnCount > sourceRowIndex) { // column by column, but skip resulting zero columns at the beginning int columnInit = sourceRowIndex - sourceColumnIndex; int offset = (columnInit + targetColumnIndex) * target.RowCount + targetRowIndex; int step = target.RowCount + 1; int count = Math.Min(columnCount - columnInit, rowCount); for (int k = 0, j = offset; k < count; j += step, k++) { target.Data[j] = f(targetRowIndex + k, targetColumnIndex + columnInit + k, Data[sourceRowIndex + k]); } } else if (sourceRowIndex < sourceColumnIndex && sourceRowIndex + rowCount > sourceColumnIndex) { // row by row, but skip resulting zero rows at the beginning int rowInit = sourceColumnIndex - sourceRowIndex; int offset = targetColumnIndex * target.RowCount + rowInit + targetRowIndex; int step = target.RowCount + 1; int count = Math.Min(columnCount, rowCount - rowInit); for (int k = 0, j = offset; k < count; j += step, k++) { target.Data[j] = f(targetRowIndex + rowInit + k, targetColumnIndex + k, Data[sourceColumnIndex + k]); } } else { int offset = targetColumnIndex * target.RowCount + targetRowIndex; int step = target.RowCount + 1; var count = Math.Min(columnCount, rowCount); for (int k = 0, j = offset; k < count; j += step, k++) { target.Data[j] = f(targetRowIndex + k, targetColumnIndex + k, Data[sourceRowIndex + k]); } } } }
internal override void MapIndexedToUnchecked <TU>(VectorStorage <TU> target, Func <int, T, TU> f, Zeros zeros, ExistingData existingData) { if (target is SparseVectorStorage <TU> sparseTarget) { var indices = new List <int>(); var values = new List <TU>(); if (zeros == Zeros.Include || !Zero.Equals(f(0, Zero))) { int k = 0; for (int i = 0; i < Length; i++) { var item = k < ValueCount && (Indices[k]) == i?f(i, Values[k++]) : f(i, Zero); if (!Zero.Equals(item)) { values.Add(item); indices.Add(i); } } } else { for (int i = 0; i < ValueCount; i++) { var item = f(Indices[i], Values[i]); if (!Zero.Equals(item)) { values.Add(item); indices.Add(Indices[i]); } } } sparseTarget.Indices = indices.ToArray(); sparseTarget.Values = values.ToArray(); sparseTarget.ValueCount = values.Count; return; } if (target is DenseVectorStorage <TU> denseTarget) { if (existingData == ExistingData.Clear) { denseTarget.Clear(); } if (zeros == Zeros.Include || !Zero.Equals(f(0, Zero))) { int k = 0; for (int i = 0; i < Length; i++) { denseTarget.Data[i] = k < ValueCount && (Indices[k]) == i ? f(i, Values[k++]) : f(i, Zero); } } else { CommonParallel.For(0, ValueCount, 4096, (a, b) => { for (int i = a; i < b; i++) { denseTarget.Data[Indices[i]] = f(Indices[i], Values[i]); } }); } return; } // FALL BACK base.MapIndexedToUnchecked(target, f, zeros, existingData); }
/// <summary> /// Solves A*X=B for X using a previously QR factored matrix. /// </summary> /// <param name="q"> /// The Q matrix obtained by calling <seecreQRFactor( double[], int, int, double[], double[]).</param> /// <param name="r"> /// The R matrix obtained by calling <seecreQRFactor( double[], int, int, double[], double[]). </param> /// <param name="rowsA">The number of rows in the A matrix.</param> /// <param name="columnsA">The number of columns in the A matrix.</param> /// <param name="tau"> /// Contains additional information on Q. Only used for the native solver /// and can be <c>null</c> for the managed provider. /// </param> /// <param name="b">The B matrix.</param> /// <param name="columnsB">The number of columns of B.</param> /// <param name="x">On exit, the solution matrix.</param> /// <param name="method">The type of QR factorization to perform. <seealsocreQRMethod</param> /// <remarks>Rows must be greater or equal to columns.</remarks> public virtual void QRSolveFactored(double[] q, double[] r, int rowsA, int columnsA, double[] tau, double[] b, int columnsB, double[] x, QRMethod method = QRMethod.Full) { if (r == null) { throw new ArgumentNullException(nameof(r)); } if (q == null) { throw new ArgumentNullException(nameof(q)); } if (b == null) { throw new ArgumentNullException(nameof(q)); } if (x == null) { throw new ArgumentNullException(nameof(q)); } if (rowsA < columnsA) { //throw new ArgumentException(Resources.RowsLessThanColumns); } int rowsQ, columnsQ, rowsR, columnsR; if (method == QRMethod.Full) { rowsQ = columnsQ = rowsR = rowsA; columnsR = columnsA; } else { rowsQ = rowsA; columnsQ = rowsR = columnsR = columnsA; } if (r.Length != rowsR * columnsR) { //throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsR*columnsR), nameof(r)); } if (q.Length != rowsQ * columnsQ) { //throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsQ*columnsQ), nameof(q)); } if (b.Length != rowsA * columnsB) { //throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, rowsA*columnsB), nameof(b)); } if (x.Length != columnsA * columnsB) { //throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, columnsA*columnsB), nameof(x)); } var sol = new double[b.Length]; // Copy B matrix to "sol", so B data will not be changed Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfDouble); // Compute Y = transpose(Q)*B var column = new double[rowsA]; for (var j = 0; j < columnsB; j++) { var jm = j * rowsA; Array.Copy(sol, jm, column, 0, rowsA); CommonParallel.For(0, columnsA, (u, v) => { for (var i = u; i < v; i++) { var im = i * rowsA; var sum = 0.0; for (var k = 0; k < rowsA; k++) { sum += q[im + k] * column[k]; } sol[jm + i] = sum; } }); } // Solve R*X = Y; for (var k = columnsA - 1; k >= 0; k--) { var km = k * rowsR; for (var j = 0; j < columnsB; j++) { sol[j * rowsA + k] /= r[km + k]; } for (var i = 0; i < k; i++) { for (var j = 0; j < columnsB; j++) { var jm = j * rowsA; sol[jm + i] -= sol[jm + k] * r[km + i]; } } } // Fill result matrix for (var col = 0; col < columnsB; col++) { Array.Copy(sol, col * rowsA, x, col * columnsA, columnsR); } }
/// <summary> /// Calculates the infinity norm of the vector. /// </summary> /// <returns>The square root of the sum of the squared values.</returns> public override double InfinityNorm() { return(CommonParallel.Aggregate(0, Count, i => At(i).Magnitude, Math.Max, 0f)); }
/// <summary> /// Multiplies two matrices and updates another with the result. <c>c = alpha*op(a)*op(b) + beta*c</c> /// </summary> /// <param name="transposeA">How to transpose the <paramref name="a matrix.</param> /// <param name="transposeB">How to transpose the <paramref name="b matrix.</param> /// <param name="alpha">The value to scale <paramref name="a matrix.</param> /// <param name="a">The a matrix.</param> /// <param name="rowsA">The number of rows in the <paramref name="a matrix.</param> /// <param name="columnsA">The number of columns in the <paramref name="a matrix.</param> /// <param name="b">The b matrix</param> /// <param name="rowsB">The number of rows in the <paramref name="b matrix.</param> /// <param name="columnsB">The number of columns in the <paramref name="b matrix.</param> /// <param name="beta">The value to scale the <paramref name="c matrix.</param> /// <param name="c">The c matrix.</param> public virtual void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, int rowsA, int columnsA, double[] b, int rowsB, int columnsB, double beta, double[] c) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (c == null) { throw new ArgumentNullException(nameof(c)); } if (transposeA != Transpose.DontTranspose) { var swap = rowsA; rowsA = columnsA; columnsA = swap; } if (transposeB != Transpose.DontTranspose) { var swap = rowsB; rowsB = columnsB; columnsB = swap; } if (columnsA != rowsB) { throw new ArgumentOutOfRangeException(string.Format("columnsA ({0}) != rowsB ({1})", columnsA, rowsB)); } if (rowsA * columnsA != a.Length) { throw new ArgumentOutOfRangeException(string.Format("rowsA ({0}) * columnsA ({1}) != a.Length ({2})", rowsA, columnsA, a.Length)); } if (rowsB * columnsB != b.Length) { throw new ArgumentOutOfRangeException(string.Format("rowsB ({0}) * columnsB ({1}) != b.Length ({2})", rowsB, columnsB, b.Length)); } if (rowsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(string.Format("rowsA ({0}) * columnsB ({1}) != c.Length ({2})", rowsA, columnsB, c.Length)); } // handle degenerate cases if (beta == 0.0) { Array.Clear(c, 0, c.Length); } else if (beta != 1.0) { ScaleArray(beta, c, c); } if (alpha == 0.0) { return; } // Extract column arrays var columnDataB = new double[columnsB][]; for (var i = 0; i < columnDataB.Length; i++) { var column = new double[rowsB]; GetColumn(transposeB, i, rowsB, columnsB, b, column); columnDataB[i] = column; } var shouldNotParallelize = rowsA + columnsB + columnsA < Control.ParallelizeOrder || Control.MaxDegreeOfParallelism < 2; if (shouldNotParallelize) { var row = new double[columnsA]; for (var i = 0; i < rowsA; i++) { GetRow(transposeA, i, rowsA, columnsA, a, row); for (var j = 0; j < columnsB; j++) { var col = columnDataB[j]; double sum = 0; for (var ii = 0; ii < row.Length; ii++) { sum += row[ii] * col[ii]; } c[j * rowsA + i] += alpha * sum; } } } else { CommonParallel.For(0, rowsA, 1, (u, v) => { var row = new double[columnsA]; for (var i = u; i < v; i++) { GetRow(transposeA, i, rowsA, columnsA, a, row); for (var j = 0; j < columnsB; j++) { var column = columnDataB[j]; double sum = 0; for (var ii = 0; ii < row.Length; ii++) { sum += row[ii] * column[ii]; } c[j * rowsA + i] += alpha * sum; } } }); } }
/// <summary> /// Multiples two matrices. <c>result = x * y</c> /// </summary> /// <param name="x">The x matrix.</param> /// <param name="rowsX">The number of rows in the x matrix.</param> /// <param name="columnsX">The number of columns in the x matrix.</param> /// <param name="y">The y matrix.</param> /// <param name="rowsY">The number of rows in the y matrix.</param> /// <param name="columnsY">The number of columns in the y matrix.</param> /// <param name="result">Where to store the result of the multiplication.</param> /// <remarks> /// This is a simplified version of the BLAS GEMM routine with alpha /// set to 1.0 and beta set to 0.0, and x and y are not transposed. /// </remarks> public virtual void MatrixMultiply(double[] x, int rowsX, int columnsX, double[] y, int rowsY, int columnsY, double[] result) { if (x == null) { throw new ArgumentNullException(nameof(x)); } if (y == null) { throw new ArgumentNullException(nameof(y)); } if (result == null) { throw new ArgumentNullException(nameof(result)); } if (columnsX != rowsY) { throw new ArgumentOutOfRangeException(string.Format("columnsA ({0}) != rowsB ({1})", columnsX, rowsY)); } if (rowsX * columnsX != x.Length) { throw new ArgumentOutOfRangeException(string.Format("rowsA ({0}) * columnsA ({1}) != a.Length ({2})", rowsX, columnsX, x.Length)); } if (rowsY * columnsY != y.Length) { throw new ArgumentOutOfRangeException(string.Format("rowsB ({0}) * columnsB ({1}) != b.Length ({2})", rowsY, columnsY, y.Length)); } if (rowsX * columnsY != result.Length) { throw new ArgumentOutOfRangeException(string.Format("rowsA ({0}) * columnsB ({1}) != c.Length ({2})", rowsX, columnsY, result.Length)); } // handle degenerate cases Array.Clear(result, 0, result.Length); // Extract column arrays var columnDataB = new double[columnsY][]; for (var i = 0; i < columnDataB.Length; i++) { var column = new double[rowsY]; GetColumn(Transpose.DontTranspose, i, rowsY, columnsY, y, column); columnDataB[i] = column; } var shouldNotParallelize = rowsX + columnsY + columnsX < Control.ParallelizeOrder || Control.MaxDegreeOfParallelism < 2; if (shouldNotParallelize) { var row = new double[columnsX]; for (var i = 0; i < rowsX; i++) { GetRow(Transpose.DontTranspose, i, rowsX, columnsX, x, row); for (var j = 0; j < columnsY; j++) { var col = columnDataB[j]; double sum = 0; for (var ii = 0; ii < row.Length; ii++) { sum += row[ii] * col[ii]; } result[j * rowsX + i] += 1.0 * sum; } } } else { CommonParallel.For(0, rowsX, 1, (u, v) => { var row = new double[columnsX]; for (var i = u; i < v; i++) { GetRow(Transpose.DontTranspose, i, rowsX, columnsX, x, row); for (var j = 0; j < columnsY; j++) { var column = columnDataB[j]; double sum = 0; for (var ii = 0; ii < row.Length; ii++) { sum += row[ii] * column[ii]; } result[j * rowsX + i] += 1.0 * sum; } } }); } }
public override void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, int rowsA, int columnsA, double[] b, int rowsB, int columnsB, double beta, double[] c) { if (a == null) { throw new ArgumentNullException("a"); } if (b == null) { throw new ArgumentNullException("b"); } if (c == null) { throw new ArgumentNullException("c"); } if (transposeA != Transpose.DontTranspose) { Swap(ref rowsA, ref columnsA); } if (transposeB != Transpose.DontTranspose) { Swap(ref rowsB, ref columnsB); } if (columnsA != rowsB) { throw new ArgumentOutOfRangeException(String.Format("columnsA ({0}) != rowsB ({1})", columnsA, rowsB)); } if (rowsA * columnsA != a.Length) { throw new ArgumentOutOfRangeException(String.Format("rowsA ({0}) * columnsA ({1}) != a.Length ({2})", rowsA, columnsA, a.Length)); } if (rowsB * columnsB != b.Length) { throw new ArgumentOutOfRangeException(String.Format("rowsB ({0}) * columnsB ({1}) != b.Length ({2})", rowsB, columnsB, b.Length)); } if (rowsA * columnsB != c.Length) { throw new ArgumentOutOfRangeException(String.Format("rowsA ({0}) * columnsB ({1}) != c.Length ({2})", rowsA, columnsB, c.Length)); } // handle the degenerate cases if (beta == 0.0) { Array.Clear(c, 0, c.Length); } else if (beta != 1.0) { ScaleArray(beta, c, c); } if (alpha == 0.0) { return; } // Extract column arrays var columnDataB = new double[columnsB][]; for (int i = 0; i < columnDataB.Length; i++) { columnDataB[i] = GetColumn(transposeB, i, rowsB, columnsB, b); } var shouldNotParallelize = rowsA + columnsB + columnsA < Control.ParallelizeOrder || Control.MaxDegreeOfParallelism < 2; if (shouldNotParallelize) { for (int i = 0; i < rowsA; i++) { var row = GetRow(transposeA, i, rowsA, columnsA, a); for (int j = 0; j < columnsB; j++) { var col = columnDataB[j]; double sum = 0; for (int ii = 0; ii < row.Length; ii++) { sum += row[ii] * col[ii]; } c[j * rowsA + i] += alpha * sum; } } } else { CommonParallel.For(0, rowsA, 1, (u, v) => { for (int i = u; i < v; i++) { // for each row in a var row = GetRow(transposeA, i, rowsA, columnsA, a); for (int j = 0; j < columnsB; j++) { var column = columnDataB[j]; double sum = 0; for (int ii = 0; ii < row.Length; ii++) { sum += row[ii] * column[ii]; } c[j * rowsA + i] += alpha * sum; } } }); } }
static void CacheObliviousMatrixMultiply(double[] matrixA, int shiftArow, int shiftAcol, double[] matrixB, int shiftBrow, int shiftBcol, double[] result, int shiftCrow, int shiftCcol, int m, int n, int k, int constM, int constN, int constK, int level) { if (m + n <= Control.ParallelizeOrder) { fixed(double *resultPtr = &result[0]) fixed(double *aPtr = &matrixA[0]) fixed(double *bPtr = &matrixB[0]) { double *a = aPtr + shiftArow; double *c = resultPtr + shiftCrow; for (var m1 = 0; m1 < m; m1++) { for (var n1 = 0; n1 < n; ++n1) { double *b = bPtr + (n1 + shiftBcol) * constK + shiftBrow; double sum = 0; for (var k1 = 0; k1 < k; ++k1) { sum += a[((k1 + shiftAcol) * constM)] * b[k1]; } c[((n1 + shiftCcol) * constM)] += sum; } a++; c++; } } return; } // divide and conquer int m2 = m / 2, n2 = n / 2, k2 = k / 2; level++; if (level <= 2) { CommonParallel.Invoke( () => CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol, matrixB, shiftBrow, shiftBcol, result, shiftCrow, shiftCcol, m2, n2, k2, constM, constN, constK, level), () => CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol, matrixB, shiftBrow, shiftBcol + n2, result, shiftCrow, shiftCcol + n2, m2, n - n2, k2, constM, constN, constK, level), () => CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol, matrixB, shiftBrow, shiftBcol, result, shiftCrow + m2, shiftCcol, m - m2, n2, k2, constM, constN, constK, level), () => CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol, matrixB, shiftBrow, shiftBcol + n2, result, shiftCrow + m2, shiftCcol + n2, m - m2, n - n2, k2, constM, constN, constK, level)); CommonParallel.Invoke( () => CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol, result, shiftCrow, shiftCcol, m2, n2, k - k2, constM, constN, constK, level), () => CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol + n2, result, shiftCrow, shiftCcol + n2, m2, n - n2, k - k2, constM, constN, constK, level), () => CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol, result, shiftCrow + m2, shiftCcol, m - m2, n2, k - k2, constM, constN, constK, level), () => CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol + n2, result, shiftCrow + m2, shiftCcol + n2, m - m2, n - n2, k - k2, constM, constN, constK, level)); } else { CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol, matrixB, shiftBrow, shiftBcol, result, shiftCrow, shiftCcol, m2, n2, k2, constM, constN, constK, level); CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol, matrixB, shiftBrow, shiftBcol + n2, result, shiftCrow, shiftCcol + n2, m2, n - n2, k2, constM, constN, constK, level); CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol, result, shiftCrow, shiftCcol, m2, n2, k - k2, constM, constN, constK, level); CacheObliviousMatrixMultiply(matrixA, shiftArow, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol + n2, result, shiftCrow, shiftCcol + n2, m2, n - n2, k - k2, constM, constN, constK, level); CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol, matrixB, shiftBrow, shiftBcol, result, shiftCrow + m2, shiftCcol, m - m2, n2, k2, constM, constN, constK, level); CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol, matrixB, shiftBrow, shiftBcol + n2, result, shiftCrow + m2, shiftCcol + n2, m - m2, n - n2, k2, constM, constN, constK, level); CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol, result, shiftCrow + m2, shiftCcol, m - m2, n2, k - k2, constM, constN, constK, level); CacheObliviousMatrixMultiply(matrixA, shiftArow + m2, shiftAcol + k2, matrixB, shiftBrow + k2, shiftBcol + n2, result, shiftCrow + m2, shiftCcol + n2, m - m2, n - n2, k - k2, constM, constN, constK, level); } }
/// <summary> /// Learns a model that can map the given inputs to the given outputs. /// </summary> /// <param name="trainer">The learning algorithm.</param> /// <param name="numberOfClasses">The number of classes.</param> /// <param name="x">The input vectors <paramref name="x"/>.</param> /// <param name="y">The expected binary output <paramref name="y"/>.</param> /// <param name="weights">The <c>weight</c> of importance for each input vector (if supported by the learning algorithm).</param> /// <param name="cancellationToken">The cancellationToken token used to notify the machine that the operation should be canceled.</param> /// <exception cref="ArgumentNullException"> /// <para><paramref name="trainer"/> is <b>null</b>.</para> /// <para>-or-</para> /// <para><paramref name="x"/> is <b>null</b>.</para> /// <para>-or-</para> /// <para><paramref name="y"/> is <b>null</b>.</para> /// </exception> /// <exception cref="ArgumentException"> /// <para><paramref name="numberOfClasses"/> is less than 2.</para> /// <para>-or-</para> /// <para>The number of elements in <paramref name="y"/> does not match the number of elements in <paramref name="x"/>.</para> /// <para>-or-</para> /// <para><paramref name="weights"/> is not <b>null</b> and the number of elements in <paramref name="weights"/> does not match the number of elements in <paramref name="x"/>.</para> /// </exception> /// <returns> /// The <see cref="OneVsAllSupportVectorMachine"/> learned by this method. /// A model that has learned how to produce <paramref name="y"/> given <paramref name="x"/>. /// </returns> public static OneVsAllSupportVectorMachine Learn( ISupportVectorMachineLearning trainer, int numberOfClasses, IList <float[]> x, IList <int> y, IList <float> weights, CancellationToken cancellationToken) { if (trainer == null) { throw new ArgumentNullException(nameof(trainer)); } if (x == null) { throw new ArgumentNullException(nameof(x)); } if (y == null) { throw new ArgumentNullException(nameof(y)); } if (numberOfClasses < 2) { throw new ArgumentException("The machine must have at least two classes.", nameof(numberOfClasses)); } if (y.Count != x.Count) { throw new ArgumentException("The number of output labels must match the number of input vectors.", nameof(y)); } // create the machines SupportVectorMachine[] machines = new SupportVectorMachine[numberOfClasses]; // train each machine int sampleCount = x.Count; CommonParallel.For( 0, machines.Length, (a, b) => { for (int i = a; i < b; i++) { bool[] expected = new bool[sampleCount]; for (int j = 0; j < sampleCount; j++) { expected[j] = y[j] == i; } machines[i] = SupportVectorMachine.Learn(trainer, x, expected, weights, cancellationToken); } }, new ParallelOptions() { CancellationToken = cancellationToken, }); return(new OneVsAllSupportVectorMachine(machines)); }