# Copyright (C) 2020 NumS Development Team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# pylint: disable = redefined-builtin, too-many-lines, anomalous-backslash-in-string, unused-wildcard-import, wildcard-import
from nums.core.application_manager import instance as _instance
from nums.core.array.blockarray import BlockArray
from nums.numpy.api.creation import *
from nums.numpy.api.generated import *
from nums.numpy.api.manipulation import *
from nums.numpy.api.logic import *
############################################
# Linear Algebra Ops
############################################
[docs]def dot(a: BlockArray, b: BlockArray, out=None) -> BlockArray:
"""Dot product of two arrays.
This docstring was copied from numpy.dot.
Some inconsistencies with the NumS version may exist.
- If both `a` and `b` are 1-D arrays, it is inner product of vectors
(without complex conjugation).
- If both `a` and `b` are 2-D arrays, it is matrix multiplication,
but using :func:`matmul` or ``a @ b`` is preferred.
- If either `a` or `b` is 0-D (scalar), it is equivalent to :func:`multiply`
and using ``numpy.multiply(a, b)`` or ``a * b`` is preferred.
- If `a` is an N-D array and `b` is a 1-D array, it is a sum product over
the last axis of `a` and `b`.
- If `a` is an N-D array and `b` is an M-D array (where ``M>=2``), it is a
sum product over the last axis of `a` and the second-to-last axis of `b`::
dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
Parameters
----------
a : BlockArray
First argument.
b : BlockArray
Second argument.
out : BlockArray, optional
Output argument. This must have the exact kind that would be returned
if it was not used. In particular, it must have the right type, must be
C-contiguous, and its dtype must be the dtype that would be returned
for `dot(a,b)`. This is a performance feature. Therefore, if these
conditions are not met, an exception is raised, instead of attempting
to be flexible.
Returns
-------
output : BlockArray
Returns the dot product of `a` and `b`. If `a` and `b` are both
scalars or both 1-D arrays then a scalar is returned; otherwise
an array is returned.
If `out` is given, then it is returned.
Raises
------
ValueError
If the last dimension of `a` is not the same size as
the second-to-last dimension of `b`.
See Also
--------
tensordot : Sum products over arbitrary axes.
matmul : '@' operator as method with out parameter.
Examples
--------
The doctests shown below are copied from NumPy.
They won’t show the correct result until you operate ``get()``.
For 2-D arrays it is the matrix product:
>>> a = nps.array([[1, 0], [0, 1]]) # doctest: +SKIP
>>> b = nps.array([[4, 1], [2, 2]]) # doctest: +SKIP
>>> nps.dot(a, b).get() # doctest: +SKIP
array([[4, 1],
[2, 2]])
"""
assert out is None, "Specifying an output array is not supported."
a_len, b_len = len(a.shape), len(b.shape)
if a_len == b_len == 1:
return inner(a, b)
elif a_len == b_len == 2:
return matmul(a, b)
elif a_len == 0 or b_len == 0:
return multiply(a, b)
else:
raise NotImplementedError(
"The dot operation on arbitrary arrays is not yet supported."
)
[docs]def inner(a: BlockArray, b: BlockArray):
"""Inner product of two arrays.
This docstring was copied from numpy.inner.
Some inconsistencies with the NumS version may exist.
Ordinary inner product of vectors for 1-D arrays (without complex
conjugation), in higher dimensions a sum product over the last axes.
Parameters
----------
a, b : BlockArray
If `a` and `b` are nonscalar, their last dimensions must match.
Returns
-------
out : BlockArray
`out.shape = a.shape[:-1] + b.shape[:-1]`
Raises
------
ValueError
If the last dimension of `a` and `b` has different size.
See Also
--------
tensordot : Sum products over arbitrary axes.
dot : Generalised matrix product, using second last dimension of `b`.
Notes
-----
Only single-axis inputs supported.
Examples
--------
The doctests shown below are copied from NumPy.
They won’t show the correct result until you operate ``get()``.
Ordinary inner product for vectors:
>>> a = nps.array([1,2,3]) # doctest: +SKIP
>>> b = nps.array([0,1,0]) # doctest: +SKIP
>>> nps.inner(a, b).get() # doctest: +SKIP
array(2)
"""
assert len(a.shape) == len(b.shape) == 1, "Only single-axis inputs supported."
return a.transpose(defer=True) @ b
[docs]def matmul(x1: BlockArray, x2: BlockArray) -> BlockArray:
"""Matrix product of two arrays.
This docstring was copied from numpy.matmul.
Some inconsistencies with the NumS version may exist.
Parameters
----------
x1, x2 : BlockArray
Input arrays, scalars not allowed.
Returns
-------
y : BlockArray
The matrix product of the inputs.
This is a scalar only when both x1, x2 are 1-d vectors.
Raises
------
ValueError
If the last dimension of `a` is not the same size as
the second-to-last dimension of `b`.
If a scalar value is passed in.
See Also
--------
tensordot : Sum products over arbitrary axes.
dot : alternative matrix product with different broadcasting rules.
"""
return _instance().matmul(arr_1=x1, arr_2=x2)
[docs]def tensordot(x1: BlockArray, x2: BlockArray, axes=2) -> BlockArray:
"""Compute tensor dot product along specified axes.
This docstring was copied from numpy.tensordot.
Some inconsistencies with the NumS version may exist.
Given two tensors, `a` and `b`, and an array_like object containing
two array_like objects, ``(a_axes, b_axes)``, sum the products of
`a`'s and `b`'s elements (components) over the axes specified by
``a_axes`` and ``b_axes``. The third argument can be a single non-negative
integer_like scalar, ``N``; if it is such, then the last ``N`` dimensions
of `a` and the first ``N`` dimensions of `b` are summed over.
Parameters
----------
a, b : BlockArray
Tensors to "dot".
axes : int or (2,) array_like
* integer_like
If an int N, sum over the last N axes of `a` and the first N axes
of `b` in order. The sizes of the corresponding axes must match.
* (2,) array_like
Or, a list of axes to be summed over, first sequence applying to `a`,
second to `b`. Both elements array_like must be of the same length.
Returns
-------
output : BlockArray
The tensor dot product of the input.
See Also
--------
dot
Notes
-----
Three common use cases are:
* ``axes = 0`` : tensor product :math:`a\otimes b`
* ``axes = 1`` : tensor dot product :math:`a\cdot b`
* ``axes = 2`` : (default) tensor double contraction :math:`a:b`
When `axes` is integer_like, the sequence for evaluation will be: first
the -Nth axis in `a` and 0th axis in `b`, and the -1th axis in `a` and
Nth axis in `b` last.
When there is more than one axis to sum over - and they are not the last
(first) axes of `a` (`b`) - the argument `axes` should consist of
two sequences of the same length, with the first axis to sum over given
first in both sequences, the second axis second, and so forth.
The shape of the result consists of the non-contracted axes of the
first tensor, followed by the non-contracted axes of the second.
Non-integer axes is currently not supported.
"""
return _instance().tensordot(arr_1=x1, arr_2=x2, axes=axes)
[docs]def outer(a: BlockArray, b: BlockArray):
"""Compute the outer product of two vectors.
This docstring was copied from numpy.outer.
Some inconsistencies with the NumS version may exist.
Given two vectors, ``a = [a0, a1, ..., aM]`` and
``b = [b0, b1, ..., bN]``,
the outer product [1]_ is::
[[a0*b0 a0*b1 ... a0*bN ]
[a1*b0 .
[ ... .
[aM*b0 aM*bN ]]
Parameters
----------
a : (M,) BlockArray
First input vector. Input is flattened if
not already 1-dimensional.
b : (N,) BlockArray
Second input vector. Input is flattened if
not already 1-dimensional.
Returns
-------
out : (M, N) BlockArray
``out[i, j] = a[i] * b[j]``
See also
--------
inner
outer
tensordot
Notes
-----
Only single-axis inputs supported.
"""
assert len(a.shape) == len(b.shape) == 1, "Only single-axis inputs supported."
return a.reshape((a.shape[0], 1)) @ b.reshape((1, b.shape[0]))
[docs]def trace(a: BlockArray, offset=0, axis1=0, axis2=1, dtype=None, out=None):
"""Return the sum along diagonals of the array.
This docstring was copied from numpy.trace.
Some inconsistencies with the NumS version may exist.
If `a` is 2-D, the sum along its diagonal with the given offset
is returned, i.e., the sum of elements ``a[i,i+offset]`` for all i.
If `a` has more than two dimensions, then the axes specified by axis1 and
axis2 are used to determine the 2-D sub-arrays whose traces are returned.
The shape of the resulting array is the same as that of `a` with `axis1`
and `axis2` removed.
Parameters
----------
a : BlockArray
Input array, from which the diagonals are taken.
offset : int, optional
Offset of the diagonal from the main diagonal. Can be both positive
and negative. Defaults to 0.
axis1, axis2 : int, optional
Axes to be used as the first and second axis of the 2-D sub-arrays
from which the diagonals should be taken. Defaults are the first two
axes of `a`.
dtype : dtype, optional
Determines the data-type of the returned array and of the accumulator
where the elements are summed. If dtype has the value None and `a` is
of integer type of precision less than the default integer
precision, then the default integer precision is used. Otherwise,
the precision is the same as that of `a`.
out : BlockArray, optional
Array into which the output is placed. Its type is preserved and
it must be of the right shape to hold the output.
Returns
-------
sum_along_diagonals : BlockArray
If `a` is 2-D, the sum along the diagonal is returned. If `a` has
larger dimensions, then an array of sums along diagonals is returned.
See Also
--------
diag, diagonal
Notes
-----
offset != 0 is currently not supported.
out is currently not supported.
axis1 != 0 or axis2 != 1 is currently not supported.
Examples
--------
The doctests shown below are copied from NumPy.
They won’t show the correct result until you operate ``get()``.
>>> nps.trace(nps.eye(3)).get() # doctest: +SKIP
array(3.)
>>> a = nps.arange(8).reshape((2,2,2)) # doctest: +SKIP
"""
if offset != 0:
raise NotImplementedError("offset != 0 is currently not supported.")
if out is not None:
raise NotImplementedError("out is currently not supported.")
if axis1 != 0 or axis2 != 1:
raise NotImplementedError(
" axis1 != 0 or axis2 != 1 is currently not supported."
)
return sum(diag(a, offset), dtype=dtype, out=out)