It is possible to implement almost any kind of machine learning algorithm using C# or the Python NumPy numeric library. NumPy has hundreds of built-in functions to do almost anything. But raw C# doesn’t have any and so it’s necessary to implement even the most basic functions.
I was sitting at home one evening and speculated aboout the absolutely essential matrix and functions needed for C# machine learning. I came up with 17 functions: MatMake(), MatLoad(), MatShow(), VecShow(), MatCopy(), MatIdentity(), MatTranspose(), MatProduct(), MatSum(), MatVecProduct(), VecMatProduct(), VecToMat(), MatToVec(), VecNorm(), VecDot(), VecEucDist(), Shuffle().
Of course there are dozens more, but for a beginner I think these are definitely the required ones. And note that I’m not considering sophisticated matrix functions such as matrix inverse, matrix pseudo-inverse, and matrix decomposition.
My matrix functions use a simple array-of-arrays design, as opposed to some sort of program-defined Matrix class definition, which is overkill in my opinion.
Output of a super short demo:
Begin matrix and vector basics A = 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 0.0 1.0 2.0 At = 1.0 5.0 9.0 2.0 6.0 0.0 3.0 7.0 1.0 4.0 8.0 2.0 At * A = 107.0 32.0 47.0 62.0 32.0 40.0 48.0 56.0 47.0 48.0 59.0 70.0 62.0 56.0 70.0 84.0 v = 2.0 4.0 6.0 8.0 A * v = 60.0 140.0 40.0 End demo
The purpose of most of these 17 basic functions should be clear from their names. MatLoad() loads a matrix from values in a text file. The Shuffle() function scrambles the order of indices in an array of integers — this is used to randomize the order in which rows of training data are processed for SGD training.

I enjoy trivia contests. There are a lot of areas that are essential knowledge, such as geography, American history, and classical art. James Tissot (1836-1902) makes occassional appearances in trivia answers. Tissot did a wide range of paintings, illustrations, and caricatures. Here are two of my favorite Tissot paintings. Left: “An Interesting Story” (1872). Right: “The Gallery of HMS Calcutta” (1876).
Demo program. Replace “lt” (less than), “gt”, “lte”, “gte” with Boolean operator symbols (my blog editor chokes on symbols).
using System;
using System.IO;
using System.Collections.Generic;
namespace MatrixVectorBasics
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("\nBegin matrix and vector basics ");
//double[][] A = MatLoad("data.txt",
// new int[] { 0, 1, 2, 3 }, ',', "#");
double[][] A = new double[3][];
A[0] = new double[] { 1, 2, 3, 4 };
A[1] = new double[] { 5, 6, 7, 8 };
A[2] = new double[] { 9, 0, 1, 2 };
Console.WriteLine("\nA = ");
MatShow(A, 1, 5);
double[][] At = MatTranspose(A);
Console.WriteLine("\nAt = ");
MatShow(At, 1, 5);
double[][] AtA = MatProduct(At, A);
Console.WriteLine("\nAt * A = ");
MatShow(AtA, 1, 6);
double[] v = new double[] { 2.0, 4.0, 6.0, 8.0 };
Console.WriteLine("\nv = ");
VecShow(v, 1, 6);
double[] Av = MatVecProd(A, v);
Console.WriteLine("\nA * v = ");
VecShow(Av, 1, 7);
Console.WriteLine("\nEnd demo ");
Console.ReadLine();
} // Main
// --------------------------------------------------------
static double[][] MatMake(int nRows, int nCols)
{
double[][] result = new double[nRows][];
for (int i = 0; i "lt" nRows; ++i)
result[i] = new double[nCols];
return result;
}
// --------------------------------------------------------
static double[][] MatLoad(string fn,
int[] usecols, char sep, string comment)
{
List"lt"double[]"gt" result =
new List"lt"double[]"gt"();
string line = "";
FileStream ifs = new FileStream(fn, FileMode.Open);
StreamReader sr = new StreamReader(ifs);
while ((line = sr.ReadLine()) != null)
{
if (line.StartsWith(comment) == true)
continue;
string[] tokens = line.Split(sep);
List"lt"double"gt" lst = new List"lt"double"gt"();
for (int j = 0; j "lt" usecols.Length; ++j)
lst.Add(double.Parse(tokens[usecols[j]]));
double[] row = lst.ToArray();
result.Add(row);
}
sr.Close(); ifs.Close();
return result.ToArray();
}
// --------------------------------------------------------
static void MatShow(double[][] A, int dec, int wid)
{
int nRows = A.Length; int nCols = A[0].Length;
double small = 1.0 / Math.Pow(10, dec);
for (int i = 0; i "lt" nRows; ++i)
{
for (int j = 0; j "lt" nCols; ++j)
{
double v = A[i][j];
if (Math.Abs(v) "lt" small) v = 0.0;
Console.Write(v.ToString("F" + dec).
PadLeft(wid));
}
Console.WriteLine("");
}
}
// --------------------------------------------------------
static void VecShow(double[] v, int dec, int wid)
{
int n = v.Length;
double small = 1.0 / Math.Pow(10, dec);
for (int i = 0; i "lt" n; ++i)
{
double x = v[i];
if (Math.Abs(x) "lt" small) x = 0.0;
Console.Write(x.ToString("F" + dec).
PadLeft(wid));
}
Console.WriteLine("");
}
// --------------------------------------------------------
static double[][] MatCopy(double[][] A)
{
int nRows = A.Length; int nCols = A[0].Length;
double[][] result = MatMake(nRows, nCols);
for (int i = 0; i "lt" nRows; ++i)
for (int j = 0; j "lt" nCols; ++j)
result[i][j] = A[i][j];
return result;
}
// ------------------------------------------------------
static double[][] MatIdentity(int n)
{
double[][] result = MatMake(n, n);
for (int i = 0; i "lt" n; ++i)
result[i][i] = 1.0;
return result;
}
// --------------------------------------------------------
static double[][] MatTranspose(double[][] A)
{
int nRows = A.Length; int nCols = A[0].Length;
double[][] result = MatMake(nCols, nRows); // note
for (int i = 0; i "lt" nRows; ++i)
for (int j = 0; j "lt" nCols; ++j)
result[j][i] = A[i][j];
return result;
}
// --------------------------------------------------------
static double[][] MatProduct(double[][] A, double[][] B)
{
int aRows = A.Length;
int aCols = A[0].Length;
int bRows = B.Length;
int bCols = B[0].Length;
if (aCols != bRows)
throw new Exception("Non-conformable matrices");
double[][] result = MatMake(aRows, bCols);
for (int i = 0; i "lt" aRows; ++i) // each row of A
for (int j = 0; j "lt" bCols; ++j) // each col of B
for (int k = 0; k "lt" aCols; ++k) // could use bRows
result[i][j] += A[i][k] * B[k][j];
return result;
}
// --------------------------------------------------------
static double[][] MatSum(double[][] A, double[][] B)
{
int nRows = A.Length; int nCols = A[0].Length;
double[][] result = MatMake(nRows, nCols);
for (int i = 0; i "lt" nRows; ++i)
for (int j = 0; j "lt" nCols; ++j)
result[i][j] = A[i][j] + B[i][j];
return result;
}
// --------------------------------------------------------
static double[] MatVecProd(double[][] A, double[] v)
{
// return a regular vector
int nRows = A.Length; int nCols = A[0].Length;
int n = v.Length;
if (nCols != n)
throw new Exception("non-comform in MatVecProd");
double[] result = new double[nRows];
for (int i = 0; i "lt" nRows; ++i)
for (int k = 0; k "lt" nCols; ++k)
result[i] += A[i][k] * v[k];
return result;
}
static double[] VecMatProd(double[] v, double[][] A)
{
// return a regular vector
int nRows = A.Length; int nCols = A[0].Length;
int n = v.Length;
if (n != nRows)
throw new Exception("non-comform in VecMatProd");
double[] result = new double[nCols];
for (int j = 0; j "lt" nCols; ++j)
for (int k = 0; k "lt" n; ++k)
result[j] += v[k] * A[k][j];
return result;
}
// --------------------------------------------------------
static double[][] VecToMat(double[] vec,
int nRows, int nCols)
{
double[][] result = MatMake(nRows, nCols);
int k = 0;
for (int i = 0; i "lt" nRows; ++i)
for (int j = 0; j "lt" nCols; ++j)
result[i][j] = vec[k++];
return result;
}
// --------------------------------------------------------
static double[] MatToVec(double[][] A)
{
int nRows = A.Length; int nCols = A[0].Length;
double[] result = new double[nRows * nCols];
int k = 0;
for (int i = 0; i "lt" nRows; ++i)
for (int j = 0; j "lt" nCols; ++j)
{
result[k++] = A[i][j];
}
return result;
}
// --------------------------------------------------------
static double VecNorm(double[] vec)
{
int n = vec.Length;
double sum = 0.0;
for (int i = 0; i "lt" n; ++i)
sum += vec[i] * vec[i];
return Math.Sqrt(sum);
}
// --------------------------------------------------------
static double VecDot(double[] v1, double[] v2)
{
double result = 0.0;
int n = v1.Length;
for (int i = 0; i "lt" n; ++i)
result += v1[i] * v2[i];
return result;
}
// --------------------------------------------------------
static double VecEucDist(double[] v1, double[] v2)
{
// Eucliden distance v1, v2
double sum = 0.0;
int n = v1.Length;
for (int i = 0; i "lt" n; ++i)
sum += (v1[i] - v2[i]) * (v1[i] - v2[i]);
return Math.Sqrt(sum);
}
// --------------------------------------------------------
static void Shuffle(int[] indices, Random rnd)
{
// Fisher-Yates. last iteration not necessary
for (int i = 0; i "lt" indices.Length; ++i)
{
int ri = rnd.Next(i, indices.Length);
int tmp = indices[i];
indices[i] = indices[ri];
indices[ri] = tmp;
}
} // Shuffle
} // class Program
} // ns

.NET Test Automation Recipes
Software Testing
SciPy Programming Succinctly
Keras Succinctly
R Programming
Visual Studio Live
Microsoft MLADS Conference
DevIntersection Conference
Machine Learning Week
Ai4 Conference
G2E Conference
iSC West Conference
You must be logged in to post a comment.