from typing import Optional, Tuple, Union
import numpy as np
from mygrad.operation_base import _NoValue
from mygrad.tensor_base import (
_REGISTERED_DIFFERENTIABLE_NUMPY_FUNCS,
Tensor,
implements_numpy_override,
)
from mygrad.typing import ArrayLike
from .ops import *
Axis = Union[None, int, Tuple[int, ...]]
__all__ = [
"sum",
"mean",
"var",
"std",
"amax",
"amin",
"max",
"min",
"prod",
"cumprod",
"cumsum",
]
[docs]@implements_numpy_override()
def sum(
x: ArrayLike,
axis: Axis = None,
keepdims: bool = False,
*,
constant: Optional[bool] = None,
) -> Tensor:
"""
Sum of tensor elements over a given axis.
Parameters
----------
x : ArrayLike
axis : Optional[int, Tuple[ints, ...]]
Axis or axes along which a sum is performed. The default,
axis=None, will sum all of the elements of the input tensor. If
axis is negative it counts from the last to the first axis.
If axis is a tuple of ints, a sum is performed on all of the axes
specified in the tuple instead of a single axis or all the axes as
before.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the input tensor.
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
sum_along_axis : mygrad.Tensor
A Tensor with the same shape as `self`, with the specified
axis/axes removed. If `self` is a 0-d tensor, or if `axis` is None,
a 0-dim Tensor is returned.
See Also
--------
mygrad.Tensor.sum : Equivalent method.
cumsum : Cumulative sum of array elements.
mean, average
Notes
-----
Arithmetic is modular when using integer types, and no error is
raised on overflow.
The sum of an empty tensor is the neutral element 0:
>>> mygrad.sum([])
Tensor(0.0)
Examples
--------
>>> import mygrad as mg
>>> import numpy as np
>>> mg.sum([0.5, 1.5])
Tensor(2.0)
>>> mg.sum([0.5, 0.7, 0.2, 1.5], dtype=np.int32)
Tensor(1)
>>> mg.sum([[0, 1], [0, 5]])
Tensor(6)
>>> mg.sum([[0, 1], [0, 5]], axis=0)
Tensor([0, 6])
>>> mg.sum([[0, 1], [0, 5]], axis=1)
Tensor([1, 5])
If the accumulator is too small, overflow occurs:
>>> mg.ones(128, dtype=mg.int8).sum(dtype=np.int8)
Tensor(-128)
"""
return Tensor._op(
Sum, x, op_kwargs={"axis": axis, "keepdims": keepdims}, constant=constant
)
[docs]@implements_numpy_override()
def mean(
x: ArrayLike,
axis: Axis = None,
keepdims: bool = False,
*,
constant: Optional[bool] = None,
) -> Tensor:
"""
Mean of tensor elements over a given axis.
Parameters
----------
x : ArrayLike
axis : Optional[int, Tuple[ints, ...]
Axis or axes along which a mean is performed. The default,
axis=None, will mean all of the elements of the input tensor. If
axis is negative it counts from the last to the first axis.
If axis is a tuple of ints, a mean is performed on all of the axes
specified in the tuple instead of a single axis or all the axes as
before.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the input tensor.
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
mean_along_axis : Tensor
A Tensor with the same shape as `self`, with the specified
axis/axes removed. If `self` is a 0-d tensor, or if `axis` is None,
a 0-dim Tensor is returned.
Examples
--------
>>> import mygrad as mg
>>> import numpy as np
>>> a = mg.Tensor([[1, 2],
... [3, 4]])
>>> mg.mean(a)
Tensor(2.5)
>>> mg.mean(a, axis=0)
Tensor([ 2., 3.])
>>> mg.mean(a, axis=1)
Tensor([ 1.5, 3.5])
In single precision, `mean` can be inaccurate:
>>> a = mg.zeros((2, 512*512), dtype=np.float32)
>>> a[0, :] = 1.0
>>> a[1, :] = 0.1
>>> mg.mean(a)
Tensor(0.54999924)
Computing the mean in float64 is more accurate:
>>> mg.mean(a, dtype=np.float64)
Tensor(0.55000000074505806)
"""
return Tensor._op(
Mean, x, op_kwargs={"axis": axis, "keepdims": keepdims}, constant=constant
)
[docs]@implements_numpy_override()
def var(
x: ArrayLike,
axis: Axis = None,
ddof: int = 0,
keepdims: bool = False,
*,
constant: Optional[bool] = None,
) -> Tensor:
"""
Compute the variance along the specified axis.
Returns the variance of the array elements, a measure of the spread of a
distribution. The variance is computed for the flattened array by
default, otherwise over the specified axis.
Parameters
----------
x : ArrayLike
Array containing numbers whose variance is desired.
axis : Optional[int, Tuple[int, ...]]
Axis or axes along which the variance is computed. The default is to
compute the variance of the flattened array.
ddof : int, optional (default=0)
"Delta Degrees of Freedom": the divisor used in the calculation is
``N - ddof``, where ``N`` represents the number of elements. By
default `ddof` is zero.
keepdims : bool, optional (default=False)
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the input array..
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
variance : mygrad.Tensor
Notes
-----
The variance is the average of the squared deviations from the mean,
i.e., ``var = mean(abs(x - x.mean())**2)``.
The mean is normally calculated as ``x.sum() / N``, where ``N = len(x)``.
If, however, `ddof` is specified, the divisor ``N - ddof`` is used
instead. In standard statistical practice, ``ddof=1`` provides an
unbiased estimator of the variance of a hypothetical infinite population.
``ddof=0`` provides a maximum likelihood estimate of the variance for
normally distributed variables.
Examples
--------
>>> import mygrad as mg
>>> import numpy as np
>>> a = mg.Tensor([[1, 2],
... [3, 4]])
>>> mg.var(a)
Tensor(1.25)
>>> mg.var(a, axis=0)
Tensor([ 1., 1.])
>>> mg.var(a, axis=1)
Tensor([ 0.25, 0.25])
In single precision, ``var()`` can be inaccurate:
>>> a = mg.zeros((2, 512*512), dtype=np.float32)
>>> a[0, :] = 1.0
>>> a[1, :] = 0.1
>>> mg.var(a)
Tensor(0.20250003)
Computing the variance in float64 is more accurate:
>>> mg.var(a, dtype=np.float64)
Tensor(0.20249999932944759)
>>> ((1-0.55)**2 + (0.1-0.55)**2)/2
Tensor(0.2025)
"""
return Tensor._op(
Variance,
x,
op_kwargs={"axis": axis, "keepdims": keepdims, "ddof": ddof},
constant=constant,
)
[docs]@implements_numpy_override()
def std(
x: ArrayLike,
axis: Axis = None,
ddof: int = 0,
keepdims: bool = False,
*,
constant: Optional[bool] = None,
) -> Tensor:
"""
Compute the standard deviation along the specified axis.
Returns the variance of the array elements, a measure of the spread of a
distribution. The variance is computed for the flattened array by
default, otherwise over the specified axis.
Parameters
----------
x : ArrayLike
Array containing numbers whose standard deviation is desired.
axis : Optional[int, Tuple[int, ...]]
Axis or axes along which the variance is computed. The default is to
compute the variance of the flattened array.
ddof : int, optional (default=0)
"Delta Degrees of Freedom": the divisor used in the calculation is
``N - ddof``, where ``N`` represents the number of elements. By
default `ddof` is zero.
keepdims : bool, optional (default=False)
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the input array.
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
std : mygrad.Tensor
Notes
-----
The variance is the average of the squared deviations from the mean,
i.e., ``var = mean(abs(x - x.mean())**2)``.
The mean is normally calculated as ``x.sum() / N``, where ``N = len(x)``.
If, however, `ddof` is specified, the divisor ``N - ddof`` is used
instead. In standard statistical practice, ``ddof=1`` provides an
unbiased estimator of the variance of a hypothetical infinite population.
``ddof=0`` provides a maximum likelihood estimate of the variance for
normally distributed variables.
Examples
--------
>>> import mygrad as mg
>>> import numpy as np
>>> a = mg.Tensor([[1, 2],
... [3, 4]])
>>> mg.std(a)
Tensor(1.1180339887498949)
>>> mg.std(a, axis=0)
Tensor([ 1., 1.])
>>> mg.std(a, axis=1)
Tensor([ 0.5, 0.5])
In single precision, ``var()`` can be inaccurate:
>>> a = mg.zeros((2, 512*512), dtype=np.float32)
>>> a[0, :] = 1.0
>>> a[1, :] = 0.1
>>> mg.std(a)
Tensor(0.45000005)
Computing the variance in float64 is more accurate:
>>> mg.std(a, dtype=np.float64)
Tensor(0.44999999925494177)
"""
return Tensor._op(
StdDev,
x,
op_kwargs={"axis": axis, "keepdims": keepdims, "ddof": ddof},
constant=constant,
)
[docs]@implements_numpy_override()
def max(
x: ArrayLike,
axis: Axis = None,
keepdims: bool = False,
*,
constant: Optional[bool] = None,
) -> Tensor:
"""
Return the maximum of a tensor or maximum along its axes.
Parameters
----------
x : ArrayLike
axis : Optional[int, Tuple[int, ...]]
Axis or axes along which to operate. By default, flattened input is used.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the original `arr`.
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
max : mygrad.Tensor
Maximum of `a`. If `axis` is None, the result is a 0-D tensor.
Examples
--------
>>> import mygrad as mg
>>> import numpy as np
>>> a = mg.arange(4).reshape((2,2))
>>> a
Tensor([[0, 1],
[2, 3]])
>>> mg.amax(a) # Maximum of the flattened array
Tensor(3)
>>> mg.amax(a, axis=0) # Maxima along the first axis
Tensor([2, 3])
>>> mg.amax(a, axis=1) # Maxima along the second axis
Tensor([1, 3])
>>> b = mg.arange(5, dtype=float)
>>> b[2] = np.NaN
>>> mg.amax(b)
Tensor(nan)
"""
return Tensor._op(
Max,
x,
op_kwargs={"axis": axis, "keepdims": keepdims, "dtype": _NoValue},
constant=constant,
)
[docs]@implements_numpy_override()
def min(
x: ArrayLike,
axis: Axis = None,
keepdims: bool = False,
*,
constant: Optional[bool] = None,
) -> Tensor:
"""
Return the minimum of a tensor or minimum along its axes.
Parameters
----------
axis : Optional[int, Tuple[int, ...]]
Axis or axes along which to operate. By default, flattened input is used.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the original `arr`.
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
min : mygrad.Tensor
Minimum of `a`. If `axis` is None, the result is a 0-D tensor.
Examples
--------
>>> import mygrad as mg
>>> import numpy as np
>>> a = mg.arange(4).reshape((2,2))
>>> a
Tensor([[0, 1],
[2, 3]])
>>> mg.amin(a) # Minimum of the flattened array
Tensor(0)
>>> mg.amin(a, axis=0) # Minima along the first axis
Tensor([0, 1])
>>> mg.amin(a, axis=1) # Minima along the second axis
Tensor([0, 2])
>>> b = mg.arange(5, dtype=float)
>>> b[2] = np.NaN
>>> mg.amin(b)
Tensor(nan)
"""
return Tensor._op(
Min,
x,
op_kwargs={"axis": axis, "keepdims": keepdims, "dtype": _NoValue},
constant=constant,
)
# aliases
amin = min
amax = max
_REGISTERED_DIFFERENTIABLE_NUMPY_FUNCS[np.amin] = amin
_REGISTERED_DIFFERENTIABLE_NUMPY_FUNCS[np.amax] = amax
[docs]@implements_numpy_override()
def prod(
a: ArrayLike,
axis: Axis = None,
keepdims: bool = False,
*,
constant: Optional[bool] = None,
) -> Tensor:
"""
Return the product of array elements over given axes.
Parameters
----------
a : ArrayLike
Input data.
axis : Optional[int, Tuple[int, ...]]
Axis or axes along which to operate. By default, flattened input is used.
keepdims : bool, optional (default=False)
If this is set to True, the axes which are reduced are left in the
result as dimensions with size one. With this option, the result
will broadcast correctly against the input array.
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
product_along_axis : mygrad.Tensor
A tensor shaped as `a` but with the specified axis removed.
Notes
-----
The product of an empty tensor is the neutral element 1:
>>> import mygrad
>>> mygrad.prod([])
Tensor(1.0)
Examples
--------
By default, calculate the product of all elements:
>>> import mygrad as mg
>>> mg.prod([1.,2.])
Tensor(2.0)
Even when the input array is two-dimensional:
>>> mg.prod([[1.,2.],
... [3.,4.]])
Tensor(24.0)
But we can also specify the axis over which to multiply:
>>> mg.prod([[1.,2.],
... [3.,4.]], axis=1)
Tensor([ 2., 12.])"""
return Tensor._op(
Prod, a, op_kwargs={"axis": axis, "keepdims": keepdims}, constant=constant
)
[docs]@implements_numpy_override()
def cumprod(
a: ArrayLike, axis: Axis = None, *, constant: Optional[bool] = None
) -> Tensor:
"""
Return the cumulative product of elements along a given axis.
This docstring was adapted from the official numpy documentation
Parameters
----------
a : ArrayLike
Input array.
axis : Optional[int]
Axis along which the cumulative product is computed. By default
the input is flattened.
constant : bool, optional(default=False)
If ``True``, the returned tensor is a constant (it
does not back-propagate a gradient)
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
mygrad.Tensor
Notes
-----
Arithmetic is modular when using integer types, and no error is
raised on overflow.
Examples
--------
>>> from mygrad import cumprod, Tensor
>>> a = Tensor([[1, 2, 3],
... [4, 5, 6]])
>>> cumprod(a)
Tensor([ 1 2 6 24 120 720])
The cumulative product for each column (i.e., over the rows) of `a`:
>>> cumprod(a, axis=0)
Tensor([[ 1, 2, 3],
[ 4, 10, 18]])
The cumulative product for each row (i.e. over the columns) of `a`:
>>> cumprod(a, axis=1)
Tensor([[ 1, 2, 6],
[ 4, 20, 120]])"""
return Tensor._op(CumProd, a, op_kwargs={"axis": axis}, constant=constant)
[docs]@implements_numpy_override()
def cumsum(
a: ArrayLike, axis: Axis = None, *, constant: Optional[bool] = None
) -> Tensor:
"""
Return the cumulative sum of the elements along a given axis.
This docstring was adapted from the official numpy documentation
Parameters
----------
a : ArrayLike
Input array.
axis : int, optional
Axis along which the cumulative sum is computed. The default
(None) is to compute the cumsum over the flattened array.
constant : Optional[bool]
If ``True``, this tensor is treated as a constant, and thus does not
facilitate back propagation (i.e. ``constant.grad`` will always return
``None``).
Defaults to ``False`` for float-type data.
Defaults to ``True`` for integer-type data.
Integer-type tensors must be constant.
Returns
-------
mygrad.Tensor
Examples
--------
>>> from mygrad import cumsum, Tensor
>>> a = Tensor([[1, 2, 3],
... [4, 5, 6]])
>>> cumsum(a)
Tensor([ 1, 3, 6, 10, 15, 21])
>>> cumsum(a, axis=0) # sum over rows for each of the 3 columns
Tensor([[1, 2, 3],
[5, 7, 9]])
>>> cumsum(a, axis=1) # sum over columns for each of the 2 rows
Tensor([[ 1, 3, 6],
[ 4, 9, 15]])
"""
return Tensor._op(CumSum, a, op_kwargs={"axis": axis}, constant=constant)