easy_ml/matrices/views/
ranges.rs

1use crate::matrices::views::{DataLayout, MatrixMut, MatrixRef, NoInteriorMutability};
2use crate::matrices::{Column, Row};
3
4use std::marker::PhantomData;
5use std::ops::Range;
6
7/**
8 * A 2 dimensional range over a matrix, hiding the values **outside** the range from view.
9 *
10 * The entire source is still owned by the MatrixRange however, so this does not permit
11 * creating multiple mutable ranges into a single matrix even if they wouldn't overlap.
12 *
13 * For non overlapping mutable ranges into a single matrix see
14 * [`partition`](crate::matrices::Matrix::partition).
15 *
16 * See also: [MatrixMask](MatrixMask)
17 */
18#[derive(Clone, Debug)]
19pub struct MatrixRange<T, S> {
20    source: S,
21    rows: IndexRange,
22    columns: IndexRange,
23    _type: PhantomData<T>,
24}
25
26/**
27 * A 2 dimensional mask over a matrix, hiding the values **inside** the range from view.
28 *
29 * The entire source is still owned by the MatrixMask however, so this does not permit
30 * creating multiple mutable masks into a single matrix even if they wouldn't overlap.
31 *
32 * See also: [MatrixRange](MatrixRange)
33 */
34#[derive(Clone, Debug)]
35pub struct MatrixMask<T, S> {
36    source: S,
37    rows: IndexRange,
38    columns: IndexRange,
39    _type: PhantomData<T>,
40}
41
42impl<T, S> MatrixRange<T, S>
43where
44    S: MatrixRef<T>,
45{
46    /**
47     * Creates a new MatrixRange giving a view of only the data within the row and column
48     * [IndexRange](IndexRange)s.
49     *
50     * # Examples
51     *
52     * Creating a view and manipulating a matrix from it.
53     * ```
54     * use easy_ml::matrices::Matrix;
55     * use easy_ml::matrices::views::{MatrixView, MatrixRange};
56     * let mut matrix = Matrix::from(vec![
57     *     vec![ 2, 3, 4 ],
58     *     vec![ 5, 1, 8 ]]);
59     * {
60     *     let mut view = MatrixView::from(MatrixRange::from(&mut matrix, 0..1, 1..3));
61     *     assert_eq!(vec![3, 4], view.row_major_iter().collect::<Vec<_>>());
62     *     view.map_mut(|x| x + 10);
63     * }
64     * assert_eq!(matrix, Matrix::from(vec![
65     *     vec![ 2, 13, 14 ],
66     *     vec![ 5,  1,  8 ]]));
67     * ```
68     *
69     * Various ways to construct a MatrixRange
70     * ```
71     * use easy_ml::matrices::Matrix;
72     * use easy_ml::matrices::views::{IndexRange, MatrixRange};
73     * let matrix = Matrix::from(vec![vec![1]]);
74     * let index_range = MatrixRange::from(&matrix, IndexRange::new(0, 4), IndexRange::new(1, 3));
75     * let tuple = MatrixRange::from(&matrix, (0, 4), (1, 3));
76     * let array = MatrixRange::from(&matrix, [0, 4], [1, 3]);
77     * // Note std::ops::Range is start..end not start and length!
78     * let range = MatrixRange::from(&matrix, 0..4, 1..4);
79     * ```
80     *
81     * NOTE: In previous versions (<=1.8.1), this erroneously did not clip the IndexRange input to
82     * not exceed the rows and columns of the source, which led to the possibility to create
83     * MatrixRanges that reported a greater number of rows and columns in their shape than their
84     * actual data. This function will now correctly clip any ranges that exceed their sources.
85     */
86    pub fn from<R>(source: S, rows: R, columns: R) -> MatrixRange<T, S>
87    where
88        R: Into<IndexRange>,
89    {
90        let max_rows = source.view_rows();
91        let max_columns = source.view_columns();
92        MatrixRange {
93            source,
94            rows: {
95                let mut rows = rows.into();
96                rows.clip(max_rows);
97                rows
98            },
99            columns: {
100                let mut columns = columns.into();
101                columns.clip(max_columns);
102                columns
103            },
104            _type: PhantomData,
105        }
106    }
107
108    /**
109     * Consumes the MatrixRange, yielding the source it was created from.
110     */
111    #[allow(dead_code)]
112    pub fn source(self) -> S {
113        self.source
114    }
115
116    /**
117     * Gives a reference to the MatrixRange's source (in which the data is not clipped).
118     */
119    // # Safety
120    //
121    // Giving out a mutable reference to our source could allow it to be changed out from under us
122    // and make our range checks invalid. However, since the source implements MatrixRef
123    // interior mutability is not allowed, so we can give out shared references without breaking
124    // our own integrity.
125    #[allow(dead_code)]
126    pub fn source_ref(&self) -> &S {
127        &self.source
128    }
129}
130
131impl<T, S> MatrixMask<T, S>
132where
133    S: MatrixRef<T>,
134{
135    /**
136     * Creates a new MatrixMask giving a view of only the data outside the row and column
137     * [IndexRange](IndexRange)s. If the index range given for rows or columns exceeds the
138     * size of the matrix, they will be clipped to fit the actual size without an error.
139     *
140     * # Examples
141     *
142     * Creating a view and manipulating a matrix from it.
143     * ```
144     * use easy_ml::matrices::Matrix;
145     * use easy_ml::matrices::views::{MatrixView, MatrixMask};
146     * let mut matrix = Matrix::from(vec![
147     *     vec![ 2, 3, 4 ],
148     *     vec![ 5, 1, 8 ]]);
149     * {
150     *     let mut view = MatrixView::from(MatrixMask::from(&mut matrix, 0..1, 2..3));
151     *     assert_eq!(vec![5, 1], view.row_major_iter().collect::<Vec<_>>());
152     *     view.map_mut(|x| x + 10);
153     * }
154     * assert_eq!(matrix, Matrix::from(vec![
155     *     vec![ 2,   3,  4 ],
156     *     vec![ 15, 11,  8 ]]));
157     * ```
158     *
159     * Various ways to construct a MatrixMask
160     * ```
161     * use easy_ml::matrices::Matrix;
162     * use easy_ml::matrices::views::{IndexRange, MatrixMask};
163     * let matrix = Matrix::from(vec![vec![1]]);
164     * let index_range = MatrixMask::from(&matrix, IndexRange::new(0, 4), IndexRange::new(1, 3));
165     * let tuple = MatrixMask::from(&matrix, (0, 4), (1, 3));
166     * let array = MatrixMask::from(&matrix, [0, 4], [1, 3]);
167     * // Note std::ops::Range is start..end not start and length!
168     * let range = MatrixMask::from(&matrix, 0..4, 1..4);
169     * ```
170     */
171    pub fn from<R>(source: S, rows: R, columns: R) -> MatrixMask<T, S>
172    where
173        R: Into<IndexRange>,
174    {
175        let max_rows = source.view_rows();
176        let max_columns = source.view_columns();
177        MatrixMask {
178            source,
179            rows: {
180                let mut rows = rows.into();
181                rows.clip(max_rows);
182                rows
183            },
184            columns: {
185                let mut columns = columns.into();
186                columns.clip(max_columns);
187                columns
188            },
189            _type: PhantomData,
190        }
191    }
192
193    /**
194     * Consumes the MatrixMask, yielding the source it was created from.
195     */
196    #[allow(dead_code)]
197    pub fn source(self) -> S {
198        self.source
199    }
200
201    /**
202     * Gives a reference to the MatrixMask's source (in which the data is not masked).
203     */
204    // # Safety
205    //
206    // Giving out a mutable reference to our source could allow it to be changed out from under us
207    // and make our mask checks invalid. However, since the source implements MatrixRef
208    // interior mutability is not allowed, so we can give out shared references without breaking
209    // our own integrity.
210    #[allow(dead_code)]
211    pub fn source_ref(&self) -> &S {
212        &self.source
213    }
214}
215
216/**
217 * A range bounded between `start` inclusive and `start + length` exclusive.
218 *
219 * # Examples
220 *
221 * Converting between [Range](std::ops::Range) and IndexRange.
222 * ```
223 * use std::ops::Range;
224 * use easy_ml::matrices::views::IndexRange;
225 * assert_eq!(IndexRange::new(3, 2), (3..5).into());
226 * assert_eq!(IndexRange::new(1, 5), (1..6).into());
227 * assert_eq!(IndexRange::new(0, 4), (0..4).into());
228 * ```
229 *
230 * Creating a Range
231 *
232 * ```
233 * use easy_ml::matrices::views::IndexRange;
234 * let range = IndexRange::new(3, 2);
235 * let also_range: IndexRange = (3, 2).into();
236 * let also_also_range: IndexRange = [3, 2].into();
237 * ```
238 *
239 * NB: You can construct an IndexRange where start+length exceeds isize::MAX or even
240 * usize::MAX, however matrices and tensors themselves cannot contain more than isize::MAX
241 * elements. Concerned readers should note that on a 64 bit computer this maximum
242 * value is 9,223,372,036,854,775,807 so running out of memory is likely to occur first.
243 */
244#[derive(Clone, Debug, Eq, PartialEq)]
245pub struct IndexRange {
246    pub(crate) start: usize,
247    pub(crate) length: usize,
248}
249
250impl IndexRange {
251    pub fn new(start: usize, length: usize) -> IndexRange {
252        IndexRange { start, length }
253    }
254
255    // TODO: If we make these public we need to disambiguate Range from Mask behaviour better
256    /**
257     * Maps from a coordinate space of the ith index accessible by this range to the actual index
258     * into the entire dimension's data.
259     */
260    #[inline]
261    pub(crate) fn map(&self, index: usize) -> Option<usize> {
262        if index < self.length {
263            Some(index + self.start)
264        } else {
265            None
266        }
267    }
268
269    // NOTE: This doesn't perform bounds checks, adding the length of the mask could push
270    // the index out of the valid bounds of the dimension it is for, but if we performed
271    // bounds checks here they would be redundant since performing the get with the masked index
272    // will bounds check if required
273    #[inline]
274    pub(crate) fn mask(&self, index: usize) -> usize {
275        if index < self.start {
276            index
277        } else {
278            index + self.length
279        }
280    }
281
282    // Clips the range or mask to not exceed an index. Note, this may yield 0 length ranges
283    // that have non zero starting positions, however map and mask will still calculate correctly.
284    pub(crate) fn clip(&mut self, max_index: usize) {
285        let end = self.start + self.length;
286        let end = std::cmp::min(end, max_index);
287        let length = end.saturating_sub(self.start);
288        self.length = length;
289    }
290}
291
292/**
293 * Converts from a range of start..end to an IndexRange of start and length
294 *
295 * NOTE: In previous versions (<=1.8.1) this did not saturate when attempting to subtract the
296 * start of the range from the end to calculate the length. It will now correctly produce an
297 * IndexRange with a length of 0 if the end is before or equal to the start.
298 */
299impl From<Range<usize>> for IndexRange {
300    fn from(range: Range<usize>) -> IndexRange {
301        IndexRange::new(range.start, range.end.saturating_sub(range.start))
302    }
303}
304
305/** Converts from an IndexRange of start and length to a range of start..end */
306impl From<IndexRange> for Range<usize> {
307    fn from(range: IndexRange) -> Range<usize> {
308        Range {
309            start: range.start,
310            end: range.start + range.length,
311        }
312    }
313}
314
315/**
316 * Converts from a tuple of start and length to an IndexRange
317 *
318 * NOTE: In previous versions (<=1.8.1), this was erroneously implemented as conversion from a
319 * tuple of start and end, not start and length as documented.
320 */
321impl From<(usize, usize)> for IndexRange {
322    fn from(range: (usize, usize)) -> IndexRange {
323        let (start, length) = range;
324        IndexRange::new(start, length)
325    }
326}
327
328/**
329 * Converts from an array of start and length to an IndexRange
330 *
331 * NOTE: In previous versions (<=1.8.1), this was erroneously implemented as conversion from an
332 * array of start and end, not start and length as documented.
333 */
334impl From<[usize; 2]> for IndexRange {
335    fn from(range: [usize; 2]) -> IndexRange {
336        let [start, length] = range;
337        IndexRange::new(start, length)
338    }
339}
340
341#[test]
342fn test_index_range_clipping() {
343    let mut range: IndexRange = (0..6).into();
344    range.clip(4);
345    assert_eq!(range, (0..4).into());
346    let mut range: IndexRange = (1..4).into();
347    range.clip(5);
348    assert_eq!(range, (1..4).into());
349    range.clip(2);
350    assert_eq!(range, (1..2).into());
351    let mut range: IndexRange = (3..5).into();
352    range.clip(2);
353    assert_eq!(range, (3..2).into());
354    assert_eq!(range.map(0), None);
355    assert_eq!(range.map(1), None);
356    assert_eq!(range.mask(0), 0);
357    assert_eq!(range.mask(1), 1);
358}
359
360// # Safety
361//
362// Since the MatrixRef we own must implement MatrixRef correctly, so do we by delegating to it,
363// as we don't introduce any interior mutability.
364/**
365 * A MatrixRange of a MatrixRef type implements MatrixRef.
366 */
367unsafe impl<T, S> MatrixRef<T> for MatrixRange<T, S>
368where
369    S: MatrixRef<T>,
370{
371    fn try_get_reference(&self, row: Row, column: Column) -> Option<&T> {
372        let row = self.rows.map(row)?;
373        let column = self.columns.map(column)?;
374        self.source.try_get_reference(row, column)
375    }
376
377    fn view_rows(&self) -> Row {
378        self.rows.length
379    }
380
381    fn view_columns(&self) -> Column {
382        self.columns.length
383    }
384
385    unsafe fn get_reference_unchecked(&self, row: Row, column: Column) -> &T {
386        unsafe {
387            // It is the caller's responsibiltiy to always call with row/column indexes in range,
388            // therefore the unwrap() case should never happen because on an arbitary MatrixRef
389            // it would be undefined behavior.
390            let row = self.rows.map(row).unwrap();
391            let column = self.columns.map(column).unwrap();
392            self.source.get_reference_unchecked(row, column)
393        }
394    }
395
396    fn data_layout(&self) -> DataLayout {
397        self.source.data_layout()
398    }
399}
400
401// # Safety
402//
403// Since the MatrixMut we own must implement MatrixMut correctly, so do we by delegating to it,
404// as we don't introduce any interior mutability.
405/**
406 * A MatrixRange of a MatrixMut type implements MatrixMut.
407 */
408unsafe impl<T, S> MatrixMut<T> for MatrixRange<T, S>
409where
410    S: MatrixMut<T>,
411{
412    fn try_get_reference_mut(&mut self, row: Row, column: Column) -> Option<&mut T> {
413        let row = self.rows.map(row)?;
414        let column = self.columns.map(column)?;
415        self.source.try_get_reference_mut(row, column)
416    }
417
418    unsafe fn get_reference_unchecked_mut(&mut self, row: Row, column: Column) -> &mut T {
419        unsafe {
420            // It is the caller's responsibility to always call with row/column indexes in range,
421            // therefore the unwrap() case should never happen because on an arbitary MatrixRef
422            // it would be undefined behavior.
423            let row = self.rows.map(row).unwrap();
424            let column = self.columns.map(column).unwrap();
425            self.source.get_reference_unchecked_mut(row, column)
426        }
427    }
428}
429
430// # Safety
431//
432// Since the NoInteriorMutability we own must implement NoInteriorMutability correctly, so
433// do we by delegating to it, as we don't introduce any interior mutability.
434/**
435 * A MatrixRange of a NoInteriorMutability type implements NoInteriorMutability.
436 */
437unsafe impl<T, S> NoInteriorMutability for MatrixRange<T, S> where S: NoInteriorMutability {}
438
439#[test]
440fn test_matrix_range_shape_clips() {
441    use crate::matrices::Matrix;
442    let matrix = Matrix::from(vec![vec![1, 2, 3], vec![4, 5, 6]]);
443    let range = MatrixRange::from(&matrix, 0..7, 1..4);
444    assert_eq!(2, range.view_rows());
445    assert_eq!(2, range.view_columns());
446    assert_eq!(2, range.rows.length);
447    assert_eq!(2, range.columns.length);
448}
449
450// # Safety
451//
452// Since the MatrixRef we own must implement MatrixRef correctly, so do we by delegating to it,
453// as we don't introduce any interior mutability.
454/**
455 * A MatrixMask of a MatrixRef type implements MatrixRef.
456 */
457unsafe impl<T, S> MatrixRef<T> for MatrixMask<T, S>
458where
459    S: MatrixRef<T>,
460{
461    fn try_get_reference(&self, row: Row, column: Column) -> Option<&T> {
462        let row = self.rows.mask(row);
463        let column = self.columns.mask(column);
464        self.source.try_get_reference(row, column)
465    }
466
467    fn view_rows(&self) -> Row {
468        // We enforce in the constructor that the mask is clipped to the size of our actual
469        // matrix, hence the mask cannot be longer than our data in either dimension. If the
470        // mask is the same length as our data, we'd return 0 which for MatrixRef is allowed.
471        self.source.view_rows() - self.rows.length
472    }
473
474    fn view_columns(&self) -> Column {
475        // We enforce in the constructor that the mask is clipped to the size of our actual
476        // matrix, hence the mask cannot be longer than our data in either dimension. If the
477        // mask is the same length as our data, we'd return 0 which for MatrixRef is allowed.
478        self.source.view_columns() - self.columns.length
479    }
480
481    unsafe fn get_reference_unchecked(&self, row: Row, column: Column) -> &T {
482        unsafe {
483            // It is the caller's responsibility to always call with row/column indexes in range,
484            // therefore calling get_reference_unchecked with indexes beyond the size of the matrix
485            // should never happen because on an arbitary MatrixRef it would be undefined behavior.
486            let row = self.rows.mask(row);
487            let column = self.columns.mask(column);
488            self.source.get_reference_unchecked(row, column)
489        }
490    }
491
492    fn data_layout(&self) -> DataLayout {
493        self.source.data_layout()
494    }
495}
496
497// # Safety
498//
499// Since the MatrixMut we own must implement MatrixMut correctly, so do we by delegating to it,
500// as we don't introduce any interior mutability.
501/**
502 * A MatrixMask of a MatrixMut type implements MatrixMut.
503 */
504unsafe impl<T, S> MatrixMut<T> for MatrixMask<T, S>
505where
506    S: MatrixMut<T>,
507{
508    fn try_get_reference_mut(&mut self, row: Row, column: Column) -> Option<&mut T> {
509        let row = self.rows.mask(row);
510        let column = self.columns.mask(column);
511        self.source.try_get_reference_mut(row, column)
512    }
513
514    unsafe fn get_reference_unchecked_mut(&mut self, row: Row, column: Column) -> &mut T {
515        unsafe {
516            // It is the caller's responsibility to always call with row/column indexes in range,
517            // therefore calling get_reference_unchecked with indexes beyond the size of the matrix
518            // should never happen because on an arbitary MatrixRef it would be undefined behavior.
519            let row = self.rows.mask(row);
520            let column = self.columns.mask(column);
521            self.source.get_reference_unchecked_mut(row, column)
522        }
523    }
524}
525
526// # Safety
527//
528// Since the NoInteriorMutability we own must implement NoInteriorMutability correctly, so
529// do we by delegating to it, as we don't introduce any interior mutability.
530/**
531 * A MatrixMask of a NoInteriorMutability type implements NoInteriorMutability.
532 */
533unsafe impl<T, S> NoInteriorMutability for MatrixMask<T, S> where S: NoInteriorMutability {}