Esempio n. 1
0
 def test_convert_not_contiguous_2(self, dt1, dt2):
     xbase = nlcpy.random.rand(3, 4, 3, 4).astype(dt1)
     xin = nlcpy.moveaxis(xbase, 0, 2)
     xopt = nlcpy.sca.convert_optimized_array(xin, dtype=dt2)
     testing.assert_allclose(xin, xopt)
     assert xopt.strides != xin.strides
     assert xopt.dtype == dt2
Esempio n. 2
0
def _lange(x, norm, axis):
    order = 'F' if x.flags.f_contiguous and not x.flags.c_contiguous else 'C'
    dtype = 'f' if x.dtype.char in 'fF' else 'd'
    if x.size == 0:
        shape = [x.shape[i] for i in set(range(x.ndim)) - set(axis)]
        return nlcpy.zeros(shape, dtype=dtype)
    if norm in (None, 'fro', 'f'):
        if x.dtype.kind == 'c':
            x = abs(x)
        return nlcpy.sqrt(nlcpy.sum(x * x, axis=axis))
    if norm == nlcpy.inf:
        norm = 'I'
    else:
        norm = '1'
    lwork = x.shape[0] if norm == 'I' else 1
    x = nlcpy.asarray(nlcpy.moveaxis(x, (axis[0], axis[1]), (0, 1)), order='F')
    y = nlcpy.empty(x.shape[2:], dtype=dtype, order='F')
    work = nlcpy.empty(lwork, dtype=dtype)
    fpe = request._get_fpe_flag()
    args = (
        ord(norm),
        x._ve_array,
        y._ve_array,
        work._ve_array,
        veo.OnStack(fpe, inout=veo.INTENT_OUT),
    )

    request._push_and_flush_request(
        'nlcpy_norm',
        args,
    )

    return nlcpy.asarray(y, order=order)
Esempio n. 3
0
 def test_moveaxis_invalid5_3(self):
     a = testing.shaped_arange((2, 3, 4), nlcpy)
     with self.assertRaises(nlcpy.AxisError):
         return nlcpy.moveaxis(a, [0, 1], [1, 1])
Esempio n. 4
0
def _geev(a, jobvr):
    a = nlcpy.asarray(a)
    util._assertRankAtLeast2(a)
    util._assertNdSquareness(a)

    # used to match the contiguous of result to numpy.
    c_order = a.flags.c_contiguous or sum([i > 1 for i in a.shape[:-2]]) < 2

    a_complex = a.dtype.char in 'FD'
    if a.dtype.char == 'F':
        dtype = 'D'
        f_dtype = 'f'
        c_dtype = 'F'
    elif a.dtype.char == 'D':
        dtype = 'D'
        f_dtype = 'd'
        c_dtype = 'D'
    else:
        dtype = 'd'
        if a.dtype.char == 'f':
            f_dtype = 'f'
            c_dtype = 'F'
        else:
            f_dtype = 'd'
            c_dtype = 'D'

    if a.size == 0:
        dtype = c_dtype if a_complex else f_dtype
        w = nlcpy.empty(shape=a.shape[:-1], dtype=dtype)
        if jobvr:
            vr = nlcpy.empty(shape=a.shape, dtype=dtype)
            return w, vr
        else:
            return w

    a = nlcpy.array(nlcpy.moveaxis(a, (-1, -2), (1, 0)), dtype=dtype, order='F')
    wr = nlcpy.empty(a.shape[1:], dtype=dtype, order='F')
    wi = nlcpy.empty(a.shape[1:], dtype=dtype, order='F')
    vr = nlcpy.empty(a.shape if jobvr else 1, dtype=dtype, order='F')
    vc = nlcpy.empty(a.shape if jobvr else 1, dtype='D', order='F')

    n = a.shape[0]
    work = nlcpy.empty(
        65 * n if a_complex else 66 * n, dtype=dtype, order='F')
    rwork = nlcpy.empty(2 * n if a_complex else 1, dtype=f_dtype, order='F')
    info = numpy.empty(1, dtype='l')
    fpe = request._get_fpe_flag()
    args = (
        a._ve_array,
        wr._ve_array,
        wi._ve_array,
        vr._ve_array,
        vc._ve_array,
        work._ve_array,
        rwork._ve_array,
        ord('V') if jobvr else ord('N'),
        veo.OnStack(info, inout=veo.INTENT_OUT),
        veo.OnStack(fpe, inout=veo.INTENT_OUT),
    )

    request._push_and_flush_request(
        'nlcpy_eig',
        args,
    )

    if a_complex:
        w_complex = True
        w = wr
        vc = vr
    else:
        w_complex = nlcpy.any(wi)
        w = wr + wi * 1.0j
    if w_complex:
        if c_order:
            w = nlcpy.asarray(nlcpy.moveaxis(w, 0, -1), dtype=c_dtype, order='C')
        else:
            w = nlcpy.moveaxis(nlcpy.asarray(w, dtype=c_dtype), 0, -1)
    else:
        wr = w.real
        w = nlcpy.moveaxis(nlcpy.asarray(wr, dtype=f_dtype), 0, -1)

    if jobvr:
        if w_complex:
            if c_order:
                vr = nlcpy.asarray(
                    nlcpy.moveaxis(vc, (1, 0), (-1, -2)), dtype=c_dtype, order='C')
            else:
                vr = nlcpy.moveaxis(
                    nlcpy.asarray(vc, dtype=c_dtype), (1, 0), (-1, -2))
        else:
            if c_dtype == "F":
                vr = nlcpy.asarray(vc.real, dtype=f_dtype, order='C')
            else:
                vc = nlcpy.moveaxis(
                    nlcpy.asarray(vc, dtype=c_dtype), (1, 0), (-1, -2))
                vr = vc.real

    if jobvr:
        return w, vr
    else:
        return w
Esempio n. 5
0
def _syevd(a, jobz, UPLO):
    a = nlcpy.asarray(a)
    util._assertRankAtLeast2(a)
    util._assertNdSquareness(a)
    UPLO = UPLO.upper()
    if UPLO not in 'UL':
        raise ValueError("UPLO argument must be 'L' or 'U'")

    # used to match the contiguous of result to numpy.
    c_order = a.flags.c_contiguous or sum([i > 1 for i in a.shape[:-2]]) < 2

    a_complex = a.dtype.char in 'FD'
    if a.dtype.char == 'F':
        dtype = 'F'
        f_dtype = 'f'
    elif a.dtype.char == 'D':
        dtype = 'D'
        f_dtype = 'd'
    else:
        if a.dtype.char == 'f':
            dtype = 'f'
            f_dtype = 'f'
        else:
            dtype = 'd'
            f_dtype = 'd'

    if a.size == 0:
        w = nlcpy.empty(shape=a.shape[:-1], dtype=f_dtype)
        if jobz:
            vr = nlcpy.empty(shape=a.shape, dtype=dtype)
            return w, vr
        else:
            return w

    a = nlcpy.array(nlcpy.moveaxis(a, (-1, -2), (1, 0)), dtype=dtype, order='F')
    w = nlcpy.empty(a.shape[1:], dtype=f_dtype, order='F')
    n = a.shape[0]
    if a.size > 1:
        if a_complex:
            lwork = max(2 * n + n * n, n + 48)
            lrwork = 1 + 5 * n + 2 * n * n if jobz else n
        else:
            lwork = max(2 * n + 32, 1 + 6 * n + 2 * n * n) if jobz else 2 * n + 32
            lrwork = 1
        liwork = 3 + 5 * n if jobz else 1
    else:
        lwork = 1
        lrwork = 1
        liwork = 1

    work = nlcpy.empty(lwork, dtype=dtype)
    rwork = nlcpy.empty(lrwork, dtype=f_dtype)
    iwork = nlcpy.empty(liwork, dtype='l')
    info = numpy.empty(1, dtype='l')
    fpe = request._get_fpe_flag()
    args = (
        a._ve_array,
        w._ve_array,
        work._ve_array,
        rwork._ve_array,
        iwork._ve_array,
        ord('V') if jobz else ord('N'),
        ord(UPLO),
        veo.OnStack(info, inout=veo.INTENT_OUT),
        veo.OnStack(fpe, inout=veo.INTENT_OUT),
    )

    request._push_and_flush_request(
        'nlcpy_eigh',
        args,
    )

    if c_order:
        w = nlcpy.asarray(nlcpy.moveaxis(w, 0, -1), order='C')
    else:
        w = nlcpy.moveaxis(w, 0, -1)
    if jobz:
        if c_order:
            a = nlcpy.asarray(nlcpy.moveaxis(a, (1, 0), (-1, -2)), order='C')
        else:
            a = nlcpy.moveaxis(a, (1, 0), (-1, -2))
        return w, a
    else:
        return w
Esempio n. 6
0
def unique(ar, return_index=False, return_inverse=False, return_counts=False, axis=None):
    """Finds the unique elements of an array.

    Returns the sorted unique elements of an array.
    There are three optional outputs in addition to the unique elements:

    - the indices of the input array that give the unique values
    - the indices of the unique array that reconstruct the input array
    - the number of times each unique value comes up in the input array

    Parameters
    ----------
    ar : array_like
        Input array.
        Unless *axis* is specified, this will be flattened if it is not already 1-D.

    return_index : bool, optional
        If True, also return the indices of *ar* (along the specified axis, if provided,
        or in the flattened array) that result in the unique array.

    return_inverse : bool, optional
        If True, also return the indices of the unique array (for the specified axis,
        if provided) that can be used to reconstruct *ar*.

    return_counts : bool, optional
        If True, also return the number of times each unique item appears in *ar*.

    axis : int or None, optional
        The axis to operate on. If None, *ar* will be flattened. If an integer, the
        subarrays indexed by the given axis will be flattened and treated as the
        elements of a 1-D array with the dimension of the given axis, see the notes
        for more details. Object arrays or structured arrays that contain objects are
        not supported if the *axis* kwarg is used. The default is None.

    Returns
    -------
    unique : ndarray
        The sorted unique values.

    unique_indices : ndarray, optional
        The indices of the first occurrences of the unique values in the original array.
        Only provided if *return_index* is True.

    unique_inverse : ndarray, optional
        The indices to reconstruct the original array from the unique array.
        Only provided if *return_inverse* is True.

    unique_count : ndarray, optional
        The number of times each of the unique values comes up in the original array.
        Only provided if *return_counts* is True.

    Restriction
    -----------
    *NotImplementedError*:

      - If 'c' is contained in *ar.dtype.kind*.

    Note
    ----
    When an axis is specified the subarrays indexed by the axis are sorted. This is done
    by making the specified axis the first dimension of the array and then flattening
    the subarrays in C order. The flattened subarrays are then viewed as a structured
    type with each element given a label, with the effect that we end up with a 1-D
    array of structured types that can be treated in the same way as any other 1-D
    array. The result is that the flattened subarrays are sorted in lexicographic order
    starting with the first element.

    Examples
    --------
    >>> import nlcpy as vp
    >>> vp.unique([1, 1, 2, 2, 3, 3])
    array([1, 2, 3])
    >>> a =vp.array([[1, 1], [2, 3]])
    >>> vp.unique(a)
    array([1, 2, 3])

    Return the unique rows of a 2D array

    >>> a = vp.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
    >>> vp.unique(a, axis=0)
    array([[1, 0, 0],
           [2, 3, 4]])

    Return the indices of the original array that give the unique values:

    >>> a = vp.array([1, 2, 2, 3, 1])
    >>> u, indices = vp.unique(a, return_index=True)
    >>> u
    array([1, 2, 3])
    >>> indices
    array([0, 1, 3])
    >>> a[indices]
    array([1, 2, 3])

    Reconstruct the input array from the unique values:

    >>> a = vp.array([1, 2, 6, 4, 2, 3, 2])
    >>> u, indices = vp.unique(a, return_inverse=True)
    >>> u
    array([1, 2, 3, 4, 6])
    >>> indices
    array([0, 1, 4, 3, 1, 2, 1])
    >>> u[indices]
    array([1, 2, 6, 4, 2, 3, 2])
    """
    ar = nlcpy.asanyarray(ar)
    if axis is not None:
        if axis < 0:
            axis = axis + ar.ndim
        if axis < 0 or axis >= ar.ndim:
            raise AxisError('Axis out of range')
    if ar.ndim > 1 and axis is not None:
        if ar.size == 0:
            if axis is None:
                shape = ()
            else:
                shape = list(ar.shape)
                shape[axis] = int(shape[axis] / 2)
            return nlcpy.empty(shape, dtype=ar.dtype)
        ar = nlcpy.moveaxis(ar, axis, 0)
        orig_shape = ar.shape
        ar = ar.reshape(orig_shape[0], -1)
        aux = nlcpy.array(ar)
        perm = nlcpy.empty(ar.shape[0], dtype='l')
        request._push_request(
            'nlcpy_sort_multi',
            'sorting_op',
            (ar, aux, perm, return_index)
        )
        mask = nlcpy.empty(aux.shape[0], dtype='?')
        mask[0] = True
        mask[1:] = nlcpy.any(aux[1:] != aux[:-1], axis=1)
        ret = aux[mask]
        ret = ret.reshape(-1, *orig_shape[1:])
        ret = nlcpy.moveaxis(ret, 0, axis)
    else:
        ar = ar.flatten()
        if return_index or return_inverse:
            perm = ar.argsort(kind='stable' if return_index else None)
            aux = ar[perm]
        else:
            ar.sort()
            aux = ar
        mask = nlcpy.empty(aux.shape[0], dtype='?')
        if mask.size:
            mask[0] = True
            mask[1:] = aux[1:] != aux[:-1]
        ret = aux[mask]

    if not return_index and not return_inverse and not return_counts:
        return ret

    ret = (ret,)
    if return_index:
        ret += (perm[mask],)
    if return_inverse:
        imask = nlcpy.cumsum(mask) - 1
        inv_idx = nlcpy.empty(mask.shape, dtype=nlcpy.intp)
        inv_idx[perm] = imask
        ret += (inv_idx,)
    if return_counts:
        nonzero = nlcpy.nonzero(mask)[0]
        idx = nlcpy.empty((nonzero.size + 1,), nonzero.dtype)
        idx[:-1] = nonzero
        idx[-1] = mask.size
        ret += (idx[1:] - idx[:-1],)
    return ret
Esempio n. 7
0
def insert(arr, obj, values, axis=None):
    """Inserts values along the given axis before the given indices.

    Parameters
    ----------
    arr : array_like
        Input array.
    obj : int, slice or sequence of ints
        Object that defines the index or indices before which values is inserted.
        Support for multiple insertions when obj is a single scalar or a sequence
        with one element (similar to calling insert multiple times).
    values : array_like
        Values to insert into arr. If the type of values is different from that of
        arr, values is converted to the type of arr. values should be shaped so that
        arr[...,obj,...] = values is legal.
    axis : int, optional
        Axis along which to insert values. If axis is None then arr is flattened
        first.

    Returns
    -------
    out : ndarray
        A copy of arr with values inserted. Note that insert does not occur in-place:
        a new array is returned. If axis is None, out is a flattened array.

    Note:
        Note that for higher dimensional inserts obj=0 behaves very different from
        obj=[0] just like arr[:,0,:] = values is different from arr[:,[0],:] = values.

    See Also
    --------
    append : Appends values to the end of an array.
    concatenate : Joins a sequence of arrays along an existing axis.
    delete : Returns a new array with sub-arrays along an axis deleted.

    Examples
    --------
    >>> import nlcpy as vp
    >>> from nlcpy import testing
    >>> a = vp.array([[1, 1], [2, 2], [3, 3]])
    >>> a
    array([[1, 1],
           [2, 2],
           [3, 3]])
    >>> vp.insert(a, 1, 5)
    array([1, 5, 1, 2, 2, 3, 3])
    >>> vp.insert(a, 1, 5, axis=1)
    array([[1, 5, 1],
           [2, 5, 2],
           [3, 5, 3]])

    Difference between sequence and scalars:

    >>> vp.insert(a, [1], [[1],[2],[3]], axis=1)
    array([[1, 1, 1],
           [2, 2, 2],
           [3, 3, 3]])
    >>> vp.testing.assert_array_equal(
    ...                vp.insert(a, 1, [1, 2, 3], axis=1),
    ...                vp.insert(a, [1], [[1],[2],[3]], axis=1))
    >>> b = a.flatten()
    >>> b
    array([1, 1, 2, 2, 3, 3])
    >>> vp.insert(b, [2, 2], [5, 6])
    array([1, 1, 5, 6, 2, 2, 3, 3])
    >>> vp.insert(b, slice(2, 4), [5, 6])
    array([1, 1, 5, 2, 6, 2, 3, 3])
    >>> vp.insert(b, [2, 2], [7.13, False]) # type casting
    array([1, 1, 7, 0, 2, 2, 3, 3])
    >>> x = vp.arange(8).reshape(2, 4)
    >>> idx = (1, 3)
    >>> vp.insert(x, idx, 999, axis=1)
    array([[  0, 999,   1,   2, 999,   3],
           [  4, 999,   5,   6, 999,   7]])

    """
    a = nlcpy.asarray(arr)
    if axis is None:
        if a.ndim != 1:
            a = a.ravel()
        axis = 0
    elif isinstance(axis, nlcpy.ndarray) or isinstance(axis, numpy.ndarray):
        axis = int(axis)
    elif not isinstance(axis, int):
        raise TypeError("an integer is required "
                        "(got type {0})".format(type(axis).__name__))

    if axis < -a.ndim or axis >= a.ndim:
        raise nlcpy.AxisError(
            "axis {0} is out of bounds for array of dimension {1}".format(axis, a.ndim))

    if axis < 0:
        axis += a.ndim

    if type(obj) is slice:
        start, stop, step = obj.indices(a.shape[axis])
        obj = nlcpy.arange(start, stop, step)
    else:
        obj = nlcpy.array(obj)
        if obj.dtype.char == '?':
            warnings.warn(
                "in the future insert will treat boolean arrays and "
                "array-likes as a boolean index instead of casting it to "
                "integer", FutureWarning, stacklevel=3)
        elif obj.dtype.char in 'fdFD':
            if obj.size == 1:
                raise TypeError(
                    "slice indices must be integers or "
                    "None or have an __index__ method")
            elif obj.size > 0:
                raise IndexError(
                    'arrays used as indices must be of integer (or boolean) type')
        elif obj.dtype.char in 'IL':
            if obj.size == 1:
                objval = obj[()] if obj.ndim == 0 else obj[0]
                if objval > a.shape[axis]:
                    raise IndexError(
                        "index {0} is out of bounds for axis {1} with size {2}".format(
                            objval, axis, a.shape[axis]))
            else:
                tmp = 'float64' if obj.dtype.char == 'L' else 'int64'
                raise UFuncTypeError(
                    "Cannot cast ufunc 'add' output from dtype('{0}') to "
                    "dtype('{1}') with casting rule 'same_kind'".format(tmp, obj.dtype))
        obj = obj.astype('l')
        if obj.ndim > 1:
            raise ValueError(
                "index array argument obj to insert must be one dimensional or scalar")

    if obj.ndim == 0:
        if obj > a.shape[axis] or obj < -a.shape[axis]:
            raise IndexError(
                "index {0} is out of bounds for axis {1} with size {2}".format(
                    obj[()] if obj > 0 else obj[()] + a.shape[axis],
                    axis, a.shape[axis]))

    newshape = list(a.shape)
    if obj.size == 1:
        values = nlcpy.array(values, copy=False, ndmin=a.ndim, dtype=a.dtype)
        if obj.ndim == 0:
            values = nlcpy.moveaxis(values, 0, axis)
        newshape[axis] += values.shape[axis]
        obj = nlcpy.array(nlcpy.broadcast_to(obj, values.shape[axis]))
        val_shape = list(a.shape)
        val_shape[axis] = values.shape[axis]
        values = nlcpy.broadcast_to(values, val_shape)
    else:
        newshape[axis] += obj.size
        values = nlcpy.array(values, copy=False, ndmin=a.ndim, dtype=a.dtype)
        val_shape = list(a.shape)
        val_shape[axis] = obj.size
        values = nlcpy.broadcast_to(values, val_shape)

    out = nlcpy.empty(newshape, dtype=a.dtype)
    work = nlcpy.zeros(obj.size + out.shape[axis] + 2, dtype='l')
    work[-1] = -1
    request._push_request(
        'nlcpy_insert',
        'manipulation_op',
        (a, obj, values, out, axis, work)
    )
    if work[-1] != -1:
        raise IndexError(
            "index {0} is out of bounds for axis {1} with size {2}"
            .format(obj[work[-1]], axis, out.shape[axis]))
    return out
Esempio n. 8
0
def norm(x, ord=None, axis=None, keepdims=False):
    """Returns matrix or vector norm.

    This function is able to return one of eight different matrix norms, or one of an
    infinite number of vector norms (described below), depending on the value of the
    ``ord`` parameter.

    Parameters
    ----------
    x : array_like
        Input array. If *axis* is None, *x* must be 1-D or 2-D.
    ord : {non-zero int, inf, -inf, 'fro', 'nuc'}, optional
        Order of the norm (see table under ``Note``). inf means nlcpy's *inf* object.
    axis : {None, int, 2-tuple of ints}, optional
        If *axis* is an integer, it specifies the axis of *x* along which to compute the
        vector norms. If *axis* is a 2-tuple, it specifies the axes that hold 2-D
        matrices, and the matrix norms of these matrices are computed. If *axis* is None
        then either a vector norm (when *x* is 1-D) or a matrix norm (when *x* is 2-D) is
        returned.
    keepdims : bool, optional
        If this is set to True, the axes which are normed over are left in the result as
        dimensions with size one. With this option the result will broadcast correctly
        against the original x.

    Returns
    -------
    n : ndarray
        Norm of the matrix or vector(s).

    Note
    ----
    For values of ``ord < 1``, the result is, strictly speaking, not a mathematical
    'norm', but it may still be useful for various numerical purposes. The following
    norms can be calculated:

    .. csv-table::
        :header: ord, norm for matrices, norm for vectors

        None, Frobenius norm, 2-norm
        'fro', Frobenius norm, \-
        'nuc', nuclear norm, \-
        inf, "max(sum(abs(x), axis=1))", max(abs(x))
        -inf, "min(sum(abs(x), axis=1))", min(abs(x))
        0, \-, sum(x != 0)
        1, "max(sum(abs(x), axis=0))", as below
        -1, "min(sum(abs(x), axis=0))", as below
        2, 2-norm (largest sing. value), as below
        -2, smallest singular value, as below
        other, \-, sum(abs(x)**ord)**(1./ord)

    The Frobenius norm is given by :math:`|A|_F = [\\sum_{i,j}abs(a_{i,j})^2]^{1/2}`

    The nuclear norm is the sum of the singular values.

    Examples
    --------
    >>> import nlcpy as vp
    >>> a = vp.arange(9) - 4
    >>> a
    array([-4, -3, -2, -1,  0,  1,  2,  3,  4])
    >>> b = a.reshape((3, 3))
    >>> b
    array([[-4, -3, -2],
           [-1,  0,  1],
           [ 2,  3,  4]])
    >>> vp.linalg.norm(a)    # doctest: +SKIP
    array(7.74596669)
    >>> vp.linalg.norm(b)    # doctest: +SKIP
    array(7.74596669)
    >>> vp.linalg.norm(b, 'fro')   # doctest: +SKIP
    array(7.74596669)
    >>> vp.linalg.norm(a, vp.inf)  # doctest: +SKIP
    array(4.)
    >>> vp.linalg.norm(b, vp.inf)  # doctest: +SKIP
    array(9.)
    >>> vp.linalg.norm(a, -vp.inf)  # doctest: +SKIP
    array(0.)
    >>> vp.linalg.norm(b, -vp.inf)  # doctest: +SKIP
    array(2.)
    >>> vp.linalg.norm(a, 1)   # doctest: +SKIP
    array(20.)
    >>> vp.linalg.norm(b, 1)   # doctest: +SKIP
    array(7.)
    >>> vp.linalg.norm(a, -1)  # doctest: +SKIP
    array(0.)
    >>> vp.linalg.norm(b, -1)  # doctest: +SKIP
    array(6.)
    >>> vp.linalg.norm(a, 2)   # doctest: +SKIP
    array(7.74596669)
    >>> vp.linalg.norm(b, 2)  # doctest: +SKIP
    array(7.34846923)
    >>> vp.linalg.norm(a, -2) # doctest: +SKIP
    array(0.)
    >>> vp.linalg.norm(b, -2) # doctest: +SKIP
    array(3.75757704e-16)
    >>> vp.linalg.norm(a, 3)  # doctest: +SKIP
    array(5.84803548)
    >>> vp.linalg.norm(a, -3) # doctest: +SKIP
    array(0.)

    Using the *axis* argument to compute vector norms:

    >>> c = vp.array([[ 1, 2, 3],
    ...               [-1, 1, 4]])
    >>> vp.linalg.norm(c, axis=0)   # doctest: +SKIP
    array([1.41421356, 2.23606798, 5.        ])
    >>> vp.linalg.norm(c, axis=1)   # doctest: +SKIP
    array([3.74165739, 4.24264069])
    >>> vp.linalg.norm(c, ord=1, axis=1)  # doctest: +SKIP
    array([6., 6.])

    Using the axis argument to compute matrix norms:

    >>> m = vp.arange(8).reshape(2,2,2)
    >>> vp.linalg.norm(m, axis=(1,2))   # doctest: +SKIP
    array([ 3.74165739, 11.22497216])
    >>> vp.linalg.norm(m[0, :, :]), vp.linalg.norm(m[1, :, :])  # doctest: +SKIP
    (array(3.74165739), array(11.22497216))

    """
    x = nlcpy.asarray(x)
    if x.dtype.char in '?ilIL':
        x = nlcpy.array(x, dtype='d')
    # used to match the contiguous of result to numpy.
    order = 'F' if x.flags.f_contiguous and not x.flags.c_contiguous else 'C'

    # Immediately handle some default, simple, fast, and common cases.
    if axis is None:
        ret = None
        ndim = x.ndim
        axis = tuple(range(x.ndim))
        if ord is None and x.ndim == 2:
            ret = _lange(x, ord, axis)
        elif ord is None or ord == 2 and x.ndim == 1:
            x = x.ravel()
            if x.dtype.char in 'FD':
                sqnorm = nlcpy.dot(x.real, x.real) + nlcpy.dot(x.imag, x.imag)
            else:
                sqnorm = nlcpy.dot(x, x)
            ret = nlcpy.sqrt(sqnorm)
        if ret is not None:
            if keepdims:
                ret = ret.reshape(ndim * [1])
            return ret
    elif not isinstance(axis, tuple):
        try:
            axis = (int(axis), )
        except Exception:
            raise TypeError(
                "'axis' must be None, an integer or a tuple of integers")

    if len(axis) == 1:
        if ord == nlcpy.inf:
            return abs(x).max(axis=axis, keepdims=keepdims)
        elif ord == -nlcpy.inf:
            return abs(x).min(axis=axis, keepdims=keepdims)
        elif ord == 0:
            return nlcpy.sum((x != 0).astype(x.real.dtype),
                             axis=axis,
                             keepdims=keepdims)
        elif ord == 1:
            return nlcpy.add.reduce(abs(x), axis=axis, keepdims=keepdims)
        elif ord is None or ord == 2:
            s = (nlcpy.conj(x) * x).real
            ret = nlcpy.sqrt(nlcpy.add.reduce(s, axis=axis, keepdims=keepdims))
            return nlcpy.asarray(ret, order=order)
        else:
            try:
                ord + 1
            except TypeError:
                raise ValueError("Invalid norm order for vectors.")
            ret = abs(x)**ord
            ret = nlcpy.add.reduce(ret, axis=axis, keepdims=keepdims)
            ret **= (1 / ord)
            if (keepdims or x.ndim > 1) and x.dtype.char in 'fF':
                ret = nlcpy.asarray(ret, dtype='f')
            else:
                ret = nlcpy.asarray(ret, dtype='d')
            return ret
    elif len(axis) == 2:
        row_axis, col_axis = axis
        if row_axis < 0:
            row_axis += x.ndim
        if col_axis < 0:
            col_axis += x.ndim
        if row_axis == col_axis:
            raise ValueError('Duplicate axes given.')
        if ord == 2:
            y = nlcpy.moveaxis(x, (row_axis, col_axis), (-2, -1))
            ret = nlcpy.linalg.svd(y, compute_uv=0).max(axis=-1)
        elif ord == -2:
            y = nlcpy.moveaxis(x, (row_axis, col_axis), (-2, -1))
            ret = nlcpy.linalg.svd(y, compute_uv=0).min(axis=-1)
        elif ord == 1:
            if x.shape[col_axis] == 0:
                raise ValueError(
                    'zero-size array to '
                    'reduction operation maximum which has no identity')
            ret = _lange(x, ord, axis)
        elif ord == nlcpy.inf:
            if x.shape[row_axis] == 0:
                raise ValueError(
                    'zero-size array to '
                    'reduction operation maximum which has no identity')
            ret = _lange(x, ord, axis)
        elif ord in (None, 'fro', 'f'):
            ret = _lange(x, ord, axis)
        elif ord == -1:
            if col_axis > row_axis:
                col_axis -= 1
            ret = nlcpy.add.reduce(abs(x), axis=row_axis).min(axis=col_axis)
        elif ord == -nlcpy.inf:
            if row_axis > col_axis:
                row_axis -= 1
            ret = nlcpy.add.reduce(abs(x), axis=col_axis).min(axis=row_axis)
        elif ord == 'nuc':
            y = nlcpy.moveaxis(x, (row_axis, col_axis), (-2, -1))
            ret = nlcpy.sum(nlcpy.linalg.svd(y, compute_uv=0), axis=-1)
        else:
            raise ValueError("Invalid norm order for matrices.")
        if keepdims:
            ret_shape = list(x.shape)
            ret_shape[axis[0]] = 1
            ret_shape[axis[1]] = 1
            ret = ret.reshape(ret_shape)
        ret = nlcpy.asarray(ret, order=order)
        return ret
    else:
        raise ValueError("Improper number of dimensions to norm.")
Esempio n. 9
0
def svd(a, full_matrices=True, compute_uv=True, hermitian=False):
    """Singular Value Decomposition.

    When *a* is a 2D array, it is factorized as
    ``u @ nlcpy.diag(s) @ vh = (u * s) @ vh``,
    where *u* and *vh* are 2D unitary arrays and *s* is a 1D array of *a*'s singular
    values.
    When *a* is higher-dimensional, SVD is applied in stacked mode as explained below.

    Parameters
    ----------
    a : (..., M, N) array_like
        A real or complex array with a.ndim >= 2.
    full_matrices : bool, optional
        If True (default), *u* and *vh* have the shapes ``(..., M, M)`` and ``(..., N,
        N)``, respectively. Otherwise, the shapes are ``(..., M, K)`` and ``(..., K,
        N)``, respectively, where ``K = min(M, N)``.
    compute_uv : bool, optional
        Whether or not to compute *u* and *vh* in addition to *s*. True by default.
    hermitian : bool, optional
        If True, *a* is assumed to be Hermitian (symmetric if real-valued), enabling a
        more efficient method for finding singular values. Defaults to False.

    Returns
    -------
    u : {(..., M, M), (..., M, K)} ndarray
        Unitary array(s). The first ``a.ndim - 2`` dimensions have the same size as those
        of the input *a*. The size of the last two dimensions depends on the value of
        *full_matrices*. Only returned when *compute_uv* is True.
    s : (..., K) ndarray
        Vector(s) with the singular values, within each vector sorted in descending
        order. The first ``a.ndim - 2`` dimensions have the same size as those of the
        input *a*.
    vh : {(..., N, N), (..., K, N)} ndarray
        Unitary array(s). The first ``a.ndim - 2`` dimensions have the same size as those
        of the input *a*. The size of the last two dimensions depends on the value of
        *full_matrices*. Only returned when *compute_uv* is True.

    Note
    ----
    The decomposition is performed using LAPACK routine ``_gesdd``.

    SVD is usually described for the factorization of a 2D matrix :math:`A`.
    The higher-dimensional case will be discussed below. In the 2D case, SVD is written
    as
    :math:`A=USV^{H}`, where :math:`A = a`, :math:`U = u`, :math:`S = nlcpy.diag(s)`
    and :math:`V^{H} = vh`. The 1D array `s` contains the singular values of `a` and
    `u` and `vh` are unitary. The rows of `vh` are the eigenvectors of :math:`A^{H}A`
    and the columns of `u` are the eigenvectors of :math:`AA^{H}`. In both cases the
    corresponding (possibly non-zero) eigenvalues are given by ``s**2``.

    If `a` has more than two dimensions, then broadcasting rules apply, as explained in
    :ref:`Linear algebra on several matrices at once <linalg_several_matrices_at_once>`.
    This means that SVD is working in "stacked" mode: it iterates over all indices
    of the first ``a.ndim - 2`` dimensions and for each combination SVD is applied to the
    last two indices.

    Examples
    --------
    >>> import nlcpy as vp
    >>> from nlcpy import testing
    >>> a = vp.random.randn(9, 6) + 1j*vp.random.randn(9, 6)

    Reconstruction based on full SVD, 2D case:

    >>> u, s, vh = vp.linalg.svd(a, full_matrices=True)
    >>> u.shape, s.shape, vh.shape
    ((9, 9), (6,), (6, 6))
    >>> vp.testing.assert_allclose(a, vp.dot(u[:, :6] * s, vh))
    >>> smat = vp.zeros((9, 6), dtype=complex)
    >>> smat[:6, :6] = vp.diag(s)
    >>> vp.testing.assert_allclose(a, vp.dot(u, vp.dot(smat, vh)))

    Reconstruction based on reduced SVD, 2D case:

    >>> u, s, vh = vp.linalg.svd(a, full_matrices=False)
    >>> u.shape, s.shape, vh.shape
    ((9, 6), (6,), (6, 6))
    >>> vp.testing.assert_allclose(a, vp.dot(u * s, vh))
    >>> smat = vp.diag(s)
    >>> vp.testing.assert_allclose(a, vp.dot(u, vp.dot(smat, vh)))

    """
    a = nlcpy.asarray(a)
    util._assertRankAtLeast2(a)
    if hermitian:
        util._assertNdSquareness(a)

    # used to match the contiguous of result to numpy.
    c_order = a.flags.c_contiguous or sum([i > 1 for i in a.shape[:-2]]) < 2

    a_complex = a.dtype.char in 'FD'
    if a.dtype == 'F':
        dtype = 'F'
        f_dtype = 'f'
    elif a.dtype == 'D':
        dtype = 'D'
        f_dtype = 'd'
    elif a.dtype == 'f':
        dtype = 'f'
        f_dtype = 'f'
    else:
        dtype = 'd'
        f_dtype = 'd'

    if hermitian:
        if compute_uv:
            # lapack returns eigenvalues in reverse order, so to reconsist.
            s, u = nlcpy.linalg.eigh(a)
            signs = nlcpy.sign(s)
            s = abs(s)
            sidx = nlcpy.argsort(s)[..., ::-1]
            signs = _take_along_axis(signs, sidx, signs.ndim - 1)
            s = _take_along_axis(s, sidx, s.ndim - 1)
            u = _take_along_axis(u, sidx[..., None, :], u.ndim - 1)
            # singular values are unsigned, move the sign into v
            vt = nlcpy.conjugate(u * signs[..., None, :])
            vt = nlcpy.moveaxis(vt, -2, -1)
            return u, s, vt
        else:
            s = nlcpy.linalg.eigvalsh(a)
            s = nlcpy.sort(abs(s))[..., ::-1]
            return s

    m = a.shape[-2]
    n = a.shape[-1]
    min_mn = min(m, n)
    max_mn = max(m, n)
    if a.size == 0:
        s = nlcpy.empty(a.shape[:-2] + (min_mn, ), f_dtype)
        if compute_uv:
            if full_matrices:
                u_shape = a.shape[:-1] + (m, )
                vt_shape = a.shape[:-2] + (n, n)
            else:
                u_shape = a.shape[:-1] + (min_mn, )
                vt_shape = a.shape[:-2] + (min_mn, n)
            u = nlcpy.empty(u_shape, dtype=dtype)
            vt = nlcpy.empty(vt_shape, dtype=dtype)
            return u, s, vt
        else:
            return s

    a = nlcpy.array(nlcpy.moveaxis(a, (-1, -2), (1, 0)),
                    dtype=dtype,
                    order='F')
    if compute_uv:
        if full_matrices:
            u = nlcpy.empty((m, m) + a.shape[2:], dtype=dtype, order='F')
            vt = nlcpy.empty((n, n) + a.shape[2:], dtype=dtype, order='F')
            job = 'A'
        else:
            u = nlcpy.empty((m, m) + a.shape[2:], dtype=dtype, order='F')
            vt = nlcpy.empty((min_mn, n) + a.shape[2:], dtype=dtype, order='F')
            job = 'S'
    else:
        u = nlcpy.empty(1)
        vt = nlcpy.empty(1)
        job = 'N'

    if a_complex:
        mnthr1 = int(min_mn * 17.0 / 9.0)
        if max_mn >= mnthr1:
            if job == 'N':
                lwork = 130 * min_mn
            elif job == 'S':
                lwork = (min_mn + 130) * min_mn
            else:
                lwork = max(
                    (min_mn + 130) * min_mn,
                    (min_mn + 1) * min_mn + 32 * max_mn,
                )
        else:
            lwork = 64 * (min_mn + max_mn) + 2 * min_mn
    else:
        mnthr = int(min_mn * 11.0 / 6.0)
        if m >= n:
            if m >= mnthr:
                if job == 'N':
                    lwork = 131 * n
                elif job == 'S':
                    lwork = max((131 + n) * n, (4 * n + 7) * n)
                else:
                    lwork = max((n + 131) * n, (n + 1) * n + 32 * m,
                                (4 * n + 6) * n + m)
            else:
                if job == 'N':
                    lwork = 64 * m + 67 * n
                elif job == 'S':
                    lwork = max(64 * m + 67 * n, (3 * n + 7) * n)
                else:
                    lwork = (3 * n + 7) * n
        else:
            if n >= mnthr:
                if job == 'N':
                    lwork = 131 * m
                elif job == 'S':
                    lwork = max((m + 131) * m, (4 * m + 7) * m)
                else:
                    lwork = max((m + 131) * m, (m + 1) * m + 32 * n,
                                (4 * m + 7) * m)
            else:
                if job == 'N':
                    lwork = 67 * m + 64 * n
                else:
                    lwork = max(67 * m + 64 * n, (3 * m + 7) * m)

    s = nlcpy.empty((min_mn, ) + a.shape[2:], dtype=f_dtype, order='F')
    work = nlcpy.empty(lwork, dtype=dtype)
    if a_complex:
        if job == 'N':
            lrwork = 5 * min_mn
        else:
            lrwork = min_mn * max(5 * min_mn + 7,
                                  2 * max(m, n) + 2 * min_mn + 1)
    else:
        lrwork = 1
    rwork = nlcpy.empty(lrwork, dtype=f_dtype)
    iwork = nlcpy.empty(8 * min_mn, dtype=f_dtype)
    info = numpy.empty(1, dtype='l')
    fpe = request._get_fpe_flag()
    args = (
        ord(job),
        a._ve_array,
        s._ve_array,
        u._ve_array,
        vt._ve_array,
        work._ve_array,
        rwork._ve_array,
        iwork._ve_array,
        veo.OnStack(info, inout=veo.INTENT_OUT),
        veo.OnStack(fpe, inout=veo.INTENT_OUT),
    )

    request._push_and_flush_request(
        'nlcpy_svd',
        args,
    )

    if c_order:
        s = nlcpy.asarray(nlcpy.moveaxis(s, 0, -1), order='C')
    else:
        s = nlcpy.moveaxis(s, 0, -1)
    if compute_uv:
        u = nlcpy.moveaxis(u, (1, 0), (-1, -2))
        if not full_matrices:
            u = u[..., :m, :min_mn]
        if c_order:
            u = nlcpy.asarray(u, dtype=dtype, order='C')
            vt = nlcpy.asarray(nlcpy.moveaxis(vt, (1, 0), (-1, -2)),
                               dtype,
                               order='C')
        else:
            vt = nlcpy.moveaxis(nlcpy.asarray(vt, dtype), (1, 0), (-1, -2))
        return u, s, vt
    else:
        return s
Esempio n. 10
0
def cholesky(a):
    """Cholesky decomposition.

    Return the Cholesky decomposition, *L* * *L.H*, of the square matrix *a*, where *L*
    is lower-triangular and *.H* is the conjugate transpose operator (which is the
    ordinary transpose if *a* is real-valued). *a* must be Hermitian (symmetric if
    real-valued) and positive-definite. Only *L* is actually returned.

    Parameters
    ----------
    a : (..., M, M) array_like
        Hermitian (symmetric if all elements are real), positive-definite input matrix.

    Returns
    -------
    L : (..., M, M) ndarray
        Upper or lower-triangular Cholesky factor of *a*.

    Note
    ----
    The Cholesky decomposition is often used as a fast way of solving :math:`Ax = b`

    (when *A* is both Hermitian/symmetric and positive-definite).

    First, we solve for y in :math:`Ly = b`, and then for x in :math:`L.Hx = y`.

    Examples
    --------
    >>> import nlcpy as vp
    >>> A = vp.array([[1,-2j],[2j,5]])
    >>> A
    array([[ 1.+0.j, -0.-2.j],
           [ 0.+2.j,  5.+0.j]])
    >>> L = vp.linalg.cholesky(A)
    >>> L
    array([[1.+0.j, 0.+0.j],
           [0.+2.j, 1.+0.j]])
    >>> vp.dot(L, vp.conjugate(L.T)) # verify that L * L.H = A
    array([[1.+0.j, 0.-2.j],
           [0.+2.j, 5.+0.j]])

    """
    a = nlcpy.asarray(a)
    util._assertRankAtLeast2(a)
    util._assertNdSquareness(a)

    if a.dtype == 'F':
        dtype = 'D'
        L_dtype = 'F'
    elif a.dtype == 'D':
        dtype = 'D'
        L_dtype = 'D'
    elif a.dtype == 'f':
        dtype = 'd'
        L_dtype = 'f'
    else:
        dtype = 'd'
        L_dtype = 'd'

    if a.size == 0:
        return nlcpy.empty(a.shape, dtype=L_dtype)

    # used to match the contiguous of result to numpy.
    c_order = a.flags.c_contiguous or sum([i > 1 for i in a.shape[:-2]]) < 2

    a = nlcpy.array(nlcpy.moveaxis(a, (-1, -2), (1, 0)),
                    dtype=dtype,
                    order='F')
    info = numpy.empty(1, dtype='l')
    fpe = request._get_fpe_flag()
    args = (
        a._ve_array,
        veo.OnStack(info, inout=veo.INTENT_OUT),
        veo.OnStack(fpe, inout=veo.INTENT_OUT),
    )

    request._push_and_flush_request(
        'nlcpy_cholesky', args, callback=util._assertPositiveDefinite(info))

    if c_order:
        L = nlcpy.asarray(nlcpy.moveaxis(a, (1, 0), (-1, -2)),
                          dtype=L_dtype,
                          order='C')
    else:
        L = nlcpy.moveaxis(nlcpy.asarray(a, dtype=L_dtype), (1, 0), (-1, -2))
    return L
Esempio n. 11
0
def linspace(start,
             stop,
             num=50,
             endpoint=True,
             retstep=False,
             dtype=None,
             axis=0):
    """Returns evenly spaced numbers over a specified interval.

    Returns *num* evenly spaced samples, calculated over the interval ``[start, stop]``.
    The endpoint of the interval can optionally be excluded.

    Parameters
    ----------
    start : array_like
        The starting value of the sequence.
    stop : array_like
        The end value of the sequence, unless *endpoint* is set to False. In that case,
        the sequence consists of all but the last of ``num + 1`` evenly spaced samples,
        so that *stop* is excluded. Note that the step size changes when *endpoint* is
        False.
    num : int, optional
        Number of samples to generate. Default is 50. Must be non-negative.
    endpoint : bool, optional
        If True, *stop* is the last sample. Otherwise, it is not included. Default is
        True.
    retstep : bool, optional
        If True, return (*samples*, *step*) where *step* is the spacing between samples.
    dtype : dtype, optional
        The type of the output array. If *dtype* is not given, infer the data type from
        the other input arguments.
    axis : int, optional
        The axis in the result to store the samples. Relevant only if start or stop are
        array-like. By default (0), the samples will be along a new axis inserted at the
        beginning. Use -1 to get an axis at the end.

    Returns
    -------
    samples : ndarray
        There are *num* equally spaced samples in the closed interval ``[start, stop]``
        or the half-open interval ``[start, stop)`` (depending on whether *endpoint* is
        True or False).
    step : float, optional
        Only returned if *retstep* is True
        Size of spacing between samples.

    See Also
    --------
    arange : Returns evenly spaced values within a given interval.

    Examples
    --------
    >>> import nlcpy as vp
    >>> vp.linspace(2.0, 3.0, num=5)
    array([2.  , 2.25, 2.5 , 2.75, 3.  ])
    >>> vp.linspace(2.0, 3.0, num=5, endpoint=False)
    array([2. , 2.2, 2.4, 2.6, 2.8])
    >>> vp.linspace(2.0, 3.0, num=5, retstep=True)
    (array([2.  , 2.25, 2.5 , 2.75, 3.  ]), array([0.25]))

    """
    num = operator.index(num)
    if num < 0:
        raise ValueError("Number of samples, %s, must be non-negative." % num)

    dtype_kind = numpy.dtype(dtype).kind
    if dtype_kind == 'V':
        raise NotImplementedError(
            'void dtype in linspace is not implemented yet.')

    start = nlcpy.asarray(start)
    stop = nlcpy.asarray(stop)
    dt = numpy.result_type(start, stop, float(num))
    if start.dtype.char in '?iIlL' or stop.dtype.char in '?iIlL':
        dt = 'D' if dt.char in 'FD' else 'd'

    if dtype is None:
        dtype = dt

    start = nlcpy.asarray(start, dtype=dt)
    stop = nlcpy.asarray(stop, dtype=dt)
    delta = stop - start
    div = (num - 1) if endpoint else num
    if num == 0:
        ret = nlcpy.empty((num, ) + delta.shape, dtype=dtype)
        if retstep:
            ret = (ret, nlcpy.NaN)
        return ret
    elif div == 0 or num == 1:
        ret = nlcpy.resize(start, (1, ) + delta.shape).astype(dtype)
        if retstep:
            ret = (ret, stop)
        return ret
    else:
        ret = nlcpy.empty((num, ) + delta.shape, dtype=dtype)
    retdata = ret

    delta = delta[nlcpy.newaxis]
    start = nlcpy.array(nlcpy.broadcast_to(start, delta.shape))
    stop = nlcpy.array(nlcpy.broadcast_to(stop, delta.shape))
    step = delta / div if div > 1 else delta
    if retdata._memloc in {on_VE, on_VE_VH}:
        denormal = nlcpy.zeros(1, dtype='l')
        request._push_request(
            "nlcpy_linspace", "creation_op",
            (ret, start, stop, delta, step, int(endpoint), denormal))
        if axis != 0:
            ret = nlcpy.moveaxis(ret, 0, axis)
        if retstep:
            ret = (ret, step)

    if retdata._memloc in {on_VH, on_VE_VH}:
        del retdata.vh_data
        del step.vh_data
        typ = numpy.dtype(dtype).type
        if retstep:
            (retdata.vh_data,
             step.vh_data) = numpy.linspace(typ(start),
                                            typ(stop), num, endpoint,
                                            typ(retstep), dtype, axis)
        else:
            retdata.vh_data = numpy.linspace(typ(start),
                                             typ(stop), num, endpoint,
                                             typ(retstep), dtype, axis)
    return ret
Esempio n. 12
0
def inv(a):
    """Computes the (multiplicative) inverse of a matrix.

    Given a square matrix *a*, return the matrix *ainv* satisfying

    ::

        dot(a, ainv) = dot(ainv, a) = eye(a.shape[0]).

    Parameters
    ----------
    a : (..., M, M) array_like
        Matrix to be inverted.

    Returns
    -------
    ainv : (..., M, M) ndarray
        (Multiplicative) inverse of the matrix *a*.

    Note
    ----
    Broadcasting rules apply, see the :ref:`nlcpy.linalg <nlcpy_linalg>`
    documentation for details.

    Examples
    --------
    >>> import nlcpy as vp
    >>> from nlcpy import testing
    >>> a = vp.array([[1., 2.], [3., 4.]])
    >>> ainv = vp.linalg.inv(a)
    >>> vp.testing.assert_allclose(vp.dot(a, ainv), vp.eye(2), atol=1e-8, rtol=1e-5)
    >>> vp.testing.assert_allclose(vp.dot(ainv, a), vp.eye(2), atol=1e-8, rtol=1e-5)

    Inverses of several matrices can be computed at once:

    >>> a = vp.array([[[1., 2.], [3., 4.]], [[1, 3], [3, 5]]])
    >>> vp.linalg.inv(a)   # doctest: +SKIP
    array([[[-2.  ,  1.  ],
            [ 1.5 , -0.5 ]],
    <BLANKLINE>
           [[-1.25,  0.75],
            [ 0.75, -0.25]]])

    """
    a = nlcpy.asarray(a)
    # used to match the contiguous of result to numpy.
    c_order = a.flags.c_contiguous or sum([i > 1 for i in a.shape[:-2]]) < 2
    util._assertRankAtLeast2(a)
    util._assertNdSquareness(a)
    if a.dtype.char in 'FD':
        dtype = 'D'
        if a.dtype.char in 'fF':
            ainv_dtype = 'F'
        else:
            ainv_dtype = 'D'
    else:
        dtype = 'd'
        if a.dtype.char == 'f':
            ainv_dtype = 'f'
        else:
            ainv_dtype = 'd'
    if a.size == 0:
        return nlcpy.asarray(a, dtype=ainv_dtype)
    a = nlcpy.array(nlcpy.moveaxis(a, (-1, -2), (1, 0)),
                    dtype=dtype,
                    order='F')
    ipiv = nlcpy.empty(a.shape[-1])
    work = nlcpy.empty(a.shape[-1] * 256)
    info = numpy.empty(1, dtype='l')
    fpe = request._get_fpe_flag()
    args = (
        a._ve_array,
        ipiv._ve_array,
        work._ve_array,
        veo.OnStack(info, inout=veo.INTENT_OUT),
        veo.OnStack(fpe, inout=veo.INTENT_OUT),
    )

    request._push_and_flush_request('nlcpy_inv',
                                    args,
                                    callback=util._assertNotSingular(info))

    if c_order:
        a = nlcpy.moveaxis(a, (1, 0), (-1, -2))
        return nlcpy.asarray(a, dtype=ainv_dtype, order='C')
    else:
        a = nlcpy.asarray(a, dtype=ainv_dtype)
        return nlcpy.moveaxis(a, (1, 0), (-1, -2))
Esempio n. 13
0
def solve(a, b):
    """Solves a linear matrix equation, or system of linear scalar equations.

    Computes the "exact" solution, *x*, of the well-determined, i.e., full rank, linear
    matrix equation :math:`ax = b`.

    Parameters
    ----------
    a : (..., M, M) array_like
        Coefficient matrix.
    b : {(..., M,), (..., M, K)} array_like
        Ordinate or "dependent variable" values.

    Returns
    -------
    x : {(..., M,), (..., M, K)} ndarray
        Solution to the system a x = b. Returned shape is identical to *b*.

    Note
    ----
    The solutions are computed using LAPACK routine ``_gesv``.

    `a` must be square and of full-rank, i.e., all rows (or, equivalently, columns) must
    be linearly independent; if either is not true, use :func:`lstsq` for the
    least-squares best "solution" of the system/equation.

    Examples
    --------
    Solve the system of equations ``3 * x0 + x1 = 9`` and ``x0 + 2 * x1 = 8``:

    >>> import nlcpy as vp
    >>> a = vp.array([[3,1], [1,2]])
    >>> b = vp.array([9,8])
    >>> x = vp.linalg.solve(a, b)
    >>> x
    array([2., 3.])

    """
    a = nlcpy.asarray(a)
    b = nlcpy.asarray(b)
    c_order = (a.flags.c_contiguous or a.ndim < 4 or
               a.ndim - b.ndim < 2 and b.flags.c_contiguous) and \
        not (a.ndim < b.ndim and not b.flags.c_contiguous)
    util._assertRankAtLeast2(a)
    util._assertNdSquareness(a)

    if a.ndim - 1 == b.ndim:
        if a.shape[-1] != b.shape[-1]:
            raise ValueError(
                'solve1: Input operand 1 has a mismatch in '
                'its core dimension 0, with gufunc signature (m,m),(m)->(m) '
                '(size {0} is different from {1})'.format(
                    b.shape[-1], a.shape[-1]))
    elif b.ndim == 1:
        raise ValueError(
            'solve: Input operand 1 does not have enough dimensions '
            '(has 1, gufunc core with signature (m,m),(m,n)->(m,n) requires 2)'
        )
    else:
        if a.shape[-1] != b.shape[-2]:
            raise ValueError(
                'solve: Input operand 1 has a mismatch in '
                'its core dimension 0, with gufunc signature (m,m),(m,n)->(m,n) '
                '(size {0} is different from {1})'.format(
                    b.shape[-2], a.shape[-1]))

    if b.ndim == 1 or a.ndim - 1 == b.ndim and a.shape[-1] == b.shape[-1]:
        tmp = 1
        _newaxis = (None, )
    else:
        tmp = 2
        _newaxis = (None, None)
    for i in range(1, min(a.ndim - 2, b.ndim - tmp) + 1):
        if a.shape[-2 - i] != b.shape[-tmp - i] and \
           1 not in (a.shape[-2 - i], b.shape[-tmp - i]):
            raise ValueError(
                'operands could not be broadcast together with '
                'remapped shapes [original->remapped]: {0}->({1}) '
                '{2}->({3}) and requested shape ({4})'.format(
                    str(a.shape).replace(' ', ''),
                    str(a.shape[:-2] + _newaxis).replace(' ', '').replace(
                        'None', 'newaxis').strip('(,)'),
                    str(b.shape).replace(' ', ''),
                    str(b.shape[:-tmp] + _newaxis).replace(' ', '').replace(
                        'None', 'newaxis').replace('None',
                                                   'newaxis').strip('(,)'),
                    str(b.shape[-tmp:]).replace(' ', '').strip('(,)')))

    if a.dtype.char in 'FD' or b.dtype.char in 'FD':
        dtype = 'complex128'
        if a.dtype.char in 'fF' and b.dtype.char in 'fF':
            x_dtype = 'complex64'
        else:
            x_dtype = 'complex128'
    else:
        dtype = 'float64'
        if a.dtype.char == 'f' and b.dtype.char == 'f':
            x_dtype = 'float32'
        else:
            x_dtype = 'float64'

    x_shape = b.shape
    if b.ndim == a.ndim - 1:
        b = b[..., nlcpy.newaxis]
    diff = abs(a.ndim - b.ndim)
    if a.ndim < b.ndim:
        bcast_shape = [
            b.shape[i] if b.shape[i] != 1 or i < diff else a.shape[i - diff]
            for i in range(b.ndim - 2)
        ]
    else:
        bcast_shape = [
            a.shape[i] if a.shape[i] != 1 or i < diff else b.shape[i - diff]
            for i in range(a.ndim - 2)
        ]
    bcast_shape_a = bcast_shape + list(a.shape[-2:])
    bcast_shape_b = bcast_shape + list(b.shape[-2:])
    a = nlcpy.broadcast_to(a, bcast_shape_a)
    if bcast_shape_b != list(b.shape):
        b = nlcpy.broadcast_to(b, bcast_shape_b)
        x_shape = b.shape
    if b.size == 0:
        return nlcpy.empty(x_shape, dtype=x_dtype)

    a = nlcpy.array(nlcpy.moveaxis(a, (-1, -2), (1, 0)),
                    dtype=dtype,
                    order='F')
    b = nlcpy.array(nlcpy.moveaxis(b, (-1, -2), (1, 0)),
                    dtype=dtype,
                    order='F')

    info = numpy.empty(1, dtype='l')
    fpe = request._get_fpe_flag()
    args = (
        a._ve_array,
        b._ve_array,
        veo.OnStack(info, inout=veo.INTENT_OUT),
        veo.OnStack(fpe, inout=veo.INTENT_OUT),
    )

    request._push_and_flush_request('nlcpy_solve',
                                    args,
                                    callback=util._assertNotSingular(info))

    if c_order:
        x = nlcpy.moveaxis(b, (1, 0), (-1, -2)).reshape(x_shape)
        return nlcpy.asarray(x, x_dtype, 'C')
    else:
        x = nlcpy.asarray(b, x_dtype)
        return nlcpy.moveaxis(x, (1, 0), (-1, -2)).reshape(x_shape)