Struct easy_ml::differentiation::RecordContainer
source · pub struct RecordContainer<'a, T: Primitive, S, const D: usize> { /* private fields */ }
Expand description
A pluralisation of Record that groups together a source of numbers of type T and stores the WengertList only once.
Typically you would refer to one of the type aliases to disambiguate the type of S
and
use more succinct generics: RecordMatrix, RecordTensor.
For both Matrix and Tensor source types, the containers implement +
and
-
and have the methods elementwise_multiply
and elementwise_divide
.
In all cases the containers must have the same size for the operation and will panic if
mismatched.
*
is also implemented for 2 dimensional tensors and matrices as matrix
multiplication.
For convenience, as with Trace and Record, many unary operations including Cos, Exp, Ln, Neg, Pow, Sin, and Sqrt are implemented as well, applying the unary function to each element in the tensor.
+
, -
, *
and /
operations with a RecordContainer and a scalar are also implemented,
treating the right hand side scalar as a constant. These are also unary functions in terms of
the derivative tracking, for example X + 5
applies the function +5
to each element in
X
. Due to the orphan rule, the standard library scalars cannot be implemented for a left hand
side scalar, see SwappedOperations.
Both RecordMatrix and RecordTensor implement
MatrixRef and TensorRef respectively, which provide read and write
access to the underlying numbers and indexes into the WengertList. These APIs along with the
from_existing
constructors for RecordMatrix, RecordTensor, and Record allow arbitary
manipulation of specific elements in a record container if needed. However, any arithmetic
performed on the raw data won’t be tracked on the WengertList and overwriting data within a
record container already tracked on the WengertList could result in nonsense. These APIs exist
for easy read access to check the data in a record container and for read/write access when
manipulating the shape of a record container, and are designed to be used only for moving data
around - you should put it back unchanged in a RecordContainer or Record before doing further
arithmetic that needs to be tracked on the WengertList.
If you just want to manipulate the data in record containers as if they were Records you can use the iterator APIs of AsRecords instead and collect them back into containers when you’re done, or to manipulate a single container, the map and map_mut methods.
Implementations§
source§impl<'a, T, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
impl<'a, T, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
sourcepub fn from_iter<I>(
shape: [(Dimension, usize); D],
iter: I
) -> Result<Self, InvalidRecordIteratorError<'a, T, D>>where
I: IntoIterator<Item = Record<'a, T>>,
pub fn from_iter<I>(
shape: [(Dimension, usize); D],
iter: I
) -> Result<Self, InvalidRecordIteratorError<'a, T, D>>where
I: IntoIterator<Item = Record<'a, T>>,
Given an iterator of records and a matching shape, collects them back into a RecordTensor.
This should generally be preferred over converting the iterator to a Vec of Records, since a Vec of Records has to store the WengertList reference for each individual record whereas a RecordTensor only stores it once.
However, since a RecordTensor only stores the WengertList once, this conversion will fail if there are different histories in the iterator. It also fails if the iterator is empty or doesn’t match the number of elements for the shape.
See also: elements
sourcepub fn from_iters<I, const N: usize>(
shape: [(Dimension, usize); D],
iter: I
) -> [Result<Self, InvalidRecordIteratorError<'a, T, D>>; N]
pub fn from_iters<I, const N: usize>( shape: [(Dimension, usize); D], iter: I ) -> [Result<Self, InvalidRecordIteratorError<'a, T, D>>; N]
Given an iterator of N record pairs and a matching shape, collects them back into N RecordTensors.
This should generally be preferred over converting the iterator to N Vecs of Records, since a Vec of Records has to store the WengertList reference for each individual record whereas a RecordTensor only stores it once.
However, since a RecordTensor only stores the WengertList once, this conversion will fail if there are different histories in the iterator. It also fails if the iterator is empty or doesn’t match the number of elements for the shape. Each failure due to different histories is seperate, if the ith elements in the records of the iterator have a consistent history but the jth elements do not then the ith result will be Ok but the jth will be Err.
See also: elements
source§impl<'a, T> RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
impl<'a, T> RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
sourcepub fn from_iter<I>(
size: (Row, Column),
iter: I
) -> Result<Self, InvalidRecordIteratorError<'a, T, 2>>where
I: IntoIterator<Item = Record<'a, T>>,
pub fn from_iter<I>(
size: (Row, Column),
iter: I
) -> Result<Self, InvalidRecordIteratorError<'a, T, 2>>where
I: IntoIterator<Item = Record<'a, T>>,
Given an iterator of records and a matching size, collects them back into a RecordMatrix.
This should generally be preferred over converting the iterator to a Vec of Records, since a Vec of Records has to store the WengertList reference for each individual record whereas a RecordMatrix only stores it once.
However, since a RecordMatrix only stores the WengertList once, this conversion will fail if there are different histories in the iterator. It also fails if the iterator is empty or doesn’t match the R x C number of elements expected.
sourcepub fn from_iters<I, const N: usize>(
size: (Row, Column),
iter: I
) -> [Result<Self, InvalidRecordIteratorError<'a, T, 2>>; N]
pub fn from_iters<I, const N: usize>( size: (Row, Column), iter: I ) -> [Result<Self, InvalidRecordIteratorError<'a, T, 2>>; N]
Given an iterator of N record pairs and a matching shape, collects them back into N RecordMatrixs.
This should generally be preferred over converting the iterator to N Vecs of Records, since a Vec of Records has to store the WengertList reference for each individual record whereas a RecordMatrix only stores it once.
However, since a RecordMatrix only stores the WengertList once, this conversion will fail if there are different histories in the iterator. It also fails if the iterator is empty or doesn’t match the R x C number of elements expected. Each failure due to different histories is seperate, if the ith elements in the records of the iterator have a consistent history but the jth elements do not then the ith result will be Ok but the jth will be Err.
See also: elements
source§impl<'a, T, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
impl<'a, T, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
sourcepub fn constants<S>(c: S) -> Selfwhere
S: TensorMut<T, D>,
pub fn constants<S>(c: S) -> Selfwhere
S: TensorMut<T, D>,
Creates multiple untracked Records which have no backing WengertList.
This is provided for using constants along with Records in operations.
For example with Y = X + 4
the computation graph could be conceived as many
Y[i,j]
nodes with parent nodes of X[i,j]
and 4 combined with the operation +
.
However there is no need to record the derivatives of a constant, so
instead the computation graph can be conceived as Y[i,j]
nodes each with a single
parent node of X[i,j]
and the unary operation of +4
.
sourcepub fn variables<S>(history: &'a WengertList<T>, x: S) -> Selfwhere
S: TensorMut<T, D>,
pub fn variables<S>(history: &'a WengertList<T>, x: S) -> Selfwhere
S: TensorMut<T, D>,
Creates multiple records backed by the provided WengertList.
The records cannot live longer than the WengertList, hence the following example does not compile
use easy_ml::differentiation::RecordTensor;
use easy_ml::differentiation::WengertList;
use easy_ml::tensors::Tensor;
let record = {
let list = WengertList::new();
RecordTensor::variables(
&list,
Tensor::from([("r", 2), ("c", 2)], vec![ 1.0, 2.0, 3.0, 4.0 ])
)
}; // list no longer in scope
source§impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
sourcepub fn elements(&self) -> usize
pub fn elements(&self) -> usize
Returns the number of elements stored by this container’s source.
For a 2 x 3 Tensor, this would return 6, and for a 2 x 3 x 4 Tensor this would return 24 and so on.
see also dimensions::elements
sourcepub fn from_existing(
history: Option<&'a WengertList<T>>,
numbers: TensorView<(T, Index), S, D>
) -> Self
pub fn from_existing( history: Option<&'a WengertList<T>>, numbers: TensorView<(T, Index), S, D> ) -> Self
Creates a container from constants/variables directly, most likely obtained by getting a tensor view of an existing container. The inputs are not checked for validity. It is possible to pass in the wrong Wengert list here or even numbers with indexes that aren’t tracked on the WengertList.
It is recommended to use this constructor only in conjunction with
resizing or masking an existing container and not for creating new variables. Any variables
created outside of RecordContainer::variables
would have to be manually added to the
correct Wengert list, and any arithmetic operations would also need tracking correctly.
use easy_ml::differentiation::RecordTensor;
use easy_ml::differentiation::WengertList;
use easy_ml::tensors::Tensor;
use easy_ml::tensors::views::{TensorView, TensorRange};
let list = WengertList::new();
let x = RecordTensor::variables(
&list,
Tensor::from_fn([("x", 2), ("y", 2)], |[r, c]| ((r + 3) * (c + 2)) as f64)
);
// oh no wrong shape!
let fixed = TensorView::from(TensorRange::from(x, [("y", 0..1)]).unwrap()); // we can unwrap here because we know the range is valid
let x = RecordTensor::from_existing(Some(&list), fixed);
assert_eq!([("x", 2), ("y", 1)], x.shape());
sourcepub fn rename_view(
self,
dimensions: [Dimension; D]
) -> RecordTensor<'a, T, TensorRename<(T, Index), S, D>, D>
pub fn rename_view( self, dimensions: [Dimension; D] ) -> RecordTensor<'a, T, TensorRename<(T, Index), S, D>, D>
Returns a record tensor with the dimension names of the shape renamed to the provided dimensions. The data of this container and the dimension lengths and order remain unchanged.
This is a shorthand for constructing the RecordTensor via manipulating this TensorView. See
RecordTensor::from_existing
.
§Panics
If a dimension name is not unique
use easy_ml::differentiation::RecordTensor;
use easy_ml::differentiation::WengertList;
use easy_ml::tensors::Tensor;
use easy_ml::tensors::views::{TensorView, TensorRename};
let list = WengertList::new();
let x = RecordTensor::variables(
&list,
Tensor::from_fn([("x", 2), ("y", 2)], |[r, c]| ((r + 3) * (c + 2)) as f64)
);
// oh no wrong dimension names!
let x = x.rename_view(["a", "b"]);
assert_eq!([("a", 2), ("b", 2)], x.shape());
sourcepub fn view(&self) -> TensorView<(T, Index), &RecordTensor<'a, T, S, D>, D>
pub fn view(&self) -> TensorView<(T, Index), &RecordTensor<'a, T, S, D>, D>
Returns a TensorView of this record container, both the T
for each record element and
also the index for that record’s entry in the WengertList. These can be parsed back into
a RecordTensor with from_existing
or individually into
Records with Record::from_existing
to continue tracking
numerical operations on the data.
sourcepub fn index_by(
&self,
dimensions: [Dimension; D]
) -> TensorAccess<(T, Index), &RecordTensor<'a, T, S, D>, D>
pub fn index_by( &self, dimensions: [Dimension; D] ) -> TensorAccess<(T, Index), &RecordTensor<'a, T, S, D>, D>
Returns a TensorAccess which can be indexed in the order of the supplied dimensions
to read values from this record container, both the T
for each record element and
also the index for that record’s entry in the WengertList. These can be parsed back into
a RecordTensor with from_existing
or individually into
Records with Record::from_existing
to continue tracking
numerical operations on the data.
§Panics
If the set of dimensions supplied do not match the set of dimensions in this tensor’s shape.
sourcepub fn index(&self) -> TensorAccess<(T, Index), &RecordTensor<'a, T, S, D>, D>
pub fn index(&self) -> TensorAccess<(T, Index), &RecordTensor<'a, T, S, D>, D>
Creates a TensorAccess which will index into the dimensions this record was created with
in the same order as they were provided, both the T
for each record element and
also the index for that record’s entry in the WengertList. These can be parsed back into
a RecordTensor with from_existing
or individually into
Records with Record::from_existing
to continue tracking
numerical operations on the data.
See TensorAccess::from_source_order, get_as_record.
use easy_ml::differentiation::RecordTensor;
use easy_ml::differentiation::WengertList;
use easy_ml::tensors::Tensor;
let list = WengertList::new();
let X = RecordTensor::variables(
&list,
Tensor::from([("a", 3)], vec![ 3.0, 4.0, 5.0 ])
);
let x = X.index().get_as_record([0]);
assert_eq!(x.number, 3.0);
sourcepub fn iter_as_records<'b>(
&'b self
) -> AsRecords<'a, TensorIterator<'b, (T, Index), RecordTensor<'a, T, S, D>, D>, T> ⓘ
pub fn iter_as_records<'b>( &'b self ) -> AsRecords<'a, TensorIterator<'b, (T, Index), RecordTensor<'a, T, S, D>, D>, T> ⓘ
Returns an iterator over this record tensor as Records instead of the raw (T, Index)
data. After manipulating the iterator it can be collected back into a RecordTensor with
RecordTensor::from_iter.
This is a shorthand for AsRecords::from(tensor.history(), TensorIterator::from(&tensor))
source§impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
source§impl<'a, T> RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
impl<'a, T> RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
sourcepub fn constants<S>(c: S) -> Selfwhere
S: MatrixMut<T> + NoInteriorMutability,
pub fn constants<S>(c: S) -> Selfwhere
S: MatrixMut<T> + NoInteriorMutability,
Creates multiple untracked Records which have no backing WengertList.
This is provided for using constants along with Records in operations.
For example with Y = X + 4
the computation graph could be conceived as many
Y[i,j]
nodes with parent nodes of X[i,j]
and 4 combined with the operation +
.
However there is no need to record the derivatives of a constant, so
instead the computation graph can be conceived as Y[i,j]
nodes each with a single
parent node of X[i,j]
and the unary operation of +4
.
sourcepub fn variables<S>(history: &'a WengertList<T>, x: S) -> Selfwhere
S: MatrixMut<T> + NoInteriorMutability,
pub fn variables<S>(history: &'a WengertList<T>, x: S) -> Selfwhere
S: MatrixMut<T> + NoInteriorMutability,
Creates multiple records backed by the provided WengertList.
The records cannot live longer than the WengertList, hence the following example does not compile
use easy_ml::differentiation::RecordMatrix;
use easy_ml::differentiation::WengertList;
use easy_ml::matrices::Matrix;
let record = {
let list = WengertList::new();
RecordMatrix::variables(
&list,
Matrix::from(vec![vec![1.0, 2.0], vec![3.0, 4.0]])
)
}; // list no longer in scope
source§impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>
impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>
sourcepub fn elements(&self) -> usize
pub fn elements(&self) -> usize
Returns the number of elements stored by this container’s source.
For a 2 x 3 Matrix, this would return 6, and for a 3 x 4 Matrix this would return 12 and so on.
sourcepub fn size(&self) -> (Row, Column)
pub fn size(&self) -> (Row, Column)
Returns the dimensionality of this matrix container in Row, Column format
sourcepub fn from_existing(
history: Option<&'a WengertList<T>>,
numbers: MatrixView<(T, Index), S>
) -> Self
pub fn from_existing( history: Option<&'a WengertList<T>>, numbers: MatrixView<(T, Index), S> ) -> Self
Creates a container from constants/variables directly, most likely obtained by getting a matrix view of an existing container. The inputs are not checked for validity. It is possible to pass in the wrong Wengert list here or even numbers with indexes that aren’t tracked on the WengertList.
It is recommended to use this constructor only in conjunction with
resizing or masking an existing container and not for creating new variables. Any variables
created outside of RecordContainer::variables
would have to be manually added to the
correct Wengert list, and any arithmetic operations would also need tracking correctly.
use easy_ml::differentiation::RecordMatrix;
use easy_ml::differentiation::WengertList;
use easy_ml::matrices::Matrix;
use easy_ml::matrices::views::{MatrixView, MatrixRange};
let list = WengertList::new();
let x = RecordMatrix::variables(
&list,
Matrix::from_fn((2, 2), |(r, c)| ((r + 3) * (c + 2)) as f64)
);
// oh no wrong shape!
let fixed = MatrixView::from(MatrixRange::from(x, 0..2, 0..1));
let x = RecordMatrix::from_existing(Some(&list), fixed);
assert_eq!((2, 1), x.size());
sourcepub fn view(&self) -> MatrixView<(T, Index), &RecordMatrix<'a, T, S>>
pub fn view(&self) -> MatrixView<(T, Index), &RecordMatrix<'a, T, S>>
Returns a MatrixView of this record container, both the T
for each record element and
also the index for that record’s entry in the WengertList. These can be parsed back into
a RecordMatrix with from_existing
or individually into
Records with Record::from_existing
to continue tracking
numerical operations on the data.
sourcepub fn iter_row_major_as_records<'b>(
&'b self
) -> AsRecords<'a, RowMajorIterator<'b, (T, Index), RecordMatrix<'a, T, S>>, T> ⓘ
pub fn iter_row_major_as_records<'b>( &'b self ) -> AsRecords<'a, RowMajorIterator<'b, (T, Index), RecordMatrix<'a, T, S>>, T> ⓘ
Returns an iterator over this record matrix as Records instead of the raw (T, Index)
data. After manipulating the iterator it can be collected back into a RecordMatrix with
RecordMatrix::from_iter.
This is a shorthand for AsRecords::from(matrix.history(), RowMajorIterator::from(&matrix))
sourcepub fn iter_column_major_as_records<'b>(
&'b self
) -> AsRecords<'a, ColumnMajorIterator<'b, (T, Index), RecordMatrix<'a, T, S>>, T> ⓘ
pub fn iter_column_major_as_records<'b>( &'b self ) -> AsRecords<'a, ColumnMajorIterator<'b, (T, Index), RecordMatrix<'a, T, S>>, T> ⓘ
Returns an iterator over this record matrix as Records instead of the raw (T, Index)
data. After manipulating the iterator it can be collected back into a RecordMatrix with
RecordMatrix::from_iter.
This is a shorthand for
AsRecords::from(matrix.history(), ColumnMajorIterator::from(&matrix))
sourcepub fn get_as_record(&self, row: Row, column: Column) -> Record<'a, T>
pub fn get_as_record(&self, row: Row, column: Column) -> Record<'a, T>
Returns a copy of the data at the index as a Record. If you need to access all the data as records instead of just a specific index you should probably use one of the iterator APIs instead.
See also: iter_row_major_as_records, iter_column_major_as_records
§Panics
If the index is out of range.
For a non panicking API see try_get_as_record
sourcepub fn try_get_as_record(
&self,
row: Row,
column: Column
) -> Option<Record<'a, T>>
pub fn try_get_as_record( &self, row: Row, column: Column ) -> Option<Record<'a, T>>
Returns a copy of the data at the index as a Record, or None if the index is out of range. If you need to access all the data as records instead of just a specific index you should probably use one of the iterator APIs instead.
See also: iter_row_major_as_records, iter_column_major_as_records
source§impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>
impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>
source§impl<'a, T, S, const D: usize> RecordContainer<'a, T, S, D>where
T: Primitive,
impl<'a, T, S, const D: usize> RecordContainer<'a, T, S, D>where
T: Primitive,
sourcepub fn history(&self) -> Option<&'a WengertList<T>>
pub fn history(&self) -> Option<&'a WengertList<T>>
Gets the WengertList these records are backed by if variables, and None if constants.
source§impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
sourcepub fn unary(
&self,
fx: impl Fn(T) -> T,
dfx_dx: impl Fn(T) -> T
) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
pub fn unary( &self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T ) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
Creates a new RecordContainer from a reference to an existing RecordContainer by applying
some unary function from T
to T
to every element in the container.
To compute the new records, the unary function of some input x to some output y is needed along with its derivative with respect to its input x.
For example, tanh is a commonly used activation function, but the Real trait does not include this operation and Record has no operations for it specifically. However, you can use this function to compute the tanh for a record container like so:
use easy_ml::differentiation::{RecordTensor, WengertList};
use easy_ml::tensors::Tensor;
let list = WengertList::new();
let X = RecordTensor::variables(
&list,
Tensor::from_fn(
[("rows", 2), ("columns", 2)],
|[r, c]| 0.15 * ((1 + r + c) as f32)
)
);
// the derivative of tanh(x) is sech(x) * sech(x) which is equivalent to
// 1 / (cosh(x) * cosh(x))
let Y = X.unary(|x| x.tanh(), |x| 1.0 / (x.cosh() * x.cosh()));
// we can unwrap here because we know Y contains variables not constants
let derivatives = Y.derivatives().unwrap();
let derivatives_indexing = derivatives.index_by(["rows", "columns"]);
assert_eq!(
derivatives_indexing.get_ref([0, 0]).at_tensor(&X),
Tensor::from(
[("rows", 2), ("columns", 2)],
// [0, 0] element in Y only had the one input variable [0, 0] in X
vec![
0.9778332, 0.0,
0.0, 0.0
]
),
);
assert_eq!(
derivatives_indexing.get_ref([0, 1]).at_tensor(&X),
Tensor::from(
[("rows", 2), ("columns", 2)],
vec![
0.0, 0.915137,
0.0, 0.0
]
),
);
assert_eq!(
// [0, 1] and [1, 0] elements in X had the same starting value so end up with the same
// derivative for their corresponding input variable in X
derivatives_indexing.get_ref([0, 1]).at_tensor(&X).index().get([0, 1]),
derivatives_indexing.get_ref([1, 0]).at_tensor(&X).index().get([1, 0]),
);
assert_eq!(
derivatives_indexing.get_ref([1, 1]).at_tensor(&X),
Tensor::from(
[("rows", 2), ("columns", 2)],
vec![
0.0, 0.0,
0.0, 0.8220013
]
),
);
sourcepub fn binary<S2>(
&self,
rhs: &RecordTensor<'a, T, S2, D>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
pub fn binary<S2>( &self, rhs: &RecordTensor<'a, T, S2, D>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T ) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
Creates a new RecordContainer from two RecordContainers by applying
some binary function from T
to T
to every element pair in the containers. Both
containers must have the same shape.
To compute the new records, the binary function of some inputs x and y to some output z is needed along with its derivative with respect to its first input x and its derivative with respect to its second input y.
For example, atan2 takes two arguments, but the Real trait does not include this operation and Record has no operations for it specifically. However, you can use this function to compute the atan2 for two record containers like so:
use easy_ml::differentiation::{RecordTensor, WengertList};
use easy_ml::tensors::Tensor;
let list = WengertList::new();
let X = RecordTensor::variables(
&list,
Tensor::from_fn(
[("rows", 2), ("columns", 2)],
|[r, c]| ((1 + r + c) as f32)
)
);
let Y = RecordTensor::variables(
&list,
Tensor::from_fn(
[("rows", 2), ("columns", 2)],
|[r, c]| ((1 + r + c) as f32)
)
);
// the derivative of atan2 with respect to x is y/(x*x + y*y)
// https://www.wolframalpha.com/input/?i=d%28atan2%28x%2Cy%29%29%2Fdx
// the derivative of atan2 with respect to y is -x/(x*x + y*y)
// https://www.wolframalpha.com/input/?i=d%28atan2%28x%2Cy%29%29%2Fdy
let Z = X.binary(&Y,
|x, y| x.atan2(y),
|x, y| y/((x*x) + (y*y)),
|x, y| -x/((x*x) + (y*y))
);
// we can unwrap here because we know Z contains variables not constants
let derivatives = Z.derivatives().unwrap();
// Just as in the unary example, only one pair of the four inputs in X and Y influence the
// outputs in Z, so we have a lot of 0.0 derivatives, and the inputs in [0, 1] and [1, 0]
// are identical so we see the same derivative.
let dZ_dX = derivatives.map(|d| d.at_tensor(&X));
assert_eq!(
dZ_dX,
Tensor::from([("rows", 2), ("columns", 2)], vec![
Tensor::from([("rows", 2), ("columns", 2)], vec![
0.5, 0.0,
0.0, 0.0
]),
Tensor::from([("rows", 2), ("columns", 2)], vec![
0.0, 0.25,
0.0, 0.0
]),
Tensor::from([("rows", 2), ("columns", 2)], vec![
0.0, 0.0,
0.25, 0.0
]),
Tensor::from([("rows", 2), ("columns", 2)], vec![
0.0, 0.0,
0.0, 0.16666667
])
])
);
let dZ_dY = derivatives.map(|d| d.at_tensor(&Y));
assert_eq!(
dZ_dY,
Tensor::from([("rows", 2), ("columns", 2)], vec![
Tensor::from([("rows", 2), ("columns", 2)], vec![
-0.5, 0.0,
0.0, 0.0
]),
Tensor::from([("rows", 2), ("columns", 2)], vec![
0.0, -0.25,
0.0, 0.0
]),
Tensor::from([("rows", 2), ("columns", 2)], vec![
0.0, 0.0,
-0.25, 0.0
]),
Tensor::from([("rows", 2), ("columns", 2)], vec![
0.0, 0.0,
0.0, -0.16666667
])
])
);
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn map(
&self,
fx: impl Fn(Record<'a, T>) -> Record<'a, T>
) -> Result<RecordTensor<'a, T, Tensor<(T, Index), D>, D>, InconsistentHistory<'a, T>>
pub fn map( &self, fx: impl Fn(Record<'a, T>) -> Record<'a, T> ) -> Result<RecordTensor<'a, T, Tensor<(T, Index), D>, D>, InconsistentHistory<'a, T>>
Creates a new RecordContainer from a reference to an existing RecordContainer by applying
some unary function on Record<T>
to Record<T>
to every element in the container. This
will fail if the function would create records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
use easy_ml::numeric::Numeric;
use easy_ml::numeric::extra::Real;
use easy_ml::tensors::Tensor;
use easy_ml::differentiation::{RecordTensor, WengertList};
fn sigmoid<T: Numeric + Real+ Copy>(x: T) -> T {
T::one() / (T::one() + (-x).exp())
}
let history = WengertList::new();
let layer = RecordTensor::variables(&history, Tensor::from([("x", 2)], vec![ 0.2, 0.6 ]));
let after = layer.map(sigmoid).unwrap(); // sigmoid can't introduce inconsistent histories
NB: Mapping a RecordTensor of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
See also: AsRecords
sourcepub fn map_with_index(
&self,
fx: impl Fn([usize; D], Record<'a, T>) -> Record<'a, T>
) -> Result<RecordTensor<'a, T, Tensor<(T, Index), D>, D>, InconsistentHistory<'a, T>>
pub fn map_with_index( &self, fx: impl Fn([usize; D], Record<'a, T>) -> Record<'a, T> ) -> Result<RecordTensor<'a, T, Tensor<(T, Index), D>, D>, InconsistentHistory<'a, T>>
Creates a new RecordContainer from a reference to an existing RecordContainer by applying
some unary function on Record<T>
and each index of that position in the Record to
Record<T>
to every element in the container. This will fail if the function would create
records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
NB: Mapping a RecordTensor of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
sourcepub fn derivatives(&self) -> Option<Tensor<Derivatives<T>, D>>
pub fn derivatives(&self) -> Option<Tensor<Derivatives<T>, D>>
For each record in the container, peforms a backward pass up its WengertList from it as the output, computing all the derivatives for the inputs involving this output.
If this container has no backing WengertList, ie was created as constants, then None is returned instead. Otherwise the returned Tensor will have the same shape as this container, with the respective derivatives matching each element in this container.
If you have N inputs x1 to xN, and this output is Y with M outputs, then this computes all the derivatives δyj/δxi for i = 1 to N and j = 1 to M.
If you have a lot of outputs this could be very expensive! Reverse auto diff is optimised for domains where there are many more inputs than outputs.
If you only need some of the derivatives then derivatives_for can be used instead to avoid calculating the rest.
sourcepub fn derivatives_for(&self, indexes: [usize; D]) -> Option<Derivatives<T>>
pub fn derivatives_for(&self, indexes: [usize; D]) -> Option<Derivatives<T>>
For the record at the index, peforms a backward pass up its WengertList from it as the output, computing all the derivatives for the inputs involving this output.
If the index is invalid or this container has no backing WengertList, ie was created as constants, then None is returned instead.
If you have N inputs x1 to xN, and this output is y, then this computes all the derivatives δy/δxi for i = 1 to N.
sourcepub fn elementwise_multiply<S2>(
&self,
other: &RecordTensor<'a, T, S2, D>
) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
pub fn elementwise_multiply<S2>( &self, other: &RecordTensor<'a, T, S2, D> ) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
Performs elementwise multiplication for two record tensors of the same shape.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn elementwise_divide<S2>(
&self,
other: &RecordTensor<'a, T, S2, D>
) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
pub fn elementwise_divide<S2>( &self, other: &RecordTensor<'a, T, S2, D> ) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
Performs elementwise division for two record tensors of the same shape.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
source§impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
sourcepub fn unary_assign(&mut self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T)
pub fn unary_assign(&mut self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T)
Overwrites a RecordContainer by applying
some unary function from T
to T
to every element in the container.
To compute the new records, the unary function of some input x to some output y is needed along with its derivative with respect to its input x.
sourcepub fn binary_left_assign<S2>(
&mut self,
rhs: &RecordTensor<'a, T, S2, D>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
)
pub fn binary_left_assign<S2>( &mut self, rhs: &RecordTensor<'a, T, S2, D>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T )
Overwrites the left hand side of a RecordContainer with the result of applying
some binary function from T
to T
to every element pair in the containers. Both
containers must have the same shape.
To compute the new records, the binary function of some inputs x and y to some
output z is needed along with its derivative with respect to its first input x and
its derivative with respect to its second input y.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn do_unary_assign(
self,
fx: impl Fn(T) -> T,
dfx_dx: impl Fn(T) -> T
) -> Self
pub fn do_unary_assign( self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T ) -> Self
A convenience helper function which takes the RecordContainer value and calls unary_assign on it, returning the record container which now contains the result of the operation.
sourcepub fn do_binary_left_assign<S2>(
self,
rhs: &RecordTensor<'a, T, S2, D>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
) -> Self
pub fn do_binary_left_assign<S2>( self, rhs: &RecordTensor<'a, T, S2, D>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T ) -> Self
A convenience helper function which takes the left hand side by value and calls binary_left_assign on it, returning the left hand side which now contains the result of the operation.
sourcepub fn map_mut(
&mut self,
fx: impl Fn(Record<'a, T>) -> Record<'a, T>
) -> Result<(), InconsistentHistory<'a, T>>
pub fn map_mut( &mut self, fx: impl Fn(Record<'a, T>) -> Record<'a, T> ) -> Result<(), InconsistentHistory<'a, T>>
Updates this RecordContainer in place by applying some unary function on Record<T>
to
Record<T>
to every element in the container. This will fail if the function would create
records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
Since this updates the container in place, if Err is returned then the data in this RecordContainer is still available but it has been corrupted - at least one of the elements should have a different history than what it will have because the mapping function created inconsistent histories that couldn’t be represented by the container as it only stores one.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
NB: Mapping a RecordTensor of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
You might also use this function at the end of a training loop to update all the weights to reduce their loss.
use easy_ml::numeric::Numeric;
use easy_ml::tensors::Tensor;
use easy_ml::differentiation::{Record, RecordTensor, WengertList};
let history = WengertList::new();
let mut weights = RecordTensor::variables(
&history,
Tensor::from([("w1", 4)], vec![ 0.3, 0.2, -1.2, -0.4 ])
);
let error = {
// this is over-simplified for brevity, obviously in a real scenario we wouldn't have a
// function that calculates the error like this or we wouldn't be doing machine learning
// to fit it in the first place
let mut loss = Record::variable(0.0, &history);
for r in weights.iter_as_records() {
loss = loss + r;
}
loss
};
let derivatives = error.derivatives();
let learning_rate = 0.1;
// update the weights to contain less error than they did before
let result = weights.map_mut(|x| x - (derivatives[&x] * learning_rate));
assert!(result.is_ok()); // we know we didn't introduce an inconsistent history just updating the weights
sourcepub fn map_mut_with_index(
&mut self,
fx: impl Fn([usize; D], Record<'a, T>) -> Record<'a, T>
) -> Result<(), InconsistentHistory<'a, T>>
pub fn map_mut_with_index( &mut self, fx: impl Fn([usize; D], Record<'a, T>) -> Record<'a, T> ) -> Result<(), InconsistentHistory<'a, T>>
Updates this RecordContainer in place by applying some unary function on Record<T>
and
each index of that position in the Record to Record<T>
to every element in the container.
This will fail if the function would create records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
Since this updates the container in place, if Err is returned then the data in this RecordContainer is still available but it has been corrupted - at least one of the elements should have a different history than what it will have because the mapping function created inconsistent histories that couldn’t be represented by the container as it only stores one.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
NB: Mapping a RecordTensor of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
source§impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
impl<'a, T, S, const D: usize> RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>
sourcepub fn binary_right_assign<S2>(
&self,
rhs: &mut RecordTensor<'a, T, S2, D>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
)
pub fn binary_right_assign<S2>( &self, rhs: &mut RecordTensor<'a, T, S2, D>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T )
Overwrites the right hand side of a RecordContainer with the result of applying
some binary function from T
to T
to every element pair in the containers. Both
containers must have the same shape.
To compute the new records, the binary function of some inputs x and y to some
output z is needed along with its derivative with respect to its first input x and
its derivative with respect to its second input y.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn do_binary_right_assign<S2>(
&self,
rhs: RecordTensor<'a, T, S2, D>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
) -> RecordTensor<'a, T, S2, D>
pub fn do_binary_right_assign<S2>( &self, rhs: RecordTensor<'a, T, S2, D>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T ) -> RecordTensor<'a, T, S2, D>
A convenience helper function which takes the right hand side by value and calls binary_right_assign on it, returning the right hand side which now contains the result of the operation.
source§impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
sourcepub fn unary(
&self,
fx: impl Fn(T) -> T,
dfx_dx: impl Fn(T) -> T
) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
pub fn unary( &self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T ) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
Creates a new RecordContainer from a reference to an existing RecordContainer by applying
some unary function from T
to T
to every element in the container.
To compute the new records, the unary function of some input x to some output y is needed along with its derivative with respect to its input x.
For example, tanh is a commonly used activation function, but the Real trait does not include this operation and Record has no operations for it specifically. However, you can use this function to compute the tanh for a record container like so:
use easy_ml::differentiation::{RecordMatrix, WengertList};
use easy_ml::matrices::Matrix;
let list = WengertList::new();
let X = RecordMatrix::variables(
&list,
Matrix::from_fn((2, 2), |(r, c)| 0.15 * ((1 + r + c) as f32))
);
// the derivative of tanh(x) is sech(x) * sech(x) which is equivalent to
// 1 / (cosh(x) * cosh(x))
let Y = X.unary(|x| x.tanh(), |x| 1.0 / (x.cosh() * x.cosh()));
// we can unwrap here because we know Y contains variables not constants
let derivatives = Y.derivatives().unwrap();
assert_eq!(
derivatives.get_reference(0, 0).at_matrix(&X),
Matrix::from(vec![
// (0, 0) element in Y only had the one input variable (0, 0) in X
vec![0.9778332, 0.0],
vec![0.0, 0.0]
]),
);
assert_eq!(
derivatives.get_reference(0, 1).at_matrix(&X),
Matrix::from(vec![
vec![0.0, 0.915137],
vec![0.0, 0.0]
]),
);
assert_eq!(
// (0, 1) and (1, 0) elements in X had the same starting value so end up with the same
// derivative for their corresponding input variable in X
derivatives.get_reference(0, 1).at_matrix(&X).get(0, 1),
derivatives.get_reference(1, 0).at_matrix(&X).get(1, 0),
);
assert_eq!(
derivatives.get_reference(1, 1).at_matrix(&X),
Matrix::from(vec![
vec![0.0, 0.0 ],
vec![0.0, 0.8220013]
]),
);
sourcepub fn binary<S2>(
&self,
rhs: &RecordMatrix<'a, T, S2>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
pub fn binary<S2>( &self, rhs: &RecordMatrix<'a, T, S2>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T ) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
Creates a new RecordContainer from two RecordContainers by applying
some binary function from T
to T
to every element pair in the containers. Both
containers must have the same shape.
To compute the new records, the binary function of some inputs x and y to some output z is needed along with its derivative with respect to its first input x and its derivative with respect to its second input y.
For example, atan2 takes two arguments, but the Real trait does not include this operation and Record has no operations for it specifically. However, you can use this function to compute the atan2 for two record containers like so:
use easy_ml::differentiation::{RecordMatrix, WengertList};
use easy_ml::matrices::Matrix;
let list = WengertList::new();
let X = RecordMatrix::variables(
&list,
Matrix::from_fn((2, 2), |(r, c)| ((1 + r + c) as f32))
);
let Y = RecordMatrix::variables(
&list,
Matrix::from_fn((2, 2), |(r, c)| ((1 + r + c) as f32))
);
// the derivative of atan2 with respect to x is y/(x*x + y*y)
// https://www.wolframalpha.com/input/?i=d%28atan2%28x%2Cy%29%29%2Fdx
// the derivative of atan2 with respect to y is -x/(x*x + y*y)
// https://www.wolframalpha.com/input/?i=d%28atan2%28x%2Cy%29%29%2Fdy
let Z = X.binary(&Y,
|x, y| x.atan2(y),
|x, y| y/((x*x) + (y*y)),
|x, y| -x/((x*x) + (y*y))
);
// we can unwrap here because we know Z contains variables not constants
let derivatives = Z.derivatives().unwrap();
// Just as in the unary example, only one pair of the four inputs in X and Y influence the
// outputs in Z, so we have a lot of 0.0 derivatives, and the inputs in [0, 1] and [1, 0]
// are identical so we see the same derivative.
let dZ_dX = derivatives.map(|d| d.at_matrix(&X));
assert_eq!(
dZ_dX,
Matrix::from(vec![
vec![
Matrix::from(vec![
vec![ 0.5, 0.0 ],
vec![ 0.0, 0.0 ]
]),
Matrix::from(vec![
vec![ 0.0, 0.25 ],
vec![ 0.0, 0.0 ]
])
],
vec![
Matrix::from(vec![
vec![ 0.0, 0.0 ],
vec![ 0.25, 0.0 ]
]),
Matrix::from(vec![
vec![ 0.0, 0.0 ],
vec![ 0.0, 0.16666667 ]
])
]
])
);
let dZ_dY = derivatives.map(|d| d.at_matrix(&Y));
assert_eq!(
dZ_dY,
Matrix::from(vec![
vec![
Matrix::from(vec![
vec![ -0.5, 0.0 ],
vec![ 0.0, 0.0 ]
]),
Matrix::from(vec![
vec![ 0.0, -0.25 ],
vec![ 0.0, 0.0 ]
])
],
vec![
Matrix::from(vec![
vec![ 0.0, 0.0 ],
vec![ -0.25, 0.0 ]
]),
Matrix::from(vec![
vec![ 0.0, 0.0 ],
vec![ 0.0, -0.16666667 ]
])
]
])
);
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn map(
&self,
fx: impl Fn(Record<'a, T>) -> Record<'a, T>
) -> Result<RecordMatrix<'a, T, Matrix<(T, Index)>>, InconsistentHistory<'a, T>>
pub fn map( &self, fx: impl Fn(Record<'a, T>) -> Record<'a, T> ) -> Result<RecordMatrix<'a, T, Matrix<(T, Index)>>, InconsistentHistory<'a, T>>
Creates a new RecordContainer from a reference to an existing RecordContainer by applying
some unary function on Record<T>
to Record<T>
to every element in the container. This
will fail if the function would create records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
use easy_ml::numeric::Numeric;
use easy_ml::numeric::extra::Real;
use easy_ml::matrices::Matrix;
use easy_ml::differentiation::{RecordMatrix, WengertList};
fn sigmoid<T: Numeric + Real+ Copy>(x: T) -> T {
T::one() / (T::one() + (-x).exp())
}
let history = WengertList::new();
let layer = RecordMatrix::variables(&history, Matrix::from(vec![vec![ 0.2, 0.6 ]]));
let after = layer.map(sigmoid).unwrap(); // sigmoid can't introduce inconsistent histories
NB: Mapping a RecordMatrix of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
See also: AsRecords
sourcepub fn map_with_index(
&self,
fx: impl Fn(Record<'a, T>, Row, Column) -> Record<'a, T>
) -> Result<RecordMatrix<'a, T, Matrix<(T, Index)>>, InconsistentHistory<'a, T>>
pub fn map_with_index( &self, fx: impl Fn(Record<'a, T>, Row, Column) -> Record<'a, T> ) -> Result<RecordMatrix<'a, T, Matrix<(T, Index)>>, InconsistentHistory<'a, T>>
Creates a new RecordContainer from a reference to an existing RecordContainer by applying
some unary function on Record<T>
and each index of that position in the Record to
Record<T>
to every element in the container. This will fail if the function would
create records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
NB: Mapping a RecordMatrix of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
sourcepub fn derivatives(&self) -> Option<Matrix<Derivatives<T>>>
pub fn derivatives(&self) -> Option<Matrix<Derivatives<T>>>
For each record in the container, peforms a backward pass up its WengertList from it as the output, computing all the derivatives for the inputs involving this output.
If this container has no backing WengertList, ie was created as constants, then None is returned instead. Otherwise the returned Matrix will have the same size as this container, with the respective derivatives matching each element in this container.
If you have N inputs x1 to xN, and this output is Y with M outputs, then this computes all the derivatives δyj/δxi for i = 1 to N and j = 1 to M.
If you have a lot of outputs this could be very expensive! Reverse auto diff is optimised for domains where there are many more inputs than outputs.
If you only need some of the derivatives then derivatives_for can be used instead to avoid calculating the rest.
sourcepub fn derivatives_for(
&self,
row: Row,
column: Column
) -> Option<Derivatives<T>>
pub fn derivatives_for( &self, row: Row, column: Column ) -> Option<Derivatives<T>>
For the record at the index, peforms a backward pass up its WengertList from it as the output, computing all the derivatives for the inputs involving this output.
If the index is invalid or this container has no backing WengertList, ie was created as constants, then None is returned instead.
If you have N inputs x1 to xN, and this output is y, then this computes all the derivatives δy/δxi for i = 1 to N.
sourcepub fn elementwise_multiply<S2>(
&self,
other: &RecordMatrix<'a, T, S2>
) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
pub fn elementwise_multiply<S2>( &self, other: &RecordMatrix<'a, T, S2> ) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
Performs elementwise multiplication for two record matrices of the same size.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn elementwise_divide<S2>(
&self,
other: &RecordMatrix<'a, T, S2>
) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
pub fn elementwise_divide<S2>( &self, other: &RecordMatrix<'a, T, S2> ) -> RecordMatrix<'a, T, Matrix<(T, Index)>>
Performs elementwise division for two record matrices of the same size.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
source§impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S: MatrixMut<(T, Index)> + NoInteriorMutability,
impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S: MatrixMut<(T, Index)> + NoInteriorMutability,
sourcepub fn unary_assign(&mut self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T)
pub fn unary_assign(&mut self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T)
Overwrites a RecordContainer by applying
some unary function from T
to T
to every element in the container.
To compute the new records, the unary function of some input x to some output y is needed along with its derivative with respect to its input x.
sourcepub fn binary_left_assign<S2>(
&mut self,
rhs: &RecordMatrix<'a, T, S2>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
)
pub fn binary_left_assign<S2>( &mut self, rhs: &RecordMatrix<'a, T, S2>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T )
Overwrites the left hand side of a RecordContainer with the result of applying
some binary function from T
to T
to every element pair in the containers. Both
containers must have the same shape.
To compute the new records, the binary function of some inputs x and y to some
output z is needed along with its derivative with respect to its first input x and
its derivative with respect to its second input y.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn do_unary_assign(
self,
fx: impl Fn(T) -> T,
dfx_dx: impl Fn(T) -> T
) -> Self
pub fn do_unary_assign( self, fx: impl Fn(T) -> T, dfx_dx: impl Fn(T) -> T ) -> Self
A convenience helper function which takes the RecordContainer value and calls unary_assign on it, returning the record container which now contains the result of the operation.
sourcepub fn do_binary_left_assign<S2>(
self,
rhs: &RecordMatrix<'a, T, S2>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
) -> Self
pub fn do_binary_left_assign<S2>( self, rhs: &RecordMatrix<'a, T, S2>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T ) -> Self
A convenience helper function which takes the left hand side by value and calls binary_left_assign on it, returning the left hand side which now contains the result of the operation.
sourcepub fn map_mut(
&mut self,
fx: impl Fn(Record<'a, T>) -> Record<'a, T>
) -> Result<(), InconsistentHistory<'a, T>>
pub fn map_mut( &mut self, fx: impl Fn(Record<'a, T>) -> Record<'a, T> ) -> Result<(), InconsistentHistory<'a, T>>
Updates this RecordContainer in place by applying some unary function on Record<T>
to
Record<T>
to every element in the container. This will fail if the function would create
records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
Since this updates the container in place, if Err is returned then the data in this RecordContainer is still available but it has been corrupted - at least one of the elements should have a different history than what it will have because the mapping function created inconsistent histories that couldn’t be represented by the container as it only stores one.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
NB: Mapping a RecordMatrix of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
You might also use this function at the end of a training loop to update all the weights to reduce their loss.
use easy_ml::numeric::Numeric;
use easy_ml::matrices::Matrix;
use easy_ml::differentiation::{Record, RecordMatrix, WengertList};
let history = WengertList::new();
let mut weights = RecordMatrix::variables(
&history,
Matrix::from(vec![vec![ 0.3, 0.2, -1.2, -0.4 ]])
);
let error = {
// this is over-simplified for brevity, obviously in a real scenario we wouldn't have a
// function that calculates the error like this or we wouldn't be doing machine learning
// to fit it in the first place
let mut loss = Record::variable(0.0, &history);
for r in weights.iter_row_major_as_records() {
loss = loss + r;
}
loss
};
let derivatives = error.derivatives();
let learning_rate = 0.1;
// update the weights to contain less error than they did before
let result = weights.map_mut(|x| x - (derivatives[&x] * learning_rate));
assert!(result.is_ok()); // we know we didn't introduce an inconsistent history just updating the weights
sourcepub fn map_mut_with_index(
&mut self,
fx: impl Fn(Record<'a, T>, Row, Column) -> Record<'a, T>
) -> Result<(), InconsistentHistory<'a, T>>
pub fn map_mut_with_index( &mut self, fx: impl Fn(Record<'a, T>, Row, Column) -> Record<'a, T> ) -> Result<(), InconsistentHistory<'a, T>>
Updates this RecordContainer in place by applying some unary function on Record<T>
and
each index of that position in the Record to Record<T>
to every element in the container.
This will fail if the function would create records with inconsistent histories.
When used with pure functions that can’t return different histories for different inputs unwrapping with always succeed.
Since this updates the container in place, if Err is returned then the data in this RecordContainer is still available but it has been corrupted - at least one of the elements should have a different history than what it will have because the mapping function created inconsistent histories that couldn’t be represented by the container as it only stores one.
This API can allow you to call a generic function that operates on Numeric numbers and apply all the correct derivative tracking during the intermediate calculations for you, without having to resort to storing the Record types.
NB: Mapping a RecordMatrix of constants to variables is not inconsistent, the history after mapping doesn’t have to be the same as before, only must be the same for every mapped element.
source§impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S> RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
sourcepub fn binary_right_assign<S2>(
&self,
rhs: &mut RecordMatrix<'a, T, S2>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
)
pub fn binary_right_assign<S2>( &self, rhs: &mut RecordMatrix<'a, T, S2>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T )
Overwrites the right hand side of a RecordContainer with the result of applying
some binary function from T
to T
to every element pair in the containers. Both
containers must have the same shape.
To compute the new records, the binary function of some inputs x and y to some
output z is needed along with its derivative with respect to its first input x and
its derivative with respect to its second input y.
§Panics
- If both record containers have a WengertList that are different to each other
- If the record containers have different shapes
sourcepub fn do_binary_right_assign<S2>(
&self,
rhs: RecordMatrix<'a, T, S2>,
fxy: impl Fn(T, T) -> T,
dfxy_dx: impl Fn(T, T) -> T,
dfxy_dy: impl Fn(T, T) -> T
) -> RecordMatrix<'a, T, S2>
pub fn do_binary_right_assign<S2>( &self, rhs: RecordMatrix<'a, T, S2>, fxy: impl Fn(T, T) -> T, dfxy_dx: impl Fn(T, T) -> T, dfxy_dy: impl Fn(T, T) -> T ) -> RecordMatrix<'a, T, S2>
A convenience helper function which takes the right hand side by value and calls binary_right_assign on it, returning the right hand side which now contains the result of the operation.
Trait Implementations§
source§impl<'a, T, S1, S2> Add<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Add<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices with both referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
+
operator.source§impl<'a, T, S1, S2> Add<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Add<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices with the right referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
+
operator.source§impl<'a, T, S1, S2, const D: usize> Add<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Add<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
Operation for two record tensors with both referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
+
operator.source§impl<'a, T, S1, S2, const D: usize> Add<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Add<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
Operation for two record tensors with the right referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
+
operator.source§impl<'a, T, S1, S2> Add<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Add<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices with the left referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
+
operator.source§impl<'a, T, S1, S2> Add<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Add<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices of the same type.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
+
operator.source§impl<'a, T, S1, S2, const D: usize> Add<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Add<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
Operation for two record tensors with the left referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
+
operator.source§impl<'a, T, S1, S2, const D: usize> Add<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Add<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
Operation for two record tensors of the same type.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
+
operator.source§impl<'a, T, S, const D: usize> Clone for RecordContainer<'a, T, S, D>
impl<'a, T, S, const D: usize> Clone for RecordContainer<'a, T, S, D>
Any record container of a Cloneable type implements clone
source§impl<'a, T: Debug + Primitive, S: Debug, const D: usize> Debug for RecordContainer<'a, T, S, D>
impl<'a, T: Debug + Primitive, S: Debug, const D: usize> Debug for RecordContainer<'a, T, S, D>
source§impl<'a, T, S> From<&RecordContainer<'a, T, TensorView<(T, usize), S, 0>, 0>> for Record<'a, T>
impl<'a, T, S> From<&RecordContainer<'a, T, TensorView<(T, usize), S, 0>, 0>> for Record<'a, T>
A zero dimensional record tensor can be converted losslessly into a record.
source§fn from(scalar: &RecordTensor<'a, T, S, 0>) -> Record<'a, T>
fn from(scalar: &RecordTensor<'a, T, S, 0>) -> Record<'a, T>
Converts the sole element in the zero dimensional record tensor into a record.
source§impl<'a, T, S> From<RecordContainer<'a, T, TensorView<(T, usize), S, 0>, 0>> for Record<'a, T>
impl<'a, T, S> From<RecordContainer<'a, T, TensorView<(T, usize), S, 0>, 0>> for Record<'a, T>
A zero dimensional record tensor can be converted losslessly into a record.
source§fn from(scalar: RecordTensor<'a, T, S, 0>) -> Record<'a, T>
fn from(scalar: RecordTensor<'a, T, S, 0>) -> Record<'a, T>
Converts the sole element in the zero dimensional record tensor into a record.
source§impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Matrix multiplication for two record matrices with both referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
*
operator.source§impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Matrix multiplication for two record matrices with the right referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
*
operator.source§impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for &RecordTensor<'a, T, S1, 2>
impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for &RecordTensor<'a, T, S1, 2>
Matrix multiplication for two record tensors with both referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
*
operator.source§impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for RecordTensor<'a, T, S1, 2>
impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for RecordTensor<'a, T, S1, 2>
Matrix multiplication for two record tensors with the right referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
*
operator.source§impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Matrix multiplication for two record matrices with the left referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
*
operator.source§impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Matrix multiplication for two record matrices of the same type.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
*
operator.source§impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for &RecordTensor<'a, T, S1, 2>
impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for &RecordTensor<'a, T, S1, 2>
Matrix multiplication for two record tensors with the left referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
*
operator.source§impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for RecordTensor<'a, T, S1, 2>
impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for RecordTensor<'a, T, S1, 2>
Matrix multiplication for two record tensors of the same type.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>
*
operator.source§impl<'a, T, S> Pow<&RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for &Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S> Pow<&RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for &Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for a constant and a record matrix of the same type with both referenced. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
fn pow(self, rhs: &RecordMatrix<'a, T, S>) -> Self::Output
source§impl<'a, T, S> Pow<&RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S> Pow<&RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for a constant and a referenced record matrix of the same type. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
fn pow(self, rhs: &RecordMatrix<'a, T, S>) -> Self::Output
source§impl<'a, T, S, const D: usize> Pow<&RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for &T
impl<'a, T, S, const D: usize> Pow<&RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for &T
Operation for a constant and a record tensor of the same type with both referenced. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
fn pow(self, rhs: &RecordTensor<'a, T, S, D>) -> Self::Output
source§impl<'a, T, S, const D: usize> Pow<&RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for T
impl<'a, T, S, const D: usize> Pow<&RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for T
Operation for a constant and a referenced record tensor of the same type. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
fn pow(self, rhs: &RecordTensor<'a, T, S, D>) -> Self::Output
source§impl<'a, T, S> Pow<RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for &Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S> Pow<RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for &Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for a referenced constant and a record matrix of the same type. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
fn pow(self, rhs: RecordMatrix<'a, T, S>) -> Self::Output
source§impl<'a, T, S> Pow<RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S> Pow<RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for Twhere
T: Numeric + Real + Primitive,
for<'t> &'t T: NumericRef<T> + RealRef<T>,
S: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for a constant and a record matrix of the same type. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
fn pow(self, rhs: RecordMatrix<'a, T, S>) -> Self::Output
source§impl<'a, T, S, const D: usize> Pow<RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for &T
impl<'a, T, S, const D: usize> Pow<RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for &T
Operation for a referenced constant and a record tensor of the same type. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
fn pow(self, rhs: RecordTensor<'a, T, S, D>) -> Self::Output
source§impl<'a, T, S, const D: usize> Pow<RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for T
impl<'a, T, S, const D: usize> Pow<RecordContainer<'a, T, TensorView<(T, usize), S, D>, D>> for T
Operation for a constant and a record tensor of the same type. The scalar is applied to all elements, this is a shorthand for unary().
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
fn pow(self, rhs: RecordTensor<'a, T, S, D>) -> Self::Output
source§impl<'a, T, S1, S2> Sub<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Sub<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices with both referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
-
operator.source§impl<'a, T, S1, S2> Sub<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Sub<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices with the right referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
-
operator.source§impl<'a, T, S1, S2, const D: usize> Sub<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Sub<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
Operation for two record tensors with both referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
-
operator.source§impl<'a, T, S1, S2, const D: usize> Sub<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Sub<&RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
Operation for two record tensors with the right referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
-
operator.source§impl<'a, T, S1, S2> Sub<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Sub<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices with the left referenced.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
-
operator.source§impl<'a, T, S1, S2> Sub<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
impl<'a, T, S1, S2> Sub<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>where
T: Numeric + Primitive,
for<'t> &'t T: NumericRef<T>,
S1: MatrixRef<(T, Index)> + NoInteriorMutability,
S2: MatrixRef<(T, Index)> + NoInteriorMutability,
Operation for two record matrices of the same type.
§type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
-
operator.source§impl<'a, T, S1, S2, const D: usize> Sub<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Sub<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for &RecordTensor<'a, T, S1, D>
Operation for two record tensors with the left referenced.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
-
operator.source§impl<'a, T, S1, S2, const D: usize> Sub<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
impl<'a, T, S1, S2, const D: usize> Sub<RecordContainer<'a, T, TensorView<(T, usize), S2, D>, D>> for RecordTensor<'a, T, S1, D>
Operation for two record tensors of the same type.
§type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>
-
operator.