Skip to main content

diffsol/vector/
mod.rs

1use crate::matrix::DenseMatrix;
2use crate::scalar::Scale;
3use crate::{Context, IndexType, Scalar};
4use num_traits::Zero;
5use std::fmt::Debug;
6use std::ops::{Add, AddAssign, Div, Index, IndexMut, Mul, MulAssign, Sub, SubAssign};
7
8#[cfg(feature = "faer")]
9pub mod faer_serial;
10#[cfg(feature = "nalgebra")]
11pub mod nalgebra_serial;
12
13#[cfg(feature = "cuda")]
14pub mod cuda;
15
16#[macro_use]
17mod utils;
18
19/// A trait for types that represent a collection of indices into a vector.
20///
21/// This is used to represent subsets of vector elements, typically for algebraic constraints
22/// or when operating on specific vector components.
23pub trait VectorIndex: Sized + Debug + Clone {
24    type C: Context;
25    fn context(&self) -> &Self::C;
26    fn zeros(len: IndexType, ctx: Self::C) -> Self;
27    fn len(&self) -> IndexType;
28    fn clone_as_vec(&self) -> Vec<IndexType>;
29    fn is_empty(&self) -> bool {
30        self.len() == 0
31    }
32    fn from_vec(v: Vec<IndexType>, ctx: Self::C) -> Self;
33}
34
35/// Common interface for vector-like types, providing access to scalar type, context, and inner representation.
36pub trait VectorCommon: Sized + Debug {
37    type T: Scalar;
38    type C: Context;
39    type Inner;
40
41    fn inner(&self) -> &Self::Inner;
42}
43
44impl<V> VectorCommon for &V
45where
46    V: VectorCommon,
47{
48    type T = V::T;
49    type C = V::C;
50    type Inner = V::Inner;
51    fn inner(&self) -> &Self::Inner {
52        V::inner(self)
53    }
54}
55
56impl<V> VectorCommon for &mut V
57where
58    V: VectorCommon,
59{
60    type T = V::T;
61    type C = V::C;
62    type Inner = V::Inner;
63    fn inner(&self) -> &Self::Inner {
64        V::inner(self)
65    }
66}
67
68/// Operations on vectors by value (addition and subtraction).
69///
70/// This trait defines vector addition and subtraction when both operands are owned or references.
71pub trait VectorOpsByValue<Rhs = Self, Output = Self>:
72    VectorCommon + Add<Rhs, Output = Output> + Sub<Rhs, Output = Output>
73{
74}
75
76impl<V, Rhs, Output> VectorOpsByValue<Rhs, Output> for V where
77    V: VectorCommon + Add<Rhs, Output = Output> + Sub<Rhs, Output = Output>
78{
79}
80
81/// In-place operations on vectors (addition and subtraction).
82///
83/// This trait defines in-place vector addition and subtraction (self += rhs, self -= rhs).
84pub trait VectorMutOpsByValue<Rhs = Self>: VectorCommon + AddAssign<Rhs> + SubAssign<Rhs> {}
85
86impl<V, Rhs> VectorMutOpsByValue<Rhs> for V where V: VectorCommon + AddAssign<Rhs> + SubAssign<Rhs> {}
87
88/// Operations on a reference to a vector, supporting addition, subtraction, and scalar multiplication.
89///
90/// This trait ensures that vector references can be used in arithmetic operations with owned vectors,
91/// other references, and vector views, enabling flexible composition of vector operations.
92pub trait VectorRef<V: Vector>:
93    VectorOpsByValue<V, V>
94    + for<'a> VectorOpsByValue<&'a V, V>
95    + for<'a> VectorOpsByValue<V::View<'a>, V>
96    + for<'a, 'b> VectorOpsByValue<&'a V::View<'b>, V>
97    + Mul<Scale<V::T>, Output = V>
98{
99}
100
101impl<RefT, V: Vector> VectorRef<V> for RefT where
102    RefT: VectorOpsByValue<V, V>
103        + for<'a> VectorOpsByValue<&'a V, V>
104        + for<'a> VectorOpsByValue<V::View<'a>, V>
105        + for<'a, 'b> VectorOpsByValue<&'a V::View<'b>, V>
106        + Mul<Scale<V::T>, Output = V>
107{
108}
109
110/// A mutable view into a vector, supporting in-place operations and modifications.
111///
112/// This trait represents a temporary mutable reference to a vector's data, allowing in-place
113/// arithmetic operations (+=, -=, *=) and other modifications. Mutable views can be created
114/// via the `as_view_mut()` method on a `Vector`.
115pub trait VectorViewMut<'a>:
116    VectorMutOpsByValue<Self::View>
117    + VectorMutOpsByValue<Self::Owned>
118    + for<'b> VectorMutOpsByValue<&'b Self::View>
119    + for<'b> VectorMutOpsByValue<&'b Self::Owned>
120    + MulAssign<Scale<Self::T>>
121{
122    type Owned;
123    type View;
124    type Index: VectorIndex;
125    /// Copy values from an owned vector into this view.
126    fn copy_from(&mut self, other: &Self::Owned);
127    /// Copy values from another vector view into this view.
128    fn copy_from_view(&mut self, other: &Self::View);
129    /// Compute the AXPY operation: self = alpha * x + beta * self
130    fn axpy(&mut self, alpha: Self::T, x: &Self::Owned, beta: Self::T);
131    /// Set the value at the specified index (sets across all batches when nbatch > 1).
132    fn set_index(&mut self, index: IndexType, value: Self::T);
133}
134
135/// A borrowed immutable view of a vector, supporting read-only arithmetic operations.
136///
137/// This trait represents a temporary immutable reference to a vector's data, allowing read-only
138/// operations like addition, subtraction, and scalar multiplication. Vector views can be created
139/// via the `as_view()` method on a `Vector` and are cheaper to create than cloning.
140pub trait VectorView<'a>:
141    VectorOpsByValue<Self, Self::Owned>
142    + VectorOpsByValue<Self::Owned, Self::Owned>
143    + for<'b> VectorOpsByValue<&'b Self::Owned, Self::Owned>
144    + for<'b> VectorOpsByValue<&'b Self, Self::Owned>
145    + Mul<Scale<Self::T>, Output = Self::Owned>
146{
147    type Owned;
148    /// Get the value at the specified index (panics if nbatch > 1).
149    fn get_index(&self, index: IndexType) -> Self::T;
150    /// Compute the squared weighted norm: sum_i ((self_i) / (|y_i| * rtol + atol_i))^2
151    ///
152    /// This is commonly used for error control in ODE solvers.
153    fn squared_norm(&self, y: &Self::Owned, atol: &Self::Owned, rtol: Self::T) -> Self::T;
154    /// Convert this view into an owned vector, cloning the data if necessary.
155    fn into_owned(self) -> Self::Owned;
156}
157
158/// A complete vector abstraction supporting arithmetic operations, norms, and index operations.
159///
160/// This is the main vector trait used throughout diffsol. Implementing vectors can be hosted on CPU (see `VectorHost`) or GPU.
161/// Users typically do not need to implement this trait; use provided implementations like
162/// `NalgebraVec` or `FaerVec`.
163pub trait Vector:
164    VectorOpsByValue<Self>
165    + for<'b> VectorOpsByValue<&'b Self>
166    + for<'a> VectorOpsByValue<Self::View<'a>>
167    + for<'a, 'b> VectorOpsByValue<&'b Self::View<'a>>
168    + Mul<Scale<Self::T>, Output = Self>
169    + Div<Scale<Self::T>, Output = Self>
170    + VectorMutOpsByValue<Self>
171    + for<'a> VectorMutOpsByValue<Self::View<'a>>
172    + for<'b> VectorMutOpsByValue<&'b Self>
173    + for<'a, 'b> VectorMutOpsByValue<&'b Self::View<'a>>
174    + MulAssign<Scale<Self::T>>
175    + Clone
176    + Send
177{
178    type View<'a>: VectorView<'a, T = Self::T, Owned = Self>
179    where
180        Self: 'a;
181    type ViewMut<'a>: VectorViewMut<'a, T = Self::T, Owned = Self, View = Self::View<'a>>
182    where
183        Self: 'a;
184    type Index: VectorIndex;
185
186    /// Get the context associated with this vector (for device placement, threading, etc.).
187    fn context(&self) -> &Self::C;
188
189    /// Get a mutable reference to the inner representation of the vector.
190    fn inner_mut(&mut self) -> &mut Self::Inner;
191
192    /// Set the value at the specified index to `value`.
193    fn set_index(&mut self, index: IndexType, value: Self::T);
194
195    /// Get the value at the specified index.
196    fn get_index(&self, index: IndexType) -> Self::T;
197
198    /// Compute the $\ell_k$ norm: $(\sum_i |x_i|^k)^{1/k}$
199    fn norm(&self, k: i32) -> Self::T;
200
201    /// Compute the squared weighted norm for error control: $\sum_i (x_i / (|y_i| \cdot \text{rtol} + \text{atol}_i))^2$
202    ///
203    /// This norm is used by ODE solvers for adaptive error control.
204    fn squared_norm(&self, y: &Self, atol: &Self, rtol: Self::T) -> Self::T;
205
206    /// Get the per-batch length (number of states) in this vector.
207    /// For batched vectors, this returns `nstates`, not `nstates * nbatch`.
208    fn len(&self) -> IndexType;
209
210    /// Get the total number of elements stored, including all batches.
211    /// Returns `self.len() * self.context().nbatch()`.
212    fn total_len(&self) -> IndexType {
213        self.len() * self.context().nbatch()
214    }
215
216    /// Check if the vector is empty.
217    fn is_empty(&self) -> bool {
218        self.len() == 0
219    }
220
221    /// Create a vector of length `nstates` with all elements initialized to `value`.
222    fn from_element(nstates: usize, value: Self::T, ctx: Self::C) -> Self;
223
224    /// Create a vector of length `nstates` with all elements set to zero.
225    fn zeros(nstates: usize, ctx: Self::C) -> Self {
226        Self::from_element(nstates, Self::T::zero(), ctx)
227    }
228
229    /// Fill all elements of this vector with `value`.
230    fn fill(&mut self, value: Self::T);
231
232    /// Create an immutable view of this vector.
233    fn as_view(&self) -> Self::View<'_>;
234
235    /// Create a mutable view of this vector.
236    fn as_view_mut(&mut self) -> Self::ViewMut<'_>;
237
238    /// Get an immutable view of a single batch (with nbatch=1 context).
239    fn get_batch(&self, batch: usize) -> Self::View<'_>;
240
241    /// Get a mutable view of a single batch (with nbatch=1 context).
242    fn get_batch_mut(&mut self, batch: usize) -> Self::ViewMut<'_>;
243
244    /// Copy all values from `other` into this vector.
245    fn copy_from(&mut self, other: &Self);
246
247    /// Copy all values from a vector view into this vector.
248    fn copy_from_view(&mut self, other: &Self::View<'_>);
249
250    // TODO: would prefer to use From trait but not implemented for faer::Col
251    /// Create a vector from a Rust `Vec`.
252    fn from_vec(vec: Vec<Self::T>, ctx: Self::C) -> Self;
253
254    /// Create a vector from a slice.
255    fn from_slice(slice: &[Self::T], ctx: Self::C) -> Self;
256
257    // TODO: would prefer to use From trait but not implemented for faer::Col
258    /// Clone this vector as a Rust `Vec`.
259    fn clone_as_vec(&self) -> Vec<Self::T>;
260
261    /// Compute the AXPY operation: self = alpha * x + beta * self
262    fn axpy(&mut self, alpha: Self::T, x: &Self, beta: Self::T);
263
264    /// Compute the AXPY operation with a vector view: self = alpha * x + beta * self
265    fn axpy_v(&mut self, alpha: Self::T, x: &Self::View<'_>, beta: Self::T);
266
267    /// Per-batch AXPY: `self[i]_b = alpha[b] * x[i]_b + beta * self[i]_b`
268    ///
269    /// `alpha` must have length equal to `self.context().nbatch()`.
270    /// Each batch uses its own scalar multiplier `alpha[b]`.
271    /// The `x` operand may broadcast if its `nbatch == 1`.
272    fn batched_axpy(&mut self, alpha: &[Self::T], x: &Self, beta: Self::T);
273
274    /// Element-wise multiplication: self_i *= other_i
275    fn component_mul_assign(&mut self, other: &Self);
276
277    /// Element-wise division: self_i /= other_i
278    fn component_div_assign(&mut self, other: &Self);
279
280    /// Detect roots (zero crossings) between this vector (as g0) and another vector (g1).
281    ///
282    /// Returns a tuple of:
283    /// - `bool`: true if a zero crossing is found (g1_i == 0 for some i)
284    /// - `T`: the interpolation factor at the maximum crossing (0 if none found) (given by maxmimum |g0_i / (g1_i - g0_i)|)
285    /// - `i32`: the index of the maximum crossing (-1 if none found)
286    fn root_finding(&self, g1: &Self) -> (bool, Self::T, i32);
287
288    /// Assign `value` to all elements at the specified indices.
289    fn assign_at_indices(&mut self, indices: &Self::Index, value: Self::T);
290
291    /// Copy values from `other` at the specified indices: self\[indices\[i\]\] = other\[indices\[i\]\]
292    fn copy_from_indices(&mut self, other: &Self, indices: &Self::Index);
293
294    /// Gather values from `other` at indices: self\[i\] = other\[indices\[i\]\]
295    fn gather(&mut self, other: &Self, indices: &Self::Index);
296
297    /// Scatter values to `other` at indices: other\[indices\[i\]\] = self\[i\]
298    fn scatter(&self, indices: &Self::Index, other: &mut Self);
299
300    /// Assert that this vector equals `other` within a scalar tolerance `tol`.
301    fn assert_eq_st(&self, other: &Self, tol: Self::T) {
302        let tol = vec![tol; self.total_len()];
303        Self::assert_eq_vec(self.clone_as_vec(), other.clone_as_vec(), tol);
304    }
305
306    /// Assert that this vector equals `other` using a weighted norm (same as used by ODE solvers).
307    ///
308    /// Uses `squared_norm` internally with the scaling factors, and asserts that the resulting
309    /// norm is less than `factor`.
310    fn assert_eq_norm(&self, other: &Self, atol: &Self, rtol: Self::T, factor: Self::T) {
311        let error = self.clone() - other.clone();
312        let error_norm = error.squared_norm(other, atol, rtol).sqrt();
313        assert!(
314            error_norm < factor,
315            "error_norm: {error_norm}. self: {self:?}, other: {other:?}",
316        );
317    }
318
319    /// Assert that this vector equals `other` using a vector of per-element tolerances.
320    fn assert_eq(&self, other: &Self, tol: &Self) {
321        assert_eq!(
322            self.len(),
323            other.len(),
324            "Vector length mismatch: {} != {}",
325            self.len(),
326            other.len()
327        );
328        let s = self.clone_as_vec();
329        let other = other.clone_as_vec();
330        let tol = tol.clone_as_vec();
331        Self::assert_eq_vec(s, other, tol);
332    }
333
334    fn assert_eq_vec(s: Vec<Self::T>, other: Vec<Self::T>, tol: Vec<Self::T>) {
335        for i in 0..s.len() {
336            if num_traits::abs(s[i] - other[i]) > tol[i] {
337                eprintln!(
338                    "Vector element mismatch at index {i}: {} != {}",
339                    s[i], other[i]
340                );
341                if s.len() <= 3 {
342                    eprintln!("left: {s:?}");
343                    eprintln!("right: {other:?}");
344                } else if i == 0 {
345                    eprintln!(
346                        "left: [{}, {}, {}] != [{}, {}, {}]",
347                        s[0], s[1], s[2], other[0], other[1], other[2]
348                    );
349                } else if i == s.len() - 1 {
350                    eprintln!(
351                        "left: [..., {}, {}, {}] != [..., {}, {}, {}]",
352                        s[i - 2],
353                        s[i - 1],
354                        s[i],
355                        other[i - 2],
356                        other[i - 1],
357                        other[i]
358                    );
359                } else {
360                    eprintln!(
361                        "left: [..., {}, {}, {}, ...] != [..., {}, {}, {}, ...]",
362                        s[i - 1],
363                        s[i],
364                        s[i + 1],
365                        other[i - 1],
366                        other[i],
367                        other[i + 1]
368                    );
369                }
370                panic!(
371                    "Vector element mismatch at index {}: {} != {}",
372                    i, s[i], other[i]
373                );
374            }
375        }
376    }
377}
378
379/// A vector hosted on the CPU, supporting direct indexing and slice access.
380///
381/// This trait extends `Vector` with the ability to directly access vector elements via indexing
382/// (`v[i]`) and to get slices of the underlying data. This is useful for algorithms that need
383/// direct CPU-side access to vector data. GPU vectors typically do not implement this trait.
384pub trait VectorHost:
385    Vector + Index<IndexType, Output = Self::T> + IndexMut<IndexType, Output = Self::T>
386{
387    /// Get the vector data as an immutable slice.
388    fn as_slice(&self) -> &[Self::T];
389
390    /// Get the vector data as a mutable slice.
391    fn as_mut_slice(&mut self) -> &mut [Self::T];
392}
393
394/// Marker trait for vectors that have a default associated dense matrix type.
395///
396/// This trait associates a vector type with its corresponding dense matrix representation,
397/// enabling vectors to be easily combined with matrix types for linear algebra operations.
398pub trait DefaultDenseMatrix: Vector {
399    type M: DenseMatrix<V = Self, T = Self::T, C = Self::C>;
400}
401
402#[cfg(test)]
403pub(crate) mod tests {
404    use std::panic::{catch_unwind, AssertUnwindSafe};
405
406    use super::{Vector, VectorCommon, VectorIndex, VectorView, VectorViewMut};
407    use crate::context::nalgebra::NalgebraContext;
408    use crate::scalar::Scale;
409    use crate::vector::nalgebra_serial::NalgebraVec;
410    use crate::Context;
411    use num_traits::FromPrimitive;
412
413    fn f<V: Vector>(x: f64) -> V::T {
414        V::T::from_f64(x).unwrap()
415    }
416
417    fn fv<V: Vector>(xs: &[f64]) -> Vec<V::T> {
418        xs.iter().map(|&x| f::<V>(x)).collect()
419    }
420
421    pub fn test_root_finding<V: Vector>() {
422        let g0 = V::from_vec(fv::<V>(&[1.0, -2.0, 3.0]), Default::default());
423        let g1 = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
424        let (found_root, max_frac, max_frac_index) = g0.root_finding(&g1);
425        assert!(!found_root);
426        assert_eq!(max_frac, f::<V>(0.5));
427        assert_eq!(max_frac_index, 1);
428
429        let g0 = V::from_vec(fv::<V>(&[1.0, -2.0, 3.0]), Default::default());
430        let g1 = V::from_vec(fv::<V>(&[1.0, 2.0, 0.0]), Default::default());
431        let (found_root, max_frac, max_frac_index) = g0.root_finding(&g1);
432        assert!(found_root);
433        assert_eq!(max_frac, f::<V>(0.5));
434        assert_eq!(max_frac_index, 1);
435
436        let g0 = V::from_vec(fv::<V>(&[1.0, -2.0, 3.0]), Default::default());
437        let g1 = V::from_vec(fv::<V>(&[1.0, -2.0, 3.0]), Default::default());
438        let (found_root, max_frac, max_frac_index) = g0.root_finding(&g1);
439        assert!(!found_root);
440        assert_eq!(max_frac, f::<V>(0.0));
441        assert_eq!(max_frac_index, -1);
442    }
443
444    pub fn test_from_slice<V: Vector>() {
445        let slice = fv::<V>(&[1.0, 2.0, 3.0]);
446        let v = V::from_slice(&slice, Default::default());
447        assert_eq!(v.clone_as_vec(), slice);
448    }
449
450    pub fn test_mul_scalar<V: Vector>() {
451        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
452        let result = v * Scale(f::<V>(2.0));
453        assert_eq!(result.clone_as_vec(), fv::<V>(&[2.0, 4.0, 6.0]));
454    }
455
456    pub fn test_div_scalar<V: Vector>() {
457        let v = V::from_vec(fv::<V>(&[2.0, 4.0, 6.0]), Default::default());
458        let result = v / Scale(f::<V>(2.0));
459        assert_eq!(result.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0]));
460    }
461
462    pub fn test_axpy<V: Vector>() {
463        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
464        let x = V::from_vec(fv::<V>(&[4.0, 5.0, 6.0]), Default::default());
465        y.axpy(f::<V>(2.0), &x, f::<V>(1.0));
466        assert_eq!(y.clone_as_vec(), fv::<V>(&[9.0, 12.0, 15.0]));
467        y.axpy(f::<V>(2.0), &x, f::<V>(0.0));
468        assert_eq!(y.clone_as_vec(), fv::<V>(&[8.0, 10.0, 12.0]));
469        y.axpy(f::<V>(0.0), &x, f::<V>(1.0));
470        assert_eq!(y.clone_as_vec(), fv::<V>(&[8.0, 10.0, 12.0]));
471    }
472
473    pub fn test_copy_from_indices<V: Vector>() {
474        let mut v1 = V::zeros(5, Default::default());
475        let v2 = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0, 40.0, 50.0]), Default::default());
476        let indices = V::Index::from_vec(vec![0, 2, 4], Default::default());
477        v1.copy_from_indices(&v2, &indices);
478        assert_eq!(v1.clone_as_vec(), fv::<V>(&[10.0, 0.0, 30.0, 0.0, 50.0]));
479    }
480
481    pub fn test_gather<V: Vector>() {
482        let mut result = V::zeros(3, Default::default());
483        let v = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0, 40.0]), Default::default());
484        let indices = V::Index::from_vec(vec![3, 0, 2], Default::default());
485        result.gather(&v, &indices);
486        assert_eq!(result.clone_as_vec(), fv::<V>(&[40.0, 10.0, 30.0]));
487    }
488
489    pub fn test_scatter<V: Vector>() {
490        let v = V::from_vec(fv::<V>(&[40.0, 10.0, 30.0]), Default::default());
491        let indices = V::Index::from_vec(vec![3, 0, 2], Default::default());
492        let mut result = V::zeros(4, Default::default());
493        v.scatter(&indices, &mut result);
494        assert_eq!(result.clone_as_vec(), fv::<V>(&[10.0, 0.0, 30.0, 40.0]));
495    }
496
497    pub fn test_copy_from_via_view_mut<V: Vector>() {
498        let mut v1 = V::zeros(3, Default::default());
499        let v2 = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
500        v1.as_view_mut().copy_from(&v2);
501        assert_eq!(v1.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0]));
502    }
503
504    pub fn test_set_index<V: Vector>() {
505        let mut v = V::zeros(3, Default::default());
506        v.set_index(1, f::<V>(42.0));
507        assert_eq!(v.clone_as_vec(), fv::<V>(&[0.0, 42.0, 0.0]));
508    }
509
510    pub fn test_get_index<V: Vector>() {
511        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
512        assert_eq!(v.get_index(0), f::<V>(1.0));
513        assert_eq!(v.get_index(2), f::<V>(3.0));
514    }
515
516    pub fn test_norm<V: Vector>() {
517        let v = V::from_vec(fv::<V>(&[3.0, 4.0]), Default::default());
518        let norm = v.norm(2);
519        let diff = norm - f::<V>(5.0);
520        assert!(num_traits::abs(diff) < f::<V>(1e-12));
521    }
522
523    pub fn test_norm_l1<V: Vector>() {
524        let v = V::from_vec(fv::<V>(&[3.0, -4.0]), Default::default());
525        let norm = v.norm(1);
526        let diff = norm - f::<V>(7.0);
527        assert!(num_traits::abs(diff) < f::<V>(1e-12));
528    }
529
530    pub fn test_squared_norm<V: Vector>() {
531        let x = V::from_vec(fv::<V>(&[1.0, 2.0]), Default::default());
532        let y = V::from_vec(fv::<V>(&[1.0, 1.0]), Default::default());
533        let atol = V::from_vec(fv::<V>(&[1e-3, 1e-3]), Default::default());
534        let rtol = f::<V>(1e-2);
535        let norm = x.squared_norm(&y, &atol, rtol);
536        let denom = f::<V>(1.0) * rtol + f::<V>(1e-3);
537        let err0 = f::<V>(1.0) / denom;
538        let err1 = f::<V>(2.0) / denom;
539        let expected = (err0 * err0 + err1 * err1) / f::<V>(2.0);
540        assert!(num_traits::abs(norm - expected) < f::<V>(1e-12));
541    }
542
543    pub fn test_fill<V: Vector>() {
544        let mut v = V::zeros(3, Default::default());
545        v.fill(f::<V>(7.0));
546        assert_eq!(v.clone_as_vec(), fv::<V>(&[7.0, 7.0, 7.0]));
547    }
548
549    pub fn test_from_element<V: Vector>() {
550        let v = V::from_element(2, f::<V>(5.0), Default::default());
551        assert_eq!(v.clone_as_vec(), fv::<V>(&[5.0, 5.0]));
552    }
553
554    pub fn test_copy_from<V: Vector>() {
555        let mut v1 = V::zeros(3, Default::default());
556        let v2 = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
557        v1.copy_from(&v2);
558        assert_eq!(v1.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0]));
559    }
560
561    pub fn test_from_vec<V: Vector>() {
562        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
563        assert_eq!(v.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0]));
564    }
565
566    pub fn test_component_mul_assign<V: Vector>() {
567        let mut a = V::from_vec(fv::<V>(&[2.0, 3.0]), Default::default());
568        let b = V::from_vec(fv::<V>(&[10.0, 20.0]), Default::default());
569        a.component_mul_assign(&b);
570        assert_eq!(a.clone_as_vec(), fv::<V>(&[20.0, 60.0]));
571    }
572
573    pub fn test_component_div_assign<V: Vector>() {
574        let mut a = V::from_vec(fv::<V>(&[6.0, 12.0]), Default::default());
575        let b = V::from_vec(fv::<V>(&[2.0, 3.0]), Default::default());
576        a.component_div_assign(&b);
577        assert_eq!(a.clone_as_vec(), fv::<V>(&[3.0, 4.0]));
578    }
579
580    pub fn test_assign_at_indices<V: Vector>() {
581        let mut v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
582        let indices = V::Index::from_vec(vec![0, 2], Default::default());
583        v.assign_at_indices(&indices, f::<V>(0.0));
584        assert_eq!(v.clone_as_vec(), fv::<V>(&[0.0, 2.0, 0.0]));
585    }
586
587    pub fn test_add<V: Vector>() {
588        let a = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
589        let b = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0]), Default::default());
590        let c = a + b;
591        assert_eq!(c.clone_as_vec(), fv::<V>(&[11.0, 22.0, 33.0]));
592    }
593
594    pub fn test_sub<V: Vector>() {
595        let a = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0]), Default::default());
596        let b = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
597        let c = a - b;
598        assert_eq!(c.clone_as_vec(), fv::<V>(&[9.0, 18.0, 27.0]));
599    }
600
601    pub fn test_add_assign<V: Vector>() {
602        let mut a = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
603        let b = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0]), Default::default());
604        a += b;
605        assert_eq!(a.clone_as_vec(), fv::<V>(&[11.0, 22.0, 33.0]));
606    }
607
608    pub fn test_sub_assign<V: Vector>() {
609        let mut a = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0]), Default::default());
610        let b = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
611        a -= b;
612        assert_eq!(a.clone_as_vec(), fv::<V>(&[9.0, 18.0, 27.0]));
613    }
614
615    pub fn test_axpy_v<V: Vector>() {
616        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
617        let x = V::from_vec(fv::<V>(&[4.0, 5.0, 6.0]), Default::default());
618        let x_view = x.as_view();
619        y.axpy_v(f::<V>(2.0), &x_view, f::<V>(1.0));
620        assert_eq!(y.clone_as_vec(), fv::<V>(&[9.0, 12.0, 15.0]));
621    }
622
623    pub fn test_mul_assign_scalar<V: Vector>() {
624        let mut v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
625        v *= Scale(f::<V>(2.0));
626        assert_eq!(v.clone_as_vec(), fv::<V>(&[2.0, 4.0, 6.0]));
627    }
628
629    pub fn test_copy_from_view<V: Vector>() {
630        let mut v1 = V::zeros(3, Default::default());
631        let v2 = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
632        let view = v2.as_view();
633        v1.copy_from_view(&view);
634        assert_eq!(v1.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0]));
635    }
636
637    pub fn test_view_squared_norm<V: Vector>() {
638        let x = V::from_vec(fv::<V>(&[1.0, 2.0]), Default::default());
639        let y = V::from_vec(fv::<V>(&[1.0, 1.0]), Default::default());
640        let atol = V::from_vec(fv::<V>(&[1e-3, 1e-3]), Default::default());
641        let rtol = f::<V>(1e-2);
642        let view = x.as_view();
643        let norm = VectorView::squared_norm(&view, &y, &atol, rtol);
644        let denom = f::<V>(1.0) * rtol + f::<V>(1e-3);
645        let err0 = f::<V>(1.0) / denom;
646        let err1 = f::<V>(2.0) / denom;
647        let expected = (err0 * err0 + err1 * err1) / f::<V>(2.0);
648        assert!(num_traits::abs(norm - expected) < f::<V>(1e-12));
649    }
650
651    pub fn test_view_into_owned<V: Vector>() {
652        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
653        let view = v.as_view();
654        let owned = view.into_owned();
655        assert_eq!(owned.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0]));
656    }
657
658    pub fn test_view_get_index<V: Vector>() {
659        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
660        let view = v.as_view();
661        assert_eq!(view.get_index(1), f::<V>(2.0));
662        assert_eq!(view.get_index(2), f::<V>(3.0));
663    }
664
665    pub fn test_view_mut_axpy<V: Vector>() {
666        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
667        let x = V::from_vec(fv::<V>(&[4.0, 5.0, 6.0]), Default::default());
668        {
669            let mut y_view = y.as_view_mut();
670            y_view.axpy(f::<V>(2.0), &x, f::<V>(1.0));
671        }
672        assert_eq!(y.clone_as_vec(), fv::<V>(&[9.0, 12.0, 15.0]));
673    }
674
675    pub fn test_view_mut_set_index<V: Vector>() {
676        let mut v = V::zeros(3, Default::default());
677        {
678            let mut view = v.as_view_mut();
679            view.set_index(1, f::<V>(42.0));
680        }
681        assert_eq!(v.clone_as_vec(), fv::<V>(&[0.0, 42.0, 0.0]));
682    }
683
684    pub fn test_view_mut_mul_assign_scalar<V: Vector>() {
685        let mut v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
686        {
687            let mut view = v.as_view_mut();
688            view *= Scale(f::<V>(2.0));
689        }
690        assert_eq!(v.clone_as_vec(), fv::<V>(&[2.0, 4.0, 6.0]));
691    }
692
693    pub fn test_view_mut_copy_from_view<V: Vector>() {
694        let mut v1 = V::zeros(3, Default::default());
695        let v2 = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), Default::default());
696        {
697            let v2_view = v2.as_view();
698            let mut v1_view = v1.as_view_mut();
699            v1_view.copy_from_view(&v2_view);
700        }
701        assert_eq!(v1.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0]));
702    }
703
704    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
705    pub fn test_batched_len_and_total_len<V: Vector>(ctx: V::C) {
706        let nbatch = ctx.nbatch();
707        assert!(nbatch > 1);
708        let v = V::zeros(4, ctx);
709        assert_eq!(v.len(), 4);
710        assert_eq!(v.total_len(), 4 * nbatch);
711    }
712
713    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
714    pub fn test_batched_from_vec<V: Vector>(ctx: V::C) {
715        assert_eq!(ctx.nbatch(), 2);
716        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]), ctx);
717        assert_eq!(v.len(), 3);
718        assert_eq!(v.total_len(), 6);
719        assert_eq!(v.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]));
720    }
721
722    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
723    pub fn test_batched_from_vec_bad_length<V: Vector>(ctx: V::C) {
724        assert_eq!(ctx.nbatch(), 2);
725        let _v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0]), ctx);
726    }
727
728    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
729    pub fn test_batched_from_element<V: Vector>(ctx: V::C) {
730        assert_eq!(ctx.nbatch(), 3);
731        let v = V::from_element(2, f::<V>(5.0), ctx);
732        assert_eq!(v.len(), 2);
733        assert_eq!(v.clone_as_vec(), fv::<V>(&[5.0, 5.0, 5.0, 5.0, 5.0, 5.0]));
734    }
735
736    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
737    pub fn test_batched_axpy<V: Vector>(ctx: V::C) {
738        assert_eq!(ctx.nbatch(), 2);
739        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 10.0, 20.0]), ctx.clone());
740        let x = V::from_vec(fv::<V>(&[3.0, 4.0, 30.0, 40.0]), ctx);
741        y.axpy(f::<V>(2.0), &x, f::<V>(1.0));
742        assert_eq!(y.clone_as_vec(), fv::<V>(&[7.0, 10.0, 70.0, 100.0]));
743    }
744
745    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
746    pub fn test_batched_add<V: Vector>(ctx: V::C) {
747        assert_eq!(ctx.nbatch(), 2);
748        let a = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0]), ctx.clone());
749        let b = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0, 40.0]), ctx);
750        let c = a + b;
751        assert_eq!(c.len(), 2);
752        assert_eq!(c.clone_as_vec(), fv::<V>(&[11.0, 22.0, 33.0, 44.0]));
753    }
754
755    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
756    pub fn test_batched_norm_max_across_batches<V: Vector>(ctx: V::C) {
757        assert_eq!(ctx.nbatch(), 2);
758        let v = V::from_vec(fv::<V>(&[1.0, 0.0, 0.0, 3.0]), ctx);
759        let norm = v.norm(2);
760        let diff = norm - f::<V>(3.0);
761        assert!(num_traits::abs(diff) < f::<V>(1e-12));
762    }
763
764    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
765    pub fn test_batched_norm_l1<V: Vector>(ctx: V::C) {
766        assert_eq!(ctx.nbatch(), 2);
767        let v = V::from_vec(fv::<V>(&[1.0, -2.0, 3.0, 0.0]), ctx);
768        let norm = v.norm(1);
769        // batch0: |1|+|-2| = 3, batch1: |3|+|0| = 3, max = 3
770        let diff = norm - f::<V>(3.0);
771        assert!(num_traits::abs(diff) < f::<V>(1e-12));
772    }
773
774    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
775    pub fn test_batched_squared_norm<V: Vector>(ctx: V::C) {
776        assert_eq!(ctx.nbatch(), 2);
777        let x = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0]), ctx.clone());
778        let y = V::from_vec(fv::<V>(&[1.0, 1.0, 1.0, 1.0]), ctx);
779        let atol = V::from_vec(fv::<V>(&[1e-3, 1e-3]), V::C::default());
780        let rtol = f::<V>(1e-2);
781        let norm = x.squared_norm(&y, &atol, rtol);
782        let denom = f::<V>(1.0) * rtol + f::<V>(1e-3);
783        let err3 = f::<V>(3.0) / denom;
784        let err4 = f::<V>(4.0) / denom;
785        let batch1 = (err3 * err3 + err4 * err4) / f::<V>(2.0);
786        let diff = norm - batch1;
787        assert!(num_traits::abs(diff) < f::<V>(1e-12));
788    }
789
790    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
791    pub fn test_batched_set_index<V: Vector>(ctx: V::C) {
792        assert_eq!(ctx.nbatch(), 3);
793        let mut v = V::zeros(2, ctx);
794        v.set_index(0, f::<V>(42.0));
795        assert_eq!(
796            v.clone_as_vec(),
797            fv::<V>(&[42.0, 0.0, 42.0, 0.0, 42.0, 0.0])
798        );
799    }
800
801    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
802    pub fn test_batched_get_index_panics<V: Vector>(ctx: V::C) {
803        assert!(ctx.nbatch() > 1);
804        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0]), ctx);
805        let _val = v.get_index(0);
806    }
807
808    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
809    pub fn test_batched_fill<V: Vector>(ctx: V::C) {
810        assert_eq!(ctx.nbatch(), 2);
811        let mut v = V::zeros(3, ctx);
812        v.fill(f::<V>(7.0));
813        assert_eq!(v.clone_as_vec(), fv::<V>(&[7.0, 7.0, 7.0, 7.0, 7.0, 7.0]));
814    }
815
816    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
817    pub fn test_batched_component_mul<V: Vector>(ctx: V::C) {
818        assert_eq!(ctx.nbatch(), 2);
819        let mut a = V::from_vec(fv::<V>(&[2.0, 3.0, 4.0, 5.0]), ctx.clone());
820        let b = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0, 40.0]), ctx);
821        a.component_mul_assign(&b);
822        assert_eq!(a.clone_as_vec(), fv::<V>(&[20.0, 60.0, 120.0, 200.0]));
823    }
824
825    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
826    pub fn test_batched_assign_at_indices<V: Vector>(ctx: V::C) {
827        assert_eq!(ctx.nbatch(), 2);
828        let mut v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]), ctx);
829        let indices = V::Index::from_vec(vec![0, 2], Default::default());
830        v.assign_at_indices(&indices, f::<V>(0.0));
831        assert_eq!(v.clone_as_vec(), fv::<V>(&[0.0, 2.0, 0.0, 0.0, 5.0, 0.0]));
832    }
833
834    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
835    pub fn test_batched_root_finding_consistent<V: Vector>(ctx: V::C) {
836        assert_eq!(ctx.nbatch(), 2);
837        let g0 = V::from_vec(fv::<V>(&[1.0, -1.0, 1.0, -1.0]), ctx.clone());
838        let g1 = V::from_vec(fv::<V>(&[-1.0, 1.0, -1.0, 1.0]), ctx);
839        let (found, _frac, idx) = g0.root_finding(&g1);
840        assert!(!found);
841        assert!(idx >= 0);
842    }
843
844    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
845    pub fn test_batched_root_finding_inconsistent<V: Vector>(ctx: V::C) {
846        assert_eq!(ctx.nbatch(), 2);
847        let g0 = V::from_vec(fv::<V>(&[1.0, 1.0, 1.0, -1.0]), ctx.clone());
848        let g1 = V::from_vec(fv::<V>(&[-1.0, 1.0, 1.0, 1.0]), ctx);
849        let _result = g0.root_finding(&g1);
850    }
851
852    // --- Broadcasting tests ---
853
854    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
855    pub fn test_batched_axpy_broadcast<V: Vector>(ctx: V::C) {
856        assert_eq!(ctx.nbatch(), 2);
857        // self has nbatch=2, x has nbatch=1
858        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 10.0, 20.0]), ctx);
859        let x = V::from_vec(fv::<V>(&[3.0, 4.0]), V::C::default());
860        y.axpy(f::<V>(2.0), &x, f::<V>(1.0));
861        // batch0: [1+6, 2+8]=[7,10], batch1: [10+6, 20+8]=[16,28]
862        assert_eq!(y.clone_as_vec(), fv::<V>(&[7.0, 10.0, 16.0, 28.0]));
863    }
864
865    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
866    pub fn test_batched_copy_from_broadcast<V: Vector>(ctx: V::C) {
867        assert_eq!(ctx.nbatch(), 2);
868        let mut y = V::zeros(2, ctx);
869        let x = V::from_vec(fv::<V>(&[5.0, 7.0]), V::C::default());
870        y.copy_from(&x);
871        // both batches get [5, 7]
872        assert_eq!(y.clone_as_vec(), fv::<V>(&[5.0, 7.0, 5.0, 7.0]));
873    }
874
875    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
876    pub fn test_batched_component_div<V: Vector>(ctx: V::C) {
877        assert_eq!(ctx.nbatch(), 2);
878        let mut a = V::from_vec(fv::<V>(&[6.0, 8.0, 12.0, 20.0]), ctx.clone());
879        let b = V::from_vec(fv::<V>(&[2.0, 4.0, 3.0, 5.0]), ctx);
880        a.component_div_assign(&b);
881        assert_eq!(a.clone_as_vec(), fv::<V>(&[3.0, 2.0, 4.0, 4.0]));
882    }
883
884    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
885    pub fn test_batched_component_mul_broadcast<V: Vector>(ctx: V::C) {
886        assert_eq!(ctx.nbatch(), 2);
887        let mut a = V::from_vec(fv::<V>(&[2.0, 3.0, 4.0, 5.0]), ctx);
888        let b = V::from_vec(fv::<V>(&[10.0, 20.0]), V::C::default());
889        a.component_mul_assign(&b);
890        // batch0: [20, 60], batch1: [40, 100]
891        assert_eq!(a.clone_as_vec(), fv::<V>(&[20.0, 60.0, 40.0, 100.0]));
892    }
893
894    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
895    pub fn test_batched_component_div_broadcast<V: Vector>(ctx: V::C) {
896        assert_eq!(ctx.nbatch(), 2);
897        let mut a = V::from_vec(fv::<V>(&[6.0, 8.0, 12.0, 20.0]), ctx);
898        let b = V::from_vec(fv::<V>(&[2.0, 4.0]), V::C::default());
899        a.component_div_assign(&b);
900        // batch0: [3, 2], batch1: [6, 5]
901        assert_eq!(a.clone_as_vec(), fv::<V>(&[3.0, 2.0, 6.0, 5.0]));
902    }
903
904    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
905    pub fn test_batched_add_assign_broadcast<V: Vector>(ctx: V::C) {
906        assert_eq!(ctx.nbatch(), 2);
907        let mut a = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0]), ctx);
908        let b = V::from_vec(fv::<V>(&[10.0, 20.0]), V::C::default());
909        a += &b;
910        assert_eq!(a.clone_as_vec(), fv::<V>(&[11.0, 22.0, 13.0, 24.0]));
911    }
912
913    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
914    pub fn test_batched_sub_assign_broadcast<V: Vector>(ctx: V::C) {
915        assert_eq!(ctx.nbatch(), 2);
916        let mut a = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0, 40.0]), ctx);
917        let b = V::from_vec(fv::<V>(&[1.0, 2.0]), V::C::default());
918        a -= &b;
919        assert_eq!(a.clone_as_vec(), fv::<V>(&[9.0, 18.0, 29.0, 38.0]));
920    }
921
922    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
923    pub fn test_batched_sub<V: Vector>(ctx: V::C) {
924        assert_eq!(ctx.nbatch(), 2);
925        let a = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0, 40.0]), ctx.clone());
926        let b = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0]), ctx);
927        let c = a - b;
928        assert_eq!(c.clone_as_vec(), fv::<V>(&[9.0, 18.0, 27.0, 36.0]));
929    }
930
931    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
932    pub fn test_batched_sub_assign<V: Vector>(ctx: V::C) {
933        assert_eq!(ctx.nbatch(), 2);
934        let mut a = V::from_vec(fv::<V>(&[10.0, 20.0, 30.0, 40.0]), ctx.clone());
935        let b = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 4.0]), ctx);
936        a -= b;
937        assert_eq!(a.clone_as_vec(), fv::<V>(&[9.0, 18.0, 27.0, 36.0]));
938    }
939
940    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
941    pub fn test_batched_from_slice<V: Vector>(ctx: V::C) {
942        assert_eq!(ctx.nbatch(), 2);
943        let slice = fv::<V>(&[1.0, 2.0, 3.0, 1.0, 2.0, 3.0]);
944        let v = V::from_slice(&slice, ctx);
945        assert_eq!(v.clone_as_vec(), fv::<V>(&[1.0, 2.0, 3.0, 1.0, 2.0, 3.0]));
946    }
947
948    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
949    pub fn test_batched_mul_scalar<V: Vector>(ctx: V::C) {
950        assert_eq!(ctx.nbatch(), 2);
951        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 10.0, 20.0]), ctx);
952        let result = v * Scale(f::<V>(2.0));
953        assert_eq!(result.clone_as_vec(), fv::<V>(&[2.0, 4.0, 20.0, 40.0]));
954    }
955
956    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
957    pub fn test_batched_div_scalar<V: Vector>(ctx: V::C) {
958        assert_eq!(ctx.nbatch(), 2);
959        let v = V::from_vec(fv::<V>(&[2.0, 4.0, 20.0, 40.0]), ctx);
960        let result = v / Scale(f::<V>(2.0));
961        assert_eq!(result.clone_as_vec(), fv::<V>(&[1.0, 2.0, 10.0, 20.0]));
962    }
963
964    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
965    pub fn test_batched_copy_from_indices<V: Vector>(ctx: V::C) {
966        assert_eq!(ctx.nbatch(), 2);
967        let mut v1 = V::zeros(4, ctx.clone());
968        let v2 = V::from_vec(
969            fv::<V>(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0]),
970            ctx,
971        );
972        let indices = V::Index::from_vec(vec![0, 2, 3], Default::default());
973        v1.copy_from_indices(&v2, &indices);
974        assert_eq!(
975            v1.clone_as_vec(),
976            fv::<V>(&[10.0, 0.0, 30.0, 40.0, 50.0, 0.0, 70.0, 80.0])
977        );
978    }
979
980    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
981    pub fn test_batched_gather<V: Vector>(ctx: V::C) {
982        assert_eq!(ctx.nbatch(), 2);
983        let mut result = V::zeros(3, ctx.clone());
984        let v = V::from_vec(
985            fv::<V>(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0]),
986            ctx,
987        );
988        let indices = V::Index::from_vec(vec![3, 0, 2], Default::default());
989        result.gather(&v, &indices);
990        assert_eq!(
991            result.clone_as_vec(),
992            fv::<V>(&[40.0, 10.0, 30.0, 80.0, 50.0, 70.0])
993        );
994    }
995
996    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
997    pub fn test_batched_scatter<V: Vector>(ctx: V::C) {
998        assert_eq!(ctx.nbatch(), 2);
999        let v = V::from_vec(fv::<V>(&[40.0, 10.0, 30.0, 80.0, 50.0, 70.0]), ctx.clone());
1000        let indices = V::Index::from_vec(vec![3, 0, 2], Default::default());
1001        let mut result = V::zeros(4, ctx);
1002        v.scatter(&indices, &mut result);
1003        assert_eq!(
1004            result.clone_as_vec(),
1005            fv::<V>(&[10.0, 0.0, 30.0, 40.0, 50.0, 0.0, 70.0, 80.0])
1006        );
1007    }
1008
1009    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1010    pub fn test_batched_get_batch<V: Vector>(ctx: V::C) {
1011        assert_eq!(ctx.nbatch(), 2);
1012        let v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 10.0, 20.0, 30.0]), ctx);
1013        let batch0 = v.get_batch(0);
1014        assert_eq!(batch0.get_index(0), f::<V>(1.0));
1015        assert_eq!(batch0.get_index(1), f::<V>(2.0));
1016        assert_eq!(batch0.get_index(2), f::<V>(3.0));
1017        let batch1 = v.get_batch(1);
1018        assert_eq!(batch1.get_index(0), f::<V>(10.0));
1019        assert_eq!(batch1.get_index(1), f::<V>(20.0));
1020        assert_eq!(batch1.get_index(2), f::<V>(30.0));
1021    }
1022
1023    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1024    pub fn test_batched_get_batch_mut<V: Vector>(ctx: V::C) {
1025        assert_eq!(ctx.nbatch(), 2);
1026        let mut v = V::from_vec(fv::<V>(&[1.0, 2.0, 3.0, 10.0, 20.0, 30.0]), ctx);
1027        {
1028            let mut batch0 = v.get_batch_mut(0);
1029            batch0.set_index(1, f::<V>(99.0));
1030        }
1031        assert_eq!(
1032            v.clone_as_vec(),
1033            fv::<V>(&[1.0, 99.0, 3.0, 10.0, 20.0, 30.0])
1034        );
1035    }
1036
1037    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1038    pub fn test_batched_axpy_v<V: Vector>(ctx: V::C) {
1039        assert_eq!(ctx.nbatch(), 2);
1040        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 10.0, 20.0]), ctx.clone());
1041        let x = V::from_vec(fv::<V>(&[3.0, 4.0, 30.0, 40.0]), ctx);
1042        let x_view = x.as_view();
1043        y.axpy_v(f::<V>(2.0), &x_view, f::<V>(1.0));
1044        assert_eq!(y.clone_as_vec(), fv::<V>(&[7.0, 10.0, 70.0, 100.0]));
1045    }
1046
1047    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1048    pub fn test_batched_mul_assign_scalar<V: Vector>(ctx: V::C) {
1049        assert_eq!(ctx.nbatch(), 2);
1050        let mut v = V::from_vec(fv::<V>(&[1.0, 2.0, 10.0, 20.0]), ctx);
1051        v *= Scale(f::<V>(2.0));
1052        assert_eq!(v.clone_as_vec(), fv::<V>(&[2.0, 4.0, 20.0, 40.0]));
1053    }
1054
1055    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1056    pub fn test_batched_copy_from_view<V: Vector>(ctx: V::C) {
1057        assert_eq!(ctx.nbatch(), 2);
1058        let mut v1 = V::zeros(2, ctx);
1059        let v2 = V::from_vec(fv::<V>(&[5.0, 7.0]), V::C::default());
1060        let view = v2.as_view();
1061        v1.copy_from_view(&view);
1062        assert_eq!(v1.clone_as_vec(), fv::<V>(&[5.0, 7.0, 5.0, 7.0]));
1063    }
1064
1065    // --- batched_axpy tests ---
1066
1067    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1068    pub fn test_batched_axpy_new<V: Vector>(ctx: V::C) {
1069        assert_eq!(ctx.nbatch(), 2);
1070        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 10.0, 20.0]), ctx.clone());
1071        let x = V::from_vec(fv::<V>(&[3.0, 4.0, 30.0, 40.0]), ctx);
1072        y.batched_axpy(&[f::<V>(2.0), f::<V>(0.5)], &x, f::<V>(1.0));
1073        // batch0: [1,2] + 2*[3,4] = [7,10]
1074        // batch1: [10,20] + 0.5*[30,40] = [25,40]
1075        assert_eq!(y.clone_as_vec(), fv::<V>(&[7.0, 10.0, 25.0, 40.0]));
1076    }
1077
1078    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1079    pub fn test_batched_axpy_new_broadcast<V: Vector>(ctx: V::C) {
1080        assert_eq!(ctx.nbatch(), 2);
1081        let mut y = V::from_vec(fv::<V>(&[1.0, 2.0, 10.0, 20.0]), ctx);
1082        let x = V::from_vec(fv::<V>(&[3.0, 4.0]), V::C::default());
1083        y.batched_axpy(&[f::<V>(2.0), f::<V>(0.5)], &x, f::<V>(1.0));
1084        // both batches: beta*y + alpha_b * x
1085        // batch0: [1,2] + 2*[3,4] = [7,10]
1086        // batch1: [10,20] + 0.5*[3,4] = [11.5,22]
1087        assert_eq!(y.clone_as_vec(), fv::<V>(&[7.0, 10.0, 11.5, 22.0]));
1088    }
1089
1090    #[allow(clippy::type_complexity)]
1091    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1092    pub fn test_batched_axpy_new_bad_length<V: Vector>(ctx: V::C) {
1093        assert_eq!(ctx.nbatch(), 2);
1094        let mut y = V::zeros(2, ctx);
1095        let x = V::zeros(2, V::C::default());
1096        y.batched_axpy(&[f::<V>(1.0)], &x, f::<V>(0.0));
1097    }
1098
1099    // --- Incompatible batch tests ---
1100
1101    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1102    pub fn test_batched_axpy_incompatible<V: Vector>(ctx2: V::C, ctx3: V::C) {
1103        assert_eq!(ctx2.nbatch(), 2);
1104        assert_eq!(ctx3.nbatch(), 3);
1105        let mut y = V::zeros(2, ctx2);
1106        let x = V::zeros(2, ctx3);
1107        y.axpy(f::<V>(1.0), &x, f::<V>(1.0));
1108    }
1109
1110    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1111    pub fn test_batched_copy_from_incompatible<V: Vector>(ctx2: V::C, ctx3: V::C) {
1112        assert_eq!(ctx2.nbatch(), 2);
1113        assert_eq!(ctx3.nbatch(), 3);
1114        let mut y = V::zeros(2, ctx2);
1115        let x = V::zeros(2, ctx3);
1116        y.copy_from(&x);
1117    }
1118
1119    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1120    pub fn test_batched_add_assign_incompatible<V: Vector>(ctx2: V::C, ctx3: V::C) {
1121        assert_eq!(ctx2.nbatch(), 2);
1122        assert_eq!(ctx3.nbatch(), 3);
1123        let mut a = V::zeros(2, ctx2);
1124        let b = V::zeros(2, ctx3);
1125        a += &b;
1126    }
1127
1128    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1129    pub fn test_batched_component_mul_incompatible<V: Vector>(ctx2: V::C, ctx3: V::C) {
1130        assert_eq!(ctx2.nbatch(), 2);
1131        assert_eq!(ctx3.nbatch(), 3);
1132        let mut a = V::zeros(2, ctx2);
1133        let b = V::zeros(2, ctx3);
1134        a.component_mul_assign(&b);
1135    }
1136
1137    #[test]
1138    fn vector_common_for_references_and_default_helpers_work() {
1139        let mut v = NalgebraVec::from_vec(vec![1.0, 2.0], NalgebraContext::default());
1140        assert_eq!(<NalgebraVec<f64> as VectorCommon>::inner(&v).len(), 2);
1141        assert_eq!(<&NalgebraVec<f64> as VectorCommon>::inner(&&v).len(), 2);
1142        assert_eq!(
1143            <&mut NalgebraVec<f64> as VectorCommon>::inner(&&mut v).len(),
1144            2
1145        );
1146
1147        let empty = NalgebraVec::<f64>::zeros(0, NalgebraContext::default());
1148        assert!(empty.is_empty());
1149
1150        let non_empty = NalgebraVec::<f64>::zeros(2, NalgebraContext::default());
1151        assert!(!non_empty.is_empty());
1152        assert_eq!(non_empty.clone_as_vec(), vec![0.0, 0.0]);
1153    }
1154
1155    #[test]
1156    fn vector_assert_eq_panics_for_length_mismatch() {
1157        let left = NalgebraVec::from_vec(vec![1.0, 2.0], NalgebraContext::default());
1158        let right = NalgebraVec::from_vec(vec![1.0], NalgebraContext::default());
1159        let tol = NalgebraVec::from_vec(vec![0.0, 0.0], NalgebraContext::default());
1160        assert!(catch_unwind(AssertUnwindSafe(|| left.assert_eq(&right, &tol))).is_err());
1161    }
1162
1163    #[test]
1164    fn vector_assert_helpers_cover_success_and_failure_paths() {
1165        let left = NalgebraVec::from_vec(vec![1.0, 2.0, 3.0], NalgebraContext::default());
1166        let right = NalgebraVec::from_vec(vec![1.0, 2.0, 3.0], NalgebraContext::default());
1167        let tol = NalgebraVec::from_vec(vec![0.0, 0.0, 0.0], NalgebraContext::default());
1168        left.assert_eq(&right, &tol);
1169        left.assert_eq_st(&right, 0.0);
1170        left.assert_eq_norm(&right, &tol, 1e-6, 1.0);
1171
1172        let perturbed = NalgebraVec::from_vec(vec![1.1, 2.0, 3.0], NalgebraContext::default());
1173        assert!(catch_unwind(AssertUnwindSafe(
1174            || left.assert_eq_norm(&perturbed, &tol, 1e-6, 0.01)
1175        ))
1176        .is_err());
1177    }
1178
1179    #[test]
1180    fn vector_assert_eq_vec_panics_for_short_vector_mismatch() {
1181        assert!(catch_unwind(AssertUnwindSafe(|| {
1182            <NalgebraVec<f64> as Vector>::assert_eq_vec(
1183                vec![1.0, 2.0, 3.0],
1184                vec![0.0, 2.0, 3.0],
1185                vec![0.0, 0.0, 0.0],
1186            )
1187        }))
1188        .is_err());
1189    }
1190
1191    #[test]
1192    fn vector_assert_eq_vec_panics_for_first_middle_and_last_mismatch_in_long_vectors() {
1193        assert!(catch_unwind(AssertUnwindSafe(|| {
1194            <NalgebraVec<f64> as Vector>::assert_eq_vec(
1195                vec![1.0, 2.0, 3.0, 4.0],
1196                vec![0.0, 2.0, 3.0, 4.0],
1197                vec![0.0, 0.0, 0.0, 0.0],
1198            )
1199        }))
1200        .is_err());
1201        assert!(catch_unwind(AssertUnwindSafe(|| {
1202            <NalgebraVec<f64> as Vector>::assert_eq_vec(
1203                vec![1.0, 2.0, 3.0, 4.0, 5.0],
1204                vec![1.0, 2.0, 0.0, 4.0, 5.0],
1205                vec![0.0, 0.0, 0.0, 0.0, 0.0],
1206            )
1207        }))
1208        .is_err());
1209        assert!(catch_unwind(AssertUnwindSafe(|| {
1210            <NalgebraVec<f64> as Vector>::assert_eq_vec(
1211                vec![1.0, 2.0, 3.0, 4.0],
1212                vec![1.0, 2.0, 3.0, 0.0],
1213                vec![0.0, 0.0, 0.0, 0.0],
1214            )
1215        }))
1216        .is_err());
1217    }
1218
1219    use crate::matrix::DenseMatrix;
1220
1221    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1222    fn make_strided_test_matrix<M: DenseMatrix>(nbatch: usize) -> M {
1223        let ctx = M::C::default().clone_with_nbatch(nbatch).unwrap();
1224        let nrows = 3;
1225        let ncols = 4;
1226        let mut data = Vec::with_capacity(nrows * ncols * nbatch);
1227        for b in 0..nbatch {
1228            for col in 0..ncols {
1229                for row in 0..nrows {
1230                    data.push(f::<M::V>(row as f64 + col as f64 * 10.0 + b as f64 * 100.0));
1231                }
1232            }
1233        }
1234        M::from_vec(nrows, ncols, data, ctx)
1235    }
1236
1237    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1238    pub fn test_strided_view_set_index<M: DenseMatrix>(ctx: M::C) {
1239        let mut matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1240        {
1241            let mut col1 = matrix.column_mut(1);
1242            col1.set_index(1, f::<M::V>(99.0));
1243        }
1244        let owned = matrix.column(1).into_owned();
1245        let b0 = owned.get_batch(0);
1246        let b1 = owned.get_batch(1);
1247        assert_eq!(b0.get_index(1), f::<M::V>(99.0));
1248        assert_eq!(b1.get_index(1), f::<M::V>(99.0));
1249    }
1250
1251    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1252    pub fn test_strided_view_mut_copy_from<M: DenseMatrix>(ctx: M::C) {
1253        let mut matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1254        let owned = M::V::from_vec(
1255            vec![f::<M::V>(50.0), f::<M::V>(51.0), f::<M::V>(52.0)],
1256            M::C::default(),
1257        );
1258        {
1259            let mut col1 = matrix.column_mut(1);
1260            col1.copy_from(&owned);
1261        }
1262        let owned_v = matrix.column(1).into_owned();
1263        let b0 = owned_v.get_batch(0);
1264        let b1 = owned_v.get_batch(1);
1265        assert_eq!(b0.get_index(0), f::<M::V>(50.0));
1266        assert_eq!(b0.get_index(1), f::<M::V>(51.0));
1267        assert_eq!(b0.get_index(2), f::<M::V>(52.0));
1268        assert_eq!(b1.get_index(0), f::<M::V>(50.0));
1269        assert_eq!(b1.get_index(1), f::<M::V>(51.0));
1270        assert_eq!(b1.get_index(2), f::<M::V>(52.0));
1271    }
1272
1273    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1274    pub fn test_strided_view_mut_axpy<M: DenseMatrix>(ctx: M::C) {
1275        let mut matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1276        let x = M::V::from_vec(
1277            vec![f::<M::V>(10.0), f::<M::V>(10.0), f::<M::V>(10.0)],
1278            M::C::default(),
1279        );
1280        {
1281            let mut col1 = matrix.column_mut(1);
1282            col1.axpy(f::<M::V>(2.0), &x, f::<M::V>(1.0));
1283        }
1284        let owned_v = matrix.column(1).into_owned();
1285        let b1 = owned_v.get_batch(1);
1286        assert_eq!(b1.get_index(0), f::<M::V>(130.0));
1287        assert_eq!(b1.get_index(1), f::<M::V>(131.0));
1288        assert_eq!(b1.get_index(2), f::<M::V>(132.0));
1289    }
1290
1291    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1292    pub fn test_strided_view_mut_mul_assign_scalar<M: DenseMatrix>(ctx: M::C) {
1293        let mut matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1294        {
1295            let mut col1 = matrix.column_mut(1);
1296            col1 *= Scale(f::<M::V>(2.0));
1297        }
1298        let owned_v = matrix.column(1).into_owned();
1299        let b0 = owned_v.get_batch(0);
1300        let b1 = owned_v.get_batch(1);
1301        assert_eq!(b0.get_index(0), f::<M::V>(20.0));
1302        assert_eq!(b1.get_index(0), f::<M::V>(220.0));
1303    }
1304
1305    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1306    pub fn test_strided_view_mut_add_assign<M: DenseMatrix>(ctx: M::C) {
1307        let mut matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1308        let rhs = M::V::from_vec(
1309            vec![
1310                f::<M::V>(5.0),
1311                f::<M::V>(5.0),
1312                f::<M::V>(5.0),
1313                f::<M::V>(10.0),
1314                f::<M::V>(10.0),
1315                f::<M::V>(10.0),
1316            ],
1317            ctx.clone(),
1318        );
1319        {
1320            let mut col1 = matrix.column_mut(1);
1321            col1 += &rhs;
1322        }
1323        let owned_v = matrix.column(1).into_owned();
1324        let b0 = owned_v.get_batch(0);
1325        let b1 = owned_v.get_batch(1);
1326        assert_eq!(b0.get_index(0), f::<M::V>(15.0));
1327        assert_eq!(b0.get_index(1), f::<M::V>(16.0));
1328        assert_eq!(b0.get_index(2), f::<M::V>(17.0));
1329        assert_eq!(b1.get_index(0), f::<M::V>(120.0));
1330        assert_eq!(b1.get_index(1), f::<M::V>(121.0));
1331        assert_eq!(b1.get_index(2), f::<M::V>(122.0));
1332    }
1333
1334    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1335    pub fn test_strided_view_mut_sub_assign<M: DenseMatrix>(ctx: M::C) {
1336        let mut matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1337        let rhs = M::V::from_vec(
1338            vec![
1339                f::<M::V>(1.0),
1340                f::<M::V>(1.0),
1341                f::<M::V>(1.0),
1342                f::<M::V>(1.0),
1343                f::<M::V>(1.0),
1344                f::<M::V>(1.0),
1345            ],
1346            ctx.clone(),
1347        );
1348        {
1349            let mut col1 = matrix.column_mut(1);
1350            col1 -= &rhs;
1351        }
1352        let owned_v = matrix.column(1).into_owned();
1353        let b0 = owned_v.get_batch(0);
1354        let b1 = owned_v.get_batch(1);
1355        assert_eq!(b0.get_index(0), f::<M::V>(9.0));
1356        assert_eq!(b1.get_index(0), f::<M::V>(109.0));
1357    }
1358
1359    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1360    pub fn test_strided_view_add_assign_broadcast<M: DenseMatrix>(ctx: M::C) {
1361        let mut matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1362        let rhs = M::V::from_vec(
1363            vec![f::<M::V>(5.0), f::<M::V>(5.0), f::<M::V>(5.0)],
1364            M::C::default(),
1365        );
1366        {
1367            let mut col1 = matrix.column_mut(1);
1368            col1 += &rhs;
1369        }
1370        let owned_v = matrix.column(1).into_owned();
1371        let b0 = owned_v.get_batch(0);
1372        let b1 = owned_v.get_batch(1);
1373        assert_eq!(b0.get_index(0), f::<M::V>(15.0));
1374        assert_eq!(b1.get_index(0), f::<M::V>(115.0));
1375    }
1376
1377    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1378    pub fn test_strided_view_add_owned<M: DenseMatrix>(ctx: M::C) {
1379        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1380        let nbatch = ctx.nbatch();
1381        let mut rhs_data = Vec::with_capacity(3 * nbatch);
1382        for _ in 0..nbatch {
1383            rhs_data.extend_from_slice(&[f::<M::V>(5.0), f::<M::V>(5.0), f::<M::V>(5.0)]);
1384        }
1385        let rhs = M::V::from_vec(rhs_data, ctx.clone());
1386        let col1 = matrix.column(1);
1387        let result = col1 + &rhs;
1388        let b0 = result.get_batch(0);
1389        let b1 = result.get_batch(1);
1390        assert_eq!(b0.get_index(0), f::<M::V>(15.0));
1391        assert_eq!(b1.get_index(0), f::<M::V>(115.0));
1392    }
1393
1394    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1395    pub fn test_strided_view_squared_norm<M: DenseMatrix>(ctx: M::C) {
1396        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1397        let nbatch = ctx.nbatch();
1398        let mut y_data = Vec::with_capacity(3 * nbatch);
1399        for _ in 0..nbatch {
1400            y_data.extend_from_slice(&[f::<M::V>(1.0), f::<M::V>(1.0), f::<M::V>(1.0)]);
1401        }
1402        let y = M::V::from_vec(y_data, ctx.clone());
1403        let atol = M::V::from_vec(
1404            vec![f::<M::V>(1e-3), f::<M::V>(1e-3), f::<M::V>(1e-3)],
1405            M::C::default(),
1406        );
1407        let col1 = matrix.column(1);
1408        let norm = col1.squared_norm(&y, &atol, f::<M::V>(1e-2));
1409        assert!(norm > f::<M::V>(0.0));
1410    }
1411
1412    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1413    pub fn test_strided_view_into_owned<M: DenseMatrix>(ctx: M::C) {
1414        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1415        let col1 = matrix.column(1);
1416        let owned = col1.into_owned();
1417        let b0 = owned.get_batch(0);
1418        let b1 = owned.get_batch(1);
1419        assert_eq!(b0.get_index(0), f::<M::V>(10.0));
1420        assert_eq!(b0.get_index(1), f::<M::V>(11.0));
1421        assert_eq!(b1.get_index(0), f::<M::V>(110.0));
1422        assert_eq!(b1.get_index(1), f::<M::V>(111.0));
1423    }
1424
1425    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1426    pub fn test_strided_view_component_mul<M: DenseMatrix>(ctx: M::C) {
1427        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1428        let rhs = M::V::from_vec(
1429            vec![f::<M::V>(10.0), f::<M::V>(1.0), f::<M::V>(0.0)],
1430            M::C::default(),
1431        );
1432        let col1 = matrix.column(1);
1433        let mut owned = col1.into_owned();
1434        owned.component_mul_assign(&rhs);
1435        let b0 = owned.get_batch(0);
1436        let b1 = owned.get_batch(1);
1437        assert_eq!(b0.get_index(0), f::<M::V>(100.0));
1438        assert_eq!(b0.get_index(1), f::<M::V>(11.0));
1439        assert_eq!(b1.get_index(0), f::<M::V>(1100.0));
1440        assert_eq!(b1.get_index(1), f::<M::V>(111.0));
1441    }
1442
1443    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1444    pub fn test_strided_view_component_div<M: DenseMatrix>(ctx: M::C) {
1445        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1446        let rhs = M::V::from_vec(
1447            vec![f::<M::V>(10.0), f::<M::V>(11.0), f::<M::V>(12.0)],
1448            M::C::default(),
1449        );
1450        let col1 = matrix.column(1);
1451        let mut owned = col1.into_owned();
1452        owned.component_div_assign(&rhs);
1453        let b0 = owned.get_batch(0);
1454        let b1 = owned.get_batch(1);
1455        assert_eq!(b0.get_index(0), f::<M::V>(1.0));
1456        assert_eq!(b1.get_index(0), f::<M::V>(11.0));
1457    }
1458
1459    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1460    pub fn test_strided_view_mul_scalar<M: DenseMatrix>(ctx: M::C) {
1461        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1462        let col1 = matrix.column(1);
1463        let result = col1 * Scale(f::<M::V>(2.0));
1464        let b0 = result.get_batch(0);
1465        let b1 = result.get_batch(1);
1466        assert_eq!(b0.get_index(0), f::<M::V>(20.0));
1467        assert_eq!(b1.get_index(0), f::<M::V>(220.0));
1468    }
1469
1470    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1471    pub fn test_strided_view_fill<M: DenseMatrix>(ctx: M::C) {
1472        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1473        let col1 = matrix.column(1);
1474        let mut owned = col1.into_owned();
1475        owned.fill(f::<M::V>(7.0));
1476        let b0 = owned.get_batch(0);
1477        let b1 = owned.get_batch(1);
1478        assert_eq!(b0.get_index(0), f::<M::V>(7.0));
1479        assert_eq!(b1.get_index(0), f::<M::V>(7.0));
1480    }
1481
1482    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1483    pub fn test_strided_view_assign_at_indices<M: DenseMatrix>(ctx: M::C) {
1484        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1485        let indices = <M::V as Vector>::Index::from_vec(vec![0, 2], M::C::default());
1486        let col1 = matrix.column(1);
1487        let mut owned = col1.into_owned();
1488        owned.assign_at_indices(&indices, f::<M::V>(0.0));
1489        let b0 = owned.get_batch(0);
1490        let b1 = owned.get_batch(1);
1491        assert_eq!(b0.get_index(0), f::<M::V>(0.0));
1492        assert_eq!(b1.get_index(0), f::<M::V>(0.0));
1493    }
1494
1495    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1496    pub fn test_strided_view_copy_from_indices<M: DenseMatrix>(ctx: M::C) {
1497        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1498        let nbatch = ctx.nbatch();
1499        let mut other_data = Vec::with_capacity(3 * nbatch);
1500        for _ in 0..nbatch {
1501            other_data.extend_from_slice(&[f::<M::V>(50.0), f::<M::V>(0.0), f::<M::V>(0.0)]);
1502        }
1503        let other = M::V::from_vec(other_data, ctx.clone());
1504        let indices = <M::V as Vector>::Index::from_vec(vec![0], M::C::default());
1505        let col1 = matrix.column(1);
1506        let mut owned = col1.into_owned();
1507        owned.copy_from_indices(&other, &indices);
1508        let b0 = owned.get_batch(0);
1509        let b1 = owned.get_batch(1);
1510        assert_eq!(b0.get_index(0), f::<M::V>(50.0));
1511        assert_eq!(b1.get_index(0), f::<M::V>(50.0));
1512    }
1513
1514    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1515    pub fn test_strided_view_gather<M: DenseMatrix>(ctx: M::C) {
1516        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1517        let nbatch = ctx.nbatch();
1518        let mut result = M::V::zeros(2, M::C::default().clone_with_nbatch(nbatch).unwrap());
1519        let indices = <M::V as Vector>::Index::from_vec(vec![0, 2], M::C::default());
1520        let col1 = matrix.column(1);
1521        let owned = col1.into_owned();
1522        result.gather(&owned, &indices);
1523        let b1 = result.get_batch(1);
1524        assert_eq!(b1.get_index(0), f::<M::V>(110.0));
1525    }
1526
1527    #[cfg_attr(not(feature = "cuda"), allow(dead_code))]
1528    pub fn test_strided_view_scatter<M: DenseMatrix>(ctx: M::C) {
1529        let matrix = make_strided_test_matrix::<M>(ctx.nbatch());
1530        let nbatch = ctx.nbatch();
1531        let col1 = matrix.column(1);
1532        let owned = col1.into_owned();
1533        let indices = <M::V as Vector>::Index::from_vec(vec![0, 1, 2], M::C::default());
1534        let mut result = M::V::zeros(3, M::C::default().clone_with_nbatch(nbatch).unwrap());
1535        owned.scatter(&indices, &mut result);
1536        let b1 = result.get_batch(1);
1537        assert_eq!(b1.get_index(0), f::<M::V>(110.0));
1538    }
1539}
1540
1541#[cfg(test)]
1542macro_rules! generate_vector_tests_nonbatched {
1543    ($suffix:ident, $V:ty) => {
1544        paste::paste! {
1545            #[test]
1546            fn [<test_root_finding_ $suffix>]() {
1547                $crate::vector::tests::test_root_finding::<$V>();
1548            }
1549            #[test]
1550            fn [<test_from_slice_ $suffix>]() {
1551                $crate::vector::tests::test_from_slice::<$V>();
1552            }
1553            #[test]
1554            fn [<test_mul_scalar_ $suffix>]() {
1555                $crate::vector::tests::test_mul_scalar::<$V>();
1556            }
1557            #[test]
1558            fn [<test_div_scalar_ $suffix>]() {
1559                $crate::vector::tests::test_div_scalar::<$V>();
1560            }
1561            #[test]
1562            fn [<test_axpy_ $suffix>]() {
1563                $crate::vector::tests::test_axpy::<$V>();
1564            }
1565            #[test]
1566            fn [<test_copy_from_indices_ $suffix>]() {
1567                $crate::vector::tests::test_copy_from_indices::<$V>();
1568            }
1569            #[test]
1570            fn [<test_gather_ $suffix>]() {
1571                $crate::vector::tests::test_gather::<$V>();
1572            }
1573            #[test]
1574            fn [<test_scatter_ $suffix>]() {
1575                $crate::vector::tests::test_scatter::<$V>();
1576            }
1577            #[test]
1578            fn [<test_copy_from_via_view_mut_ $suffix>]() {
1579                $crate::vector::tests::test_copy_from_via_view_mut::<$V>();
1580            }
1581            #[test]
1582            fn [<test_set_index_ $suffix>]() {
1583                $crate::vector::tests::test_set_index::<$V>();
1584            }
1585            #[test]
1586            fn [<test_get_index_ $suffix>]() {
1587                $crate::vector::tests::test_get_index::<$V>();
1588            }
1589            #[test]
1590            fn [<test_norm_ $suffix>]() {
1591                $crate::vector::tests::test_norm::<$V>();
1592            }
1593            #[test]
1594            fn [<test_norm_l1_ $suffix>]() {
1595                $crate::vector::tests::test_norm_l1::<$V>();
1596            }
1597            #[test]
1598            fn [<test_squared_norm_ $suffix>]() {
1599                $crate::vector::tests::test_squared_norm::<$V>();
1600            }
1601            #[test]
1602            fn [<test_fill_ $suffix>]() {
1603                $crate::vector::tests::test_fill::<$V>();
1604            }
1605            #[test]
1606            fn [<test_from_element_ $suffix>]() {
1607                $crate::vector::tests::test_from_element::<$V>();
1608            }
1609            #[test]
1610            fn [<test_copy_from_ $suffix>]() {
1611                $crate::vector::tests::test_copy_from::<$V>();
1612            }
1613            #[test]
1614            fn [<test_from_vec_ $suffix>]() {
1615                $crate::vector::tests::test_from_vec::<$V>();
1616            }
1617            #[test]
1618            fn [<test_component_mul_assign_ $suffix>]() {
1619                $crate::vector::tests::test_component_mul_assign::<$V>();
1620            }
1621            #[test]
1622            fn [<test_component_div_assign_ $suffix>]() {
1623                $crate::vector::tests::test_component_div_assign::<$V>();
1624            }
1625            #[test]
1626            fn [<test_assign_at_indices_ $suffix>]() {
1627                $crate::vector::tests::test_assign_at_indices::<$V>();
1628            }
1629            #[test]
1630            fn [<test_add_ $suffix>]() {
1631                $crate::vector::tests::test_add::<$V>();
1632            }
1633            #[test]
1634            fn [<test_sub_ $suffix>]() {
1635                $crate::vector::tests::test_sub::<$V>();
1636            }
1637            #[test]
1638            fn [<test_add_assign_ $suffix>]() {
1639                $crate::vector::tests::test_add_assign::<$V>();
1640            }
1641            #[test]
1642            fn [<test_sub_assign_ $suffix>]() {
1643                $crate::vector::tests::test_sub_assign::<$V>();
1644            }
1645            #[test]
1646            fn [<test_axpy_v_ $suffix>]() {
1647                $crate::vector::tests::test_axpy_v::<$V>();
1648            }
1649            #[test]
1650            fn [<test_mul_assign_scalar_ $suffix>]() {
1651                $crate::vector::tests::test_mul_assign_scalar::<$V>();
1652            }
1653            #[test]
1654            fn [<test_copy_from_view_ $suffix>]() {
1655                $crate::vector::tests::test_copy_from_view::<$V>();
1656            }
1657            #[test]
1658            fn [<test_view_squared_norm_ $suffix>]() {
1659                $crate::vector::tests::test_view_squared_norm::<$V>();
1660            }
1661            #[test]
1662            fn [<test_view_into_owned_ $suffix>]() {
1663                $crate::vector::tests::test_view_into_owned::<$V>();
1664            }
1665            #[test]
1666            fn [<test_view_get_index_ $suffix>]() {
1667                $crate::vector::tests::test_view_get_index::<$V>();
1668            }
1669            #[test]
1670            fn [<test_view_mut_axpy_ $suffix>]() {
1671                $crate::vector::tests::test_view_mut_axpy::<$V>();
1672            }
1673            #[test]
1674            fn [<test_view_mut_set_index_ $suffix>]() {
1675                $crate::vector::tests::test_view_mut_set_index::<$V>();
1676            }
1677            #[test]
1678            fn [<test_view_mut_mul_assign_scalar_ $suffix>]() {
1679                $crate::vector::tests::test_view_mut_mul_assign_scalar::<$V>();
1680            }
1681            #[test]
1682            fn [<test_view_mut_copy_from_view_ $suffix>]() {
1683                $crate::vector::tests::test_view_mut_copy_from_view::<$V>();
1684            }
1685        }
1686    };
1687}
1688
1689#[cfg(test)]
1690#[cfg_attr(not(feature = "cuda"), allow(unused_macros))]
1691macro_rules! generate_vector_tests_batched {
1692    ($suffix:ident, $V:ty, $ctx2:expr, $ctx3:expr) => {
1693        paste::paste! {
1694            #[test]
1695            fn [<test_batched_len_and_total_len_ $suffix>]() {
1696                $crate::vector::tests::test_batched_len_and_total_len::<$V>($ctx3);
1697            }
1698            #[test]
1699            fn [<test_batched_from_vec_ $suffix>]() {
1700                $crate::vector::tests::test_batched_from_vec::<$V>($ctx2);
1701            }
1702            #[test]
1703            #[should_panic(expected = "divisible by nbatch")]
1704            fn [<test_batched_from_vec_bad_length_ $suffix>]() {
1705                $crate::vector::tests::test_batched_from_vec_bad_length::<$V>($ctx2);
1706            }
1707            #[test]
1708            fn [<test_batched_from_element_ $suffix>]() {
1709                $crate::vector::tests::test_batched_from_element::<$V>($ctx3);
1710            }
1711            #[test]
1712            fn [<test_batched_axpy_ $suffix>]() {
1713                $crate::vector::tests::test_batched_axpy::<$V>($ctx2);
1714            }
1715            #[test]
1716            fn [<test_batched_add_ $suffix>]() {
1717                $crate::vector::tests::test_batched_add::<$V>($ctx2);
1718            }
1719            #[test]
1720            fn [<test_batched_norm_max_across_batches_ $suffix>]() {
1721                $crate::vector::tests::test_batched_norm_max_across_batches::<$V>($ctx2);
1722            }
1723            #[test]
1724            fn [<test_batched_norm_l1_ $suffix>]() {
1725                $crate::vector::tests::test_batched_norm_l1::<$V>($ctx2);
1726            }
1727            #[test]
1728            fn [<test_batched_squared_norm_ $suffix>]() {
1729                $crate::vector::tests::test_batched_squared_norm::<$V>($ctx2);
1730            }
1731            #[test]
1732            fn [<test_batched_set_index_ $suffix>]() {
1733                $crate::vector::tests::test_batched_set_index::<$V>($ctx3);
1734            }
1735            #[test]
1736            #[should_panic(expected = "not supported for batched")]
1737            fn [<test_batched_get_index_panics_ $suffix>]() {
1738                $crate::vector::tests::test_batched_get_index_panics::<$V>($ctx2);
1739            }
1740            #[test]
1741            fn [<test_batched_fill_ $suffix>]() {
1742                $crate::vector::tests::test_batched_fill::<$V>($ctx2);
1743            }
1744            #[test]
1745            fn [<test_batched_component_mul_ $suffix>]() {
1746                $crate::vector::tests::test_batched_component_mul::<$V>($ctx2);
1747            }
1748            #[test]
1749            fn [<test_batched_assign_at_indices_ $suffix>]() {
1750                $crate::vector::tests::test_batched_assign_at_indices::<$V>($ctx2);
1751            }
1752            #[test]
1753            fn [<test_batched_root_finding_consistent_ $suffix>]() {
1754                $crate::vector::tests::test_batched_root_finding_consistent::<$V>($ctx2);
1755            }
1756            #[test]
1757            #[should_panic(expected = "differ across batches")]
1758            fn [<test_batched_root_finding_inconsistent_ $suffix>]() {
1759                $crate::vector::tests::test_batched_root_finding_inconsistent::<$V>($ctx2);
1760            }
1761            #[test]
1762            fn [<test_batched_axpy_broadcast_ $suffix>]() {
1763                $crate::vector::tests::test_batched_axpy_broadcast::<$V>($ctx2);
1764            }
1765            #[test]
1766            fn [<test_batched_copy_from_broadcast_ $suffix>]() {
1767                $crate::vector::tests::test_batched_copy_from_broadcast::<$V>($ctx2);
1768            }
1769            #[test]
1770            fn [<test_batched_component_div_ $suffix>]() {
1771                $crate::vector::tests::test_batched_component_div::<$V>($ctx2);
1772            }
1773            #[test]
1774            fn [<test_batched_component_mul_broadcast_ $suffix>]() {
1775                $crate::vector::tests::test_batched_component_mul_broadcast::<$V>($ctx2);
1776            }
1777            #[test]
1778            fn [<test_batched_component_div_broadcast_ $suffix>]() {
1779                $crate::vector::tests::test_batched_component_div_broadcast::<$V>($ctx2);
1780            }
1781            #[test]
1782            fn [<test_batched_add_assign_broadcast_ $suffix>]() {
1783                $crate::vector::tests::test_batched_add_assign_broadcast::<$V>($ctx2);
1784            }
1785            #[test]
1786            fn [<test_batched_sub_assign_broadcast_ $suffix>]() {
1787                $crate::vector::tests::test_batched_sub_assign_broadcast::<$V>($ctx2);
1788            }
1789            #[test]
1790            fn [<test_batched_sub_ $suffix>]() {
1791                $crate::vector::tests::test_batched_sub::<$V>($ctx2);
1792            }
1793            #[test]
1794            fn [<test_batched_sub_assign_ $suffix>]() {
1795                $crate::vector::tests::test_batched_sub_assign::<$V>($ctx2);
1796            }
1797            #[test]
1798            fn [<test_batched_from_slice_ $suffix>]() {
1799                $crate::vector::tests::test_batched_from_slice::<$V>($ctx2);
1800            }
1801            #[test]
1802            fn [<test_batched_mul_scalar_ $suffix>]() {
1803                $crate::vector::tests::test_batched_mul_scalar::<$V>($ctx2);
1804            }
1805            #[test]
1806            fn [<test_batched_div_scalar_ $suffix>]() {
1807                $crate::vector::tests::test_batched_div_scalar::<$V>($ctx2);
1808            }
1809            #[test]
1810            fn [<test_batched_copy_from_indices_ $suffix>]() {
1811                $crate::vector::tests::test_batched_copy_from_indices::<$V>($ctx2);
1812            }
1813            #[test]
1814            fn [<test_batched_gather_ $suffix>]() {
1815                $crate::vector::tests::test_batched_gather::<$V>($ctx2);
1816            }
1817            #[test]
1818            fn [<test_batched_scatter_ $suffix>]() {
1819                $crate::vector::tests::test_batched_scatter::<$V>($ctx2);
1820            }
1821            #[test]
1822            fn [<test_batched_get_batch_ $suffix>]() {
1823                $crate::vector::tests::test_batched_get_batch::<$V>($ctx2);
1824            }
1825            #[test]
1826            fn [<test_batched_get_batch_mut_ $suffix>]() {
1827                $crate::vector::tests::test_batched_get_batch_mut::<$V>($ctx2);
1828            }
1829            #[test]
1830            fn [<test_batched_axpy_v_ $suffix>]() {
1831                $crate::vector::tests::test_batched_axpy_v::<$V>($ctx2);
1832            }
1833            #[test]
1834            fn [<test_batched_mul_assign_scalar_ $suffix>]() {
1835                $crate::vector::tests::test_batched_mul_assign_scalar::<$V>($ctx2);
1836            }
1837            #[test]
1838            fn [<test_batched_copy_from_view_ $suffix>]() {
1839                $crate::vector::tests::test_batched_copy_from_view::<$V>($ctx2);
1840            }
1841            #[test]
1842            fn [<test_batched_axpy_new_ $suffix>]() {
1843                $crate::vector::tests::test_batched_axpy_new::<$V>($ctx2);
1844            }
1845            #[test]
1846            fn [<test_batched_axpy_new_broadcast_ $suffix>]() {
1847                $crate::vector::tests::test_batched_axpy_new_broadcast::<$V>($ctx2);
1848            }
1849            #[test]
1850            #[should_panic(expected = "alpha.len() must equal")]
1851            fn [<test_batched_axpy_new_bad_length_ $suffix>]() {
1852                $crate::vector::tests::test_batched_axpy_new_bad_length::<$V>($ctx2);
1853            }
1854            #[test]
1855            #[should_panic(expected = "incompatible nbatch")]
1856            fn [<test_batched_axpy_incompatible_ $suffix>]() {
1857                $crate::vector::tests::test_batched_axpy_incompatible::<$V>($ctx2, $ctx3);
1858            }
1859            #[test]
1860            #[should_panic(expected = "incompatible nbatch")]
1861            fn [<test_batched_copy_from_incompatible_ $suffix>]() {
1862                $crate::vector::tests::test_batched_copy_from_incompatible::<$V>($ctx2, $ctx3);
1863            }
1864            #[test]
1865            #[should_panic(expected = "incompatible nbatch")]
1866            fn [<test_batched_add_assign_incompatible_ $suffix>]() {
1867                $crate::vector::tests::test_batched_add_assign_incompatible::<$V>($ctx2, $ctx3);
1868            }
1869            #[test]
1870            #[should_panic(expected = "incompatible nbatch")]
1871            fn [<test_batched_component_mul_incompatible_ $suffix>]() {
1872                $crate::vector::tests::test_batched_component_mul_incompatible::<$V>($ctx2, $ctx3);
1873            }
1874
1875            // --- Strided view tests (batched, using $ctx2) ---
1876            #[test]
1877            fn [<test_strided_view_set_index_ $suffix>]() {
1878                $crate::vector::tests::test_strided_view_set_index::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1879            }
1880            #[test]
1881            fn [<test_strided_view_mut_copy_from_ $suffix>]() {
1882                $crate::vector::tests::test_strided_view_mut_copy_from::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1883            }
1884            #[test]
1885            fn [<test_strided_view_mut_axpy_ $suffix>]() {
1886                $crate::vector::tests::test_strided_view_mut_axpy::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1887            }
1888            #[test]
1889            fn [<test_strided_view_mut_mul_assign_scalar_ $suffix>]() {
1890                $crate::vector::tests::test_strided_view_mut_mul_assign_scalar::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1891            }
1892            #[test]
1893            fn [<test_strided_view_mut_add_assign_ $suffix>]() {
1894                $crate::vector::tests::test_strided_view_mut_add_assign::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1895            }
1896            #[test]
1897            fn [<test_strided_view_mut_sub_assign_ $suffix>]() {
1898                $crate::vector::tests::test_strided_view_mut_sub_assign::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1899            }
1900            #[test]
1901            fn [<test_strided_view_add_assign_broadcast_ $suffix>]() {
1902                $crate::vector::tests::test_strided_view_add_assign_broadcast::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1903            }
1904            #[test]
1905            fn [<test_strided_view_add_owned_ $suffix>]() {
1906                $crate::vector::tests::test_strided_view_add_owned::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1907            }
1908            #[test]
1909            fn [<test_strided_view_squared_norm_ $suffix>]() {
1910                $crate::vector::tests::test_strided_view_squared_norm::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1911            }
1912            #[test]
1913            fn [<test_strided_view_into_owned_ $suffix>]() {
1914                $crate::vector::tests::test_strided_view_into_owned::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1915            }
1916            #[test]
1917            fn [<test_strided_view_component_mul_ $suffix>]() {
1918                $crate::vector::tests::test_strided_view_component_mul::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1919            }
1920            #[test]
1921            fn [<test_strided_view_component_div_ $suffix>]() {
1922                $crate::vector::tests::test_strided_view_component_div::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1923            }
1924            #[test]
1925            fn [<test_strided_view_mul_scalar_ $suffix>]() {
1926                $crate::vector::tests::test_strided_view_mul_scalar::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1927            }
1928            #[test]
1929            fn [<test_strided_view_fill_ $suffix>]() {
1930                $crate::vector::tests::test_strided_view_fill::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1931            }
1932            #[test]
1933            fn [<test_strided_view_assign_at_indices_ $suffix>]() {
1934                $crate::vector::tests::test_strided_view_assign_at_indices::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1935            }
1936            #[test]
1937            fn [<test_strided_view_copy_from_indices_ $suffix>]() {
1938                $crate::vector::tests::test_strided_view_copy_from_indices::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1939            }
1940            #[test]
1941            fn [<test_strided_view_gather_ $suffix>]() {
1942                $crate::vector::tests::test_strided_view_gather::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1943            }
1944            #[test]
1945            fn [<test_strided_view_scatter_ $suffix>]() {
1946                $crate::vector::tests::test_strided_view_scatter::<<$V as $crate::DefaultDenseMatrix>::M>($ctx2);
1947            }
1948        }
1949    };
1950}
1951#[cfg(test)]
1952#[cfg_attr(not(feature = "cuda"), allow(unused_imports))]
1953pub(crate) use generate_vector_tests_batched;
1954#[cfg(test)]
1955pub(crate) use generate_vector_tests_nonbatched;