Merge branch 'dimforge:dev' into dev

This commit is contained in:
Lishen1 2024-05-04 19:35:48 +03:00 committed by GitHub
commit 02d66f28d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 786 additions and 519 deletions

View File

@ -49,8 +49,6 @@ jobs:
build-nalgebra-all-features:
runs-on: ubuntu-latest
steps:
# Needed because the --all-features build which enables cuda support.
- uses: Jimver/cuda-toolkit@v0.2.8
- uses: actions/checkout@v2
- run: cargo build --all-features;
- run: cargo build -p nalgebra-glm --all-features;
@ -120,23 +118,6 @@ jobs:
run: xargo build --verbose --no-default-features -p nalgebra-glm --target=x86_64-unknown-linux-gnu;
- name: build thumbv7em-none-eabihf nalgebra-glm
run: xargo build --verbose --no-default-features -p nalgebra-glm --target=thumbv7em-none-eabihf;
build-cuda:
runs-on: ubuntu-latest
steps:
- uses: Jimver/cuda-toolkit@v0.2.8
with:
cuda: '11.5.0'
- name: Install nightly-2021-12-04
uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2021-12-04
override: true
- uses: actions/checkout@v2
- run: rustup target add nvptx64-nvidia-cuda
- run: cargo build --no-default-features --features cuda
- run: cargo build --no-default-features --features cuda --target=nvptx64-nvidia-cuda
env:
CUDA_ARCH: "350"
docs:
runs-on: ubuntu-latest
steps:

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ path = "src/lib.rs"
[features]
default = [ "std", "macros" ]
std = [ "matrixmultiply", "simba/std" ]
std = [ "matrixmultiply", "num-traits/std", "num-complex/std", "num-rational/std", "approx/std", "simba/std" ]
sparse = [ ]
debug = [ "approx/num-complex", "rand" ]
alloc = [ ]
@ -32,7 +32,6 @@ compare = [ "matrixcompare-core" ]
libm = [ "simba/libm" ]
libm-force = [ "simba/libm_force" ]
macros = [ "nalgebra-macros" ]
cuda = [ "cust_core", "simba/cuda" ]
# Conversion
@ -49,6 +48,7 @@ convert-glam021 = [ "glam021" ]
convert-glam022 = [ "glam022" ]
convert-glam023 = [ "glam023" ]
convert-glam024 = [ "glam024" ]
convert-glam025 = [ "glam025" ]
# Serialization
## To use serde in a #[no-std] environment, enable the
@ -104,7 +104,7 @@ glam021 = { package = "glam", version = "0.21", optional = true }
glam022 = { package = "glam", version = "0.22", optional = true }
glam023 = { package = "glam", version = "0.23", optional = true }
glam024 = { package = "glam", version = "0.24", optional = true }
cust_core = { version = "0.1", optional = true }
glam025 = { package = "glam", version = "0.25", optional = true }
rayon = { version = "1.6", optional = true }
[dev-dependencies]

View File

@ -28,7 +28,7 @@ where
}
/// Reflects a 3D vector wrt. the 3D plane with normal `plane_normal`.
/// /!\ This is an exact replicate of `reflect_wrt_hyperplane2, but for 3D.
/// /!\ This is an exact replicate of `reflect_wrt_hyperplane2`, but for 3D.
fn reflect_wrt_hyperplane3<T>(plane_normal: &Unit<Vector3<T>>, vector: &Vector3<T>) -> Vector3<T>
where
T: RealField,

View File

@ -21,7 +21,6 @@ default = [ "std" ]
std = [ "nalgebra/std", "simba/std" ]
arbitrary = [ "nalgebra/arbitrary" ]
serde-serialize = [ "nalgebra/serde-serialize-no-std" ]
cuda = [ "nalgebra/cuda" ]
# Conversion
convert-mint = [ "nalgebra/mint" ]

View File

@ -1,4 +1,4 @@
use na::{self, Unit};
use na::Unit;
use crate::aliases::Qua;
use crate::RealNumber;

View File

@ -41,7 +41,10 @@ pub fn is_comp_null<T: Number, const D: usize>(v: &TVec<T, D>, epsilon: T) -> TV
/// Returns `true` if `v` has a magnitude of 1 (up to an epsilon).
pub fn is_normalized<T: RealNumber, const D: usize>(v: &TVec<T, D>, epsilon: T) -> bool {
abs_diff_eq!(v.norm_squared(), T::one(), epsilon = epsilon * epsilon)
// sqrt(1 + epsilon_{norm²} = 1 + epsilon_{norm}
// ==> epsilon_{norm²} = epsilon_{norm}² + 2*epsilon_{norm}
// For small epsilon, epsilon² is basically zero, so use 2*epsilon.
abs_diff_eq!(v.norm_squared(), T::one(), epsilon = epsilon + epsilon)
}
/// Returns `true` if `v` is zero (up to an epsilon).

View File

@ -1,7 +1,6 @@
use approx::AbsDiffEq;
use num::{Bounded, Signed};
use core::cmp::PartialOrd;
use na::Scalar;
use simba::scalar::{ClosedAdd, ClosedMul, ClosedSub, RealField};

View File

@ -141,7 +141,7 @@ pub fn less_than<T: Number, const D: usize>(x: &TVec<T, D>, y: &TVec<T, D>) -> T
x.zip_map(y, |x, y| x < y)
}
/// Component-wise `>=` comparison.
/// Component-wise `<=` comparison.
///
/// # Examples:
///

View File

@ -1,4 +1,3 @@
use std::mem::replace;
use std::ops::Range;
use num_traits::One;
@ -369,7 +368,7 @@ where
if let Some(minor_indices) = lane {
let count = minor_indices.len();
let remaining = replace(&mut self.remaining_values, &mut []);
let remaining = std::mem::take(&mut self.remaining_values);
let (values_in_lane, remaining) = remaining.split_at_mut(count);
self.remaining_values = remaining;
self.current_lane_idx += 1;
@ -578,7 +577,7 @@ where
} else if sort {
unreachable!("Internal error: Sorting currently not supported if no values are present.");
}
if major_offsets.len() == 0 {
if major_offsets.is_empty() {
return Err(SparseFormatError::from_kind_and_msg(
SparseFormatErrorKind::InvalidStructure,
"Number of offsets should be greater than 0.",
@ -624,12 +623,12 @@ where
));
}
let minor_idx_in_lane = minor_indices.get(range_start..range_end).ok_or(
let minor_idx_in_lane = minor_indices.get(range_start..range_end).ok_or_else(|| {
SparseFormatError::from_kind_and_msg(
SparseFormatErrorKind::IndexOutOfBounds,
"A major offset is out of bounds.",
),
)?;
)
})?;
// We test for in-bounds, uniqueness and monotonicity at the same time
// to ensure that we only visit each minor index once

View File

@ -625,6 +625,15 @@ pub struct CscTripletIter<'a, T> {
values_iter: Iter<'a, T>,
}
impl<'a, T> Clone for CscTripletIter<'a, T> {
fn clone(&self) -> Self {
CscTripletIter {
pattern_iter: self.pattern_iter.clone(),
values_iter: self.values_iter.clone(),
}
}
}
impl<'a, T: Clone> CscTripletIter<'a, T> {
/// Adapts the triplet iterator to return owned values.
///

View File

@ -626,6 +626,15 @@ pub struct CsrTripletIter<'a, T> {
values_iter: Iter<'a, T>,
}
impl<'a, T> Clone for CsrTripletIter<'a, T> {
fn clone(&self) -> Self {
CsrTripletIter {
pattern_iter: self.pattern_iter.clone(),
values_iter: self.values_iter.clone(),
}
}
}
impl<'a, T: Clone> CsrTripletIter<'a, T> {
/// Adapts the triplet iterator to return owned values.
///

View File

@ -59,7 +59,7 @@ macro_rules! impl_sp_plus_minus {
let mut result = $matrix_type::try_from_pattern_and_values(pattern, values)
.unwrap();
$spadd_fn(T::zero(), &mut result, T::one(), Op::NoOp(&a)).unwrap();
$spadd_fn(T::one(), &mut result, $factor * T::one(), Op::NoOp(&b)).unwrap();
$spadd_fn(T::one(), &mut result, $factor, Op::NoOp(&b)).unwrap();
result
});

View File

@ -125,18 +125,22 @@ fn iterate_union<'a>(
) -> impl Iterator<Item = usize> + 'a {
iter::from_fn(move || {
if let (Some(a_item), Some(b_item)) = (sorted_a.first(), sorted_b.first()) {
let item = if a_item < b_item {
sorted_a = &sorted_a[1..];
a_item
} else if b_item < a_item {
sorted_b = &sorted_b[1..];
b_item
} else {
// Both lists contain the same element, advance both slices to avoid
// duplicate entries in the result
sorted_a = &sorted_a[1..];
sorted_b = &sorted_b[1..];
a_item
let item = match a_item.cmp(b_item) {
std::cmp::Ordering::Less => {
sorted_a = &sorted_a[1..];
a_item
}
std::cmp::Ordering::Greater => {
sorted_b = &sorted_b[1..];
b_item
}
std::cmp::Ordering::Equal => {
// Both lists contain the same element, advance both slices to avoid
// duplicate entries in the result
sorted_a = &sorted_a[1..];
sorted_b = &sorted_b[1..];
a_item
}
};
Some(*item)
} else if let Some(a_item) = sorted_a.first() {

View File

@ -80,7 +80,7 @@ impl SparsityPattern {
#[inline]
#[must_use]
pub fn major_dim(&self) -> usize {
assert!(self.major_offsets.len() > 0);
assert!(!self.major_offsets.is_empty());
self.major_offsets.len() - 1
}
@ -162,7 +162,7 @@ impl SparsityPattern {
// We test for in-bounds, uniqueness and monotonicity at the same time
// to ensure that we only visit each minor index once
let mut iter = minor_indices.iter();
let mut prev = None;
let mut prev: Option<usize> = None;
while let Some(next) = iter.next().copied() {
if next >= minor_dim {
@ -170,10 +170,10 @@ impl SparsityPattern {
}
if let Some(prev) = prev {
if prev > next {
return Err(NonmonotonicMinorIndices);
} else if prev == next {
return Err(DuplicateEntry);
match prev.cmp(&next) {
std::cmp::Ordering::Greater => return Err(NonmonotonicMinorIndices),
std::cmp::Ordering::Equal => return Err(DuplicateEntry),
std::cmp::Ordering::Less => {}
}
}
prev = Some(next);
@ -195,6 +195,14 @@ impl SparsityPattern {
///
/// Panics if the number of major offsets is not exactly one greater than the major dimension
/// or if major offsets do not start with 0 and end with the number of minor indices.
///
/// # Safety
///
/// Assumes that the major offsets and indices adhere to the requirements of being a valid
/// sparsity pattern.
/// Specifically, that major offsets is monotonically increasing, and
/// `major_offsets[i]..major_offsets[i+1]` refers to a major lane in the sparsity pattern,
/// and `minor_indices[major_offsets[i]..major_offsets[i+1]]` is monotonically increasing.
pub unsafe fn from_offset_and_indices_unchecked(
major_dim: usize,
minor_dim: usize,

View File

@ -42,7 +42,6 @@ use std::mem;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub struct ArrayStorage<T, const R: usize, const C: usize>(pub [[T; R]; C]);
impl<T, const R: usize, const C: usize> ArrayStorage<T, R, C> {

View File

@ -97,6 +97,8 @@ macro_rules! impl_constructors(
}
/// Creates, without bound checking, a new matrix view from the given data array.
/// # Safety
/// `data[start..start+rstride * cstride]` must be within bounds.
#[inline]
pub unsafe fn from_slice_unchecked(data: &'a [T], start: usize, $($args: usize),*) -> Self {
Self::from_slice_generic_unchecked(data, start, $($gargs),*)
@ -113,6 +115,11 @@ macro_rules! impl_constructors(
}
/// Creates, without bound checking, a new matrix view with the specified strides from the given data array.
///
/// # Safety
///
/// `start`, `rstride`, and `cstride`, with the given matrix size will not index
/// outside of `data`.
#[inline]
pub unsafe fn from_slice_with_strides_unchecked(data: &'a [T], start: usize, $($args: usize,)* rstride: usize, cstride: usize) -> Self {
Self::from_slice_with_strides_generic_unchecked(data, start, $($gargs,)* Dyn(rstride), Dyn(cstride))
@ -257,6 +264,10 @@ macro_rules! impl_constructors_mut(
}
/// Creates, without bound checking, a new mutable matrix view from the given data array.
///
/// # Safety
///
/// `data[start..start+(R * C)]` must be within bounds.
#[inline]
pub unsafe fn from_slice_unchecked(data: &'a mut [T], start: usize, $($args: usize),*) -> Self {
Self::from_slice_generic_unchecked(data, start, $($gargs),*)
@ -274,6 +285,8 @@ macro_rules! impl_constructors_mut(
}
/// Creates, without bound checking, a new mutable matrix view with the specified strides from the given data array.
/// # Safety
/// `data[start..start+rstride * cstride]` must be within bounds.
#[inline]
pub unsafe fn from_slice_with_strides_unchecked(data: &'a mut [T], start: usize, $($args: usize,)* rstride: usize, cstride: usize) -> Self {
Self::from_slice_with_strides_generic_unchecked(

View File

@ -23,7 +23,6 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
feature = "rkyv-serialize",
archive_attr(derive(bytecheck::CheckBytes))
)]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub struct Dyn(pub usize);
#[deprecated(note = "use Dyn instead.")]
@ -68,6 +67,10 @@ impl IsNotStaticOne for Dyn {}
/// Trait implemented by any type that can be used as a dimension. This includes type-level
/// integers and `Dyn` (for dimensions not known at compile-time).
///
/// # Safety
///
/// Hoists integers to the type level, including binary operations.
pub unsafe trait Dim: Any + Debug + Copy + PartialEq + Send + Sync {
#[inline(always)]
fn is<D: Dim>() -> bool {
@ -216,7 +219,6 @@ dim_ops!(
archive(as = "Self")
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub struct Const<const R: usize>;
/// Trait implemented exclusively by type-level integers.

View File

@ -519,6 +519,10 @@ impl<T, R: Dim, C: Dim, S: RawStorage<T, R, C>> Matrix<T, R, C, S> {
/// Produces a view of the data at the given index, without doing
/// any bounds checking.
///
/// # Safety
///
/// `index` must within bounds of the array.
#[inline]
#[must_use]
pub unsafe fn get_unchecked<'a, I>(&'a self, index: I) -> I::Output
@ -530,6 +534,9 @@ impl<T, R: Dim, C: Dim, S: RawStorage<T, R, C>> Matrix<T, R, C, S> {
/// Returns a mutable view of the data at the given index, without doing
/// any bounds checking.
/// # Safety
///
/// `index` must within bounds of the array.
#[inline]
#[must_use]
pub unsafe fn get_unchecked_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut

View File

@ -171,7 +171,6 @@ pub type MatrixCross<T, R1, C1, R2, C2> =
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub struct Matrix<T, R, C, S> {
/// The data storage that contains all the matrix components. Disappointed?
///
@ -313,6 +312,10 @@ where
impl<T, R, C, S> Matrix<T, R, C, S> {
/// Creates a new matrix with the given data without statically checking that the matrix
/// dimension matches the storage dimension.
///
/// # Safety
///
/// The storage dimension must match the given dimensions.
#[inline(always)]
pub const unsafe fn from_data_statically_unchecked(data: S) -> Matrix<T, R, C, S> {
Matrix {
@ -1194,6 +1197,10 @@ impl<T, R: Dim, C: Dim, S: RawStorageMut<T, R, C>> Matrix<T, R, C, S> {
}
/// Swaps two entries without bound-checking.
///
/// # Safety
///
/// Both `(r, c)` must have `r < nrows(), c < ncols()`.
#[inline]
pub unsafe fn swap_unchecked(&mut self, row_cols1: (usize, usize), row_cols2: (usize, usize)) {
debug_assert!(row_cols1.0 < self.nrows() && row_cols1.1 < self.ncols());
@ -1300,6 +1307,8 @@ impl<T, R: Dim, C: Dim, S: RawStorageMut<T, R, C>> Matrix<T, R, C, S> {
impl<T, D: Dim, S: RawStorage<T, D>> Vector<T, D, S> {
/// Gets a reference to the i-th element of this column vector without bound checking.
/// # Safety
/// `i` must be less than `D`.
#[inline]
#[must_use]
pub unsafe fn vget_unchecked(&self, i: usize) -> &T {
@ -1311,6 +1320,8 @@ impl<T, D: Dim, S: RawStorage<T, D>> Vector<T, D, S> {
impl<T, D: Dim, S: RawStorageMut<T, D>> Vector<T, D, S> {
/// Gets a mutable reference to the i-th element of this column vector without bound checking.
/// # Safety
/// `i` must be less than `D`.
#[inline]
#[must_use]
pub unsafe fn vget_unchecked_mut(&mut self, i: usize) -> &mut T {

View File

@ -43,6 +43,10 @@ macro_rules! view_storage_impl (
impl<'a, T, R: Dim, C: Dim, RStride: Dim, CStride: Dim> $T<'a, T, R, C, RStride, CStride> {
/// Create a new matrix view without bounds checking and from a raw pointer.
///
/// # Safety
///
/// `*ptr` must point to memory that is valid `[T; R * C]`.
#[inline]
pub unsafe fn from_raw_parts(ptr: $Ptr,
shape: (R, C),
@ -63,6 +67,11 @@ macro_rules! view_storage_impl (
// Dyn is arbitrary. It's just to be able to call the constructors with `Slice::`
impl<'a, T, R: Dim, C: Dim> $T<'a, T, R, C, Dyn, Dyn> {
/// Create a new matrix view without bounds checking.
///
/// # Safety
///
/// `storage` contains sufficient elements beyond `start + R * C` such that all
/// accesses are within bounds.
#[inline]
pub unsafe fn new_unchecked<RStor, CStor, S>(storage: $SRef, start: (usize, usize), shape: (R, C))
-> $T<'a, T, R, C, S::RStride, S::CStride>
@ -75,6 +84,10 @@ macro_rules! view_storage_impl (
}
/// Create a new matrix view without bounds checking.
///
/// # Safety
///
/// `strides` must be a valid stride indexing.
#[inline]
pub unsafe fn new_with_strides_unchecked<S, RStor, CStor, RStride, CStride>(storage: $SRef,
start: (usize, usize),
@ -128,12 +141,7 @@ impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Clone
{
#[inline]
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
shape: self.shape,
strides: self.strides,
_phantoms: PhantomData,
}
*self
}
}
@ -538,8 +546,8 @@ macro_rules! matrix_view_impl (
$me.$generic_view_with_steps(start, shape, steps)
}
/// Slices this matrix starting at its component `(irow, icol)` and with `(R::dim(),
/// CView::dim())` consecutive components.
/// Slices this matrix starting at its component `(irow, icol)` and with `(RVIEW, CVIEW)`
/// consecutive components.
#[inline]
#[deprecated = slice_deprecation_note!($fixed_view)]
pub fn $fixed_slice<const RVIEW: usize, const CVIEW: usize>($me: $Me, irow: usize, icol: usize)
@ -547,8 +555,8 @@ macro_rules! matrix_view_impl (
$me.$fixed_view(irow, icol)
}
/// Return a view of this matrix starting at its component `(irow, icol)` and with `(R::dim(),
/// CView::dim())` consecutive components.
/// Return a view of this matrix starting at its component `(irow, icol)` and with
/// `(RVIEW, CVIEW)` consecutive components.
#[inline]
pub fn $fixed_view<const RVIEW: usize, const CVIEW: usize>($me: $Me, irow: usize, icol: usize)
-> $MatrixView<'_, T, Const<RVIEW>, Const<CVIEW>, S::RStride, S::CStride> {

View File

@ -336,7 +336,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// Sets the magnitude of this vector unless it is smaller than `min_magnitude`.
///
/// If `self.magnitude()` is smaller than `min_magnitude`, it will be left unchanged.
/// Otherwise this is equivalent to: `*self = self.normalize() * magnitude.
/// Otherwise this is equivalent to: `*self = self.normalize() * magnitude`.
#[inline]
pub fn try_set_magnitude(&mut self, magnitude: T::RealField, min_magnitude: T::RealField)
where
@ -525,7 +525,7 @@ where
let (elt, basis) = vs[..i + 1].split_last_mut().unwrap();
for basis_element in &basis[..nbasis_elements] {
*elt -= &*basis_element * elt.dot(basis_element)
*elt -= basis_element * elt.dot(basis_element)
}
}

View File

@ -339,7 +339,7 @@ impl<T: Scalar, R: Dim, C: Dim, S: RawStorage<T, R, C>> Matrix<T, R, C, S> {
let mean = self.mean();
self.iter().cloned().fold(T::zero(), |acc, x| {
acc + (x.clone() - mean.clone()) * (x.clone() - mean.clone())
acc + (x.clone() - mean.clone()) * (x - mean.clone())
}) / n_elements
}
}

View File

@ -32,6 +32,8 @@ pub type CStride<T, R, C = U1> =
/// The trait shared by all matrix data storage.
///
/// TODO: doc
/// # Safety
///
/// In generic code, it is recommended use the `Storage` trait bound instead. The `RawStorage`
/// trait bound is generally used by code that needs to work with storages that contains
/// `MaybeUninit<T>` elements.
@ -57,7 +59,7 @@ pub unsafe trait RawStorage<T, R: Dim, C: Dim = U1>: Sized {
/// The spacing between consecutive row elements and consecutive column elements.
///
/// For example this returns `(1, 5)` for a row-major matrix with 5 columns.
/// For example this returns `(1, 5)` for a column-major matrix with 5 columns.
fn strides(&self) -> (Self::RStride, Self::CStride);
/// Compute the index corresponding to the irow-th row and icol-th column of this matrix. The
@ -129,6 +131,14 @@ pub unsafe trait RawStorage<T, R: Dim, C: Dim = U1>: Sized {
}
/// Trait shared by all matrix data storage that dont contain any uninitialized elements.
///
/// # Safety
///
/// Note that `Self` must always have a number of elements compatible with the matrix length (given
/// by `R` and `C` if they are known at compile-time). For example, implementors of this trait
/// should **not** allow the user to modify the size of the underlying buffer with safe methods
/// (for example the `VecStorage::data_mut` method is unsafe because the user could change the
/// vector's size so that it no longer contains enough elements: this will lead to UB.
pub unsafe trait Storage<T, R: Dim, C: Dim = U1>: RawStorage<T, R, C> {
/// Builds a matrix data storage that does not contain any reference.
fn into_owned(self) -> Owned<T, R, C>
@ -143,6 +153,8 @@ pub unsafe trait Storage<T, R: Dim, C: Dim = U1>: RawStorage<T, R, C> {
/// Trait implemented by matrix data storage that can provide a mutable access to its elements.
///
/// # Safety
///
/// In generic code, it is recommended use the `StorageMut` trait bound instead. The
/// `RawStorageMut` trait bound is generally used by code that needs to work with storages that
/// contains `MaybeUninit<T>` elements.
@ -194,10 +206,28 @@ pub unsafe trait RawStorageMut<T, R: Dim, C: Dim = U1>: RawStorage<T, R, C> {
///
/// # Safety
/// If the indices are out of bounds, the method will cause undefined behavior.
///
/// # Validity
/// The default implementation of this trait function is only guaranteed to be
/// sound if invocations of `self.ptr_mut()` and `self.get_address_unchecked_linear_mut()`
/// result in stable references. If any of the data pointed to by these trait methods
/// moves as a consequence of invoking either of these methods then this default
/// trait implementation may be invalid or unsound and should be overridden.
#[inline]
unsafe fn swap_unchecked_linear(&mut self, i1: usize, i2: usize) {
let a = self.get_address_unchecked_linear_mut(i1);
let b = self.get_address_unchecked_linear_mut(i2);
// we can't just use the pointers returned from `get_address_unchecked_linear_mut` because calling a
// method taking self mutably invalidates any existing (mutable) pointers. since `get_address_unchecked_linear_mut` can
// also be overriden by a custom implementation, we can't just use `wrapping_add` assuming that's what the method does.
// instead, we use `offset_from` to compute the re-calculate the pointers from the base pointer.
// this is sound as long as this trait matches the Validity preconditions
// (and it's the caller's responsibility to ensure the indices are in-bounds).
let base = self.ptr_mut();
let offset1 = self.get_address_unchecked_linear_mut(i1).offset_from(base);
let offset2 = self.get_address_unchecked_linear_mut(i2).offset_from(base);
let base = self.ptr_mut();
let a = base.offset(offset1);
let b = base.offset(offset2);
ptr::swap(a, b);
}
@ -226,6 +256,10 @@ pub unsafe trait RawStorageMut<T, R: Dim, C: Dim = U1>: RawStorage<T, R, C> {
}
/// Trait shared by all mutable matrix data storage that dont contain any uninitialized elements.
///
/// # Safety
///
/// See safety note for `Storage`, `RawStorageMut`.
pub unsafe trait StorageMut<T, R: Dim, C: Dim = U1>:
Storage<T, R, C> + RawStorageMut<T, R, C>
{
@ -241,6 +275,8 @@ where
/// Marker trait indicating that a storage is stored contiguously in memory.
///
/// # Safety
///
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols - 1]`, the value
/// `.get_unchecked_linear` returns one of the matrix component. This trait is unsafe because
/// failing to comply to this may cause Undefined Behaviors.

View File

@ -35,7 +35,6 @@ use rkyv::bytecheck;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
// #[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub struct Unit<T> {
pub(crate) value: T,
}
@ -72,16 +71,6 @@ impl<'de, T: Deserialize<'de>> Deserialize<'de> for Unit<T> {
}
}
#[cfg(feature = "cuda")]
unsafe impl<T: cust_core::DeviceCopy, R, C, S> cust_core::DeviceCopy for Unit<Matrix<T, R, C, S>>
where
T: Scalar,
R: Dim,
C: Dim,
S: RawStorage<T, R, C> + Copy,
{
}
impl<T, R, C, S> PartialEq for Unit<Matrix<T, R, C, S>>
where
T: Scalar + PartialEq,

View File

@ -55,7 +55,6 @@ use simba::scalar::{ClosedNeg, RealField};
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub struct DualQuaternion<T> {
/// The real component of the quaternion
pub real: Quaternion<T>,
@ -320,6 +319,7 @@ where
}
impl<T: RealField> DualQuaternion<T> {
#[allow(clippy::wrong_self_convention)]
fn to_vector(&self) -> OVector<T, U8> {
self.as_ref().clone().into()
}

View File

@ -54,7 +54,6 @@ use rkyv::bytecheck;
///
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde-serialize-no-std",

View File

@ -34,7 +34,6 @@ use rkyv::bytecheck;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
#[derive(Copy, Clone)]
pub struct Orthographic3<T> {
matrix: Matrix4<T>,

View File

@ -35,7 +35,6 @@ use rkyv::bytecheck;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
#[derive(Copy, Clone)]
pub struct Perspective3<T> {
matrix: Matrix4<T>,

View File

@ -86,14 +86,6 @@ where
{
}
#[cfg(feature = "cuda")]
unsafe impl<T: Scalar + cust_core::DeviceCopy, D: DimName> cust_core::DeviceCopy for OPoint<T, D>
where
DefaultAllocator: Allocator<T, D>,
OVector<T, D>: cust_core::DeviceCopy,
{
}
#[cfg(feature = "bytemuck")]
unsafe impl<T: Scalar, D: DimName> bytemuck::Zeroable for OPoint<T, D>
where
@ -317,6 +309,10 @@ where
}
/// Gets a reference to i-th element of this point without bound-checking.
///
/// # Safety
///
/// `i` must be less than `self.len()`.
#[inline]
#[must_use]
pub unsafe fn get_unchecked(&self, i: usize) -> &T {
@ -344,6 +340,10 @@ where
}
/// Gets a mutable reference to i-th element of this point without bound-checking.
///
/// # Safety
///
/// `i` must be less than `self.len()`.
#[inline]
#[must_use]
pub unsafe fn get_unchecked_mut(&mut self, i: usize) -> &mut T {
@ -351,6 +351,10 @@ where
}
/// Swaps two entries without bound-checking.
///
/// # Safety
///
/// `i1` and `i2` must be less than `self.len()`.
#[inline]
pub unsafe fn swap_unchecked(&mut self, i1: usize, i2: usize) {
self.coords.swap_unchecked((i1, 0), (i2, 0))
@ -499,10 +503,11 @@ where
let mut it = self.coords.iter();
write!(f, "{}", *it.next().unwrap())?;
<T as fmt::Display>::fmt(it.next().unwrap(), f)?;
for comp in it {
write!(f, ", {}", *comp)?;
write!(f, ", ")?;
<T as fmt::Display>::fmt(comp, f)?;
}
write!(f, "}}")

View File

@ -202,29 +202,11 @@ impl<T: Scalar> Point1<T> {
/// assert_eq!(p.x, 1.0);
/// ```
#[inline]
#[cfg(not(feature = "cuda"))]
pub const fn new(x: T) -> Self {
Point {
coords: Vector1::new(x),
}
}
/// Initializes this point from its components.
///
/// # Example
///
/// ```
/// # use nalgebra::Point1;
/// let p = Point1::new(1.0);
/// assert_eq!(p.x, 1.0);
/// ```
#[inline]
#[cfg(feature = "cuda")]
pub fn new(x: T) -> Self {
Point {
coords: Vector1::new(x),
}
}
}
macro_rules! componentwise_constructors_impl(
($($doc: expr; $Point: ident, $Vector: ident, $($args: ident:$irow: expr),*);* $(;)*) => {$(
@ -234,22 +216,9 @@ macro_rules! componentwise_constructors_impl(
#[doc = $doc]
#[doc = "```"]
#[inline]
#[cfg(not(feature = "cuda"))]
pub const fn new($($args: T),*) -> Self {
Point { coords: $Vector::new($($args),*) }
}
// TODO: always let new be const once CUDA updates its supported
// nightly version to something more recent.
#[doc = "Initializes this point from its components."]
#[doc = "# Example\n```"]
#[doc = $doc]
#[doc = "```"]
#[inline]
#[cfg(feature = "cuda")]
pub fn new($($args: T),*) -> Self {
Point { coords: $Vector::new($($args),*) }
}
}
)*}
);

View File

@ -38,7 +38,6 @@ use rkyv::bytecheck;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub struct Quaternion<T> {
/// This quaternion as a 4D vector of coordinates in the `[ x, y, z, w ]` storage order.
pub coords: Vector4<T>,
@ -1016,9 +1015,6 @@ impl<T: RealField + fmt::Display> fmt::Display for Quaternion<T> {
/// A unit quaternions. May be used to represent a rotation.
pub type UnitQuaternion<T> = Unit<Quaternion<T>>;
#[cfg(feature = "cuda")]
unsafe impl<T: cust_core::DeviceCopy> cust_core::DeviceCopy for UnitQuaternion<T> {}
impl<T: Scalar + ClosedNeg + PartialEq> PartialEq for UnitQuaternion<T> {
#[inline]
fn eq(&self, rhs: &Self) -> bool {

View File

@ -64,7 +64,6 @@ use rkyv::bytecheck;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
#[derive(Copy, Clone)]
pub struct Rotation<T, const D: usize> {
matrix: SMatrix<T, D, D>,
@ -185,6 +184,10 @@ impl<T: Scalar, const D: usize> Rotation<T, D> {
}
/// A mutable reference to the underlying matrix representation of this rotation.
///
/// # Safety
///
/// Invariants of the rotation matrix should not be violated.
#[inline]
#[deprecated(note = "Use `.matrix_mut_unchecked()` instead.")]
pub unsafe fn matrix_mut(&mut self) -> &mut SMatrix<T, D, D> {

View File

@ -1058,7 +1058,7 @@ impl<T: SimdRealField> Rotation3<T> {
{
let mut angles = [T::zero(); 3];
let eps = T::from_subset(&1e-7);
let _2 = T::from_subset(&2.0);
let two = T::from_subset(&2.0);
if extrinsic {
seq.reverse();
@ -1090,7 +1090,7 @@ impl<T: SimdRealField> Rotation3<T> {
-s1,
c1,
);
let o_t = &c * self.matrix() * (c.transpose() * r1l);
let o_t = c * self.matrix() * (c.transpose() * r1l);
angles[1] = o_t.m33.acos();
let safe1 = angles[1].abs() >= eps;
@ -1131,7 +1131,7 @@ impl<T: SimdRealField> Rotation3<T> {
// dont adjust gimbal locked rotation
if adjust && observable {
angles[0] += T::pi();
angles[1] = _2 * lambda - angles[1];
angles[1] = two * lambda - angles[1];
angles[2] -= T::pi();
}

View File

@ -32,7 +32,6 @@ use rkyv::bytecheck;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
#[derive(Copy, Clone)]
pub struct Scale<T, const D: usize> {
/// The scale coordinates, i.e., how much is multiplied to a point's coordinates when it is
@ -149,6 +148,10 @@ impl<T: Scalar, const D: usize> Scale<T, D> {
/// assert_eq!(t.inverse_unchecked() * t, Scale2::identity());
/// }
/// ```
///
/// # Safety
///
/// Should only be used if all scaling is known to be non-zero.
#[inline]
#[must_use]
pub unsafe fn inverse_unchecked(&self) -> Scale<T, D>

View File

@ -83,28 +83,28 @@ add_sub_impl!(Mul, mul, ClosedMul;
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
const D; for; where;
self: &'a Scale<T, D>, right: &'b SVector<T, D>, Output = SVector<T, D>;
SVector::from(self.vector.component_mul(right));
self.vector.component_mul(right);
'a, 'b);
add_sub_impl!(Mul, mul, ClosedMul;
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
const D; for; where;
self: &'a Scale<T, D>, right: SVector<T, D>, Output = SVector<T, D>;
SVector::from(self.vector.component_mul(&right));
self.vector.component_mul(&right);
'a);
add_sub_impl!(Mul, mul, ClosedMul;
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
const D; for; where;
self: Scale<T, D>, right: &'b SVector<T, D>, Output = SVector<T, D>;
SVector::from(self.vector.component_mul(right));
self.vector.component_mul(right);
'b);
add_sub_impl!(Mul, mul, ClosedMul;
(Const<D>, U1), (Const<D>, U1) -> (Const<D>, U1)
const D; for; where;
self: Scale<T, D>, right: SVector<T, D>, Output = SVector<T, D>;
SVector::from(self.vector.component_mul(&right)); );
self.vector.component_mul(&right); );
// Scale *= Scale
add_sub_assign_impl!(MulAssign, mul_assign, ClosedMul;

View File

@ -21,7 +21,6 @@ use rkyv::bytecheck;
/// A similarity, i.e., an uniform scaling, followed by a rotation, followed by a translation.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde-serialize-no-std",

View File

@ -60,17 +60,14 @@ where
/// Tag representing the most general (not necessarily inversible) `Transform` type.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub enum TGeneral {}
/// Tag representing the most general inversible `Transform` type.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub enum TProjective {}
/// Tag representing an affine `Transform`. Its bottom-row is equal to `(0, 0 ... 0, 1)`.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
pub enum TAffine {}
impl TCategory for TGeneral {
@ -198,16 +195,6 @@ where
{
}
#[cfg(feature = "cuda")]
unsafe impl<T: RealField + cust_core::DeviceCopy, C: TCategory, const D: usize>
cust_core::DeviceCopy for Transform<T, C, D>
where
Const<D>: DimNameAdd<U1>,
DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>,
Owned<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>: cust_core::DeviceCopy,
{
}
impl<T: RealField, C: TCategory, const D: usize> Clone for Transform<T, C, D>
where
Const<D>: DimNameAdd<U1>,
@ -411,7 +398,7 @@ where
/// 3.0, 4.0, 0.0,
/// 0.0, 0.0, 1.0);
/// let t = Transform2::from_matrix_unchecked(m);
/// assert_eq!(t.into_inner(), m);
/// assert_eq!(t.to_homogeneous(), m);
/// ```
#[inline]
#[must_use]

View File

@ -32,7 +32,6 @@ use rkyv::bytecheck;
)
)]
#[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))]
#[cfg_attr(feature = "cuda", derive(cust_core::DeviceCopy))]
#[derive(Copy, Clone)]
pub struct Translation<T, const D: usize> {
/// The translation coordinates, i.e., how much is added to a point's coordinates when it is

View File

@ -31,9 +31,6 @@ use std::cmp::{Eq, PartialEq};
/// * [Conversion to a matrix <span style="float:right;">`to_rotation_matrix`, `to_homogeneous`…</span>](#conversion-to-a-matrix)
pub type UnitComplex<T> = Unit<Complex<T>>;
#[cfg(feature = "cuda")]
unsafe impl<T: cust_core::DeviceCopy> cust_core::DeviceCopy for UnitComplex<T> {}
impl<T: Scalar + PartialEq> PartialEq for UnitComplex<T> {
#[inline]
fn eq(&self, rhs: &Self) -> bool {

View File

@ -34,6 +34,17 @@ pub fn reflection_axis_mut<T: ComplexField, D: Dim, S: StorageMut<T, D>>(
if !factor.is_zero() {
column.unscale_mut(factor.sqrt());
// Normalize again, making sure the vector is unit-sized.
// If `factor` had a very small value, the first normalization
// (dividing by `factor.sqrt()`) might end up with a slightly
// non-unit vector (especially when using 32-bits float).
// Decompositions strongly rely on that unit-vector property,
// so we run a second normalization (that is much more numerically
// stable since the norm is close to 1) to ensure it has a unit
// size.
let _ = column.normalize_mut();
(-signed_norm, true)
} else {
// TODO: not sure why we don't have a - sign here.

View File

@ -21,7 +21,7 @@ pub fn svd_ordered2<T: RealField>(
// because q >= 0 and r >= 0.
let sx = q.clone() + r.clone();
let sy = q - r;
let sy_sign = if sy < T::zero() { -one.clone() } else { one };
let sy_sign = if sy < T::zero() { -one } else { one };
let singular_values = Vector2::new(sx, sy * sy_sign.clone());
if compute_u || compute_v {

View File

@ -360,7 +360,7 @@ mod test {
#[test]
fn wilkinson_shift_random() {
for _ in 0..1000 {
let m = Matrix2::new_random();
let m = Matrix2::<f64>::new_random();
let m = m * m.transpose();
let expected = expected_shift(m);

View File

@ -20,3 +20,5 @@ mod v022;
mod v023;
#[cfg(feature = "glam024")]
mod v024;
#[cfg(feature = "glam025")]
mod v025;

18
src/third_party/glam/v025/mod.rs vendored Normal file
View File

@ -0,0 +1,18 @@
#[path = "../common/glam_isometry.rs"]
mod glam_isometry;
#[path = "../common/glam_matrix.rs"]
mod glam_matrix;
#[path = "../common/glam_point.rs"]
mod glam_point;
#[path = "../common/glam_quaternion.rs"]
mod glam_quaternion;
#[path = "../common/glam_rotation.rs"]
mod glam_rotation;
#[path = "../common/glam_similarity.rs"]
mod glam_similarity;
#[path = "../common/glam_translation.rs"]
mod glam_translation;
#[path = "../common/glam_unit_complex.rs"]
mod glam_unit_complex;
pub(self) use glam025 as glam;

View File

@ -92,3 +92,11 @@ fn to_homogeneous() {
assert_eq!(a.to_homogeneous(), expected);
}
#[test]
fn display_fmt_respects_modifiers() {
let p = Point3::new(1.23, 3.45, 5.67);
assert_eq!(&format!("{p}"), "{1.23, 3.45, 5.67}");
assert_eq!(&format!("{p:.1}"), "{1.2, 3.5, 5.7}");
assert_eq!(&format!("{p:.0}"), "{1, 3, 6}");
}

View File

@ -1,4 +1,4 @@
use na::DMatrix;
use na::{DMatrix, Matrix3};
#[cfg(feature = "proptest-support")]
mod proptest_tests {
@ -116,6 +116,31 @@ fn symmetric_eigen_singular_24x24() {
);
}
// Regression test for #1368
#[test]
fn very_small_deviation_from_identity_issue_1368() {
let m = Matrix3::<f32>::new(
1.0,
3.1575704e-23,
8.1146196e-23,
3.1575704e-23,
1.0,
1.7471054e-22,
8.1146196e-23,
1.7471054e-22,
1.0,
);
for v in m
.try_symmetric_eigen(f32::EPSILON, 0)
.unwrap()
.eigenvalues
.into_iter()
{
assert_relative_eq!(*v, 1.);
}
}
// #[cfg(feature = "arbitrary")]
// quickcheck! {
// TODO: full eigendecomposition is not implemented yet because of its complexity when some