Пример #1
0
        /// <summary>
        /// Polynomial curve fitting of degree n
        /// </summary>
        /// <param name="x">Vector of X values</param>
        /// <param name="y">Vector of Y values</param>
        /// <param name="n">Degress of polynomial to fit</param>
        /// <returns>Vector of polynomial coefficients [p1, p2, .., pn+1], fit is p(x) = p1*x^n + p2*x^(n-1) + ...</returns>
        public static ILArray <double> polyfit(ILArray <double> x, ILArray <double> y, int n)
        {
            // Create Vandermonde matrix [[x0^n, x0^(n-1), ..., 1], [x1^n, x1^(n-1), ..., 1], ...]
            if (!x.IsVector || !y.IsVector)
            {
                throw new ILArgumentException("x and y must be vectors");
            }
            if (x.Length != y.Length)
            {
                throw new ILArgumentException("x and y must have the same length");
            }
            int m = x.Length;
            ILArray <double> V = new ILArray <double>(m, n + 1);

            for (int i = 0; i < m; ++i)
            {
                double vElement = 1;
                double currentX = x.GetValue(i);
                for (int j = n; j >= 0; --j)
                {
                    V[i, j]   = vElement;
                    vElement *= currentX;
                }
            }
            ILArray <double> coefficients = ILMath.linsolve(V, y);

            return(coefficients);
        }
Пример #2
0
        /// <summary>
        /// sort data in A along dimension 'dim'
        /// </summary>
        /// <param name="A">input array: empty, scalar, vector or matrix</param>
        /// <param name="descending">Specifies the direction of sorting</param>
        /// <param name="dim">dimension to sort along</param>
        /// <param name="Indices">output parameter: returns permutation matrix also</param>
        /// <returns>sorted array of the same size as A</returns>
        /// <remarks><para>The data in A will be sorted using the quick sort algorithm. Data
        /// along the dimension <paramref name="dim"/> will therefore get sorted independently from data
        /// in the next row/column.</para>
        /// <para>This overload also returns an array 'Indices' which will hold the indices into the original
        /// elements <b>after sorting</b>. Elements of 'Indices' are of type double.</para>
        ///</remarks>
        public static ILArray <string> sort(ILArray <string> A, out ILArray <double> Indices, int dim, bool descending)
        {
            if (object.Equals(A, null))
            {
                throw new Exception("sort: parameter A must not be null");
            }
            if (A.Dimensions.NumberOfDimensions > 2)
            {
                throw new ILArgumentException("sort: for element type string only matrices are supported!");
            }
            if (dim < 0 || dim >= A.Dimensions.NumberOfDimensions)
            {
                throw new ILArgumentException("sort: invalid dimension argument");
            }
            // early exit: scalar/ empty
            if (A.IsEmpty || A.IsScalar)
            {
                Indices = 0.0;
                return(A.C);
            }
            ILArray <string> ret = new ILArray <string>(new string[A.Dimensions.NumberOfElements], A.Dimensions);
            int dim1             = dim % A.Dimensions.NumberOfDimensions;
            int dim2             = (dim1 + 1) % A.Dimensions.NumberOfDimensions;
            int maxRuns          = A.Dimensions[dim2];
            ILQueueList <string, double> ql;

            ILArray <int>[]  ind  = new ILArray <int> [2];
            int []           indI = new int[2];
            ILASCIIKeyMapper km   = new ILASCIIKeyMapper();

            Indices = ILMath.counter(0.0, 1.0, A.Dimensions.ToIntArray());
            for (int c = 0; c < maxRuns; c++)
            {
                ind[dim2]  = c;
                ind[dim1]  = null;
                ql         = ILBucketSort.BucketSort <string, char, double>(A[ind].Values, Indices[ind].Values, km, ILBucketSort.SortMethod.ConstantLength);
                indI[dim2] = c;
                if (descending)
                {
                    for (int i = ql.Count; i-- > 0;)
                    {
                        indI[dim1] = i;
                        ILListItem <string, double> item = ql.Dequeue();
                        ret.SetValue(item.Data, indI);
                        Indices.SetValue(item.m_index, indI);
                    }
                }
                else
                {
                    for (int i = 0; ql.Count > 0; i++)
                    {
                        indI[dim1] = i;
                        ILListItem <string, double> item = ql.Dequeue();
                        ret.SetValue(item.Data, indI);
                        Indices.SetValue(item.m_index, indI);
                    }
                }
            }
            return(ret);
        }
Пример #3
0
        /// <summary>
        /// sort data in A along dimension 'dim'
        /// </summary>
        /// <param name="A">input array, n-dimensional</param>
        /// <param name="descending">Specifies the direction of sorting</param>
        /// <param name="dim">dimension to sort along</param>
        /// <param name="Indices">output parameter: returns permutation matrix also</param>
        /// <returns>sorted array of the same size as A</returns>
        /// <remarks><para>The data in A will be sorted using the quick sort algorithm. Data
        /// along the dimension <paramref name="dim"/> will therefore get sorted independently from data
        /// in the next row/column.</para>
        /// <para>This overload also returns an array 'Indices' which will hold the indices into the original
        /// elements <b>after sorting</b>. Elements of 'Indices' are of type double.</para>
        /// <example><code>ILArray&lt;double&gt; A = ILMath.rand(1,5);
        /// //A:
        /// // 0.4324  0.9231  0.1231  0.1423  0.2991
        /// ILArray&lt;double&gt; I;
        /// ILArray&lt;double&gt; R = ILMath.sort(A, out I, 1, false);
        /// //R:
        /// // 0.1231  0.1423  0.2991  0.4324  0.9231
        /// //I:
        /// // 2  3  4  0  1
        /// </code>
        /// </example></remarks>
        public static ILArray <byte> sort(ILArray <byte> A, out ILArray <double> Indices, int dim, bool descending)
        {
            if (object.Equals(A, null))
            {
                throw new Exception("sort: parameter A must not be null");
            }
            if (dim < 0 || dim >= A.Dimensions.NumberOfDimensions)
            {
                throw new ILArgumentException("sort: invalid dimension argument");
            }
            // early exit: scalar/ empty
            Indices = ILMath.counter(0.0, 1.0, A.Dimensions.ToIntArray());
            if (A.IsEmpty || A.IsScalar)
            {
                return(A.C);
            }
            ILArray <byte> ret      = A.C;
            int            inc      = ret.Dimensions.SequentialIndexDistance(dim);
            int            dimLen   = A.Dimensions[dim];
            int            maxRuns  = A.Dimensions.NumberOfElements / dimLen;
            int            posInArr = 0;

            if (descending)
            {
                for (int c = 0; c < maxRuns; c++)
                {
                    if (posInArr >= A.Dimensions.NumberOfElements)
                    {
                        posInArr -= (A.Dimensions.NumberOfElements - 1);
                    }
                    ILQuickSort.QuickSortDescSolidIDX(ret.m_data, Indices.m_data, posInArr, posInArr + (dimLen - 1) * inc, inc);
                    posInArr += dimLen * inc;
                }
            }
            else
            {
                // ascending
                for (int c = 0; c < maxRuns; c++)
                {
                    if (posInArr >= A.Dimensions.NumberOfElements)
                    {
                        posInArr -= (A.Dimensions.NumberOfElements - 1);
                    }
                    ILQuickSort.QuickSortAscSolidIDX(ret.m_data, Indices.m_data, posInArr, posInArr + (dimLen - 1) * inc, inc);
                    posInArr += dimLen * inc;
                }
            }
            return(ret);
        }
Пример #4
0
        /// <summary>
        /// Create complex array from real and imaginary parts.
        /// </summary>
        /// <param name="real">Array with real part elements.</param>
        /// <param name="imag">Array with imaginary part elements.</param>
        /// <returns>Complex array constructed out of real and imaginary parts given.</returns>
        /// <exception cref="ILNumerics.Exceptions.ILArgumentException">If the size of both arguments is not the same.</exception>
        public static ILArray <complex> ccomplex(ILArray <double> real, ILArray <double> imag)
        {
            if (!real.Dimensions.IsSameSize(imag.Dimensions))
            {
                throw new ILArgumentException("complex: input arrays must have the same size!");
            }
            ILArray <complex> ret = ILMath.tocomplex(real);
            int nelem             = real.Dimensions.NumberOfElements;

            // here 'ret' is assumed to be physical (NOT reference storage!)
            for (int i = 0; i < nelem; i++)
            {
                ret.m_data[i].imag = imag.GetValue(i);
            }
            return(ret);
        }
Пример #5
0
        /// <summary>
        /// Create complex array from real and imaginary parts.
        /// </summary>
        /// <param name="real">Array with real part elements.</param>
        /// <param name="imag">Array with imaginary part elements.</param>
        /// <returns>Complex array constructed out of real and imaginary parts supplied.</returns>
        /// <exception cref="ILNumerics.Exceptions.ILArgumentException">If the size of both arguments is not the same.</exception>
        public static ILArray <fcomplex> ccomplex(ILArray <float> real, ILArray <float> imag)
        {
            if (!real.Dimensions.IsSameSize(imag.Dimensions))
            {
                throw new ILArgumentException("fcomplex: input arrays must have the same size!");
            }
            ILArray <fcomplex> ret = ILMath.tofcomplex(real);
            int nelem = real.Dimensions.NumberOfElements;
            int baseIdxRet;

            for (int i = 0; i < nelem; i++)
            {
                baseIdxRet = real.getBaseIndex(i);
                ret.m_data[baseIdxRet].imag = imag.GetValue(i);
            }
            return(ret);
        }
Пример #6
0
// DO NOT EDIT INSIDE THIS REGION !! CHANGES WILL BE LOST !!

        /// <summary>
        /// Solve linear equation system
        /// </summary>
        /// <param name="A">Matrix A. Size [n x q]</param>
        /// <param name="B">'rigth hand side' B. Size [n x m]</param>
        /// <returns> solution x solving the equation system: multiply(A, x) = B. Size [n x m]</returns>
        /// <remarks><para>depending on the structure and properties of A, the equation system will be solved in different ways:
        /// <list type="bullet">
        /// <item>If A is square (q == n) and an <b>upper or lower triangular</b> matrix, the system will directly be solved via backward- or forward substitution. Therefore the Lapack function ?trtrs will be used, whenever the memory layout of A is suitable. This may be the case even for reference ILArray's!
        /// <example><![CDATA[ILArray<double> A = ILMath.randn(4); // construct 4 x 4 matrix
        /// A = A.T; // A is a reference array now! The transpose is fast and does not consume much memory
        /// // now construct a right side and solve the equations:
        /// ILArray<double> B = new ILArray<double> (1.0,2.0,3.0).T;
        /// ILMath.linsolve(A,B); // ... will be carried out via Lapack, even for all arrays involved beeing reference arrays! ]]></example></item>
        /// <item><para>if A is square and symmetric or hermitian, A will be decomposed into a triangular equation system using cholesky factorization and Lapack. The system is than solved using performant Lapack routines.</para>
        /// <para>if during the cholesky factorization A was found to be <b>not positive definite</b> - the cholesky factorization is canceled. </para></item>
        /// <item>otherwise, if A is square only, it will be decomposed into upper and lower triangular martices using LU decomposition and Lapack. The triangular system is than solved using performant Lapack routines.</item>
        /// <item>otherwise, if A is of size [q x n] and q != n, the system is solved using QR decomposition. A may be rank deficient. The solution is computed by use of the Lapack routine '?gelsy' and may be a reference array.</item>
        /// </list></para>
        /// <para>Compatibility with Matlab<sup>(R)</sup>: If A is square, the algorithm used follows the same logic as Matlab up to Rel 14, with the exception of Hessenberg matrices wich are solved via LU factorization here. The un-squared case is handled differently. A direct Lapack driver function (?gelsy) is used here. Therefore the solutions might differ! However, the solution will of course fullfill the equation A * x = B without round off errrors. </para>
        /// <para>For specifiying the rank of A in the unsquare case (q != n), the eps member from <see cref="ILNumerics.Settings.ILSettings"/> class is used.</para></remarks>
        public static ILArray <complex> linsolve(ILArray <complex> A, ILArray <complex> B)
        {
            if (object.Equals(A, null) || object.Equals(B, null))
            {
                throw new ILArgumentException("linsolve: parameter must not be null!");
            }
            MatrixProperties props = MatrixProperties.None;

            if (A.Dimensions[0] == A.Dimensions[1])
            {
                props |= MatrixProperties.Square;
                if (ILMath.istriup(A))
                {
                    props |= MatrixProperties.UpperTriangular;
                    return(linsolve(A, B, ref props));
                }
                if (ILMath.istrilow(A))
                {
                    props |= MatrixProperties.LowerTriangular;
                    return(linsolve(A, B, ref props));
                }
                if (ILMath.ishermitian(A))
                {
                    // give cholesky a try
                    props |= MatrixProperties.Hermitian;
                    props |= MatrixProperties.PositivDefinite;
                    ILArray <complex> ret = linsolve(A, B, ref props);
                    if (!object.Equals(ret, null))
                    {
                        return(ret);
                    }
                    else
                    {
                        props ^= MatrixProperties.PositivDefinite;
                    }
                }
            }
            return(linsolve(A, B, ref props));
        }
Пример #7
0
 public IronPython.Runtime.List this[IronPython.Runtime.Slice xslice, IronPython.Runtime.Slice yslice]
 {
     get
     {
         if ((xslice.start == null) || (xslice.stop == null) || (yslice.start == null) || (yslice.stop == null))
         {
             throw new ILArgumentException("mgrid: start and stop of slices must be provided");
         }
         double xStep = 1, yStep = 1, xStart = 0, yStart = 0, xStop = 1, yStop = 1;
         //
         if (xslice.start is int)
         {
             xStart = (double)(int)(xslice.start);
         }
         if (xslice.start is double)
         {
             xStart = (double)(xslice.start);
         }
         if (xslice.stop is int)
         {
             xStop = (double)(int)(xslice.stop);
         }
         if (xslice.stop is double)
         {
             xStop = (double)(xslice.stop);
         }
         //
         if (yslice.start is int)
         {
             yStart = (double)(int)(yslice.start);
         }
         if (yslice.start is double)
         {
             yStart = (double)(yslice.start);
         }
         if (yslice.stop is int)
         {
             yStop = (double)(int)(yslice.stop);
         }
         if (yslice.stop is double)
         {
             yStop = (double)(yslice.stop);
         }
         //
         if (xslice.step == null)
         {
             if (xStop >= xStart)
             {
                 xStep = 1;
             }
             else
             {
                 xStep = -1;
             }
         }
         else
         {
             if (xslice.step is int)
             {
                 xStep = (double)(int)(xslice.step);
             }
             if (xslice.step is double)
             {
                 xStep = (double)(xslice.step);
             }
         }
         if (yslice.step == null)
         {
             if (yStop >= yStart)
             {
                 yStep = 1;
             }
             else
             {
                 yStep = -1;
             }
         }
         else
         {
             if (yslice.step is int)
             {
                 yStep = (double)(int)(yslice.step);
             }
             if (yslice.step is double)
             {
                 yStep = (double)(yslice.step);
             }
         }
         //
         IronPython.Runtime.List list = new IronPython.Runtime.List();
         int nx = (int)Math.Floor((xStop - xStart) / xStep) + 1;
         int ny = (int)Math.Floor((yStop - yStart) / yStep) + 1;
         ILArray <double>         x        = ILMath.counter(xStart, xStep, nx);
         ILArray <double>         y        = ILMath.counter(yStart, yStep, ny);
         List <ILArray <double> > meshgrid = ILMath.meshgrid(x, y);
         list.Add(meshgrid[0]); list.Add(meshgrid[1]);
         return(list);
     }
 }
Пример #8
0
// DO NOT EDIT INSIDE THIS REGION !! CHANGES WILL BE LOST !!
        /// <summary>
        /// singular value decomposition
        /// </summary>
        /// <param name="X">matrix X. The elements of X will not be altered.</param>
        /// <param name="U">(return value) left singular vectors of X as columns of matrix U.
        /// If this parameter is set, it must be not null. It might be an empty array. On return
        /// it will be set to a physical array accordingly.</param>
        /// <param name="V">right singular vectors of X as rows of matrix V.
        /// If this parameter is set, it must be not null. It might be an empty array. On return
        /// it will be set to a physical array accordingly.</param>
        /// <param name="small">if true: return only first min(M,N) columns of U and S will be
        /// of size [min(M,N),min(M,N)]</param>
        /// <param name="discardFiniteTest">if true: the matrix given will not be checked for infinte or NaN values. If such elements
        /// are contained nevertheless, this may result in failing convergence or error. In worst case
        /// the function may hang inside the Lapack lib. Use with care! </param>
        /// <returns>singluar values as diagonal matrix of same size as X</returns>
        /// <remarks>the right singular vectors V will be returned as reference array.</remarks>
        public static ILArray <double> svd(ILArray <complex> X, ref ILArray <complex> U, ref ILArray <complex> V, bool small, bool discardFiniteTest)
        {
            if (!X.IsMatrix)
            {
                throw new ILArgumentSizeException("svd is defined for matrices only!");
            }
            // early exit for small matrices
            if (X.Dimensions[1] < 4 && X.Dimensions[0] == X.Dimensions[1])
            {
                switch (X.Dimensions[0])
                {
                case 1:
                    if (!Object.Equals(U, null))
                    {
                        U = ( complex )1.0;
                    }
                    if (!Object.Equals(V, null))
                    {
                        V = ( complex )1.0;
                    }
                    return(new  ILArray <double> (ILMath.abs(X)));
                    //case 2:
                    //    return -1;
                    //case 3:
                    //    return -1;
                }
            }
            if (!discardFiniteTest && !all(all(isfinite(X))))
            {
                throw new ILArgumentException("svd: input must have only finite elements!");
            }
            if (Lapack == null)
            {
                throw new ILMathException("No Lapack package available.");
            }
            // parameter evaluation
            int M = X.Dimensions[0]; int N = X.Dimensions[1];
            int minMN = (M < N) ? M : N;
            int LDU = M; int LDVT = N;
            int LDA = M;

            double [] dS   = new  double [minMN];
            char      jobz = (small) ? 'S' : 'A';

            complex [] dU   = null;
            complex [] dVT  = null;
            int        info = 0;

            if (!Object.Equals(U, null) || !Object.Equals(V, null))
            {
                // need to return U and VT
                if (small)
                {
                    dU  = new  complex  [M * minMN];
                    dVT = new  complex [N * minMN];
                }
                else
                {
                    dU  = new  complex [M * M];
                    dVT = new  complex [N * N];
                }
            }
            else
            {
                jobz = 'N';
            }

            // must create copy of input !
            complex [] dInput = new  complex [X.m_data.Length];
            System.Array.Copy(X.m_data, dInput, X.m_data.Length);
            Lapack.zgesdd(jobz, M, N, dInput, LDA, dS, dU, LDU, dVT, LDVT, ref info);
            if (info < 0)
            {
                throw new ILArgumentException("ILMath.svd: the " + (-info).ToString() + "th argument was invalid.");
            }
            if (info > 0)
            {
                throw new ILArgumentException("svd was not converging!");
            }
            ILArray <double> ret = null;

            if (info == 0)
            {
                // success
                if (!Object.Equals(U, null) || !Object.Equals(V, null))
                {
                    if (small)
                    {
                        ret = ILArray <double> .zeros(minMN, minMN);
                    }
                    else
                    {
                        ret = ILArray <double> .zeros(M, N);
                    }
                    for (int i = 0; i < minMN; i++)
                    {
                        ret.SetValue(dS[i], i, i);
                    }
                    if (!Object.Equals(U, null))
                    {
                        U = new  ILArray <complex> (dU, M, dU.Length / M);
                    }
                    if (!Object.Equals(V, null))
                    {
                        V = conj(new  ILArray <complex> (dVT, N, dVT.Length / N).T);
                    }
                }
                else
                {
                    ret = new  ILArray <double> (dS, minMN, 1);
                }
            }
            return(ret);
        }
Пример #9
0
        /// <summary>
        /// Solve linear equation system
        /// </summary>
        /// <param name="A">Matrix A. Size [n x q]</param>
        /// <param name="B">'rigth hand side' B. Size [n x m]</param>
        /// <param name="props">Matrix properties. If defined, no checks are made for the structure of A. If the matrix A was found to be (close to or) singular, the 'MatrixProperties.Singular' flag in props will be set. This flag should be tested on return, in order to verify the reliability of the solution.</param>
        /// <returns>the solution x solving multiply(A,x) = B. Size [n x m]</returns>
        /// <remarks><para>depending on the <paramref name="props"/> parameter the equation system will be solved differently for special structures of A:
        /// <list type="bullet">
        /// <item>If A is square (q == n) and an <b>upper or lower triangular</b> matrix, the system will directly be solved via backward- or forward substitution. Therefore the Lapack function ?trtrs will be used, whenever the memory layout of A is suitable. This may be the case even for reference ILArray's!
        /// <example><![CDATA[ILArray<double> A = ILMath.randn(4); // construct 4 x 4 matrix
        /// A = A.T; // A is a reference array now! The transpose is fast and does not consume much memory
        /// // now construct a right side and solve the equations:
        /// ILArray<double> B = new ILArray<double> (1.0,2.0,3.0).T;
        /// ILMath.linsolve(A,B); // ... will be carried out via Lapack, even for all arrays involved beeing reference arrays! ]]></example></item>
        /// <item><para>if A is square and symmetric or hermitian, A will be decomposed into a triangular equation system using cholesky factorization and Lapack. The system is than solved using performant Lapack routines.</para>
        /// <para>if during the cholesky factorization A was found to be <b>not positive definite</b> - the corresponding flag in props will be cleaned and <c>null</c> will be returned.</para></item>
        /// <item>otherwise if A is square only, it will be decomposed into upper and lower triangular matrices using LU decomposition and Lapack. The triangular system is than solved using performant Lapack routines.</item>
        /// <item>otherwise, if A is of size [q x n] and q != n, the system is solved using QR decomposition. A may be rank deficient. The solution is computed by use of the Lapack routine '?gelsy' and may be a reference array.</item>
        /// </list></para>
        /// <para>Compatibility with Matlab<sup>(R)</sup>: If A is square, the algorithm used follows the same logic as Matlab up to Rel 14, with the exception of Hessenberg matrices wich are solved via LU factorization here. The un-squared case is handled differently. A direct Lapack driver function (?gelsy) is used here. Therefore the solutions might differ! However, the solution will of course fullfill the equation A * x = B without round off errrors. </para>
        /// <para>For specifiying the rank of A in the unsquare case (q != n), the eps member from <see cref="ILNumerics.Settings.ILSettings"/> class is used.</para></remarks>
        public static ILArray <complex> linsolve(ILArray <complex> A, ILArray <complex> B, ref MatrixProperties props)
        {
            if (object.Equals(A, null))
            {
                throw new ILArgumentException("linsolve: input argument A must not be null!");
            }
            if (object.Equals(B, null))
            {
                throw new ILArgumentException("linsolve: input argument B must not be null!");
            }
            if (A.IsEmpty || B.IsEmpty)
            {
                return(ILArray <complex> .empty(A.Dimensions));
            }
            if (A.Dimensions[0] != B.Dimensions[0])
            {
                throw new ILArgumentException("linsolve: number of rows for matrix A must match number of rows for RHS!");
            }
            int info = 0, m = A.Dimensions[0];
            ILArray <complex> ret;

            if (m == A.Dimensions[1])
            {
                props |= MatrixProperties.Square;
                if ((props & MatrixProperties.LowerTriangular) != 0)
                {
                    ret = solveLowerTriangularSystem(A, B, ref info);
                    if (info > 0)
                    {
                        props |= MatrixProperties.Singular;
                    }
                    return(ret);
                }
                if ((props & MatrixProperties.UpperTriangular) != 0)
                {
                    ret = solveUpperTriangularSystem(A, B, ref info);
                    if (info > 0)
                    {
                        props |= MatrixProperties.Singular;
                    }
                    return(ret);
                }
                if ((props & MatrixProperties.Hermitian) != 0)
                {
                    ILArray <complex> cholFact = A.copyUpperTriangle(m);
                    Lapack.zpotrf('U', m, cholFact.m_data, m, ref info);
                    if (info > 0)
                    {
                        props ^= MatrixProperties.Hermitian;
                        return(null);
                    }
                    else
                    {
                        // solve
                        ret = (ILArray <complex>)B.Clone();
                        Lapack.zpotrs('U', m, B.Dimensions[1], cholFact.m_data, m, ret.m_data, m, ref info);
                        return(ret);
                    }
                }
                else
                {
                    // attempt complete (expensive) LU factorization
                    ILArray <complex> L      = (ILArray <complex>)A.Clone();
                    int []            pivInd = new int[m];
                    Lapack.zgetrf(m, m, L.m_data, m, pivInd, ref info);
                    if (info > 0)
                    {
                        props |= MatrixProperties.Singular;
                    }
                    ret = (ILArray <complex>)B.Clone();
                    Lapack.zgetrs('N', m, B.Dimensions[1], L.m_data, m, pivInd, ret.m_data, m, ref info);
                    if (info < 0)
                    {
                        throw new ILArgumentException("linsolve: failed to solve via lapack dgetrs");
                    }
                    return(ret);
                }
            }
            else
            {
                // under- / overdetermined system
                int n = A.Dimensions[1], rank = 0, minMN = (m < n)? m:n, maxMN = (m > n)? m:n;
                int nrhs = B.Dimensions[1];
                if (B.Dimensions[0] != m)
                {
                    throw new ILArgumentException("linsolve: right hand side matrix B must match input A!");
                }
                ILArray <complex> tmpA = (ILArray <complex>)A.Clone();
                if (m < n)
                {
                    ret = new  ILArray <complex> (new  complex [n * nrhs], n, nrhs);
                    ret["0:" + (m - 1) + ";:"] = B;
                }
                else
                {
                    ret = (ILArray <complex>)B.Clone();
                }
                int [] JPVT = new int [n];
                Lapack.zgelsy(m, n, B.Dimensions[1], tmpA.m_data, m, ret.m_data,
                              maxMN, JPVT, ILMath.MachineParameterDouble.eps,
                              ref rank, ref info);
                if (n < m)
                {
                    ret = ret[ILMath.vector(0, n - 1), null];
                }
                if (rank < minMN)
                {
                    props |= MatrixProperties.RankDeficient;
                }
                return(ret);
            }
        }