private static NdArray <T> DotLegacy2x2 <T>(INdArray <T> x, INdArray <T> y) { // x.Shape = [m, p] // y.Shape = [p, n] // retval.Shape = [m, n] var p = x.Shape[1]; var m = x.Shape[0]; var n = y.Shape[1]; Guard.AssertShapeMatch(p == y.Shape[0], $"x.Shape[1] = {p}, y.Shape[0] = {y.Shape[0]}"); var rawImpl = new RawNdArrayImpl <T>(new IndexArray(m, n)); var buffer = rawImpl.Buffer; for (var i = 0; i < m; ++i) { for (var j = 0; j < n; ++j) { buffer.Span[n * i + j] = Zero <T>(); for (var k = 0; k < p; ++k) { buffer.Span[n * i + j] = Add(buffer.Span[n * i + j], Multiply(x[i, k], y[k, j])); } } } return(new NdArray <T>(rawImpl)); }
/// <summary> /// Applies identity operations for a n-d array. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="value"></param> /// <param name="result"> It can be same instance with <paramref name="value"/>, and in that case the instance will be overwritten. </param> /// <exception cref="ShapeMismatchException" /> /// <exception cref="NotSupportedException" /> public static void Identity <T>(INdArray <T> value, MutableNdArray <T> result) { Guard.AssertShapeMatch(value.Shape == result.Shape, "There is shape mismatch."); if (value.TryGetBufferImpl(out var xvalue) && result is RawNdArray <T> xresult) { Identity(xvalue.Buffer, xresult.Entity.Buffer); }
/// <summary> /// [Legacy] Evaluates dot operation of vector/matrix lazily. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="x"> [<c>x.Rank == 1 || x.Rank == 2</c>] </param> /// <param name="y"> [<c>y.Rank == 1 || y.Rank == 2</c>] </param> /// <returns> /// <para> The result of dot operation of <paramref name="x"/> and <paramref name="y"/>. </para> /// <para> - If <c>x.Shape == {p} && y.Rank == {p}</c>, then <c>$ReturnValue.Shape == {1}</c>. </para> /// <para> - If <c>x.Shape == {p} && y.Shape == {p, n}</c>, then <c>$ReturnValue.Shape == {n}</c>. </para> /// <para> - If <c>x.Shape == {m, p} && y.Shape == {p}</c>, then <c>$ReturnValue.Shape == {m}</c>. </para> /// <para> - If <c>x.Shape == {m, p} && y.Shape == {p, n}</c>, then <c>$ReturnValue.Shape == {m, n}</c>. </para> /// <para> - If the shape patterns does not match with above patterns, throw <see cref="ShapeMismatchException"/>. </para> /// </returns> /// <exception cref="ShapeMismatchException"></exception> public static NdArray <T> DotLegacy <T>(this INdArray <T> x, INdArray <T> y) { if (x.Rank == 1) { if (y.Rank == 1) { return(DotLegacy1x1(x, y)); } if (y.Rank == 2) { return(DotLegacy1x2(x, y)); } } else if (x.Rank == 2) { if (y.Rank == 1) { return(DotLegacy2x1(x, y)); } if (y.Rank == 2) { return(DotLegacy2x2(x, y)); } } Guard.ThrowShapeMismatch($"The ranks of x and y must be 1 or 2. (x.Rank={x.Rank}, y.Rank={y.Rank})"); throw new NotSupportedException(); }
/// <summary> /// Solves simultaneous linear equations. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="a"> [<c>a.Rank == 2 && a.Shape[0] == a.Shape[1]</c>] </param> /// <param name="b"> [<c>(b.Rank == 1 || b.Rank == 2) && b.Shape[0] == a.Shape[0]</c>] </param> /// <returns></returns> /// <exception cref="ShapeMismatchException"> /// <para> When <c>n := a.Shape[0]</c>, </para> /// <para> - If <c>b.Shape == {n}</c>, then <c>$ReturnValue.Shape == {n}</c>. </para> /// <para> /// - If <c>b.Shape == {n, p}</c>, then <c>$ReturnValue.Shape == {n, p}</c>. /// Each column of return value is the solution against corresponding column of b. /// </para> /// </exception> public static NdArray <T> Solve <T>(this INdArray <T> a, INdArray <T> b) { Guard.AssertShapeMatch(a.Rank == 2 && a.Shape[0] == a.Shape[1], "a.Rank == 2 && a.Shape[0] == a.Shape[1]"); if (b.Rank == 1 && b.Shape[0] == a.Shape[0]) { var(l, u, perms) = a.LUWithPermutationsLegacy(); var x = NdArray.CreateMutable(new T[b.Shape[0]]); SolveCore(l, u, perms, b, x); return(x.MoveToImmutable()); } if (b.Rank == 2 && b.Shape[0] == a.Shape[0]) { var(l, u, perms) = a.LUWithPermutationsLegacy(); var x = NdArray.CreateMutable(new T[b.Shape[0], b.Shape[1]]); var col = b.Shape[1]; for (var j = 0; j < col; ++j) { var bj = b[Range.Whole, new Index(j, false)]; var xj = x[Range.Whole, new Index(j, false)]; SolveCore(l, u, perms, bj, xj); } return(x.MoveToImmutable()); } Guard.ThrowShapeMismatch("(b.Rank == 1 || b.Rank == 2) && b.Shape[0] == a.Shape[0]"); throw new NotSupportedException(); }
public static bool TryGetBufferImpl <T>(this INdArray <T> array, [NotNullWhen(true)] out IBufferNdArrayImpl <T>?result) { if ((array as INdArrayInternal <T>)?.Entity is IBufferNdArrayImpl <T> xresult) { result = xresult; return(true); } result = default; return(false); }
private static void SolveTriangleMatrixRL <T>(bool isUnit, T alpha, INdArray <T> a, MutableNdArray <T> b) { var(m, n) = (b.Shape[0], b.Shape[1]); /* reference Fortran * DO 260, J = N, 1, -1 * IF( ALPHA.NE.ONE )THEN * DO 220, I = 1, M * B( I, J ) = ALPHA*B( I, J ) * 220 CONTINUE * END IF * DO 240, K = J + 1, N * IF( A( K, J ).NE.ZERO )THEN * DO 230, I = 1, M * B( I, J ) = B( I, J ) - A( K, J )*B( I, K ) * 230 CONTINUE * END IF * 240 CONTINUE * IF( NOUNIT )THEN * TEMP = ONE/A( J, J ) * DO 250, I = 1, M * B( I, J ) = TEMP*B( I, J ) * 250 CONTINUE * END IF * 260 CONTINUE */ for (var j = n - 1; j >= 0; --j) { if (ValueTrait.Equals(alpha, One <T>())) { for (var i = 0; i < m; ++i) { b[i, j] = Multiply(alpha, b[i, j]); } } for (var k = j + 1; k < n; ++k) { if (!ValueTrait.Equals(a[k, j], Zero <T>())) { for (var i = 0; i < m; ++i) { b[i, j] = Subtract(b[i, j], Multiply(a[k, j], b[i, k])); } } } if (!isUnit) { var temp = Divide(One <T>(), a[j, j]); for (var i = 0; i < m; ++i) { b[i, j] = Multiply(temp, b[i, j]); } } } }
private static void SolveTriangleMatrixRU <T>(bool isUnit, T alpha, INdArray <T> a, MutableNdArray <T> b) { var(m, n) = (b.Shape[0], b.Shape[1]); /* reference Fortran * DO 210, J = 1, N * IF( ALPHA.NE.ONE )THEN * DO 170, I = 1, M * B( I, J ) = ALPHA*B( I, J ) * 170 CONTINUE * END IF * DO 190, K = 1, J - 1 * IF( A( K, J ).NE.ZERO )THEN * DO 180, I = 1, M * B( I, J ) = B( I, J ) - A( K, J )*B( I, K ) * 180 CONTINUE * END IF * 190 CONTINUE * IF( NOUNIT )THEN * TEMP = ONE/A( J, J ) * DO 200, I = 1, M * B( I, J ) = TEMP*B( I, J ) * 200 CONTINUE * END IF * 210 CONTINUE */ for (var j = 0; j < n; ++j) { if (ValueTrait.Equals(alpha, One <T>())) { for (var i = 0; i < m; ++i) { b[i, j] = Multiply(alpha, b[i, j]); } } for (var k = 0; k < j - 1; ++k) { if (!ValueTrait.Equals(a[k, j], Zero <T>())) { for (var i = 0; i < m; ++i) { b[i, j] = Subtract(b[i, j], Multiply(a[k, j], b[i, k])); } } } if (!isUnit) { var temp = Divide(One <T>(), a[j, j]); for (var i = 0; i < m; ++i) { b[i, j] = Multiply(temp, b[i, j]); } } } }
/// <summary> /// Computes sum from all elements of the <paramref name="ndArray"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> public static T Sum <T>(this INdArray <T> ndArray) { var value = ValueTrait.Zero <T>(); var len = ndArray.Shape.TotalLength; for (var i = 0; i < len; ++i) { value = ValueTrait.Add(value, ndArray.GetItem(i)); } return(value); }
/// <summary> /// Evaluates cross operation of 3-dim vector lazily. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public static NdArray <T> Cross <T>(this INdArray <T> x, INdArray <T> y) { Guard.AssertShapeMatch(new[] { 3 }, x.Shape, nameof(x) + "." + nameof(INdArray <T> .Shape)); Guard.AssertShapeMatch(new[] { 3 }, y.Shape, nameof(y) + "." + nameof(INdArray <T> .Shape)); var p = Op.Subtract(Op.Multiply(x[1], y[2]), Op.Multiply(x[2], y[1])); var q = Op.Subtract(Op.Multiply(x[2], y[0]), Op.Multiply(x[0], y[2])); var r = Op.Subtract(Op.Multiply(x[0], y[1]), Op.Multiply(x[1], y[0])); return(NdArray.Create(new[] { p, q, r })); }
/// <summary> /// Computes unbiased variance from all elements of the <paramref name="ndArray"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> public static T UnbiasedVar <T>(this INdArray <T> ndArray) { var value = ValueTrait.Zero <T>(); var mean = ndArray.Mean(); var len = ndArray.Shape.TotalLength; for (var i = 0; i < len; ++i) { var temp = ValueTrait.Subtract(ndArray.GetItem(i), mean); value = ValueTrait.Multiply(temp, temp); } return(ValueTrait.Divide(value, ValueTrait.FromLong <T>(len - 1))); }
/// <summary> /// [Pure] Returns the 2-D norm of all elements of <paramref name="ndArray"/>. /// </summary> /// <param name="ndArray"></param> /// <returns></returns> public static T Norm2 <T>(this INdArray <T> ndArray) { var len = ndArray.Shape.TotalLength; Guard.AssertOperation(len > 0, "ndArray has no elements."); var norm = ValueTrait.Zero <T>(); for (var i = 0; i < len; ++i) { var element = ndArray.GetItem(i); norm = ValueTrait.Add(norm, ValueTrait.Multiply(element, element)); } return(NdMath.Sqrt(norm)); }
private static void SolveTriangleMatrixLL <T>(bool isUnit, T alpha, INdArray <T> a, MutableNdArray <T> b) { var(m, n) = (b.Shape[0], b.Shape[1]); /* reference Fortran * DO 100, J = 1, N * IF( ALPHA.NE.ONE )THEN * DO 70, I = 1, M * B( I, J ) = ALPHA*B( I, J ) * 70 CONTINUE * END IF * DO 90 K = 1, M * IF( B( K, J ).NE.ZERO )THEN * IF( NOUNIT ) * $ B( K, J ) = B( K, J )/A( K, K ) * DO 80, I = K + 1, M * B( I, J ) = B( I, J ) - B( K, J )*A( I, K ) * 80 CONTINUE * END IF * 90 CONTINUE * 100 CONTINUE */ for (var j = 0; j < n; ++j) { if (!ValueTrait.Equals(alpha, One <T>())) { for (var i = 0; i < m; ++i) { b[i, j] = Multiply(alpha, b[i, j]); } } for (var k = 0; k < m; ++k) { if (!ValueTrait.Equals(b[k, j], Zero <T>())) { if (!isUnit) { b[k, j] = Divide(b[k, j], b[k, k]); } for (var i = k; i < m; ++i) { b[i, j] = Subtract(b[i, j], Divide(b[k, j], a[i, k])); } } } } }
private static void SolveTriangleMatrixLU <T>(bool isUnit, T alpha, INdArray <T> a, MutableNdArray <T> b) { var(m, n) = (b.Shape[0], b.Shape[1]); /* reference Fortran * DO 60, J = 1, N * IF( ALPHA.NE.ONE )THEN * DO 30, I = 1, M * B( I, J ) = ALPHA*B( I, J ) * 30 CONTINUE * END IF * DO 50, K = M, 1, -1 * IF( B( K, J ).NE.ZERO )THEN * IF( NOUNIT ) * $ B( K, J ) = B( K, J )/A( K, K ) * DO 40, I = 1, K - 1 * B( I, J ) = B( I, J ) - B( K, J )*A( I, K ) * 40 CONTINUE * END IF * 50 CONTINUE * 60 CONTINUE */ for (var j = 0; j < n; ++j) { if (!ValueTrait.Equals(alpha, One <T>())) { for (var i = 0; i < m; ++i) { b[i, j] = Multiply(alpha, b[i, j]); } } for (var k = m - 1; k >= 0; --k) { if (!ValueTrait.Equals(b[k, j], Zero <T>())) { if (!isUnit) { b[k, j] = Divide(b[k, j], a[k, k]); } for (var i = 0; i < k - 1; ++i) { b[i, j] = Subtract(b[i, j], Multiply(b[k, j], a[i, k])); } } } } }
/// <summary> /// Returns the largest value of the <paramref name="ndArray"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> public static T Max <T>(this INdArray <T> ndArray) { var len = ndArray.Shape.TotalLength; Guard.AssertOperation(len > 0, "ndArray has no elements."); var max = ndArray.GetItem(0); for (var i = 1; i < len; ++i) { var current = ndArray.GetItem(i); if (ValueTrait.GreaterThan(current, max)) { max = current; } } return(max); }
/// <summary> /// Calculates a matrix determinant. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <param name="strategy"></param> /// <returns></returns> public static T Determinant <T>(this INdArray <T> ndArray, IIterationStrategy?strategy = default) { Guard.AssertShapeMatch(ndArray.Shape.Length == 2 && ndArray.Shape[0] == ndArray.Shape[1], "x must be a square matrix."); var(_, u, permutations) = ndArray.LUWithPermutationsLegacy(strategy); var n = ndArray.Shape[0]; var x = One <T>(); for (var i = 0; i < n; ++i) { x = Multiply(x, u[i, i]); } if (permutations.Count % 2 == 1) { x = UnaryNegate(x); } return(x); }
/// <summary> /// Returns the index of the smallest value of the <paramref name="ndArray"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> public static IndexArray ArgMin <T>(this INdArray <T> ndArray) { var len = ndArray.Shape.TotalLength; Guard.AssertOperation(len > 0, "ndArray has no elements."); var argmin = 0; var min = ndArray.GetItem(0); for (var i = 1; i < len; ++i) { var current = ndArray.GetItem(i); if (ValueTrait.LessThan(current, min)) { argmin = i; min = current; } } return(ndArray.ToShapedIndices(argmin)); }
private static NdArray <T> DotLegacy1x1 <T>(INdArray <T> x, INdArray <T> y) { // x.Shape = [p] // y.Shape = [p] // retval.Shape = [1] var p = x.Shape[0]; Guard.AssertShapeMatch(p == y.Shape[0], $"x.Shape[0] = {p}, y.Shape[0] = {y.Shape[0]}"); var rawImpl = new RawNdArrayImpl <T>(new IndexArray(1)); var buffer = rawImpl.Buffer; buffer.Span[0] = Zero <T>(); for (var i = 0; i < p; ++i) { buffer.Span[0] = Add(buffer.Span[0], Multiply(x[i], y[i])); } return(new NdArray <T>(rawImpl)); }
/// <summary> /// Calculates inverse matrix. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> public static NdArray <T> Inverse <T>(this INdArray <T> ndArray) { Guard.AssertShapeMatch((bool)(ndArray.Rank == 2 & ndArray.Shape[0] == ndArray.Shape[1]), "ndArray.Rank == 2 & ndArray.Shape[0] == ndArray.Shape[1]"); var b = Identity <T>((int)ndArray.Shape[0]); var(l, u, perms) = NdLinAlg.LUWithPermutationsLegacy <T>(ndArray); var xx = NdArray.CreateMutable(new T[b.Shape[0], b.Shape[1]]); var x = xx.Entity; var row = b.Shape[0]; var col = b.Shape[1]; for (var j = 0; j < col; ++j) { var bj = b[Range.Whole, new Index(j, false)]; var xj = xx[Range.Whole, new Index(j, false)]; SolveCore(l, u, perms, bj, xj); } for (var k = perms.Count - 1; k >= 0; --k) { var(p, q) = perms[k]; for (var i = 0; i < row; ++i) { Span <int> idx1 = stackalloc int[] { i, p }; Span <int> idx2 = stackalloc int[] { i, q }; InternalUtils.Exchange(ref x[idx1], ref x[idx2]); } } for (var k = perms.Count - 1; k >= 0; --k) { var(p, q) = perms[k]; for (var i = 0; i < row; ++i) { Span <int> idx1 = stackalloc int[] { i, p }; Span <int> idx2 = stackalloc int[] { i, q }; InternalUtils.Exchange(ref x[idx1], ref x[idx2]); } } return(xx.MoveToImmutable()); }
/// <summary> /// Computes sample variance from all elements of the <paramref name="ndArray"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> public static T StdDev <T>(this INdArray <T> ndArray) => NdMath.Sqrt(ndArray.SampleVar());
/// <summary> /// Computes mean from all elements of the <paramref name="ndArray"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> public static T Mean <T>(this INdArray <T> ndArray) => ValueTrait.Divide(ndArray.Sum(), ValueTrait.FromLong <T>(ndArray.Shape.TotalLength));
private static void SolveCore <T>(INdArray <T> l, INdArray <T> u, IReadOnlyList <(int, int)> perms, INdArray <T> b, MutableNdArray <T> x)
/// <summary> /// [Pure] Returns the Infinity-D norm of all elements of <paramref name="ndArray"/>. /// This is same with <see cref="NdStatistics.Max"/>. /// </summary> /// <param name="ndArray"></param> /// <returns></returns> public static T NormInf <T>(this INdArray <T> ndArray) => ndArray.Max();
// reference: // https://github.com/xianyi/OpenBLAS/blob/develop/reference/dtrsmf.f /// <summary> /// [dtrsm] Solves one of the matrix equations: /// <list type="bullet"> /// <item> /// <term><c>(side, transa) = (Left, None)</c></term> /// <description><c>A * X = alpha * B</c></description> /// </item> /// <item> /// <term><c>(side, transa) = (Right, None)</c></term> /// <description><c>X * A = alpha * B</c></description> /// </item> /// </list> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="side"></param> /// <param name="uplo"></param> /// <param name="diag"> <c>true</c> if <paramref name="a"/> is a unit triangular; otherwise, <c>false</c>. </param> /// <param name="alpha"></param> /// <param name="a"></param> /// <param name="b"></param> public static void SolveTriangleMatrix <T>(OperandSide side, TriangleKind uplo, bool diag, T alpha, INdArray <T> a, MutableNdArray <T> b) { if (ValueTrait.Equals(alpha, Zero <T>())) { VectorOperation.Identity(NdArray.Zeros <T>(b.Shape), b); return; } switch ((side, uplo)) { case (OperandSide.Left, TriangleKind.Upper): SolveTriangleMatrixLU(diag, alpha, a, b); break; case (OperandSide.Left, TriangleKind.Lower): SolveTriangleMatrixLL(diag, alpha, a, b); break; case (OperandSide.Right, TriangleKind.Upper): SolveTriangleMatrixRU(diag, alpha, a, b); break; case (OperandSide.Right, TriangleKind.Lower): SolveTriangleMatrixRL(diag, alpha, a, b); break; default: Guard.ThrowArgumentError("Invalid configs."); break; } }
public ArgContainer(INdArray value) => Value = value;
/// <summary> /// <para>Calculates matrix LU decomposition.</para> /// <para>If <paramref name="ndArray"/> cannot decompose to LU, the return values are <c>null</c>.</para> /// <para><c>ndArray == l.dot(u)</c></para> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="ndArray"></param> /// <returns></returns> /// <exception cref="ShapeMismatchException"></exception> public static (NdArray <T> l, NdArray <T> u) LU <T>(this INdArray <T> ndArray) {
/// <summary> /// [Pure] Returns the 1-D norm of all elements of <paramref name="ndArray"/>. /// This is same with <see cref="NdStatistics.Sum"/>. /// </summary> /// <param name="ndArray"></param> /// <returns></returns> public static T Norm1 <T>(this INdArray <T> ndArray) => ndArray.Sum();