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();
        }
Beispiel #6
0
        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;
            }
        }