private GenTensor <SafeDivisionWrapper <T> > InnerGaussianEliminationSafeDivision(int n) { InitIfNotInitted(); var elemMatrix = GenTensor <SafeDivisionWrapper <T> > .CreateMatrix(n, n, (x, y) => new SafeDivisionWrapper <T>(ConstantsAndFunctions <T> .Forward(this.GetValueNoCheck(x, y))) ); for (int k = 1; k < n; k++) { for (int j = k; j < n; j++) { var m = ConstantsAndFunctions <SafeDivisionWrapper <T> > .Divide( elemMatrix.GetValueNoCheck(j, k - 1), elemMatrix.GetValueNoCheck(k - 1, k - 1) ); for (int i = 0; i < n; i++) { var curr = elemMatrix.GetValueNoCheck(j, i); elemMatrix.SetValueNoCheck(ConstantsAndFunctions <SafeDivisionWrapper <T> > .Subtract( curr, ConstantsAndFunctions <SafeDivisionWrapper <T> > .Multiply( m, elemMatrix.GetValueNoCheck(k - 1, i) ) ), j, i); } } } return(elemMatrix); }
internal T DeterminantLaplace(int diagLength) { if (diagLength == 1) { return(ConstantsAndFunctions <T> .Forward(this.GetValueNoCheck(0, 0))); } var det = ConstantsAndFunctions <T> .CreateZero(); var sign = ConstantsAndFunctions <T> .CreateOne(); var temp = SquareMatrixFactory <T> .GetMatrix(diagLength); for (int i = 0; i < diagLength; i++) { GetCofactor(this, temp, 0, i, diagLength); det = ConstantsAndFunctions <T> .Add(det, ConstantsAndFunctions <T> .Multiply( sign, ConstantsAndFunctions <T> .Multiply( this.GetValueNoCheck(0, i), temp.DeterminantLaplace(diagLength - 1) )) ); sign = ConstantsAndFunctions <T> .Negate(sign); } return(det); }
/// <summary> /// Inverts a matrix A to B so that A * B = I /// Borrowed from here: https://www.geeksforgeeks.org/adjoint-inverse-matrix/ /// /// O(N^5) /// </summary> public void InvertMatrix() { #if ALLOW_EXCEPTIONS if (!IsSquareMatrix) { throw new InvalidShapeException("this should be a square matrix"); } #endif var diagLength = Shape.shape[0]; var det = DeterminantGaussianSafeDivision(); #if ALLOW_EXCEPTIONS if (ConstantsAndFunctions <T> .IsZero(det)) { throw new InvalidDeterminantException("Cannot invert a singular matrix"); } #endif var adj = Adjoint(); for (int x = 0; x < diagLength; x++) { for (int y = 0; y < diagLength; y++) { this.SetValueNoCheck( ConstantsAndFunctions <T> .Divide( adj.GetValueNoCheck(x, y), det ), x, y ); } } }
internal void Assign(GenTensor <T> genTensor) { foreach (var(index, value) in genTensor.Iterate()) { this.SetValueNoCheck(ConstantsAndFunctions <T> .Forward(value), index); } }
/// <summary> /// Finds a perpendicular vector to two given /// TODO: So far only implemented for 3D vectors /// </summary> public static GenTensor <T> VectorCrossProduct(GenTensor <T> a, GenTensor <T> b) { #if ALLOW_EXCEPTIONS if (!a.IsVector || !b.IsVector) { throw new InvalidShapeException($"Both {nameof(a)} and {nameof(b)} should be vectors"); } if (a.Shape[0] != b.Shape[0]) { throw new InvalidShapeException($"Length of {nameof(a)} and {nameof(b)} should be equal"); } if (a.Shape[0] != 3) { throw new NotImplementedException("Other than vectors of the length of 3 aren't supported for VectorCrossProduct yet"); } #endif return(GenTensor <T> .CreateVector( ConstantsAndFunctions <T> .Subtract( ConstantsAndFunctions <T> .Multiply(a[1], b[2]), ConstantsAndFunctions <T> .Multiply(a[2], b[1])), ConstantsAndFunctions <T> .Subtract( ConstantsAndFunctions <T> .Multiply(a[2], b[0]), ConstantsAndFunctions <T> .Multiply(a[0], b[2])), ConstantsAndFunctions <T> .Subtract( ConstantsAndFunctions <T> .Multiply(a[0], b[1]), ConstantsAndFunctions <T> .Multiply(a[1], b[0])) )); }
/// <summary> /// Finds matrix multiplication result /// a and b are matrices /// a.Shape[1] should be equal to b.Shape[0] /// the resulting matrix is [a.Shape[0] x b.Shape[1]] shape /// /// O(N^3) /// </summary> public static GenTensor <T> MatrixMultiply(GenTensor <T> a, GenTensor <T> b) { #if ALLOW_EXCEPTIONS if (!a.IsMatrix || !b.IsMatrix) { throw new InvalidShapeException($"Both {nameof(a)} and {nameof(b)} should be matrices"); } if (a.Shape[1] != b.Shape[0]) { throw new InvalidShapeException($"{nameof(a)}'s height must be equal to {nameof(b)}'s width"); } #endif var width = a.Shape[0]; var height = b.Shape[1]; var row = a.Shape[1]; var res = CreateMatrix(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { var s = ConstantsAndFunctions <T> .CreateZero(); for (int i = 0; i < row; i++) { var v1 = a.GetValueNoCheck(x, i); var v2 = b.GetValueNoCheck(i, y); s = ConstantsAndFunctions <T> .Add(s, ConstantsAndFunctions <T> .Multiply(v1, v2)); } res.SetValueNoCheck(s, x, y); } } return(res); }
/// <summary> /// You might need it to make sure you don't copy /// your data but recreate a wrapper (if have one) /// /// O(V) /// </summary> public GenTensor <T> Forward() { var res = new GenTensor <T>(Shape); foreach (var index in res.IterateOverElements()) { res.SetValueNoCheck(ConstantsAndFunctions <T> .Forward(GetValueNoCheck(index)), index); } return(res); }
/// <summary> /// [i, j, k...]th element of the resulting tensor is /// operation(a[i, j, k...], b[i, j, k...]) /// </summary> public static GenTensor <T> Zip(GenTensor <T> a, GenTensor <T> b, Func <T, T, T> operation) { #if ALLOW_EXCEPTIONS if (a.Shape != b.Shape) { throw new InvalidShapeException("Arguments should be of the same shape"); } #endif var res = new GenTensor <T>(a.Shape); if (res.Shape.shape.Length == 1) { for (int x = 0; x < res.Shape.shape[0]; x++) { res.Data[x] = ConstantsAndFunctions <T> .Forward( operation(a.GetValueNoCheck(x), b.GetValueNoCheck(x))); } } else if (res.Shape.shape.Length == 2) { for (int x = 0; x < res.Shape.shape[0]; x++) { for (int y = 0; y < res.Shape.shape[1]; y++) { res.Data[x * res.Blocks[0] + y] = ConstantsAndFunctions <T> .Forward( operation(a.GetValueNoCheck(x, y), b.GetValueNoCheck(x, y))); } } } else if (res.Shape.shape.Length == 3) { for (int x = 0; x < res.Shape.shape[0]; x++) { for (int y = 0; y < res.Shape.shape[1]; y++) { for (int z = 0; z < res.Shape.shape[2]; z++) { res.Data[x * res.Blocks[0] + y * res.Blocks[1] + z] = ConstantsAndFunctions <T> .Forward( operation(a.GetValueNoCheck(x, y, z), b.GetValueNoCheck(x, y, z))); } } } } else { foreach (var index in res.IterateOverElements()) { res.SetValueNoCheck(ConstantsAndFunctions <T> .Forward( operation(a.GetValueNoCheck(index), b.GetValueNoCheck(index))), index); } } return(res); }
// TODO: how to avoid code duplication? /// <summary> /// Performs simple Gaussian elimination method on a tensor /// /// O(N^3) /// </summary> public T DeterminantGaussianSimple() { #if ALLOW_EXCEPTIONS if (!IsMatrix) { throw new InvalidShapeException("this should be matrix"); } if (Shape[0] != Shape[1]) { throw new InvalidShapeException("this should be square matrix"); } #endif if (Shape[0] == 1) { return(ConstantsAndFunctions <T> .Forward(this.GetValueNoCheck(0, 0))); } var n = Shape[0]; var elemMatrix = this.Forward(); for (int k = 1; k < n; k++) { for (int j = k; j < n; j++) { var m = ConstantsAndFunctions <T> .Divide( ConstantsAndFunctions <T> .Forward(elemMatrix.GetValueNoCheck(j, k - 1)), ConstantsAndFunctions <T> .Forward(elemMatrix.GetValueNoCheck(k - 1, k - 1)) ); for (int i = 0; i < n; i++) { var curr = ConstantsAndFunctions <T> .Forward(elemMatrix.GetValueNoCheck(j, i)); elemMatrix.SetValueNoCheck(ConstantsAndFunctions <T> .Subtract( curr, ConstantsAndFunctions <T> .Multiply( m, elemMatrix.GetValueNoCheck(k - 1, i) ) ), j, i); } } } var det = ConstantsAndFunctions <T> .CreateOne(); for (int i = 0; i < n; i++) { det = ConstantsAndFunctions <T> .Multiply(det, elemMatrix.GetValueNoCheck(i, i)); } return(det); }
public bool Equals(GenTensor <T> obj) { if (obj.Shape != Shape) { return(false); } foreach (var(index, _) in obj.Iterate()) { if (!ConstantsAndFunctions <T> .AreEqual(this.GetValueNoCheck(index), obj.GetValueNoCheck(index))) { return(false); } } return(true); }
/// <summary> /// Creates an indentity matrix whose width and height are equal to diag /// 1 is achieved with TWrapper.SetOne() /// 0 is achieved with TWrapper.SetZero() /// </summary> public static GenTensor <T> CreateIdentityMatrix(int diag) { var res = new GenTensor <T>(diag, diag); for (int i = 0; i < res.Data.Length; i++) { res.Data[i] = ConstantsAndFunctions <T> .CreateZero(); } for (int i = 0; i < diag; i++) { res.SetValueNoCheck(ConstantsAndFunctions <T> .CreateOne, i, i); } return(res); }
private static void InitIfNotInitted() { if (isFracInitted) { return; } isFracInitted = true; ConstantsAndFunctions <SafeDivisionWrapper <T> > .Add = (a, b) => new SafeDivisionWrapper <T>( ConstantsAndFunctions <T> .Add( ConstantsAndFunctions <T> .Multiply(a.num, b.den), ConstantsAndFunctions <T> .Multiply(a.den, b.num) ), ConstantsAndFunctions <T> .Multiply(a.den, b.den) ); ConstantsAndFunctions <SafeDivisionWrapper <T> > .Subtract = (a, b) => new SafeDivisionWrapper <T>( ConstantsAndFunctions <T> .Subtract( ConstantsAndFunctions <T> .Multiply(a.num, b.den), ConstantsAndFunctions <T> .Multiply(a.den, b.num) ), ConstantsAndFunctions <T> .Multiply(a.den, b.den) ); ConstantsAndFunctions <SafeDivisionWrapper <T> > .Multiply = (a, b) => new SafeDivisionWrapper <T>( ConstantsAndFunctions <T> .Multiply(a.num, b.num), ConstantsAndFunctions <T> .Multiply(a.den, b.den) ); ConstantsAndFunctions <SafeDivisionWrapper <T> > .Divide = (a, b) => new SafeDivisionWrapper <T>( ConstantsAndFunctions <T> .Multiply(a.num, b.den), ConstantsAndFunctions <T> .Multiply(a.den, b.num) ); ConstantsAndFunctions <SafeDivisionWrapper <T> > .CreateOne = () => new SafeDivisionWrapper <T>(ConstantsAndFunctions <T> .CreateOne()); }
public static GenTensor <T> Concat(GenTensor <T> a, GenTensor <T> b) { #if ALLOW_EXCEPTIONS if (a.Shape.SubShape(1, 0) != b.Shape.SubShape(1, 0)) { throw new InvalidShapeException("Excluding the first dimension, all others should match"); } #endif if (a.IsVector) { var resultingVector = GenTensor <T> .CreateVector(a.Shape.shape[0] + b.Shape.shape[0]); for (int i = 0; i < a.Shape.shape[0]; i++) { resultingVector.SetValueNoCheck(ConstantsAndFunctions <T> .Forward(a.GetValueNoCheck(i)), i); } for (int i = 0; i < b.Shape.shape[0]; i++) { resultingVector.SetValueNoCheck(ConstantsAndFunctions <T> .Forward(b.GetValueNoCheck(i)), i + a.Shape.shape[0]); } return(resultingVector); } else { var newShape = a.Shape.Copy(); newShape.shape[0] = a.Shape.shape[0] + b.Shape.shape[0]; var res = new GenTensor <T>(newShape); for (int i = 0; i < a.Shape.shape[0]; i++) { res.SetSubtensor(a.GetSubtensor(i), i); } for (int i = 0; i < b.Shape.shape[0]; i++) { res.SetSubtensor(b.GetSubtensor(i), i + a.Shape.shape[0]); } return(res); } }
/// <summary> /// Copies a tensor calling each cell with a .Copy() /// /// O(V) /// </summary> public GenTensor <T> Copy(bool copyElements) { var res = new GenTensor <T>(Shape); if (!copyElements) { foreach (var index in res.IterateOverElements()) { res.SetValueNoCheck(ConstantsAndFunctions <T> .Forward(GetValueNoCheck(index)), index); } } else { foreach (var index in res.IterateOverElements()) { res.SetValueNoCheck(ConstantsAndFunctions <T> .Copy(GetValueNoCheck(index)), index); } } return(res); }
/// <summary> /// Returns adjugate matrix /// /// O(N^5) /// </summary> public GenTensor <T> Adjoint() { #if ALLOW_EXCEPTIONS if (!IsSquareMatrix) { throw new InvalidShapeException("Matrix should be square"); } #endif var diagLength = Shape.shape[0]; var res = GenTensor <T> .CreateSquareMatrix(diagLength); var temp = SquareMatrixFactory <T> .GetMatrix(diagLength); if (diagLength == 1) { res.SetValueNoCheck(ConstantsAndFunctions <T> .CreateOne(), 0, 0); return(res); } var toNegate = false; for (int x = 0; x < diagLength; x++) { for (int y = 0; y < diagLength; y++) { GetCofactor(this, temp, x, y, diagLength); toNegate = (x + y) % 2 == 1; var det = temp.DeterminantGaussianSafeDivision(diagLength - 1); if (toNegate) { res.SetValueNoCheck(ConstantsAndFunctions <T> .Negate(det), y, x); } else { res.SetValueNoCheck(det, y, x); } } } return(res); }
/// <summary> /// Finds the scalar product of two vectors /// /// O(N) /// </summary> public static T VectorDotProduct(GenTensor <T> a, GenTensor <T> b) { #if ALLOW_EXCEPTIONS if (!a.IsVector || !b.IsVector) { throw new InvalidShapeException($"{nameof(a)} and {nameof(b)} should be vectors"); } if (a.Shape[0] != b.Shape[0]) { throw new InvalidShapeException($"{nameof(a)}'s length should be the same as {nameof(b)}'s"); } #endif var res = ConstantsAndFunctions <T> .CreateZero(); for (int i = 0; i < a.Shape[0]; i++) { res = ConstantsAndFunctions <T> .Add(res, ConstantsAndFunctions <T> .Multiply(a.GetValueNoCheck(i), b.GetValueNoCheck(i))); } return(res); }
/// <summary> /// Finds Determinant with possible overflow /// because it uses fractions for avoiding division /// /// O(N^3) /// </summary> internal T DeterminantGaussianSafeDivision(int diagLength) { InitIfNotInitted(); #if ALLOW_EXCEPTIONS if (!IsMatrix) { throw new InvalidShapeException("this should be matrix"); } if (Shape[0] != Shape[1]) { throw new InvalidShapeException("this should be square matrix"); } #endif if (Shape[0] == 1) { return(ConstantsAndFunctions <T> .Forward(this.GetValueNoCheck(0, 0))); } var n = diagLength; var elemMatrix = InnerGaussianEliminationSafeDivision(n); var det = ConstantsAndFunctions <SafeDivisionWrapper <T> > .CreateOne(); for (int i = 0; i < n; i++) { det = ConstantsAndFunctions <SafeDivisionWrapper <T> > .Multiply(det, elemMatrix.GetValueNoCheck(i, i)); } if (ConstantsAndFunctions <T> .IsZero(det.den)) { return(ConstantsAndFunctions <T> .CreateZero()); } return(det.Count()); }
public static GenTensor <T> PiecewiseDivide( T a, GenTensor <T> b) => CreateTensor(b.Shape, ind => ConstantsAndFunctions <T> .Divide(a, b[ind]));
public SafeDivisionWrapper(W val) { num = val; den = ConstantsAndFunctions <W> .CreateOne(); }
public static GenTensor <T> PiecewiseSubtract(GenTensor <T> a, T b) => CreateTensor(a.Shape, ind => ConstantsAndFunctions <T> .Subtract(a[ind], b));
public static GenTensor <T> PiecewiseSubtract( T a, GenTensor <T> b) => CreateTensor(b.Shape, ind => ConstantsAndFunctions <T> .Subtract(a, b[ind]));
public static GenTensor <T> PiecewiseMultiply(GenTensor <T> a, T b) => CreateTensor(a.Shape, ind => ConstantsAndFunctions <T> .Multiply(a[ind], b));
public W Count() => ConstantsAndFunctions <W> .Divide(num, den);
public static GenTensor <T> PiecewiseDivide(GenTensor <T> a, T b) => CreateTensor(a.Shape, ind => ConstantsAndFunctions <T> .Divide(a[ind], b));