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>
where T: Numeric + Primitive,

source

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

source

pub fn from_iters<I, const N: usize>( shape: [(Dimension, usize); D], iter: I ) -> [Result<Self, InvalidRecordIteratorError<'a, T, D>>; N]
where I: IntoIterator<Item = [Record<'a, T>; 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>
where T: Numeric + Primitive,

source

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.

source

pub fn from_iters<I, const N: usize>( size: (Row, Column), iter: I ) -> [Result<Self, InvalidRecordIteratorError<'a, T, 2>>; N]
where I: IntoIterator<Item = [Record<'a, T>; 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>
where T: Numeric + Primitive,

source

pub fn constants<S>(c: S) -> Self
where 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.

source

pub fn variables<S>(history: &'a WengertList<T>, x: S) -> Self
where 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>
where T: Numeric + Primitive, S: TensorRef<(T, Index), D>,

source

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

source

pub fn shape(&self) -> [(Dimension, usize); D]

The shape of this container’s source.

source

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());
source

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());
source

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.

source

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.

source

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);
source

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>
where T: Numeric + Primitive, S: TensorMut<(T, Index), D>,

source

pub fn reset(&mut self)

Resets all of the records to place them back on the WengertList, for use in performing another derivation after clearing the WengertList.

This is also a preferred shorthand for map_mut(Record::do_reset) that can’t fail

source

pub fn do_reset(x: Self) -> Self

A convenience helper function which takes a RecordContainer by value and calls reset on it.

source§

impl<'a, T> RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>
where T: Numeric + Primitive,

source

pub fn constants<S>(c: S) -> Self

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.

source

pub fn variables<S>(history: &'a WengertList<T>, x: S) -> Self

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>

source

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.

source

pub fn size(&self) -> (Row, Column)

Returns the dimensionality of this matrix container in Row, Column format

source

pub fn rows(&self) -> Row

Gets the number of rows visible to this matrix container.

source

pub fn columns(&self) -> Column

Gets the number of columns visible to this matrix container.

source

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());
source

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.

source

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))

source

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))

source

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

source

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>

source

pub fn reset(&mut self)

Resets all of the records to place them back on the WengertList, for use in performing another derivation after clearing the WengertList.

This is also a preferred shorthand for map_mut(Record::do_reset) that can’t fail

source

pub fn do_reset(x: Self) -> Self

A convenience helper function which takes a RecordContainer by value and calls reset on it.

source§

impl<'a, T, S, const D: usize> RecordContainer<'a, T, S, D>
where T: Primitive,

source

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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S: TensorRef<(T, Index), D>,

source

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
       ]
    ),
);
source

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>
where S2: TensorRef<(T, Index), 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
source

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

source

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.

source

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.

source

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.

source

pub fn elementwise_multiply<S2>( &self, other: &RecordTensor<'a, T, S2, D> ) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
where S2: TensorRef<(T, Index), 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
source

pub fn elementwise_divide<S2>( &self, other: &RecordTensor<'a, T, S2, D> ) -> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
where S2: TensorRef<(T, Index), 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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S: TensorMut<(T, Index), D>,

source

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.

source

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 )
where S2: TensorRef<(T, Index), D>,

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
source

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.

source

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
where S2: TensorRef<(T, Index), D>,

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.

source

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
source

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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S: TensorRef<(T, Index), D>,

source

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 )
where S2: TensorMut<(T, Index), D>,

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
source

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>
where S2: TensorMut<(T, Index), 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>

source

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]
    ]),
);
source

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
source

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

source

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.

source

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.

source

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.

source

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
source

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>

source

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.

source

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
source

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.

source

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.

source

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
source

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>

source

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
source

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>

Operation for two record matrices with both referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the + operator.
source§

fn add(self, rhs: &RecordMatrix<'a, T, S2>) -> Self::Output

Performs the + operation. Read more
source§

impl<'a, T, S1, S2> Add<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>

Operation for two record matrices with the right referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the + operator.
source§

fn add(self, rhs: &RecordMatrix<'a, T, S2>) -> Self::Output

Performs the + operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors with both referenced.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the + operator.
source§

fn add(self, rhs: &RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the + operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors with the right referenced.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the + operator.
source§

fn add(self, rhs: &RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the + operation. Read more
source§

impl<'a, T, S1, S2> Add<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>

Operation for two record matrices with the left referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the + operator.
source§

fn add(self, rhs: RecordMatrix<'a, T, S2>) -> Self::Output

Performs the + operation. Read more
source§

impl<'a, T, S1, S2> Add<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>

Operation for two record matrices of the same type.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the + operator.
source§

fn add(self, rhs: RecordMatrix<'a, T, S2>) -> Self::Output

Performs the + operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors with the left referenced.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the + operator.
source§

fn add(self, rhs: RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the + operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors of the same type.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the + operator.
source§

fn add(self, rhs: RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the + operation. Read more
source§

impl<'a, T, S, const D: usize> Clone for RecordContainer<'a, T, S, D>
where T: Clone + Primitive, S: Clone,

Any record container of a Cloneable type implements clone

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<'a, T: Debug + Primitive, S: Debug, const D: usize> Debug for RecordContainer<'a, T, S, D>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a, T, S> From<&RecordContainer<'a, T, TensorView<(T, usize), S, 0>, 0>> for Record<'a, T>
where T: Numeric + Primitive, S: TensorRef<(T, Index), 0>,

A zero dimensional record tensor can be converted losslessly into a record.

source§

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>
where T: Numeric + Primitive, S: TensorRef<(T, Index), 0>,

A zero dimensional record tensor can be converted losslessly into a record.

source§

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>

Matrix multiplication for two record matrices with both referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: &RecordMatrix<'a, T, S2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>

Matrix multiplication for two record matrices with the right referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: &RecordMatrix<'a, T, S2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for &RecordTensor<'a, T, S1, 2>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), 2>, S2: TensorRef<(T, Index), 2>,

Matrix multiplication for two record tensors with both referenced.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), 2>, 2>, 2>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: &RecordTensor<'a, T, S2, 2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S1, S2> Mul<&RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for RecordTensor<'a, T, S1, 2>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), 2>, S2: TensorRef<(T, Index), 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>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: &RecordTensor<'a, T, S2, 2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>

Matrix multiplication for two record matrices with the left referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: RecordMatrix<'a, T, S2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>

Matrix multiplication for two record matrices of the same type.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: RecordMatrix<'a, T, S2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for &RecordTensor<'a, T, S1, 2>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), 2>, S2: TensorRef<(T, Index), 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>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: RecordTensor<'a, T, S2, 2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S1, S2> Mul<RecordContainer<'a, T, TensorView<(T, usize), S2, 2>, 2>> for RecordTensor<'a, T, S1, 2>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), 2>, S2: TensorRef<(T, Index), 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>

The resulting type after applying the * operator.
source§

fn mul(self, rhs: RecordTensor<'a, T, S2, 2>) -> Self::Output

Performs the * operation. Read more
source§

impl<'a, T, S> Pow<&RecordContainer<'a, T, MatrixView<(T, usize), S>, 2>> for &T
where 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>

source§

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 T
where 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>

source§

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
where T: Numeric + Real + Primitive, for<'t> &'t T: NumericRef<T> + RealRef<T>, S: TensorRef<(T, Index), D>,

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>

source§

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
where T: Numeric + Real + Primitive, for<'t> &'t T: NumericRef<T> + RealRef<T>, S: TensorRef<(T, Index), D>,

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>

source§

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 &T
where 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>

source§

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 T
where 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>

source§

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
where T: Numeric + Real + Primitive, for<'t> &'t T: NumericRef<T> + RealRef<T>, S: TensorRef<(T, Index), D>,

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>

source§

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
where T: Numeric + Real + Primitive, for<'t> &'t T: NumericRef<T> + RealRef<T>, S: TensorRef<(T, Index), D>,

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>

source§

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>

Operation for two record matrices with both referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: &RecordMatrix<'a, T, S2>) -> Self::Output

Performs the - operation. Read more
source§

impl<'a, T, S1, S2> Sub<&RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>

Operation for two record matrices with the right referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: &RecordMatrix<'a, T, S2>) -> Self::Output

Performs the - operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors with both referenced.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: &RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the - operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors with the right referenced.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: &RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the - operation. Read more
source§

impl<'a, T, S1, S2> Sub<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for &RecordMatrix<'a, T, S1>

Operation for two record matrices with the left referenced.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: RecordMatrix<'a, T, S2>) -> Self::Output

Performs the - operation. Read more
source§

impl<'a, T, S1, S2> Sub<RecordContainer<'a, T, MatrixView<(T, usize), S2>, 2>> for RecordMatrix<'a, T, S1>

Operation for two record matrices of the same type.

§

type Output = RecordContainer<'a, T, MatrixView<(T, usize), Matrix<(T, usize)>>, 2>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: RecordMatrix<'a, T, S2>) -> Self::Output

Performs the - operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors with the left referenced.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the - operation. Read more
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>
where T: Numeric + Primitive, for<'t> &'t T: NumericRef<T>, S1: TensorRef<(T, Index), D>, S2: TensorRef<(T, Index), D>,

Operation for two record tensors of the same type.

§

type Output = RecordContainer<'a, T, TensorView<(T, usize), Tensor<(T, usize), D>, D>, D>

The resulting type after applying the - operator.
source§

fn sub(self, rhs: RecordTensor<'a, T, S2, D>) -> Self::Output

Performs the - operation. Read more

Auto Trait Implementations§

§

impl<'a, T, S, const D: usize> Freeze for RecordContainer<'a, T, S, D>
where S: Freeze,

§

impl<'a, T, S, const D: usize> !RefUnwindSafe for RecordContainer<'a, T, S, D>

§

impl<'a, T, S, const D: usize> !Send for RecordContainer<'a, T, S, D>

§

impl<'a, T, S, const D: usize> !Sync for RecordContainer<'a, T, S, D>

§

impl<'a, T, S, const D: usize> Unpin for RecordContainer<'a, T, S, D>
where S: Unpin,

§

impl<'a, T, S, const D: usize> !UnwindSafe for RecordContainer<'a, T, S, D>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T, Rhs, Output> NumericByValue<Rhs, Output> for T
where T: Sub<Rhs, Output = Output> + Div<Rhs, Output = Output> + Mul<Rhs, Output = Output> + Neg<Output = Output> + Add<Rhs, Output = Output>,

source§

impl<RefT, T> NumericRef<T> for RefT
where RefT: NumericByValue<T, T> + for<'a> NumericByValue<&'a T, T>,

source§

impl<T, Rhs, Output> RealByValue<Rhs, Output> for T
where T: Pow<Rhs, Output = Output> + Sqrt<Output = Output> + Ln<Output = Output> + Sin<Output = Output> + Exp<Output = Output> + Cos<Output = Output>,

source§

impl<RefT, T> RealRef<T> for RefT
where RefT: RealByValue<T, T> + for<'a> RealByValue<&'a T, T>,