Skip to main content

diffsol_la/vector/
faer_serial.rs

1use std::ops::{Add, AddAssign, Div, Index, IndexMut, Mul, MulAssign, Sub, SubAssign};
2
3use faer::reborrow::{Reborrow, ReborrowMut};
4use faer::{unzip, zip, Col, Mat, MatMut, MatRef};
5
6use crate::context::broadcast_batch;
7use crate::{scalar::Scale, Context, FaerContext, FaerScalar, IndexType, Vector};
8
9use crate::{FaerMat, VectorCommon, VectorIndex, VectorView, VectorViewMut};
10
11use super::DefaultDenseMatrix;
12
13/// A batched vector, stored as one column per batch.
14///
15/// Invariant: `data.ncols() == context.nbatch()` and `data.nrows() == len()`.  Note that
16/// faer pads its column stride for alignment, so the batches are *not* contiguous with each
17/// other; only a single column is.
18#[derive(Debug, Clone, PartialEq)]
19pub struct FaerVec<T: FaerScalar> {
20    pub(crate) data: Mat<T>,
21    pub(crate) context: FaerContext,
22}
23
24#[derive(Debug, Clone, PartialEq)]
25pub struct FaerVecIndex {
26    pub(crate) data: Vec<IndexType>,
27    pub(crate) context: FaerContext,
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub struct FaerVecRef<'a, T: FaerScalar> {
32    pub(crate) data: MatRef<'a, T>,
33    pub(crate) context: FaerContext,
34}
35
36#[derive(Debug, PartialEq)]
37pub struct FaerVecMut<'a, T: FaerScalar> {
38    pub(crate) data: MatMut<'a, T>,
39    pub(crate) context: FaerContext,
40}
41
42impl<T: FaerScalar> FaerVec<T> {
43    #[inline]
44    pub(crate) fn batch(&self, batch: usize, nbatch: usize) -> usize {
45        broadcast_batch(batch, self.data.ncols(), nbatch)
46    }
47
48    pub fn check_for_nan(&self, label: &str) -> bool {
49        for b in 0..self.data.ncols() {
50            let column = self.data.rb().col(b);
51            for i in 0..column.nrows() {
52                if unsafe { *column.get_unchecked(i) }.is_nan() {
53                    eprintln!("{}: NaN at index {} of batch {}", label, i, b);
54                    return true;
55                }
56            }
57        }
58        false
59    }
60}
61impl<T: FaerScalar> FaerVecRef<'_, T> {
62    #[inline]
63    pub(crate) fn batch(&self, batch: usize, nbatch: usize) -> usize {
64        broadcast_batch(batch, self.data.ncols(), nbatch)
65    }
66}
67
68impl<T: FaerScalar> From<Col<T>> for FaerVec<T> {
69    fn from(data: Col<T>) -> Self {
70        Self {
71            data: Mat::from_fn(data.nrows(), 1, |i, _| data[i]),
72            context: FaerContext::default(),
73        }
74    }
75}
76
77impl<T: FaerScalar> DefaultDenseMatrix for FaerVec<T> {
78    type M = FaerMat<T>;
79}
80
81macro_rules! impl_vector_common {
82    ($t:ty, $inner:ty) => {
83        impl<T: FaerScalar> VectorCommon for $t {
84            type T = T;
85            type C = FaerContext;
86            type Inner = $inner;
87            fn inner(&self) -> &Self::Inner {
88                &self.data
89            }
90        }
91    };
92}
93impl_vector_common!(FaerVec<T>, Mat<T>);
94macro_rules! impl_vector_common_ref {
95    ($t:ty, $inner:ty) => {
96        impl<'a, T: FaerScalar> VectorCommon for $t {
97            type T = T;
98            type C = FaerContext;
99            type Inner = $inner;
100            fn inner(&self) -> &Self::Inner {
101                &self.data
102            }
103        }
104    };
105}
106impl_vector_common_ref!(FaerVecRef<'a, T>, MatRef<'a, T>);
107impl_vector_common_ref!(FaerVecMut<'a, T>, MatMut<'a, T>);
108
109macro_rules! impl_binary_ref_ref {
110    ($trait:ident, $method:ident, $lhs:ty, $rhs:ty, $op:tt, $binary:tt) => {
111        impl<T: FaerScalar> $trait<$rhs> for $lhs {
112            type Output = FaerVec<T>;
113
114            // the arithmetic in `batch` below is broadcast indexing over batches, not
115            // arithmetic on the operands, which is what the lint is looking for
116            #[allow(clippy::suspicious_arithmetic_impl)]
117            fn $method(self, rhs: $rhs) -> Self::Output {
118                // neither operand is owned, so the result is allocated at the left-hand
119                // side's batch count and `rhs` broadcasts into it
120                self.context
121                    .assert_broadcastable_into(rhs.context.nbatch(), stringify!($method));
122                // the unbatched path goes through the columns on purpose: faer's whole-matrix
123                // operators are ~45% slower for a single column (see lin_alg_ops
124                // add_ref_ref/faer)
125                if self.data.ncols() == 1 && rhs.data.ncols() == 1 {
126                    let mut data = Mat::zeros(self.data.nrows(), 1);
127                    zip!(
128                        data.rb_mut().col_mut(0),
129                        self.data.rb().col(0),
130                        rhs.data.rb().col(0)
131                    )
132                    .for_each(|unzip!(o, l, r)| *o = *l $binary *r);
133                    return FaerVec {
134                        data,
135                        context: self.context,
136                    };
137                }
138                if self.data.ncols() == rhs.data.ncols() {
139                    return FaerVec {
140                        data: self.data.rb() $binary rhs.data.rb(),
141                        context: self.context,
142                    };
143                }
144                let nb = self.data.ncols();
145                let mut data = Mat::zeros(self.data.nrows(), nb);
146                for b in 0..nb {
147                    let mut column = data.rb_mut().col_mut(b);
148                    column.copy_from(self.data.rb().col(b));
149                    column $op rhs.data.rb().col(rhs.batch(b, nb));
150                }
151                FaerVec {
152                    data,
153                    context: self.context,
154                }
155            }
156        }
157    };
158}
159macro_rules! impl_assign {
160    ($trait:ident, $method:ident, $lhs:ty, $rhs:ty, $op:tt) => {
161        impl<T: FaerScalar> $trait<$rhs> for $lhs {
162            // the arithmetic in `batch` below is broadcast indexing over batches, not
163            // arithmetic on the operands, which is what the lint is looking for
164            #[allow(clippy::suspicious_op_assign_impl)]
165            fn $method(&mut self, rhs: $rhs) {
166                self.context
167                    .assert_broadcastable_into(rhs.context.nbatch(), stringify!($method));
168                // the whole-matrix operator only pays off past one column: faer's 2-D
169                // iteration costs ~1.5x the single-column path on short vectors (see
170                // lin_alg_ops add_assign/faer/2)
171                if self.data.ncols() == rhs.data.ncols() && self.data.ncols() > 1 {
172                    self.data $op rhs.data.rb();
173                    return;
174                }
175                let nb = self.data.ncols();
176                for b in 0..nb {
177                    let mut column = self.data.rb_mut().col_mut(b);
178                    column $op rhs.data.rb().col(rhs.batch(b, nb));
179                }
180            }
181        }
182    };
183}
184
185/// `self` is the owned operand, so it is the destination -- which makes the in-place op the
186/// whole implementation, broadcast check included.
187macro_rules! impl_binary_owned_lhs {
188    ($trait:ident, $method:ident, $assign_trait:ident, $assign:ident, $rhs:ty) => {
189        impl<T: FaerScalar> $trait<$rhs> for FaerVec<T> {
190            type Output = FaerVec<T>;
191
192            fn $method(mut self, rhs: $rhs) -> Self::Output {
193                $assign_trait::$assign(&mut self, rhs);
194                self
195            }
196        }
197    };
198}
199
200/// `rhs` is the owned operand, so it is the destination.
201///
202/// A commutative op is just the in-place op with the operands swapped.  A non-commutative one
203/// cannot be: `rhs -= lhs` computes `rhs - lhs`, so it writes `combine(&mut rhs_i, lhs_i)`
204/// instead, which gets `lhs - rhs` in the same single pass.
205macro_rules! impl_binary_owned_rhs {
206    (commutes, $trait:ident, $method:ident, $assign_trait:ident, $assign:ident, $lhs:ty,
207     $op:tt, $combine:expr) => {
208        impl<T: FaerScalar> $trait<FaerVec<T>> for $lhs {
209            type Output = FaerVec<T>;
210
211            fn $method(self, mut rhs: FaerVec<T>) -> Self::Output {
212                $assign_trait::$assign(&mut rhs, self);
213                rhs
214            }
215        }
216    };
217    (noncommutes, $trait:ident, $method:ident, $assign_trait:ident, $assign:ident, $lhs:ty, $op:tt, $combine:expr) => {
218        impl<T: FaerScalar> $trait<FaerVec<T>> for $lhs {
219            type Output = FaerVec<T>;
220
221            fn $method(self, mut rhs: FaerVec<T>) -> Self::Output {
222                rhs.context
223                    .assert_broadcastable_into(self.context.nbatch(), stringify!($method));
224                if self.data.ncols() == rhs.data.ncols() {
225                    zip!(rhs.data.rb_mut(), self.data.rb())
226                        .for_each(|unzip!(r, l)| $combine(r, *l));
227                    return rhs;
228                }
229                let nb = rhs.data.ncols();
230                for b in 0..nb {
231                    let lhs = self.data.rb().col(self.batch(b, nb));
232                    zip!(rhs.data.rb_mut().col_mut(b), lhs)
233                        .for_each(|unzip!(r, l)| $combine(r, *l));
234                }
235                rhs
236            }
237        }
238    };
239}
240
241macro_rules! impl_binary_set {
242    ($trait:ident,$method:ident,$assign_trait:ident,$assign:ident,$commutes:ident,
243     $op:tt,$binary:tt,$combine:expr) => {
244        impl_binary_owned_lhs!($trait, $method, $assign_trait, $assign, FaerVec<T>);
245        impl_binary_owned_lhs!($trait, $method, $assign_trait, $assign, &FaerVec<T>);
246        impl_binary_owned_lhs!($trait, $method, $assign_trait, $assign, FaerVecRef<'_, T>);
247        impl_binary_owned_lhs!($trait, $method, $assign_trait, $assign, &FaerVecRef<'_, T>);
248        impl_binary_owned_rhs!(
249            $commutes,
250            $trait,
251            $method,
252            $assign_trait,
253            $assign,
254            FaerVecRef<'_, T>,
255            $op,
256            $combine
257        );
258        impl_binary_owned_rhs!(
259            $commutes,
260            $trait,
261            $method,
262            $assign_trait,
263            $assign,
264            &FaerVec<T>,
265            $op,
266            $combine
267        );
268        impl_binary_ref_ref!(
269            $trait,
270            $method,
271            FaerVecRef<'_, T>,
272            &FaerVec<T>,
273            $op,
274            $binary
275        );
276        impl_binary_ref_ref!(
277            $trait,
278            $method,
279            FaerVecRef<'_, T>,
280            FaerVecRef<'_, T>,
281            $op,
282            $binary
283        );
284        impl_binary_ref_ref!(
285            $trait,
286            $method,
287            FaerVecRef<'_, T>,
288            &FaerVecRef<'_, T>,
289            $op,
290            $binary
291        );
292        impl_binary_ref_ref!($trait, $method, &FaerVec<T>, &FaerVec<T>, $op, $binary);
293        impl_binary_ref_ref!(
294            $trait,
295            $method,
296            &FaerVec<T>,
297            FaerVecRef<'_, T>,
298            $op,
299            $binary
300        );
301        impl_binary_ref_ref!(
302            $trait,
303            $method,
304            &FaerVec<T>,
305            &FaerVecRef<'_, T>,
306            $op,
307            $binary
308        );
309    };
310}
311macro_rules! impl_assign_set {
312    ($trait:ident,$method:ident,$op:tt) => {
313        impl_assign!($trait, $method, FaerVec<T>, FaerVec<T>, $op);
314        impl_assign!($trait, $method, FaerVec<T>, &FaerVec<T>, $op);
315        impl_assign!($trait, $method, FaerVec<T>, FaerVecRef<'_, T>, $op);
316        impl_assign!($trait, $method, FaerVec<T>, &FaerVecRef<'_, T>, $op);
317        impl_assign!($trait, $method, FaerVecMut<'_, T>, FaerVec<T>, $op);
318        impl_assign!($trait, $method, FaerVecMut<'_, T>, &FaerVec<T>, $op);
319        impl_assign!($trait, $method, FaerVecMut<'_, T>, FaerVecRef<'_, T>, $op);
320        impl_assign!($trait, $method, FaerVecMut<'_, T>, &FaerVecRef<'_, T>, $op);
321    };
322}
323impl_binary_set!(Add, add, AddAssign, add_assign, commutes, +=, +, |rhs: &mut T, lhs: T| *rhs += lhs);
324impl_binary_set!(Sub, sub, SubAssign, sub_assign, noncommutes, -=, -, |rhs: &mut T, lhs: T| *rhs = lhs - *rhs);
325impl_assign_set!(AddAssign, add_assign, +=);
326impl_assign_set!(SubAssign, sub_assign, -=);
327
328impl<T: FaerScalar> Mul<Scale<T>> for FaerVec<T> {
329    type Output = Self;
330    fn mul(mut self, rhs: Scale<T>) -> Self {
331        self.data *= faer::Scale(rhs.value());
332        self
333    }
334}
335macro_rules! impl_mul_scalar_alloc {
336    ($t:ty) => {
337        impl<T: FaerScalar> Mul<Scale<T>> for $t {
338            type Output = FaerVec<T>;
339            fn mul(self, rhs: Scale<T>) -> Self::Output {
340                FaerVec {
341                    data: self.data.rb() * faer::Scale(rhs.value()),
342                    context: self.context,
343                }
344            }
345        }
346    };
347}
348impl_mul_scalar_alloc!(&FaerVec<T>);
349impl_mul_scalar_alloc!(FaerVecRef<'_, T>);
350impl_mul_scalar_alloc!(FaerVecMut<'_, T>);
351impl<T: FaerScalar> Div<Scale<T>> for FaerVec<T> {
352    type Output = Self;
353    #[allow(clippy::suspicious_arithmetic_impl)]
354    fn div(mut self, rhs: Scale<T>) -> Self {
355        // self is owned, so scale it in place rather than allocating a result
356        self.data *= faer::Scale(T::one() / rhs.value());
357        self
358    }
359}
360macro_rules! impl_mul_assign_scalar {
361    ($t:ty) => {
362        impl<T: FaerScalar> MulAssign<Scale<T>> for $t {
363            fn mul_assign(&mut self, rhs: Scale<T>) {
364                if self.data.ncols() == 1 {
365                    let mut column = self.data.rb_mut().col_mut(0);
366                    column *= faer::Scale(rhs.value());
367                    return;
368                }
369                self.data *= faer::Scale(rhs.value());
370            }
371        }
372    };
373}
374impl_mul_assign_scalar!(FaerVec<T>);
375impl_mul_assign_scalar!(FaerVecMut<'_, T>);
376macro_rules! impl_index {
377    ($t:ty) => {
378        impl<T: FaerScalar> Index<IndexType> for $t {
379            type Output = T;
380            fn index(&self, i: IndexType) -> &T {
381                assert_eq!(
382                    self.context.nbatch(),
383                    1,
384                    "indexing not supported for batched vectors"
385                );
386                &self.data[(i, 0)]
387            }
388        }
389    };
390}
391impl_index!(FaerVec<T>);
392impl_index!(FaerVecRef<'_, T>);
393impl<T: FaerScalar> IndexMut<IndexType> for FaerVec<T> {
394    fn index_mut(&mut self, i: IndexType) -> &mut T {
395        assert_eq!(
396            self.context.nbatch(),
397            1,
398            "indexing not supported for batched vectors"
399        );
400        &mut self.data[(i, 0)]
401    }
402}
403
404impl VectorIndex for FaerVecIndex {
405    type C = FaerContext;
406    fn zeros(len: IndexType, ctx: Self::C) -> Self {
407        Self {
408            data: vec![0; len],
409            context: ctx,
410        }
411    }
412    fn len(&self) -> IndexType {
413        self.data.len()
414    }
415    fn from_vec(v: Vec<IndexType>, ctx: Self::C) -> Self {
416        Self {
417            data: v,
418            context: ctx,
419        }
420    }
421    fn clone_as_vec(&self) -> Vec<IndexType> {
422        self.data.clone()
423    }
424    fn context(&self) -> &Self::C {
425        &self.context
426    }
427}
428
429// Shared bodies for the `Vector` methods below, in the owned and view flavours.
430macro_rules! copy_from_body {
431    ($self:ident, $other:ident, $method:literal) => {
432        $self
433            .context
434            .assert_broadcastable_into($other.context.nbatch(), $method);
435        assert_eq!(
436            $self.data.nrows(),
437            $other.data.nrows(),
438            "copy_from row mismatch"
439        );
440        // the unbatched path is column-wise on purpose: faer's whole-matrix copy_from is
441        // ~25% slower than copying the single column (see lin_alg_ops copy_from/faer)
442        if $self.data.ncols() == 1 && $other.data.ncols() == 1 {
443            $self
444                .data
445                .rb_mut()
446                .col_mut(0)
447                .copy_from($other.data.rb().col(0));
448            return;
449        }
450        if $self.data.ncols() == $other.data.ncols() {
451            $self.data.rb_mut().copy_from($other.data.rb());
452            return;
453        }
454        let (nb, onc) = ($self.data.ncols(), $other.data.ncols());
455        for b in 0..nb {
456            let src = broadcast_batch(b, onc, nb);
457            $self
458                .data
459                .rb_mut()
460                .col_mut(b)
461                .copy_from($other.data.rb().col(src));
462        }
463    };
464}
465
466/// `self_b = alpha_b * x_b + beta * self_b` for every batch of `self`, broadcasting a
467/// single-batch `x`.  Shared by the owned vector and its mutable view, with `x` either an
468/// owned vector or a view, and `alpha` either one scalar or one value per batch.
469macro_rules! axpy_body {
470    ($self:ident, $x:ident, $beta:expr, $op:literal, |$batch:ident| $alpha:expr) => {{
471        $self
472            .context
473            .assert_broadcastable_into($x.context.nbatch(), $op);
474        let nb = $self.data.ncols();
475        // the unbatched path keeps constant column indices and no loop, which is worth ~13%
476        // here (see lin_alg_ops axpy/faer/100)
477        if nb == 1 {
478            let $batch = 0;
479            let alpha = $alpha;
480            zip!($self.data.rb_mut().col_mut(0), $x.data.rb().col(0))
481                .for_each(|unzip!(s, xi)| *s = *s * $beta + *xi * alpha);
482            return;
483        }
484        for $batch in 0..nb {
485            let alpha = $alpha;
486            zip!(
487                $self.data.rb_mut().col_mut($batch),
488                $x.data.rb().col($x.batch($batch, nb))
489            )
490            .for_each(|unzip!(s, xi)| *s = *s * $beta + *xi * alpha);
491        }
492    }};
493}
494
495/// Weighted error norm of every batch, reduced by taking the maximum.
496macro_rules! squared_norm_body {
497    ($self:ident, $y:ident, $atol:ident, $rtol:ident) => {{
498        let nrows = $self.data.nrows();
499        assert!(
500            nrows == $y.data.nrows() && nrows == $atol.data.nrows(),
501            "squared_norm row mismatch"
502        );
503        let nstates = T::from_f64(nrows as f64).unwrap();
504        // rows of a column are contiguous whatever the batch stride, so each batch is a slice
505        let batch_norm = |xb: usize, yb: usize, atolb: usize| {
506            let x = $self
507                .data
508                .rb()
509                .col(xb)
510                .try_as_col_major()
511                .unwrap()
512                .as_slice();
513            let y = $y.data.rb().col(yb).try_as_col_major().unwrap().as_slice();
514            let atol = $atol
515                .data
516                .rb()
517                .col(atolb)
518                .try_as_col_major()
519                .unwrap()
520                .as_slice();
521            let norm = x.iter().zip(y.iter().zip(atol.iter())).fold(
522                T::zero(),
523                |norm, (&x, (&y, &atol))| {
524                    let term = x.algebraic_div(y.abs().algebraic_mul($rtol).algebraic_add(atol));
525                    norm.algebraic_add(term.algebraic_mul(term))
526                },
527            );
528            norm / nstates
529        };
530        // the reduction runs over `self`'s batches and broadcasts `y` and `atol` over them
531        let nb = $self.data.ncols();
532        $self
533            .context
534            .assert_broadcastable_into($y.context.nbatch(), "squared_norm");
535        $self
536            .context
537            .assert_broadcastable_into($atol.context.nbatch(), "squared_norm");
538        // the unbatched case keeps constant column indices, which codegens better than the
539        // loop variable below (and folds away the broadcast arithmetic)
540        if nb == 1 {
541            return batch_norm(0, 0, 0);
542        }
543        let mut max_norm = T::zero();
544        for b in 0..nb {
545            max_norm = max_norm.max(batch_norm(b, $y.batch(b, nb), $atol.batch(b, nb)));
546        }
547        max_norm
548    }};
549}
550
551impl<T: FaerScalar> Vector for FaerVec<T> {
552    type View<'a> = FaerVecRef<'a, T>;
553    type ViewMut<'a> = FaerVecMut<'a, T>;
554    type Index = FaerVecIndex;
555    fn len(&self) -> IndexType {
556        self.data.nrows()
557    }
558    fn inner_mut(&mut self) -> &mut Self::Inner {
559        &mut self.data
560    }
561    fn context(&self) -> &Self::C {
562        &self.context
563    }
564    fn norm(&self, k: i32) -> T {
565        (0..self.data.ncols())
566            .map(|b| {
567                let column = self.data.rb().col(b);
568                match k {
569                    1 => column.norm_l1(),
570                    2 => column.norm_l2(),
571                    _ => column
572                        .iter()
573                        .fold(T::zero(), |acc, x| acc.algebraic_add(x.abs().pow(k)))
574                        .pow(T::one() / T::from_f64(k as f64).unwrap()),
575                }
576            })
577            .fold(T::zero(), |a, b| a.max(b))
578    }
579    fn get_index(&self, i: IndexType) -> T {
580        assert_eq!(
581            self.context.nbatch(),
582            1,
583            "get_index not supported for batched vectors"
584        );
585        self.data[(i, 0)]
586    }
587    fn set_index(&mut self, i: IndexType, v: T) {
588        assert_eq!(
589            self.context.nbatch(),
590            1,
591            "set_index not supported for batched vectors, use fill_index"
592        );
593        self.data[(i, 0)] = v;
594    }
595    fn fill_index(&mut self, i: IndexType, v: T) {
596        if self.data.ncols() == 1 {
597            self.data[(i, 0)] = v;
598            return;
599        }
600        self.data.rb_mut().row_mut(i).fill(v);
601    }
602    fn squared_norm(&self, y: &Self, atol: &Self, rtol: T) -> T {
603        squared_norm_body!(self, y, atol, rtol)
604    }
605    fn as_view(&self) -> Self::View<'_> {
606        FaerVecRef {
607            data: self.data.rb(),
608            context: self.context,
609        }
610    }
611    fn as_view_mut(&mut self) -> Self::ViewMut<'_> {
612        FaerVecMut {
613            data: self.data.rb_mut(),
614            context: self.context,
615        }
616    }
617    fn get_batch(&self, b: usize) -> Self::View<'_> {
618        FaerVecRef {
619            data: self.data.rb().subcols(b, 1),
620            context: FaerContext::default(),
621        }
622    }
623    fn get_batch_mut(&mut self, b: usize) -> Self::ViewMut<'_> {
624        FaerVecMut {
625            data: self.data.rb_mut().subcols_mut(b, 1),
626            context: FaerContext::default(),
627        }
628    }
629    fn for_each_batch_mut_host<const M: usize, const N: usize>(
630        mut mut_args: [&mut Self; M],
631        args: [&Self; N],
632        mut f: impl FnMut([&mut [T]; M], [&[T]; N], usize),
633    ) {
634        assert!(M > 0, "for_each_batch needs at least one mutable operand");
635        let nbatch = mut_args[0].context.nbatch();
636        {
637            let ctx = &mut_args[0].context;
638            for arg in mut_args.iter() {
639                ctx.assert_broadcastable_into(arg.context.nbatch(), "for_each_batch");
640            }
641            for arg in args.iter() {
642                ctx.assert_broadcastable_into(arg.context.nbatch(), "for_each_batch");
643            }
644        }
645        // unbatched is the overwhelmingly common case, and every operand is then a single
646        // column: go straight to column 0, with no lane index arithmetic at all
647        if nbatch == 1 {
648            let ins = args.map(|a| a.data.col_as_slice(0));
649            let outs = mut_args.map(|v| v.data.col_as_slice_mut(0));
650            f(outs, ins, 0);
651            return;
652        }
653        for b in 0..nbatch {
654            let ins = args.map(|a| a.data.col_as_slice(a.batch(b, nbatch)));
655            let outs = mut_args.each_mut().map(|v| {
656                let vb = v.batch(b, nbatch);
657                v.data.col_as_slice_mut(vb)
658            });
659            f(outs, ins, b);
660        }
661    }
662    fn copy_from(&mut self, o: &Self) {
663        copy_from_body!(self, o, "copy_from");
664    }
665    fn fill(&mut self, v: T) {
666        if self.data.ncols() == 1 {
667            self.data.rb_mut().col_mut(0).fill(v);
668            return;
669        }
670        self.data.rb_mut().fill(v)
671    }
672    fn copy_from_view(&mut self, o: &Self::View<'_>) {
673        copy_from_body!(self, o, "copy_from_view");
674    }
675    fn from_element(n: usize, v: T, ctx: Self::C) -> Self {
676        Self {
677            data: Mat::from_fn(n, ctx.nbatch(), |_, _| v),
678            context: ctx,
679        }
680    }
681    fn from_vec(v: Vec<T>, ctx: Self::C) -> Self {
682        Self::from_slice(v.as_slice(), ctx)
683    }
684    fn from_slice(v: &[T], ctx: Self::C) -> Self {
685        assert!(
686            v.len().is_multiple_of(ctx.nbatch()),
687            "vector length must be divisible by nbatch"
688        );
689        let n = v.len() / ctx.nbatch();
690        Self {
691            data: Mat::from_fn(n, ctx.nbatch(), |i, b| v[b * n + i]),
692            context: ctx,
693        }
694    }
695    fn clone_as_vec(&self) -> Vec<T> {
696        let mut out = Vec::with_capacity(self.data.nrows() * self.data.ncols());
697        for b in 0..self.data.ncols() {
698            out.extend(self.data.rb().col(b).iter().copied());
699        }
700        out
701    }
702    fn zeros(n: usize, ctx: Self::C) -> Self {
703        Self {
704            data: Mat::zeros(n, ctx.nbatch()),
705            context: ctx,
706        }
707    }
708    fn axpy(&mut self, a: T, x: &Self, beta: T) {
709        axpy_body!(self, x, beta, "axpy", |_batch| a)
710    }
711    fn axpy_v(&mut self, a: T, x: &Self::View<'_>, beta: T) {
712        axpy_body!(self, x, beta, "axpy_v", |_batch| a)
713    }
714    fn batched_axpy(&mut self, a: &Self, x: &Self, beta: T) {
715        assert_eq!(
716            a.len(),
717            1,
718            "alpha must be a batched scalar, with len() == 1"
719        );
720        assert_eq!(
721            a.context.nbatch(),
722            self.context.nbatch(),
723            "alpha nbatch must equal nbatch"
724        );
725        axpy_body!(self, x, beta, "batched_axpy", |batch| a.data[(0, batch)])
726    }
727    fn component_div_assign(&mut self, o: &Self) {
728        self.context
729            .assert_broadcastable_into(o.context.nbatch(), "component_div_assign");
730        assert_eq!(
731            self.data.nrows(),
732            o.data.nrows(),
733            "component_div_assign row mismatch"
734        );
735        if self.data.ncols() == o.data.ncols() {
736            zip!(self.data.rb_mut(), o.data.rb()).for_each(|unzip!(s, o)| *s /= *o);
737            return;
738        }
739        let nb = self.data.ncols();
740        for c in 0..nb {
741            zip!(
742                self.data.rb_mut().col_mut(c),
743                o.data.rb().col(o.batch(c, nb))
744            )
745            .for_each(|unzip!(s, o)| *s /= *o);
746        }
747    }
748    fn component_mul_assign(&mut self, o: &Self) {
749        self.context
750            .assert_broadcastable_into(o.context.nbatch(), "component_mul_assign");
751        assert_eq!(
752            self.data.nrows(),
753            o.data.nrows(),
754            "component_mul_assign row mismatch"
755        );
756        if self.data.ncols() == o.data.ncols() {
757            zip!(self.data.rb_mut(), o.data.rb()).for_each(|unzip!(s, o)| *s *= *o);
758            return;
759        }
760        let nb = self.data.ncols();
761        for c in 0..nb {
762            zip!(
763                self.data.rb_mut().col_mut(c),
764                o.data.rb().col(o.batch(c, nb))
765            )
766            .for_each(|unzip!(s, o)| *s *= *o);
767        }
768    }
769    fn root_finding(&self, g1: &Self) -> (bool, T, i32) {
770        self.context
771            .assert_broadcastable_into(g1.context.nbatch(), "root_finding");
772        assert_eq!(self.len(), g1.len(), "Vector lengths do not match");
773        let mut out = None;
774        // the scan runs over `self`'s batches and broadcasts `g1` over them
775        let nb = self.data.ncols();
776        for b in 0..nb {
777            let mut found = false;
778            let mut frac = T::zero();
779            let mut idx = -1;
780            let g0_column = self.data.rb().col(b);
781            let g1_column = g1.data.rb().col(g1.batch(b, nb));
782            for (i, (&g0, &g)) in g0_column
783                .try_as_col_major()
784                .unwrap()
785                .as_slice()
786                .iter()
787                .zip(g1_column.try_as_col_major().unwrap().as_slice())
788                .enumerate()
789            {
790                if g == T::zero() {
791                    found = true
792                }
793                if g0 * g < T::zero() {
794                    let q = (g / (g - g0)).abs();
795                    if q > frac {
796                        frac = q;
797                        idx = i as i32
798                    }
799                }
800            }
801            if let Some(x) = out {
802                assert_eq!(
803                    x,
804                    (found, frac, idx),
805                    "root finding results differ across batches"
806                )
807            }
808            out = Some((found, frac, idx));
809        }
810        out.unwrap_or((false, T::zero(), -1))
811    }
812    fn assign_at_indices(&mut self, idx: &Self::Index, v: T) {
813        for b in 0..self.data.ncols() {
814            for i in idx.data.iter() {
815                self.data[(*i, b)] = v
816            }
817        }
818    }
819    fn copy_from_indices(&mut self, o: &Self, idx: &Self::Index) {
820        self.context
821            .assert_broadcastable_into(o.context.nbatch(), "copy_from_indices");
822        let nb = self.data.ncols();
823        for b in 0..nb {
824            for i in idx.data.iter() {
825                self.data[(*i, b)] = o.data[(*i, o.batch(b, nb))]
826            }
827        }
828    }
829    fn gather(&mut self, o: &Self, idx: &Self::Index) {
830        assert_eq!(self.len(), idx.len());
831        self.context
832            .assert_broadcastable_into(o.context.nbatch(), "gather");
833        let nb = self.data.ncols();
834        for b in 0..nb {
835            for (i, j) in idx.data.iter().enumerate() {
836                self.data[(i, b)] = o.data[(*j, o.batch(b, nb))]
837            }
838        }
839    }
840    fn scatter(&self, idx: &Self::Index, o: &mut Self) {
841        assert_eq!(self.len(), idx.len());
842        // `o` is the destination here, so its batch count governs the loop
843        o.context
844            .assert_broadcastable_into(self.data.ncols(), "scatter");
845        let nb = o.data.ncols();
846        for b in 0..nb {
847            let src = broadcast_batch(b, self.data.ncols(), nb);
848            for (i, j) in idx.data.iter().enumerate() {
849                o.data[(*j, b)] = self.data[(i, src)]
850            }
851        }
852    }
853}
854
855impl<'a, T: FaerScalar> VectorView<'a> for FaerVecRef<'a, T> {
856    type Owned = FaerVec<T>;
857    fn get_index(&self, i: IndexType) -> T {
858        assert_eq!(self.context.nbatch(), 1, "get_index requires nbatch == 1");
859        self.data[(i, 0)]
860    }
861    fn into_owned(self) -> Self::Owned {
862        FaerVec {
863            data: self.data.to_owned(),
864            context: self.context,
865        }
866    }
867    fn squared_norm(&self, y: &Self::Owned, atol: &Self::Owned, rtol: T) -> T {
868        squared_norm_body!(self, y, atol, rtol)
869    }
870}
871impl<'a, T: FaerScalar> VectorViewMut<'a> for FaerVecMut<'a, T> {
872    type Owned = FaerVec<T>;
873    type View = FaerVecRef<'a, T>;
874    type Index = FaerVecIndex;
875    fn copy_from(&mut self, o: &Self::Owned) {
876        copy_from_body!(self, o, "copy_from");
877    }
878    fn copy_from_view(&mut self, o: &Self::View) {
879        copy_from_body!(self, o, "copy_from_view");
880    }
881    fn set_index(&mut self, i: IndexType, v: T) {
882        assert_eq!(
883            self.context.nbatch(),
884            1,
885            "set_index not supported for batched vectors, use fill_index"
886        );
887        self.data[(i, 0)] = v;
888    }
889    fn fill_index(&mut self, i: IndexType, v: T) {
890        if self.data.ncols() == 1 {
891            self.data[(i, 0)] = v;
892            return;
893        }
894        self.data.rb_mut().row_mut(i).fill(v);
895    }
896    fn axpy(&mut self, a: T, x: &Self::Owned, beta: T) {
897        axpy_body!(self, x, beta, "axpy", |_batch| a)
898    }
899}
900#[cfg(test)]
901mod tests {
902    use super::*;
903    use crate::scalar::scale;
904
905    #[test]
906    fn test_mult() {
907        let v = FaerVec::from_vec(vec![1.0, -2.0, 3.0], Default::default());
908        let s = scale(2.0);
909        let r = FaerVec::from_vec(vec![2.0, -4.0, 6.0], Default::default());
910        assert_eq!(v * s, r);
911    }
912
913    #[test]
914    fn test_mul_assign() {
915        let mut v = FaerVec::from_vec(vec![1.0, -2.0, 3.0], Default::default());
916        let s = scale(2.0);
917        let r = FaerVec::from_vec(vec![2.0, -4.0, 6.0], Default::default());
918        v.mul_assign(s);
919        assert_eq!(v, r);
920    }
921
922    #[test]
923    fn test_error_norm() {
924        let v: FaerVec<f64> = FaerVec::from_vec(vec![1.0, -2.0, 3.0], Default::default());
925        let y = FaerVec::from_vec(vec![1.0, 2.0, 3.0], Default::default());
926        let atol = FaerVec::from_vec(vec![0.1, 0.2, 0.3], Default::default());
927        let rtol = 0.1;
928        let mut tmp = y.clone() * scale(rtol);
929        tmp += &atol;
930        let mut r = v.clone();
931        r.component_div_assign(&tmp);
932        let errorn_check = r.data.rb().col(0).squared_norm_l2() / 3.0;
933        assert!(
934            (v.squared_norm(&y, &atol, rtol) - errorn_check).abs() < 1e-10,
935            "{} vs {}",
936            v.squared_norm(&y, &atol, rtol),
937            errorn_check
938        );
939        let vview = v.as_view();
940        assert!((VectorView::squared_norm(&vview, &y, &atol, rtol) - errorn_check).abs() < 1e-10);
941    }
942
943    #[test]
944    fn test_root_finding() {
945        super::super::tests::test_root_finding::<FaerVec<f64>>();
946    }
947
948    #[test]
949    fn test_from_slice() {
950        let slice = [1.0, 2.0, 3.0];
951        let v = FaerVec::from_slice(&slice, Default::default());
952        assert_eq!(v.clone_as_vec(), slice);
953    }
954
955    #[test]
956    fn test_into() {
957        let col: Col<f64> = Col::from_fn(3, |i| (i + 1) as f64);
958        let v: FaerVec<f64> = col.into();
959        assert_eq!(v.clone_as_vec(), vec![1.0, 2.0, 3.0]);
960    }
961
962    /// The generic suite only covers `norm(1)` and `norm(2)`; an odd `k` has to take the
963    /// magnitude of each element before raising it, which the previous impl skipped.
964    #[test]
965    fn test_norm_odd_k_uses_magnitudes() {
966        let v = FaerVec::from_vec(vec![-3.0, 4.0], Default::default());
967        let expected: f64 = (27.0f64 + 64.0).powf(1.0 / 3.0);
968        assert!((v.norm(3) - expected).abs() < 1e-12, "{}", v.norm(3));
969    }
970
971    /// A zero-length vector has no columns to fill, so the constructors must not divide by
972    /// or index past it.
973    #[test]
974    fn test_empty() {
975        let ctx = FaerContext::with_nbatch(2);
976        for v in [
977            FaerVec::<f64>::zeros(0, ctx),
978            FaerVec::<f64>::from_vec(vec![], ctx),
979            FaerVec::<f64>::from_element(0, 1.0, ctx),
980        ] {
981            assert_eq!(v.len(), 0);
982            assert!(v.is_empty());
983            assert_eq!(v.clone_as_vec(), Vec::<f64>::new());
984        }
985    }
986
987    #[test]
988    fn test_host_only() {
989        super::super::tests::test_host_only::<FaerVec<f64>>();
990    }
991
992    super::super::generate_vector_tests_nonbatched!(faer, FaerVec<f64>);
993    super::super::generate_vector_tests_nonbatched!(faer_f32, FaerVec<f32>);
994    super::super::generate_vector_tests_batched!(
995        faer,
996        FaerVec<f64>,
997        FaerContext::with_nbatch(2),
998        FaerContext::with_nbatch(3)
999    );
1000    super::super::generate_vector_tests_batched!(
1001        faer_f32,
1002        FaerVec<f32>,
1003        FaerContext::with_nbatch(2),
1004        FaerContext::with_nbatch(3)
1005    );
1006}