public static Array <Real> Add(this Array <Real> a, Real b, Real alpha = 1, Array <Real> result = null) { if (result == null) { result = new Array <Real>(a.Shape); } Array_.ElementwiseOp(a, result, (n, x, offsetx, incx, z, offsetz, incz) => { if (z == x) { Blas.axpy(n, alpha, ref b, x, offsetx, incx); } else { for (int i = 0; i < n; i++) { z[offsetz] = x[offsetx] + alpha * b; offsetx += incx; offsetz += incz; } } }); return(result); }
// result = a + alpha * b public static Array <Real> Add(this Array <Real> a, Array <Real> b, Real alpha = 1, Array <Real> result = null) { return(Array_.ElementwiseOp(a, b, result, (n, x, offsetx, incx, y, offsety, incy, z, offsetz, incz) => { if (alpha == 1 && incx == 1 && incy == 1 && incz == 1) { Blas.vadd(n, x, offsetx, y, offsety, z, offsetz); } else if (alpha == -1 && incx == 1 && incy == 1 && incz == 1) { Blas.vsub(n, x, offsetx, y, offsety, z, offsetz); } else if (z == x) { Blas.axpy(n, alpha, y, offsety, incy, x, offsetx, incx); } else if (z == y && alpha == 1) { Blas.axpy(n, alpha, x, offsetx, incx, y, offsety, incy); } // TODO: else if (incx == 0) => broadcast x ?? // TODO: else if (incy == 0) => broadcast y ?? else { for (int i = 0; i < n; i++) // TODO: Blas.copy y => x, Blas.axpy(1, x, y) { z[offsetz] = x[offsetx] + alpha * y[offsety]; offsetx += incx; offsety += incy; offsetz += incz; } } // See also: mkl_?omatadd => C := alpha*op(A) + beta*op(B) })); }
public static Array <Real> TensorDotRight(this Array <Real> mat, Array <Real> vec, Array <Real> result = null, Real beta = 1) { if (vec.Shape.Length != 1) { throw new ArgumentException(); } if (mat.Shape.Length != 2) { throw new ArgumentException(); } var shape = new[] { mat.Shape[0], mat.Shape[1], vec.Shape[0] }; if (result == null) { result = new Array <Real>(shape); } else if (result.Shape.Length != shape.Length) { throw new ArgumentException(); // TODO: check axes } var offsetRes = result.Offset; for (int i = 0; i < vec.Shape[0]; ++i) { // result[_, _, i] = vec[i] * mat + beta * result[_, _, i] if (beta != 1) { Blas.scal(mat.Size, beta, mat.Values, offsetRes, mat.Stride[1]); } Blas.axpy(mat.Size, vec.Item[i], mat.Values, mat.Offset, mat.Stride[1], result.Values, offsetRes, result.Stride[1]); offsetRes += result.Stride[2]; } return(result); }
/// <summary>a += α * b </summary> public static void Acc(this Array <Real> a, Array <Real> b, Real alpha = 1) { if (alpha == 0) { return; } b.AssertOfShape(a.Shape); Array_.ElementwiseOp(b, a, (n, x, offsetx, incx, y, offsety, incy) => { // TODO: broadcasting ? Blas.axpy(n, alpha, x, offsetx, incx, y, offsety, incy); }); }
/// <summary> /// Matrix multiplication. /// Returns: alpha * dot(a, b) + beta * result /// Ie with default value: dot(a, b) /// </summary> /// <remarks> /// For 2-D arrays it is equivalent to matrix multiplication, /// and for 1-D arrays to inner product of vectors (without complex conjugation). /// For N dimensions it is a sum product over the last axis of a and the second-to-last of b: /// dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m]) /// `TensorDot` provides more control for N-dim array multiplication. /// </remarks> public static Array <Real> Dot(this Array <Real> a, Array <Real> b, Array <Real> result = null, Real alpha = 1, Real beta = 0, bool transA = false, bool transB = false) { if (transA) { if (a.Shape.Length == 1) { if (b.Shape.Length == 1) { if (transB) { return(a.Dot(b, result, alpha, beta)); } else { return(a.VectorDot(b)); } } if (b.Shape.Length != 2) { throw new NotImplementedException(); } return(b.Dot(a, result, alpha, beta, transA: !transB, transB: false)); } else { a = a.T; // TODO: optimize => avoid creating new shape, stride, ... //if (b.Shape.Length == 1 && !transB) b = b.Reshape(1, b.Shape[0]); } transA = false; } if (transB) { if (b.Shape.Length == 1) { if (a.Shape.Length == 1) { if (transA) { throw new NotImplementedException(); } return(a.Outer(b, result: result, alpha: alpha, beta: 0)); } throw new NotImplementedException(); //if (a.Shape.Length != 2) throw new NotImplementedException(); //if (a.IsTransposed()) //{ // a = a.T; // transA = !transA; //} //result = new Array<Real>(transA ? a.Shape[1] : a.Shape[0], b.Shape[0]); // TODO: result != null //Blas.gemm(Order.RowMajor, transA ? Transpose.Trans : Transpose.NoTrans, Transpose.NoTrans, // result.Shape[0], result.Shape[1], 1, // alpha, // a.Values, a.Offset, a.Stride[0], // b.Values, b.Offset, b.Stride[0], // beta, // result.Values, result.Offset, result.Stride[0]); //return result; } else { b = b.T; // TODO: optimize => avoid creating new shape, stride, ... } transB = false; } // TODO: check alpha & beta if (a.Shape.Length == 0) { return(b.Scale(a.Values[a.Offset])); } if (b.Shape.Length == 0) { return(a.Scale(b.Values[b.Offset])); } if (a.Shape.Length == 1) // vector x tensor { if (b.Shape.Length == 1) // vector x vector { if (a.Shape[0] != b.Shape[0]) { throw AssertArray.BadRank("objects are not aligned: [{0}] dot [{1}]", a.Shape[0], b.Shape[0]); } if (result == null) { result = new Array <Real>(); } else { if (result.Shape.Length != 0) { throw AssertArray.BadRank("objects are not aligned"); } } result.Values[result.Offset] = beta * result.Values[result.Offset] + alpha * Blas.dot(a.Shape[0], a.Values, a.Offset, a.Stride[0], b.Values, b.Offset, b.Stride[0]); return(result); } else if (b.Shape.Length == 2) // vector x matrix { if (a.Shape[0] != b.Shape[0]) { throw new RankException("objects are not aligned"); } if (result == null) { result = new Array <Real>(b.Shape[1]); } else { if (result.Shape.Length != 1) { throw new RankException("objects are not aligned"); } if (result.Shape[0] != b.Shape[1]) { throw new RankException("objects are not aligned"); } } // dgemv computes matrix x vector => result = M.T.dot(v.T).T transB = !transB; if (b.IsTransposed()) { transB = !transB; b = b.T; } // y:= alpha * A' * x + beta * y Blas.gemv(Order.RowMajor, transB ? Transpose.Trans : Transpose.NoTrans, b.Shape[0], b.Shape[1], alpha, b.Values, b.Offset, b.Stride[0], a.Values, a.Offset, a.Stride[0], beta, result.Values, result.Offset, result.Stride[0]); return(result); } else if (b.Shape.Length == 3) // vector x tensor3 { // TODO: beta ? if (a.Shape[0] != b.Shape[1]) { throw new RankException("objects are not aligned"); } if (result == null) { result = new Array <Real>(b.Shape[0], b.Shape[2]); } else { if (result.Shape[0] != b.Shape[0]) { throw new RankException("objects are not aligned"); } if (result.Shape[1] != b.Shape[2]) { throw new RankException("objects are not aligned"); } } var offsetk = b.Offset; var k_0 = result.Offset; for (var k = 0; k < result.Shape[0]; k++) // result.Shape[0] == b.Shape[0] { var offsetm = offsetk; var k_m = k_0; for (var m = 0; m < result.Shape[1]; m++) // result.Shape[1] == b.Shape[2] { result.Values[k_m] = alpha * Blas.dot(a.Shape[0], a.Values, a.Offset, a.Stride[0], b.Values, offsetm, b.Stride[1]); // a.Shape[axis] == b.Shape[1]; offsetm += b.Stride[2]; k_m += result.Stride[1]; } offsetk += b.Stride[0]; k_0 += result.Stride[0]; } return(result); } throw new NotImplementedException(); } else if (b.Shape.Length == 1) // tensor x vector { if (a.Shape.Length == 2) // matrix x vector { if (a.Shape[1] != b.Shape[0]) { throw new RankException("objects are not aligned"); } if (result == null) { result = new Array <Real>(a.Shape[0]); } else { if (result.Shape.Length != b.Shape.Length) { throw new RankException("objects are not aligned"); } if (result.Shape[0] != a.Shape[0]) { throw new RankException("objects are not aligned"); } // TODO: check strides } if ((a.Flags & Flags.Transposed) != 0) { transA = !transA; a = a.T; } // y:= A*x + beta*y if (a.Stride[1] == 1) { Blas.gemv(Order.RowMajor, transA ? Transpose.Trans : Transpose.NoTrans, a.Shape[0], a.Shape[1], alpha, a.Values, a.Offset, a.Stride[0], b.Values, b.Offset, b.Stride[0], beta, result.Values, result.Offset, result.Stride[0]); } else { // y *= beta if (beta != 1) { result.Scale(beta, result: result); } int offB = b.Offset; int offA = a.Offset; for (int j = 0; j < b.Shape[0]; ++j) { Blas.axpy(a.Shape[0], alpha * b.Values[offB], a.Values, offA, a.Stride[0], result.Values, result.Offset, result.Stride[0]); offB += b.Stride[0]; offA += a.Stride[1]; } } return(result); } else if (a.Shape.Length == 3) // tensor x vector = mat { if (a.Shape[2] != b.Shape[0]) { throw new RankException("objects are not aligned"); } if (result == null) { result = new Array <Real>(a.Shape[0], a.Shape[1]); } else if (result.Shape[0] != a.Shape[0] || result.Shape[1] != a.Shape[1]) { throw new RankException("objects are not aligned"); } var offsetk = a.Offset; var offsetRes = result.Offset; for (var k = 0; k < result.Shape[0]; k++) { var offsetj = offsetk; for (var j = 0; j < result.Shape[1]; j++) { result.Values[offsetRes] = alpha * Blas.dot(a.Shape[2], a.Values, offsetj, a.Stride[2], b.Values, b.Offset, b.Stride[0]); offsetj += a.Stride[1]; offsetRes += result.Stride[1]; } offsetk += a.Stride[0]; } return(result); } throw new NotImplementedException(); } else if (a.Shape.Length == 2 && b.Shape.Length == 2) // matrix x matrix { if (a.Shape[1] != b.Shape[0]) { throw AssertArray.BadRank("objects are not aligned: [{0}, {1}] dot [{2}, {3}]", a.Shape[0], a.Shape[1], b.Shape[0], b.Shape[1]); } if (result == null) { result = new Array <Real>(a.Shape[0], b.Shape[1]); } else { if (result.Shape[0] != a.Shape[0] || result.Shape[1] != b.Shape[1]) { throw AssertArray.BadRank("result target have incorrect shape: [{0}, {1}] instead of [{2}, {3}].", result.Shape[0], result.Shape[1], a.Shape[0], b.Shape[1]); } // TODO: check strides } var m = a.Shape[0]; var n = b.Shape[1]; var k = a.Shape[1]; if ((a.Flags & Flags.Transposed) != 0) { transA = !transA; a = a.T; } if ((b.Flags & Flags.Transposed) != 0) { transB = !transB; b = b.T; } // C:= alpha * op(A) * op(B) + beta * C Blas.gemm(Order.RowMajor, transA ? Transpose.Trans : Transpose.NoTrans, transB ? Transpose.Trans : Transpose.NoTrans, m, n, k, alpha, a.Values, a.Offset, a.Stride[0], b.Values, b.Offset, b.Stride[0], beta, result.Values, result.Offset, result.Stride[0]); return(result); } // tensor x tensor throw new NotImplementedException(); }
private static void _EinsteinSum(int n, Einstein[] einstein, Array <float> x, int offX, Array <float> y, int offY, Array <float> z, int offZ ) { if (n == einstein.Length) { z.Values[offZ] += x.Values[offX] * y.Values[offY]; return; } var e = einstein[n]; if (n == einstein.Length - 1) { switch (e.mode) { case EinsteinMode.ELEMENTWISE: int axisX = (int)e.axisX, axisY = (int)e.axisY, axisZ = (int)e.axisZ; if (x.Stride[axisX] == 1 && y.Stride[axisY] == 1 && z.Stride[axisZ] == 1) { Blas.vmul(x.Shape[axisX], x.Values, offX, y.Values, offY, z.Values, offZ); return; } break; case EinsteinMode.OUTERX: var axis = (int)e.axisX; Blas.axpy(x.Shape[axis], y.Values[offY], x.Values, offX, x.Stride[axis], z.Values, offZ, z.Stride[(int)e.axisZ]); return; case EinsteinMode.OUTERY: axis = (int)e.axisY; Blas.axpy(y.Shape[axis], x.Values[offX], y.Values, offY, y.Stride[axis], z.Values, offZ, z.Stride[(int)e.axisZ]); return; } } switch (e.mode) { case EinsteinMode.INNER: for (int i = 0; i < x.Shape[(int)e.axisX]; ++i) { _EinsteinSum(n + 1, einstein, x, offX, y, offY, z, offZ); offX += x.Stride[(int)e.axisX]; offY += y.Stride[(int)e.axisY]; } return; case EinsteinMode.ELEMENTWISE: for (int i = 0; i < x.Shape[(int)e.axisX]; ++i) { _EinsteinSum(n + 1, einstein, x, offX, y, offY, z, offZ); offX += x.Stride[(int)e.axisX]; offY += y.Stride[(int)e.axisY]; offZ += z.Stride[(int)e.axisZ]; } return; case EinsteinMode.OUTERX: var axis = (int)e.axisX; for (int i = 0; i < x.Shape[axis]; ++i) { _EinsteinSum(n + 1, einstein, x, offX, y, offY, z, offZ); offX += x.Stride[axis]; offZ += z.Stride[(int)e.axisZ]; } return; case EinsteinMode.OUTERY: axis = (int)e.axisY; for (int i = 0; i < y.Shape[axis]; ++i) { _EinsteinSum(n + 1, einstein, x, offX, y, offY, z, offZ); offY += y.Stride[axis]; offZ += z.Stride[(int)e.axisZ]; } return; case EinsteinMode.SUMX: axis = (int)e.axisX; for (int i = 0; i < x.Shape[axis]; ++i) { _EinsteinSum(n + 1, einstein, x, offX, y, offY, z, offZ); offX += x.Stride[axis]; } return; case EinsteinMode.SUMY: axis = (int)e.axisY; for (int i = 0; i < y.Shape[axis]; ++i) { _EinsteinSum(n + 1, einstein, x, offX, y, offY, z, offZ); offY += y.Stride[axis]; } return; } }