easy_ml/differentiation/container_record/
iterators.rs

1/*!
2 * Record container iterators, for manipulating iterators of Records and converting back to
3 * Record containers.
4 */
5
6use crate::differentiation::{Index, Primitive, Record, WengertList};
7use crate::differentiation::{RecordMatrix, RecordTensor};
8use crate::matrices::iterators::{ColumnMajorIterator, RowMajorIterator, WithIndex};
9use crate::matrices::views::{MatrixRef, MatrixView, NoInteriorMutability};
10use crate::matrices::{Column, Matrix, Row};
11use crate::numeric::Numeric;
12use crate::tensors::indexing::TensorIterator;
13use crate::tensors::views::{TensorRef, TensorView};
14use crate::tensors::{Dimension, InvalidShapeError, Tensor};
15
16use std::error::Error;
17use std::fmt;
18use std::fmt::Debug;
19use std::iter::{ExactSizeIterator, FusedIterator};
20
21/**
22 * A wrapper around another iterator of record data and a history for that iterator's data
23 * that iterates though each element in the iterator as a [Record] instead.
24 *
25 * The main purpose of this representation is to allow manipulating one or more
26 * [RecordContainer](crate::differentiation::RecordContainer)s as iterators of Records then
27 * collect the iterator back into a RecordContainer so containers of Records with a shared history
28 * don't have to store the history for each element but the richer Record API can be used for
29 * operations anyway.
30 *
31 * ```
32 * use easy_ml::differentiation::{WengertList, Record, RecordTensor};
33 * use easy_ml::tensors::Tensor;
34 * use easy_ml::numeric::extra::Real;
35 *
36 * let history = WengertList::new();
37 * let A = RecordTensor::variables(
38 *     &history,
39 *     Tensor::from_fn([("r", 3), ("c", 2)], |[r, c]| ((5 * r) + c) as f32)
40 * );
41 * let B = RecordTensor::variables(
42 *     &history,
43 *     Tensor::from([("x", 6)], vec![ 0.2, 0.1, 0.5, 0.3, 0.7, 0.9 ])
44 * );
45 *
46 * fn power<T: Real+ Copy>(x: T, y: T) -> T {
47 *     x.pow(y)
48 * }
49 *
50 * let result: RecordTensor<_, _, 2> = RecordTensor::from_iter(
51 *     A.shape(),
52 *     // iterators of records don't need to have matching shapes as long as the number
53 *     // of elements matches the final shape
54 *     A.iter_as_records().zip(B.iter_as_records()).map(|(x, y)| power(x, y))
55 * ).expect("result should have 6 elements");
56 * ```
57 */
58pub struct AsRecords<'a, I, T> {
59    numbers: I,
60    history: Option<&'a WengertList<T>>,
61}
62
63/**
64 * AsRecords can be created from a RecordTensor to manipulate the data as an iterator of Records
65 * then streamed back into a RecordTensor with [from_iter](RecordTensor::from_iter)
66 *
67 * See also: [map](RecordTensor::map), [map_mut](RecordTensor::map_mut)
68 *
69 * ```
70 * use easy_ml::differentiation::{WengertList, Record, RecordTensor};
71 * use easy_ml::tensors::Tensor;
72 *
73 * let history = WengertList::new();
74 * let X = RecordTensor::constants(
75 *     Tensor::from_fn([("r", 2), ("c", 2)], |[r, c]| (r + c) as f32)
76 * );
77 * let y = Record::variable(1.0, &history);
78 * let result = RecordTensor::from_iter(
79 *     [("r", 2), ("c", 2)],
80 *     // Here we create each variable z from the constant in X and the variable y.
81 *     // If we just did X + 1.0 we'd still have only constants, and we can't do X + y
82 *     // directly because those traits aren't implemented.
83 *     X.iter_as_records().map(|x| x + y)
84 * );
85 * // we can unwrap here because we know the iterator still contains 4 elements and they all
86 * // have the same WengertList so we can convert back to a RecordTensor (which is now
87 * // variables instead of constants)
88 * let Z = result.unwrap();
89 * let Z_indexing = Z.index();
90 * assert_eq!(1.0, Z_indexing.get([0, 0]).0);
91 * assert_eq!(2.0, Z_indexing.get([0, 1]).0);
92 * assert_eq!(3.0, Z_indexing.get([1, 1]).0);
93 * ```
94 */
95impl<'a, 'b, T, S, const D: usize>
96    AsRecords<'a, TensorIterator<'b, (T, Index), RecordTensor<'a, T, S, D>, D>, T>
97where
98    T: Numeric + Primitive,
99    S: TensorRef<(T, Index), D>,
100{
101    /**
102     * Given a record tensor returns an iterator of Records
103     *
104     * ```
105     * use easy_ml::differentiation::{WengertList, Record, RecordTensor};
106     * use easy_ml::differentiation::iterators::AsRecords;
107     * use easy_ml::tensors::Tensor;
108     *
109     * let history = WengertList::new();
110     * let X = RecordTensor::variables(
111     *     &history,
112     *     Tensor::from_fn([("r", 2), ("c", 2)], |[r, c]| (r + c) as f32)
113     * );
114     * let iter = X.iter_as_records(); // shorthand helper method
115     * let also_iter = AsRecords::from_tensor(&X);
116     * ```
117     */
118    pub fn from_tensor(tensor: &'b RecordTensor<'a, T, S, D>) -> Self {
119        AsRecords::from(tensor.history, TensorIterator::from(tensor))
120    }
121}
122
123/**
124 * AsRecords can be created from a RecordMatrix to manipulate the data as an iterator of Records
125 * then streamed back into a RecordMatrix with [from_iter](RecordMatrix::from_iter)
126 *
127 * See also: [map](RecordMatrix::map), [map_mut](RecordMatrix::map_mut)
128 *
129 * ```
130 * use easy_ml::differentiation::{WengertList, Record, RecordMatrix};
131 * use easy_ml::matrices::Matrix;
132 *
133 * let history = WengertList::new();
134 * let X = RecordMatrix::constants(
135 *     Matrix::from_fn((2, 2), |(r, c)| (r + c) as f32)
136 * );
137 * let y = Record::variable(1.0, &history);
138 * let result = RecordMatrix::from_iter(
139 *     (2, 2),
140 *     // Here we create each variable z from the constant in X and the variable y.
141 *     // If we just did X + 1.0 we'd still have only constants, and we can't do X + y
142 *     // directly because those traits aren't implemented.
143 *     X.iter_row_major_as_records().map(|x| x + y)
144 * );
145 * // we can unwrap here because we know the iterator still contains 4 elements and they all
146 * // have the same WengertList so we can convert back to a RecordMatrix (which is now
147 * // variables instead of constants)
148 * let Z = result.unwrap();
149 * let Z_view = Z.view();
150 * assert_eq!(1.0, Z_view.get(0, 0).0);
151 * assert_eq!(2.0, Z_view.get(0, 1).0);
152 * assert_eq!(3.0, Z_view.get(1, 1).0);
153 * ```
154 */
155impl<'a, 'b, T, S> AsRecords<'a, RowMajorIterator<'b, (T, Index), RecordMatrix<'a, T, S>>, T>
156where
157    T: Numeric + Primitive,
158    S: MatrixRef<(T, Index)> + NoInteriorMutability,
159{
160    /**
161     * Given a record matrix returns a row major iterator of Records
162     *
163     * ```
164     * use easy_ml::differentiation::{WengertList, Record, RecordMatrix};
165     * use easy_ml::differentiation::iterators::AsRecords;
166     * use easy_ml::matrices::Matrix;
167     *
168     * let history = WengertList::new();
169     * let X = RecordMatrix::variables(
170     *     &history,
171     *     Matrix::from_fn((2, 2), |(r, c)| (r + c) as f32)
172     * );
173     * let iter = X.iter_row_major_as_records(); // shorthand helper method
174     * let also_iter = AsRecords::from_matrix_row_major(&X);
175     * ```
176     */
177    pub fn from_matrix_row_major(matrix: &'b RecordMatrix<'a, T, S>) -> Self {
178        AsRecords::from(matrix.history, RowMajorIterator::from(matrix))
179    }
180}
181
182impl<'a, 'b, T, S> AsRecords<'a, ColumnMajorIterator<'b, (T, Index), RecordMatrix<'a, T, S>>, T>
183where
184    T: Numeric + Primitive,
185    S: MatrixRef<(T, Index)> + NoInteriorMutability,
186{
187    /**
188     * Given a record matrix returns a column major iterator of Records
189     */
190    pub fn from_matrix_column_major(matrix: &'b RecordMatrix<'a, T, S>) -> Self {
191        AsRecords::from(matrix.history, ColumnMajorIterator::from(matrix))
192    }
193}
194
195impl<'a, I, T> AsRecords<'a, I, T>
196where
197    T: Numeric + Primitive,
198    I: Iterator<Item = (T, Index)>,
199{
200    /**
201     * Given the WengertList an iterator of record numbers are for, returns an iterator of Records
202     *
203     * **The inputs are not checked for validity**. It is possible to pass in the wrong Wengert
204     * list here or even numbers with indexes that aren't tracked on the WengertList.
205     *
206     * Where possible, consider using [from_tensor](AsRecords::from_tensor),
207     * [from_matrix_row_major](AsRecords::from_matrix_row_major) or
208     * [from_matrix_column_major](AsRecords::from_matrix_row_major) instead.
209     */
210    pub fn from(history: Option<&'a WengertList<T>>, numbers: I) -> Self {
211        AsRecords { numbers, history }
212    }
213}
214
215impl<'a, I, T> AsRecords<'a, I, T>
216where
217    T: Numeric + Primitive,
218    I: Iterator<Item = (T, Index)> + Into<WithIndex<I>>,
219{
220    /**
221     * An iterator of Records that is created from an iterator which can provide the index for
222     * each element can also be coverted to a [WithIndex](WithIndex) iterator and provide the
223     * index for each record.
224     *
225     * WithIndex appears twice in the return type because the original iterator itself is wrapped
226     * in WithIndex to create an iterator that provides indexes, and AsRecords must also be
227     * wrapped in WithIndex to implement the iterator trait with indexes from the original
228     * iterator's implementation.
229     *
230     * ```
231     * use easy_ml::differentiation::{WengertList, Record, RecordTensor};
232     * use easy_ml::tensors::Tensor;
233     *
234     * let history = WengertList::new();
235     * let X = RecordTensor::variables(
236     *     &history,
237     *     Tensor::from([("r", 2), ("c", 2)], vec![ 0.5, 1.5, 2.5, 3.5 ])
238     * );
239     * let Y = RecordTensor::from_iter(
240     *     [("r", 2), ("c", 2)],
241     *     // Most Easy ML matrix and tensor iterators implement Into<WithIndex<Self>>, so we can
242     *     // call with_index after creating the iterator
243     *     X.iter_as_records().with_index().map(|([r, c], x)| x + ((r + (2 * c)) as f32))
244     * ).unwrap(); // we can unwrap here because we know the iterator is still 4 elements
245     * // so matches the shape and we added constants to each Record element so the history
246     * // is still consistent
247     * assert_eq!(
248     *     Tensor::from([("r", 2), ("c", 2)], vec![ 0.5, 3.5, 3.5, 6.5 ]),
249     *     Y.view().map(|(x, _)| x)
250     * );
251     * ```
252     */
253    pub fn with_index(self) -> WithIndex<AsRecords<'a, WithIndex<I>, T>> {
254        WithIndex {
255            iterator: AsRecords {
256                numbers: self.numbers.into(),
257                history: self.history,
258            },
259        }
260    }
261}
262
263impl<'a, I, O, T> AsRecords<'a, I, T>
264where
265    T: Numeric + Primitive,
266    I: Iterator<Item = (O, (T, Index))>,
267{
268    /**
269     * Given the WengertList an iterator of indexes and record numbers are for, returns an
270     * iterator of indexes and Records
271     *
272     * **The inputs are not checked for validity**. It is possible to pass in the wrong Wengert
273     * list here or even numbers with indexes that aren't tracked on the WengertList.
274     *
275     * Where possible, consider using [with_index](AsRecords::with_index) on an existing iterator
276     * instead.
277     */
278    pub fn from_with_index(history: Option<&'a WengertList<T>>, numbers: I) -> Self {
279        AsRecords { numbers, history }
280    }
281}
282
283impl<'a, I, T> From<AsRecords<'a, I, T>> for WithIndex<AsRecords<'a, WithIndex<I>, T>>
284where
285    T: Numeric + Primitive,
286    I: Iterator<Item = (T, Index)> + Into<WithIndex<I>>,
287{
288    fn from(iterator: AsRecords<'a, I, T>) -> Self {
289        iterator.with_index()
290    }
291}
292
293/**
294 * AsRecords is an iterator of [Record](Record)s, merging the history together with each iterator
295 * element.
296 */
297impl<'a, I, T> Iterator for AsRecords<'a, I, T>
298where
299    T: Numeric + Primitive,
300    I: Iterator<Item = (T, Index)>,
301{
302    type Item = Record<'a, T>;
303
304    fn next(&mut self) -> Option<Self::Item> {
305        self.numbers
306            .next()
307            .map(|number| Record::from_existing(number, self.history))
308    }
309
310    fn size_hint(&self) -> (usize, Option<usize>) {
311        self.numbers.size_hint()
312    }
313}
314
315impl<'a, I, T> FusedIterator for AsRecords<'a, I, T>
316where
317    T: Numeric + Primitive,
318    I: Iterator<Item = (T, Index)> + FusedIterator,
319{
320}
321
322impl<'a, I, T> ExactSizeIterator for AsRecords<'a, I, T>
323where
324    T: Numeric + Primitive,
325    I: Iterator<Item = (T, Index)> + ExactSizeIterator,
326{
327}
328
329/**
330 * When AsRecords contains an iterator `I` with the index `O` for each element, it is an iterator
331 * of `O` and [Record]s, merging the history together with each iterator element.
332 *
333 * Depending on what iterator and `with_index` implementation was used, `O` might be the tuple
334 * indexes for a matrix or the `const D: usize` length array of indexes for a tensor. In either
335 * case the iterator implementation for `WithIndex<AsRecords<..>>` just forwards the `O` values
336 * unchanged so it works with both.
337 */
338impl<'a, I, O, T> Iterator for WithIndex<AsRecords<'a, I, T>>
339where
340    T: Numeric + Primitive,
341    I: Iterator<Item = (O, (T, Index))>,
342{
343    type Item = (O, Record<'a, T>);
344
345    fn next(&mut self) -> Option<Self::Item> {
346        self.iterator
347            .numbers
348            .next()
349            .map(|(i, number)| (i, Record::from_existing(number, self.iterator.history)))
350    }
351
352    fn size_hint(&self) -> (usize, Option<usize>) {
353        self.iterator.numbers.size_hint()
354    }
355}
356
357impl<'a, I, O, T> FusedIterator for WithIndex<AsRecords<'a, I, T>>
358where
359    T: Numeric + Primitive,
360    I: Iterator<Item = (O, (T, Index))> + FusedIterator,
361{
362}
363
364impl<'a, I, O, T> ExactSizeIterator for WithIndex<AsRecords<'a, I, T>>
365where
366    T: Numeric + Primitive,
367    I: Iterator<Item = (O, (T, Index))> + ExactSizeIterator,
368{
369}
370
371/**
372 * An error due to an invalid record iterator. One of three cases
373 *
374 * - `Shape`: the iterator data didn't match the number of elements needed for a given shape to
375 * convert back into a record container
376 * - `Empty`: the iterator was empty, which is always an invalid length for any shape
377 * - `InconsistentHistory`: the iterator contains inconsistent histories in its data and so cannot
378 * be converted into a record container because a record container can only have one history for
379 * all its data
380 */
381#[derive(Clone, Debug)]
382pub enum InvalidRecordIteratorError<'a, T, const D: usize> {
383    Shape {
384        requested: InvalidShapeError<D>,
385        length: usize,
386    },
387    Empty,
388    InconsistentHistory(InconsistentHistory<'a, T>),
389}
390
391/**
392 * An error due to trying to create a RecordContainer with record data that has more than one
393 * history. Since RecordContainer stores the history once for all records it contains, it cannot
394 * support constants + variables or variables from multiple WengertLists.
395 */
396#[derive(Clone, Debug)]
397pub struct InconsistentHistory<'a, T> {
398    pub first: Option<&'a WengertList<T>>,
399    pub later: Option<&'a WengertList<T>>,
400}
401
402impl<'a, T, const D: usize> fmt::Display for InvalidRecordIteratorError<'a, T, D>
403where
404    T: Debug,
405{
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        match self {
408            Self::Shape { requested, length } => write!(
409                f,
410                "Shape {:?} does not match size of data {}",
411                requested.shape(),
412                length
413            ),
414            Self::Empty => write!(
415                f,
416                "Iterator was empty but all tensors and matrices must contain at least one element"
417            ),
418            Self::InconsistentHistory(h) => write!(
419                f,
420                "First history in iterator of records was {:?} but a later history in iterator was {:?}, record container cannot support different histories for a single tensor or matrix.",
421                h.first, h.later,
422            ),
423        }
424    }
425}
426
427impl<'a, T> fmt::Display for InconsistentHistory<'a, T>
428where
429    T: Debug,
430{
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        write!(
433            f,
434            "First history was {:?} but a later history in iterator was {:?}, record container cannot support different histories for a single tensor or matrix.",
435            self.first, self.later,
436        )
437    }
438}
439
440impl<'a, T, const D: usize> Error for InvalidRecordIteratorError<'a, T, D> where T: Debug {}
441
442impl<'a, T> Error for InconsistentHistory<'a, T> where T: Debug {}
443
444struct RecordContainerComponents<'a, T> {
445    history: Option<&'a WengertList<T>>,
446    numbers: Vec<(T, Index)>,
447}
448
449/// Converts an iterator of Records into the shared, consistent, history and a vec of (T, Index)
450/// or fails if the history is not consistent or the iterator is empty.
451fn collect_into_components<'a, T, I, const D: usize>(
452    iter: I,
453) -> Result<RecordContainerComponents<'a, T>, InvalidRecordIteratorError<'a, T, D>>
454where
455    T: Numeric + Primitive,
456    I: IntoIterator<Item = Record<'a, T>>,
457{
458    use crate::differentiation::record_operations::are_exact_same_list;
459
460    let mut history: Option<Option<&WengertList<T>>> = None;
461    let mut error: Option<InvalidRecordIteratorError<'a, T, D>> = None;
462
463    let numbers: Vec<(T, Index)> = iter
464        .into_iter()
465        .map(|record| {
466            match history {
467                None => history = Some(record.history),
468                Some(h) => {
469                    if !are_exact_same_list(h, record.history) {
470                        error = Some(InvalidRecordIteratorError::InconsistentHistory(
471                            InconsistentHistory {
472                                first: h,
473                                later: record.history,
474                            },
475                        ));
476                    }
477                }
478            }
479            (record.number, record.index)
480        })
481        .collect();
482
483    if let Some(error) = error {
484        return Err(error);
485    }
486
487    let data_length = numbers.len();
488    if data_length == 0 {
489        Err(InvalidRecordIteratorError::Empty)
490    } else {
491        // We already checked if the iterator was empty so `history` is always `Some` here
492        Ok(RecordContainerComponents {
493            history: history.unwrap(),
494            numbers,
495        })
496    }
497}
498
499/// Converts an iterator of an array of Records into n shared, consistent, histories and vecs of
500/// (T, Index) or fails individually if a history is not consistent or for all N if the iterator
501/// is empty.
502fn collect_into_n_components<'a, T, I, const D: usize, const N: usize>(
503    iter: I,
504) -> [Result<RecordContainerComponents<'a, T>, InvalidRecordIteratorError<'a, T, D>>; N]
505where
506    T: Numeric + Primitive,
507    I: IntoIterator<Item = [Record<'a, T>; N]>,
508{
509    use crate::differentiation::record_operations::are_exact_same_list;
510
511    let iter = iter.into_iter();
512
513    // We have N unique histories, potential errors, and vecs of numbers
514
515    let mut histories: [Option<Option<&WengertList<T>>>; N] = [None; N];
516
517    let mut errors: [Option<InvalidRecordIteratorError<'a, T, D>>; N] =
518        std::array::from_fn(|_| None);
519
520    let mut numbers: [Vec<(T, usize)>; N] =
521        std::array::from_fn(|_| Vec::with_capacity(iter.size_hint().0));
522
523    // The entire benefit to this method is still streaming all the record iterators without ever
524    // collecting more than one element's worth of records at a time, so we build up each vec
525    // of numbers together as we pass through the iterator and discard the duplicate histories
526    for records in iter {
527        for (n, record) in records.into_iter().enumerate() {
528            let history = &mut histories[n];
529            let error = &mut errors[n];
530            match history {
531                None => *history = Some(record.history),
532                Some(h) => {
533                    if !are_exact_same_list(*h, record.history) {
534                        *error = Some(InvalidRecordIteratorError::InconsistentHistory(
535                            InconsistentHistory {
536                                first: *h,
537                                later: record.history,
538                            },
539                        ));
540                    }
541                }
542            }
543            numbers[n].push((record.number, record.index));
544        }
545    }
546
547    let mut histories = histories.into_iter();
548    let mut errors = errors.into_iter();
549    let mut numbers = numbers.into_iter();
550    std::array::from_fn(|_| {
551        // unwrap always succeeds because we're consuming 3 iterators each of length N a total of N
552        // times
553        let history = histories.next().unwrap();
554        let error = errors.next().unwrap();
555        let numbers = numbers.next().unwrap();
556        let data_length = numbers.len();
557        match error {
558            Some(error) => Err(error),
559            None => {
560                if data_length == 0 {
561                    Err(InvalidRecordIteratorError::Empty)
562                } else {
563                    // We already checked if the iterator was empty so `history` is always
564                    // `Some` here
565                    Ok(RecordContainerComponents {
566                        history: history.unwrap(),
567                        numbers,
568                    })
569                }
570            }
571        }
572    })
573}
574
575impl<'a, T, const D: usize> RecordTensor<'a, T, Tensor<(T, Index), D>, D>
576where
577    T: Numeric + Primitive,
578{
579    /**
580     * Given an iterator of records and a matching shape, collects them back into a
581     * [RecordTensor](RecordTensor).
582     *
583     * This should generally be preferred over converting the iterator to a [Vec] of Records, since
584     * a Vec of Records has to store the [WengertList](WengertList) reference for each individual
585     * record whereas a RecordTensor only stores it once.
586     *
587     * However, since a RecordTensor only stores the WengertList once, this conversion will fail
588     * if there are different histories in the iterator. It also fails if the iterator is empty
589     * or doesn't match the number of elements for the shape.
590     *
591     * See also: [elements](crate::tensors::dimensions::elements)
592     */
593    pub fn from_iter<I>(
594        shape: [(Dimension, usize); D],
595        iter: I,
596    ) -> Result<Self, InvalidRecordIteratorError<'a, T, D>>
597    where
598        I: IntoIterator<Item = Record<'a, T>>,
599    {
600        let RecordContainerComponents { history, numbers } = collect_into_components(iter)?;
601        let data_length = numbers.len();
602        match Tensor::try_from(shape, numbers) {
603            Ok(numbers) => Ok(RecordTensor::from_existing(
604                history,
605                TensorView::from(numbers),
606            )),
607            Err(invalid_shape) => Err(InvalidRecordIteratorError::Shape {
608                requested: invalid_shape,
609                length: data_length,
610            }),
611        }
612    }
613
614    /**
615     * Given an iterator of N record pairs and a matching shape, collects them back into N
616     * [RecordTensor](RecordTensor)s.
617     *
618     * This should generally be preferred over converting the iterator to N [Vec]s of Records,
619     * since a Vec of Records has to store the [WengertList](WengertList) reference for each
620     * individual record whereas a RecordTensor only stores it once.
621     *
622     * However, since a RecordTensor only stores the WengertList once, this conversion will fail
623     * if there are different histories in the iterator. It also fails if the iterator is empty
624     * or doesn't match the number of elements for the shape. Each failure due to different
625     * histories is seperate, if the ith elements in the records of the iterator have a
626     * consistent history but the jth elements do not then the ith result will be Ok but the
627     * jth will be Err.
628     *
629     * See also: [elements](crate::tensors::dimensions::elements)
630     */
631    pub fn from_iters<I, const N: usize>(
632        shape: [(Dimension, usize); D],
633        iter: I,
634    ) -> [Result<Self, InvalidRecordIteratorError<'a, T, D>>; N]
635    where
636        I: IntoIterator<Item = [Record<'a, T>; N]>,
637    {
638        let mut components = collect_into_n_components(iter).into_iter();
639        std::array::from_fn(|_| match components.next().unwrap() {
640            Err(error) => Err(error),
641            Ok(RecordContainerComponents { history, numbers }) => {
642                let data_length = numbers.len();
643                match Tensor::try_from(shape, numbers) {
644                    Ok(numbers) => Ok(RecordTensor::from_existing(
645                        history,
646                        TensorView::from(numbers),
647                    )),
648                    Err(invalid_shape) => Err(InvalidRecordIteratorError::Shape {
649                        requested: invalid_shape,
650                        length: data_length,
651                    }),
652                }
653            }
654        })
655    }
656}
657
658impl<'a, T> RecordMatrix<'a, T, Matrix<(T, Index)>>
659where
660    T: Numeric + Primitive,
661{
662    /**
663     * Given an iterator of records and a matching size, collects them back into a
664     * [RecordMatrix](RecordMatrix).
665     *
666     * This should generally be preferred over converting the iterator to a [Vec] of Records, since
667     * a Vec of Records has to store the [WengertList](WengertList) reference for each individual
668     * record whereas a RecordMatrix only stores it once.
669     *
670     * However, since a RecordMatrix only stores the WengertList once, this conversion will fail
671     * if there are different histories in the iterator. It also fails if the iterator is empty
672     * or doesn't match the R x C number of elements expected.
673     */
674    pub fn from_iter<I>(
675        size: (Row, Column),
676        iter: I,
677    ) -> Result<Self, InvalidRecordIteratorError<'a, T, 2>>
678    where
679        I: IntoIterator<Item = Record<'a, T>>,
680    {
681        let RecordContainerComponents { history, numbers } = collect_into_components(iter)?;
682        let data_length = numbers.len();
683        if data_length == size.0 * size.1 {
684            Ok(RecordMatrix::from_existing(
685                history,
686                MatrixView::from(Matrix::from_flat_row_major(size, numbers)),
687            ))
688        } else {
689            Err(InvalidRecordIteratorError::Shape {
690                requested: InvalidShapeError::new([("rows", size.0), ("columns", size.1)]),
691                length: data_length,
692            })
693        }
694    }
695
696    /**
697     * Given an iterator of N record pairs and a matching shape, collects them back into N
698     * [RecordMatrix](RecordMatrix)s.
699     *
700     * This should generally be preferred over converting the iterator to N [Vec]s of Records,
701     * since a Vec of Records has to store the [WengertList](WengertList) reference for each
702     * individual record whereas a RecordMatrix only stores it once.
703     *
704     * However, since a RecordMatrix only stores the WengertList once, this conversion will fail
705     * if there are different histories in the iterator. It also fails if the iterator is empty
706     * or doesn't match the R x C number of elements expected. Each failure due to different
707     * histories is seperate, if the ith elements in the records of the iterator have a
708     * consistent history but the jth elements do not then the ith result will be Ok but the
709     * jth will be Err.
710     *
711     * See also: [elements](crate::tensors::dimensions::elements)
712     */
713    pub fn from_iters<I, const N: usize>(
714        size: (Row, Column),
715        iter: I,
716    ) -> [Result<Self, InvalidRecordIteratorError<'a, T, 2>>; N]
717    where
718        I: IntoIterator<Item = [Record<'a, T>; N]>,
719    {
720        let mut components = collect_into_n_components(iter).into_iter();
721        std::array::from_fn(|_| match components.next().unwrap() {
722            Err(error) => Err(error),
723            Ok(RecordContainerComponents { history, numbers }) => {
724                let data_length = numbers.len();
725                if data_length == size.0 * size.1 {
726                    Ok(RecordMatrix::from_existing(
727                        history,
728                        MatrixView::from(Matrix::from_flat_row_major(size, numbers)),
729                    ))
730                } else {
731                    Err(InvalidRecordIteratorError::Shape {
732                        requested: InvalidShapeError::new([("rows", size.0), ("columns", size.1)]),
733                        length: data_length,
734                    })
735                }
736            }
737        })
738    }
739}