nalgebra-0.33.0/.cargo_vcs_info.json0000644000000001360000000000100126720ustar { "git": { "sha1": "48c8f6a5053fac48e305f5e5e0004a332811d092" }, "path_in_vcs": "" }nalgebra-0.33.0/.github/FUNDING.yml000064400000000000000000000013420072674642500146670ustar 00000000000000# These are supported funding model platforms github: [ "dimforge" ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] nalgebra-0.33.0/.github/Xargo.toml000064400000000000000000000001650072674642500150310ustar 00000000000000[target.x86_64-unknown-linux-gnu.dependencies] alloc = {} [target.thumbv7em-none-eabihf.dependencies] alloc = {}nalgebra-0.33.0/.github/workflows/nalgebra-ci-build.yml000064400000000000000000000125570072674642500211050ustar 00000000000000name: nalgebra CI build on: push: branches: [ dev, master ] pull_request: branches: [ dev, master ] env: CARGO_TERM_COLOR: always jobs: check-fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Check formatting run: cargo fmt -- --check clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install clippy run: rustup component add clippy - name: Run clippy run: cargo clippy build-nalgebra: runs-on: ubuntu-latest # env: # RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v2 - name: Build --no-default-feature run: cargo build --no-default-features; - name: Build (default features) run: cargo build; - name: Build --features serde-serialize run: cargo build --features serde-serialize - name: Build nalgebra-lapack run: cd nalgebra-lapack; cargo build; - name: Build nalgebra-sparse --no-default-features run: cd nalgebra-sparse; cargo build --no-default-features; - name: Build nalgebra-sparse (default features) run: cd nalgebra-sparse; cargo build; - name: Build nalgebra-sparse --all-features run: cd nalgebra-sparse; cargo build --all-features; # Run this on it’s own job because it alone takes a lot of time. # So it’s best to let it run in parallel to the other jobs. build-nalgebra-all-features: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: cargo build --all-features; - run: cargo build -p nalgebra-glm --all-features; test-nalgebra: runs-on: ubuntu-latest # env: # RUSTFLAGS: -D warnings steps: # Tests are run with a specific version of the compiler to avoid # trybuild errors when a new compiler version is out. This can be # bumped as needed after running the tests with TRYBUILD=overwrite # to re-generate the error reference. - name: Select rustc version uses: actions-rs/toolchain@v1 with: toolchain: 1.79.0 override: true - uses: actions/checkout@v2 - name: test run: cargo test --features arbitrary,rand,serde-serialize,sparse,debug,io,compare,libm,proptest-support,slow-tests,rkyv-safe-deser,rayon; test-nalgebra-glm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: test nalgebra-glm run: cargo test -p nalgebra-glm --features arbitrary,serde-serialize; test-nalgebra-sparse: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: test nalgebra-sparse # Manifest-path is necessary because cargo otherwise won't correctly forward features # We increase number of proptest cases to hopefully catch more potential bugs run: PROPTEST_CASES=10000 cargo test --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io,serde-serialize - name: test nalgebra-sparse (slow tests) # Unfortunately, the "slow-tests" take so much time that we need to run them with --release run: PROPTEST_CASES=10000 cargo test --release --manifest-path=nalgebra-sparse/Cargo.toml --features compare,proptest-support,io,serde-serialize,slow-tests slow test-nalgebra-macros: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: test nalgebra-macros run: cargo test -p nalgebra-macros build-wasm: runs-on: ubuntu-latest # env: # RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v2 - run: rustup target add wasm32-unknown-unknown - name: build nalgebra run: cargo build --verbose --target wasm32-unknown-unknown; - name: build nalgebra-glm run: cargo build -p nalgebra-glm --verbose --target wasm32-unknown-unknown; build-no-std: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install latest nightly uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true components: rustfmt - name: install xargo run: cp .github/Xargo.toml .; rustup component add rust-src; cargo install -f xargo; - name: build x86_64-unknown-linux-gnu run: xargo build --verbose --no-default-features --target=x86_64-unknown-linux-gnu; - name: build x86_64-unknown-linux-gnu --features rand-no-std run: xargo build --verbose --no-default-features --target=x86_64-unknown-linux-gnu; - name: build x86_64-unknown-linux-gnu --features alloc run: xargo build --verbose --no-default-features --features alloc --target=x86_64-unknown-linux-gnu; - name: build thumbv7em-none-eabihf run: xargo build --verbose --no-default-features --target=thumbv7em-none-eabihf; - name: build x86_64-unknown-linux-gnu nalgebra-glm 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; docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Generate documentation run: cargo doc nalgebra-0.33.0/.gitignore000064400000000000000000000001600072674642500134770ustar 00000000000000*.swp *.html doc lib TODO target/ Cargo.lock *.orig *.swo site/ .vscode/ .idea/ proptest-regressionsnalgebra-0.33.0/CHANGELOG.md000064400000000000000000001436530072674642500133370ustar 00000000000000# Change Log All notable changes to `nalgebra`, starting with the version 0.6.0 will be documented here. This project adheres to [Semantic Versioning](https://semver.org/). ## [0.33.0] (23 June 2024) ### Fixed - Fix a memory leak in `Matrix::generic_resize`. - Fix `glm::is_null` to check the vector magnitude instead of individual components. - Ensure that inverting a 4x4 matrix leaves it unchanged if the inversion fails. ### Added - Add the `glam-0.28` feature to enable conversion from/to types from `glam` v0.28. - Add a `stack!` macro for concatenating matrices. See [#1375](https://github.com/dimforge/nalgebra/pull/1375). ### Modified - The `cuda` feature has been removed, as the toolchain it depends on is long abandoned. - Update to `simba` 0.9. See the [changelog](https://github.com/dimforge/simba/blob/master/CHANGELOG) of `simba` for details. - Update the `nalgebra-macros` crate to `syn` 2.0. - Remove the scalar type `T` from the `Allocator` trait parameters. Instead of `Allocator`, use the simpler `Allocator`. ## [0.32.6] (12 June 2024) ### Added - Add the `glam-0.27` feature to enable conversion from/to types from `glam` v0.27. ## [0.32.5] (28 March 2024) ## Fixed - Fix numerical issue on SVD with near-identity matrix. ## [0.32.4] (19 Feb 2023) ### Added - Add the `glam-0.25` feature to enable conversion from/to types from `glam` v0.25. ## [0.32.3] (09 July 2023) ### Modified - Statically sized matrices are now serialized as tuples to match how serde serialized plain arrays. - Don’t require `Scalar` for matrix `PartialEq` and `Eq`. ### Added - Allow trailing punctuation in macros `vector!`, `matrix!`, `point!`, etc. - Add the methods `Matrix1::as_scalar`, `::as_scalar_mut`, `::to_scalar`, `::into_scalar`. - Add `Rotation3::euler_angles_ordered`, a generalized euler angles calculation. - Add the `glam-0.24` feature to enable conversion from/to types from `glam` v0.24. - Add the `glam-0.25` feature to enable conversion from/to types from `glam` v0.25. - Add the `lerp` method to points. - Implement `Clone` for `MatrixIter`. ### Fixed - Fixed severe catastrophic cancellation issue in variance calculation. ## [0.32.2] (07 March 2023) ### Added - Add the `glam-0.23` to enable conversion from/to type from `glam` v0.23. ## [0.32.1] (14 Jan. 2023) ### Modified - Updated `nalgebra-macros` to use the new `Dyn`, avoiding macro-generated deprecation warnings. ## [0.32.0] (14 Jan. 2023) ### Modified - Renamed all `MatrixSlice` types to `MatrixView`. In general all occurrences of the world `Slice` or `slice` have been replaced by `View` or `view`. - Deprecated all the types involving `Slice` in its name, in favor of the word `View`. - Make it so that most `nalgebra` objects archive as themselves (when using `rkyv` for serialization). - Renamed `Dynamic` to `Dyn` and make `Dyn` a tuple struct. ### Added - Add `Cholesky::ln_determinant` to compute the natural logarithm of the determinant of a matrix decomposed with Cholesky. This can be more numerically stable than computing the determinant itself when very small and/or large values are involved. - Added new methods `Matrix::as_view` and `Matrix::as_view_mut`, which are very useful when working with view-based APIs. - Added parallel column iterator using `rayon`: `Matrix::par_column_iter` and `Matrix::par_column_iter_mut`. The `rayon` feature must be enabled to access these methods. - Implement `ReshapableStorage` for matrix slices (only for unit strides at the moment). - Add `U0, U1, …` constants alongside the `U0, U1, …` types. This lets us write `U4` instead of `U4::name()` or `Const::<4>` when we need const dimensions. ### Fixed - Fixed the implementation of `Rotation3::euler_angles` to return the angles in the documented order (roll, pitch, yaw). ## [0.31.4] (13 Nov. 2022) ### Added - Add a `convert-glam022` feature to enable conversion between `nalgebra` and `glam v0.22`. ## [0.31.3] (30 Oct. 2022) ### Added - Add `Matrix::try_cast` to attempt casting the inner scalar types when that cast may fail. ### Fixed - Fixed the usage of `CheckBytes` with `rkyv`. ## [0.31.2] (09 Oct. 2022) ### Modified - Use `#[inline]` on the `Dim` implementation for `Const` to improve opt-level 1 performance. - Make the `Point::new` constructions const-fn. ### Added - Add `UnitVector::cast` to change the underlying scalar type. ## [0.31.1] (31 July 2022) ### Modified - Improve performances of multiplication of two sparse matrices. ### Added - Add `Matrix::from_row_iterator` to build a matrix from an iterator yielding components in row-major order. - Add support for conversion from/to types of `glam` 0.21. - `nalgebra-sparse`: add support for the matrix-market export of sparse matrices. - `nalgebra-lapack`: add a `GE` for solving the generalized eigenvalues problem. ### Fixed - Fix `Rotation3::from_matrix` and `UnitQuaternion::from_matrix` when the input matrix is already a valid rotation matrix. ## [0.31.0] (30 Apr. 2022) ### Breaking changes - Switch to `cust` 0.3 (for CUDA support). - Switch to `rkyv` 0.7 - Remove support for serialization based on `abomonation`. - Remove support for conversions between `nalgebra` types and `glam` 0.13. ### Modified - The aliases for `Const` types have been simplified to help `rust-analyzer`. ### Added - Add `TryFrom` conversion between `UnitVector2/3/4` and `glam`’s `Vec2/3/4`. - `nalgebra-sparse`: added support for serialization of sparse matrices with `serde`. - `nalgebra-sparse`: add a CSC matrix constructor from unsorted (but valid) data. - `nalgebra-lapack`: add generalized eigenvalues/eigenvectors calculation + QZ decomposition. ### Fixed - Improve stability of SVD. - Fix slerp for `UnitComplex`. ## [0.30.1] (09 Jan. 2022) ### Added - Add conversion from/to types of `glam` 0.19 and 0.20. ## [0.30.0] (02 Jan. 2022) ### Breaking changes - The `Dim` trait is now marked as unsafe. - The `Matrix::pow` and `Matrix::pow_mut` methods only allow positive integer exponents now. To compute negative exponents, the user is free to invert the matrix before calling `pow` with the exponent’s absolute value. - Remove the `Bounded` requirement from `RealField`. Replace it by methods returning `Option` so that they can still be implemented by unbounded types (by returning `None`). - The `ComplexField` trait derives from `FromPrimitive` again. We can actually keep this because all its methods return `Option`, meaning that it could be implemented by any type. ### Modified - Use more concise debug impls for matrices and geometric transformation types. - The singular values computed by the SVD are now sorted in increasing order by default. Use `SVD::new_unordered` instead to reproduce the older behavior without the sorting overhead. - The `UnitDualQuaternion::sclerp` method will no longer panic when given two equal rotations. - The `Matrix::select_rows` and `Matrix::select_columns` methods no longer require the matrix components to implement the trait `Zero`. - The `Matrix::pow` and `Matrix::pow_mut` methods will now also work with integer matrices. ### Added - Added the conversion trait `From>` and method `from_vec_storage` for `RowDVector`. - Added implementation of `From` and `Into` for converting between `nalgebra` types and types from `glam 0.18`. These can be enabled by enabling the `convert-glam018` cargo features. - Added the methods `Matrix::product`, `::row_product`, `::row_product_tr`, and `::column_product` to compute the product of the components, rows, or columns, of a single matrix or vector. - The `Default` trait is now implemented for most geometric types: `Point`, `Isometry`, `Rotation`, `Similarity`, `Transform`, `UnitComplex`, and `UnitQuaternion`. - Added the `Scale` geometric type for representing non-uniform scaling. - Added `Cholesky::new_with_substitute` that will replace diagonal elements by a given constant whenever `Cholesky` meets a non-definite-positiveness. - Re-added the conversion from a vector/matrix slice to a static array. - Added the `cuda` feature that enables the support of [rust-cuda](https://github.com/Rust-GPU/Rust-CUDA) for using `nalgebra` features with CUDA kernels written in Rust. - Added special-cases implementations for the 2x2 and 3x3 SVDs for better accuracy and performances. - Added the methods `Matrix::polar`, `Matrix::try_polar`, and `SVD::to_polar` to compute the polar decomposition of a matrix, based on its SVD. - `nalgebra-sparse`: provide constructors for unsorted but otherwise valid data using the CSR format. - `nalgebra-sparse`: added reading MatrixMarked data files to a sparse `CooMatrix`. ### Fixed - Fixed a potential unsoundness with `matrix.get(i)` and `matrix.get_mut(i)` where `i` is an `usize`, and `matrix` is a matrix slice with non-default strides. - Fixed potential unsoundness with `vector.perp` where `vector` isn’t actually a 2D vector as expected. - Fixed linkage issue with `nalgebra-lapack`: the user of `nalgebra-lapack` no longer have to add `extern crate lapack-src` to their `main.rs`. - Fixed the `no-std` build of `nalgebra-glm`. - Fix the `pow` and `pow_mut` functions (the result was incorrect for some exponent values). ## [0.29.0] ### Breaking changes - We updated to the version 0.6 of `simba`. This means that the trait bounds `T: na::RealField`, `na::ComplexField`, `na::SimdRealField`, `na:SimdComplexField` no imply that `T: Copy` (they only imply that `T: Clone`). This may affect generic code. - The closure given to `apply`, `zip_apply`, `zip_zip_apply` must now modify the first argument inplace, instead of returning a new value. This makes these methods more versatile, and avoid useless clones when using non-Copy scalar types. - The `Allocator` trait signature has been significantly modified in order to handle uninitialized matrices in a sound way. ### Modified - `Orthographic3::from_matrix_unchecked` is now `const fn`. - `Perspective3::from_matrix_unchecked` is now `const fn`. - `Rotation::from_matrix_unchecked` is now `const fn`. - The `Scalar` is now automatically implemented for most `'static + Clone` types. Type that implement `Clone` but not `Copy` are now much safer to work with thanks to the refactoring of the `Allocator` system. ### Added - The conversion traits form the `bytemuck` crates are now implemented for the geometric types too. - Added operator overloading for `Transform * UnitComplex`, `UnitComplex * Transform`, `Transform ×= UnitComplex`, `Transform ÷= UnitComplex`. - Added `Reflection::bias()` to retrieve the bias of the reflection. - Added `Reflection1..Reflection6` aliases for 1D to 6D reflections. - Added implementation of `From` and `Into` for converting between `nalgebra` types and types from `glam 0.16` and `glam 0.17`. These can be enabled by enabling the `convert-glam016`, and/or `convert-glam017` cargo features. ## [0.28.0] ### Added - Implement `Hash` for `Transform`. - Implement `Borrow` and `BorrowMut` for contiguous slices. ### Modified - The `OPoint` type has been added. It takes the dimension number as a type-level integer (e.g. `Const<3>`) instead of a const-generic. The type `Point` is now an alias for `OPoint`. This changes doesn't affect any of the existing code using `Point`. However, it will allow the use `OPoint` in a generic context where the dimension cannot be easily expressed as a const-generic (because of the current limitation of const-generics in Rust). - Several clippy warnings were fixed. This results in some method signature changes (e.g. taking `self` instead of `&self`) but this should not have any practical infulances on existing codebase. - The `Point::new` constructors are no longer const-fn. This is due to some limitations in const-fn not allowing custom trait-bounds. Use the `point!` macro instead to build points in const environments. - `Dynamic::new` and `Unit::new_unchecked` are now const-fn. - Methods returning `Result<(), ()>` now return `bool` instead. ### Fixed - Fixed a potential unsoundess issue when converting a mutable slice to a `&mut[T]`. ## [0.27.1] ### Fixed - Fixed a bug in the conversion from `glam::Vec2` or `glam::DVec2` to `Isometry2`. ## [0.27.0] This removes the `convert-glam` and `convert-glam-unchecked` optional features. Instead, this adds the `convert-glam013`, `convert-glam014`, and `convert-glam015` optional features for conversions targeting the versions 0.13, 0.14, and 0.15 of `glam`. ### Added - Add macros `matrix!`, `dmatrix!`, `vector!`, `dvector!`, `point!` for constructing matrices/vectors/points in a more convenient way. See [#886](https://github.com/dimforge/nalgebra/pull/886) and [#899](https://github.com/dimforge/nalgebra/pull/899). - Add `CooMatrix::reserve` to `nalgebra-sparse`. - Add basic support for serialization using `rkyv`. Can be enabled with the features `rkyv-serialize` or `rkyv-serialize-no-std`. ### Fixed - Fixed a potential unsoundness issue after deserializing an invalid `DVector` using `serde`. ## [0.26.2] ### Added - Conversion from an array `[T; D]` to an isometry `Isometry` (as a translation). - Conversion from a static vector `SVector` to an isometry `Isometry` (as a translation). - Conversion from a point `Point` to an isometry `Isometry` (as a translation). - Conversion of an array `[T; D]` from/to a translation `Translation`. - Conversion of a point `Point` to a translation `Translation`. - Conversion of the tuple of glam types `(Vec3, Quat)` from/to an `Isometry2` or `Isometry3`. - Conversion of a glam type `Vec2/3/4` from/to a `Translation2/3/4`. ## [0.26.1] Fix a regression introduced in 0.26.0 preventing `DVector` from being serialized with `serde`. ## [0.26.0] This release integrates `min-const-generics` to nalgebra. See [our blog post](https://www.dimforge.com/blog/2021/04/12/integrating-const-generics-to-nalgebra) for details about this release. ### Added - Add type aliases for unit vector, e.g., `UnitVector3`. - Add a `pow` and `pow_mut` function to square matrices. - Add `Cholesky::determinant` to compute the determinant of a matrix decomposed with Cholesky. - Add the `serde-serialize-no-std` feature to enable serialization of static matrices/vectors with serde, but without requiring `std`. ### Modified - The `serde` crate isn't enabled by default now. Enable the `serde-serialize` or the `serde-serialize-no-std` features instead. - The `Const` type has been introduced to represent dimensions known at compile-time. This replaces the type-level integers from `typenum` as well as the `U1, U2, ..., U127` types from `nalgebra`. These `U1, U2, ..., U127` are now just aliases for `Const`, e.g., `type U2 = Const<2>`. - The `ArrayStorage` now uses a standard array `[[T; R]; C]` instead of a `GenericArray`. - Many trait bounds were changed to accommodate const-generics. Most of these changes should be transparent wrt. non-generic code. - The `MatrixMN` alias has been deprecated. Use `OMatrix` or `SMatrix` instead. - The `MatrixN` alias has been deprecated. Use `OMatrix` or `SMatrix` instead. - The `VectorN` alias has been deprecated. Use `OVector` or `SVector` instead. - The `Point`, `Translation`, `Isometry`, `Similarity`, and `Transformation` types now take an integer for their dimension (instead of a type-level integer). - The type parameter order of `Isometry`, `Similarity`, `Transformation` changed to put the integer dimensions in the last position (this is required by the compiler). - The `::new` constructors of translations, points, matrices, and vectors of dimensions `<= 6` are now `const fn`, making them usable to define constant globals. The `Quaternion::new` constructor is also a `const fn` now. ## [0.25.4] ### Fixed - Fix a compilation error when only the `serde-serialize` feature is enabled. ## [0.25.3] ### Added - The `Vector::simd_cap_magnitude` method to cap the magnitude of the vector with SIMD components. ## [0.25.2] ### Added - A `convert-glam` cargo feature to enable implementations of `From` traits to convert between `glam` types and `nalgebra` types. - A `convert-glam-unchecked` cargo feature to enable some extra `glam`/`nalgebra` conversions that may lead to unexpected results if used improperly. For example, this enables the conversion from a `glam::Mat4` to a `na::Isometry3`. This conversion will be cheap (without any check) but willlead to unexpected results if the glam matrix contains non-isometric components (like scaling for example). - A `cast` method has been added to most types. This can be used to change the type of the components of a given entity. Example: `vector.cast::()`. ## [0.25.1] This release replaces the version 0.25.0 which has been yanked. The 0.25.0 version added significant complication to build `nalgebra` targeting a `#[no-std]` platform not supported by `rand`. The `rand` dependency is now optional (and disabled by default). You may enable it with: - The `rand-no-std` cargo feature when targeting a `#[no-std]` environment. - The `rand` cargo feature when targeting a `std` environment. ## [0.25.0] - Yanked This updates all the dependencies of nalgebra to their latest version, including: - rand 0.8 - proptest 1.0 - simba 0.4 ### New crate: nalgebra-sparse Alongside this release of `nalgebra`, we are releasing `nalgebra-sparse`: a crate dedicated to sparse matrix computation with `nalgebra`. The `sparse` module of `nalgebra`itself still exists for backward compatibility, but it will be deprecated soon in favor of the `nalgebra-sparse` crate. ### Added * Add `UnitDualQuaternion`, a dual-quaternion with unit magnitude which can be used as an isometry transformation. * Add `UDU::new()` and `matrix.udu()` to compute the UDU factorization of a matrix. * Add `ColPivQR::new()` and `matrix.col_piv_qr()` to compute the QR decomposition with column pivoting of a matrix. * Add `from_basis_unchecked` to all the rotation types. This builds a rotation from a set of basis vectors (representing the columns of the corresponding rotation matrix). * Add `Matrix::cap_magnitude` to cap the magnitude of a vector. * Add `UnitQuaternion::append_axisangle_linearized` to approximately append a rotation represented as an axis-angle to a rotation represented as an unit quaternion. * Mark the iterators on matrix components as `DoubleEndedIter`. * Re-export `simba::simd::SimdValue` at the root of the `nalgebra` crate. ## [0.24.0] ### Added * The `DualQuaternion` type. It is still work-in-progress, but the basics are here: creation from its real and dual part, multiplication of two dual quaternions, and normalization. ### Removed * There is no blanket `impl PartialEq for Unit` anymore. Instead, it is implemented specifically for `UnitComplex`, `UnitQuaternion` and `Unit`. ## [0.23.2] In this release, we improved the documentation of some of the geometric types by applying changes similar to what we did in the version 0.23.1 for matrices. ### Added * The `Isometry::inv_mul` method which is a more efficient way of doing `isometry1.inverse() * isometry2`. ## [0.23.1] In this release we improved the documentation of the matrix and vector types by: - Grouping `impl` bocks logically, adding a title comment to these impl blocks. - Reference these impl blocks docs at the top of the documentation page for `Matrix`. - Reduce the depth of type aliasing. Now all vector and matrix types are aliases of `Matrix` directly (instead of being aliases for other aliases). ## [0.23.0] ### Added * The `.inverse_transform_unit_vector(v)` was added to `Rotation2/3`, `Isometry2/3`, `UnitQuaternion`, and `UnitComplex`. It applies the corresponding rotation to a unit vector `Unit`. * The `Point.map(f)` and `Point.apply(f)` to apply a function to each component of the point, similarly to `Vector.map(f)` and `Vector.apply(f)`. * The `Quaternion::from([N; 4])` conversion to build a quaternion from an array of four elements. * The `Isometry::from(Translation)` conversion to build an isometry from a translation. * The `Vector::ith_axis(i)` which build a unit vector, e.g., `Unit>` with its i-th component set to 1.0, and the others set to zero. * The `Isometry.lerp_slerp` and `Isometry.try_lerp_slerp` methods to interpolate between two isometries using linear interpolation for the translational part, and spherical interpolation for the rotational part. * The `Rotation2.slerp`, `Rotation3.slerp`, and `UnitQuaternion.slerp` method for spherical interpolation. ## [0.22.0] In this release, we are using the new version 0.2 of simba. One major change of that version is that the use of `libm` is now opt-in when building targeting `no-std` environment. If you are using floating-point operations with nalgebra in a `no-std` environment, you will need to enable the new `libm` feature of nalgebra for your code to compile again. ### Added * The `libm` feature that enables `libm` when building for `no-std` environment. * The `libm-force` feature that enables `libm` even when building for a not `no-std` environment. * `Cholesky::new_unchecked` which build a Cholesky decomposition without checking that its input is positive-definite. It can be used with SIMD types. * The `Default` trait is now implemented for matrices, and quaternions. They are all filled with zeros, except for `UnitQuaternion` which is initialized with the identity. * Matrix exponential `matrix.exp()`. * The `Vector::ith(i, x)` that builds a vector filled with zeros except for the `i`-th component set to `x`. ## [0.21.0] In this release, we are no longer relying on traits from the __alga__ crate for our generic code. Instead, we use traits from the new [simba](https://crates.io/crates/simba) crate which are both simpler, and allow for significant optimizations like AoSoA SIMD. Refer to the [monthly dimforge blogpost](https://www.dimforge.org/blog/2020/04/01/this-month-in-dimforge/) for details about this switch and its benefits. ### Added * It is now possible to use SIMD types like `simba::f32x4` as scalar types for nalgebra's matrices and geometric types. ### Modified * Use of traits like `alga::general::{RealField, ComplexField}` have now been replaced by `simba::scalar::{RealField, ComplexField}`. * The implementation of traits from the __alga__ crate (and well as the dependency to _alga__) are now omitted unless the `alga` cargo feature is activated. ### Removed * The `Neg` unary operator is no longer implemented for `UnitComplex` and `UnitQuaternion`. This caused hard-to-track errors when we mistakenly write, e.g., `-q * v` instead of `-(q * v)`. * The `na::convert_unchecked` is no longer marked as unsafe. ## [0.20.0] ### Added * `cholesky.rank_one_update(...)` which performs a rank-one update on the cholesky decomposition of a matrix. * `From<&Matrix>` is now implemented for matrix slices. * `.try_set_magnitude(...)` which sets the magnitude of a vector, while keeping its direction. * Implementations of `From` and `Into` for the conversion between matrix slices and standard (`&[N]` `&mut [N]`) slices. ### Modified * We started some major changes in order to allow non-Copy types to be used as scalar types inside of matrices/vectors. ## [0.19.0] ### Added * `.remove_rows_at` and `remove_columns_at` which removes a set of rows or columns (specified by indices) from a matrix. * Several formatting traits have been implemented for all matrices/vectors: `LowerExp`, `UpperExp`, `Octal`, `LowerHex`, `UpperHex`, `Binary`, `Pointer`. * `UnitQuaternion::quaternions_mean(...)` which computes the mean rotation of a set of unit quaternions. This implements the algorithm from _Oshman, Yaakov, and Avishy Carmi, "Attitude estimation from vector observations using a genetic-algorithm-embedded quaternion particle filter." ### Modified * It is now possible to get the `min/max` element of unsigned integer matrices. ### Added to nalgebra-glm * Some infinite and reversed perspectives: `::infinite_perspective_rh_no`, `::infinite_perspective_rh_zo`, `::reversed_perspective_rh_zo`, and `::reversed_infinite_perspective_rh_zo`. ## [0.18.0] This release adds full complex number support to nalgebra. This includes all common vector/matrix operations as well as matrix decomposition. This excludes geometric type (like `Isometry`, `Rotation`, `Translation`, etc.) from the `geometry` module. ### Added #### Quaternion and geometric operations * Add trigonometric functions for quaternions: `.cos, .sin, .tan, .acos, .asin, .atan, .cosh, .sinh, .tanh, .acosh, .asinh, .atanh`. * Add geometric algebra operations for quaternions: `.inner, .outer, .project, .rejection` * Add `.left_div, .right_div` for quaternions. * Add `.renormalize` to `Unit<...>` and `Rotation3` to correct potential drift due to repeated operations. Those drifts could cause them not to be pure rotations anymore. #### Convolution * `.convolve_full(kernel)` returns the convolution of `self` by `kernel`. * `.convolve_valid(kernel)` returns the convolution of `self` by `kernel` after removal of all the elements relying on zero-padding. * `.convolve_same(kernel)` returns the convolution of `self` by `kernel` with a result of the same size as `self`. #### Complex number support * Add the `::from_matrix` constructor too all rotation types to extract a rotation from a raw matrix. * Add the `::from_matrix_eps` constructor too all rotation types to extract a rotation from a raw matrix. This takes more argument than `::from_matrix` to control the convergence of the underlying optimization algorithm. * Add `.camax()` which returns the matrix component with the greatest L1-norm. * Add `.camin()` which returns the matrix component with the smallest L1-norm. * Add `.ad_mul(b)` for matrix-multiplication of `self.adjoint() * b`. * Add `.ad_mul_to(b)` which is the same as `.ad_mul` but with a provided matrix to be filled with the result of the multiplication. * Add BLAS operations involving complex conjugation (following similar names as the original BLAS spec): * `.dotc(rhs)` equal to `self.adjoint() * rhs`. * `.gerc(alpha, x, y, beta)` equivalent to `self = alpha * x * y.adjoint() + beta * self` * `.hegerc` which is like `gerc` but for Hermitian matrices. * `.syger` which is the new name of `.ger_symm` which is equivalent to `self = alpha * x * y.transpose() + beta * self`. * `.sygemv` which is the new name of `.gemv_symm` which is equivalent to `self = alpha * a * x + beta * self` with `a` symmetric. * `.hegemv(alpha, a, x, beta)` which is like `.sygemv` but with `a` Hermitian. * `.gemv_ad(alpha, a, x, beta)` which is equivalent to `self = alpha * a.adjoint() * x + beta * self`. * `.gemm_ad(alpha, a, b, beta)` which is equivalent to `self = alpha * a.adjoint() * b + beta * self`. * `.icamax()` which returns the index of the complex vector component with the greatest L1-norm. Note that all the other BLAS operation will continue to work for all fields, including floats and complex numbers. ### Renamed * `RealSchur` has been renamed `Schur` because it can now work with complex matrices. ## [0.17.0] ### Added * Add swizzling up to dimension 3 for vectors. For example, you can do `v.zxy()` as an equivalent to `Vector3::new(v.z, v.x, v.y)`. * Add swizzling up to dimension 3 for points. For example, you can do `p.zxy()` as an equivalent to `Point3::new(p.z, p.x, p.y)`. * Add `.copy_from_slice` to copy matrix components from a slice in column-major order. * Add `.dot` to quaternions. * Add `.zip_zip_map` for iterating on three matrices simultaneously, and applying a closure to them. * Add `.slerp` and `.try_slerp` to unit vectors. * Add `.lerp` to vectors. * Add `.to_projective` and `.as_projective` to `Perspective3` and `Orthographic3` in order to use them as `Projective3` structures. * Add `From/Into` impls to allow the conversion of any transformation type to a matrix. * Add `Into` impls to convert a matrix slice into an owned matrix. * Add `Point*::from_slice` to create a point from a slice. * Add `.map_with_location` to matrices to apply a map which passes the component indices to the user-defined closure alongside the component itself. * Add impl `From` for `Point`. * Add impl `From` for `Quaternion`. * Add impl `From` for `Translation`. * Add the `::from_vec` constructor to construct a matrix from a `Vec` (a `DMatrix` will reuse the original `Vec` as-is for its storage). * Add `.to_homogeneous` to square matrices (and with dimensions higher than 1x1). This will increase their number of row and columns by 1. The new column and row are filled with 0, except for the diagonal element which is set to 1. * Implement `Extend` for matrices with a dynamic storage. The provided `Vec` is assumed to represent a column-major matrix with the same number of rows as the one being extended. This will effectively append new columns on the right of the matrix being extended. * Implement `Extend` for vectors with a dynamic storage. This will concatenate the vector with the given `Vec`. * Implement `Extend>` for matrices with dynamic storage. This will concatenate the columns of both matrices. * Implement `Into` for the `MatrixVec` storage. * Implement `Hash` for all matrices. * Add a `.len()` method to retrieve the size of a `MatrixVec`. ### Modified * The orthographic projection no longer require that `bottom < top`, that `left < right`, and that `znear < zfar`. The only restriction now ith that they must not be equal (in which case the projection would be singular). * The `Point::from_coordinates` methods is deprecated. Use `Point::from` instead. * The `.transform_point` and `.transform_vector` methods are now inherent methods for matrices so that the user does not have to explicitly import the `Transform` trait from the alga crate. * Renamed the matrix storage types: `MatrixArray` -> `ArrayStorage` and `MatrixVec` -> `VecStorage`. * Renamed `.unwrap()` to `.into_inner()` for geometric types that wrap another type. This is for the case of `Unit`, `Transform`, `Orthographic3`, `Perspective3`, `Rotation`. * Deprecate several functions at the root of the crate (replaced by methods). ### Removed * Remove the `Deref` impl for `MatrixVec` as it could cause hard-to-understand compilation errors. ### nalgebra-glm * Add several alternative projection computations, e.g., `ortho_lh`, `ortho_lh_no`, `perspective_lh`, etc. * Add features matching those of nalgebra, in particular:`serde-serialize`, `abmonation-serialize`, std` (enabled by default). ## [0.16.0] All dependencies have been updated to their latest versions. ## Modified * Adjust `UnitQuaternion`s, `Rotation3`s, and `Rotation2`s generated from the `Standard` distribution to be uniformly distributed. ### Added * Add a feature `stdweb` to activate the dependency feature `rand/stdweb`. * Add blas-like methods `.imin()` and `.imax()` that return the index of the minimum and maximum entry of a vector. * Add construction of a `Point` from an array by implementing the `From` trait. * Add support for generating uniformly distributed random unit column vectors using the `Standard` distribution. ## [0.15.0] The most notable change of this release is the support for using part of the library without the rust standard library (i.e. it supports `#![no_std]`). See the corresponding [documentation](https://nalgebra.org/wasm_and_embedded_programming/). ### Modified * Rename the `core` module to `base` to avoid conflicts with the `core` crate implicitly imported when `#![no_std]` is enabled. * Constructors of the `MatrixSlice*` types have been renamed from `new_*` to `from_slice_*`. This was necessary to avoid the `incoherent_fundamental_impls` lint that is going to become a hard error. ### Added * Add `UnitQuaternion` constructor `::new_eps(...)` and `::from_scaled_axis_eps(...)` that return the identity if the magnitude of the input axisangle is smaller than the epsilon provided. * Add methods `.rotation_between_axis(...)` and `.scaled_rotation_between_axis(...)` to `UnitComplex` to compute the rotation matrix between two 2D **unit** vectors. * Add methods `.axis_angle()` to `UnitComplex` and `UnitQuaternion` in order to retrieve both the unit rotation axis, and the rotation angle simultaneously. * Add functions to construct a random matrix with a user-defined distribution: `::from_distribution(...)`. ## [0.14.0] ### Modified * Allow the `Isometry * Unit` multiplication. ### Added * Add blas-like operations: `.quadform(...)` and `.quadform_tr(...)` to compute respectively the quadratic forms `self = alpha * A.transpose() * B * A + beta * self` and `alpha * A * B * A.transpose() + beta * self`. Here, `A, B` are matrices with `B` square, and `alpha, beta` are reals. * Add blas-like operations: `.gemv_tr(...)` that behaves like `.gemv` except that the provided matrix is assumed to be transposed. * Add blas-like operations: `cmpy, cdpy` for component-wise multiplications and division with scalar factors: - `self <- alpha * self + beta * a * b` - `self <- alpha * self + beta * a / b` * `.cross_matrix()` returns the cross-product matrix of a given 3D vector, i.e., the matrix `M` such that for all vector `v` we have `M * v == self.cross(&v)`. * `.iamin()` that returns the index of the vector entry with the smallest absolute value. * The `mint` feature that can be enabled in order to allow conversions from and to types of the [mint](https://crates.io/crates/mint) crate. * Aliases for matrix and vector slices. Their are named by adding `Slice` before the dimension numbers, i.e., a 3x5 matrix slice with dimensions known at compile-time is called `MatrixSlice3x5`. A vector slice with dimensions unknown at compile-time is called `DVectorSlice`. * Add functions for constructing matrix slices from a slice `&[N]`, e.g., `MatrixSlice2::new(...)` and `MatrixSlice2::new_with_strides(...)`. * The `::repeat(...)` constructor that is an alternative name to `::from_element(...)`. * `UnitQuaternion::scaled_rotation_between_axis(...)` and `UnitQuaternion::rotation_between_axis(...)` that take Unit vectors instead of Vector as arguments. ## [0.13.0] The **nalgebra-lapack** crate has been updated. This now includes a broad range matrix decompositions using LAPACK bindings. This adds support for serialization using the [abomonation](https://crates.io/crates/abomonation) crate. ### Breaking semantic change * The implementation of slicing with steps now matches the documentation. Before, step identified the number to add to pass from one column/row index to the next one. This made 0 step invalid. Now (and on the documentation so far), the step is the number of ignored row/columns between each row/column. Thus, a step of 0 means that no row/column is ignored. For example, a step of, say, 3 on previous versions should now bet set to 2. ### Modified * The trait `Axpy` has been replaced by a method `.axpy`. * The alias `MatrixNM` is now deprecated. Use `MatrixMN` instead (we reordered M and N to be in alphabetical order). * In-place componentwise multiplication and division `.component_mul_mut(...)` and `.component_div_mut(...)` have been deprecated for a future renaming. Use `.component_mul_assign(...)` and `.component_div_assign(...)` instead. ### Added * `alga::general::Real` is now re-exported by nalgebra. elements.) * `::zeros(...)` that creates a matrix filled with zeroes. * `::from_partial_diagonal(...)` that creates a matrix from diagonal elements. The matrix can be rectangular. If not enough elements are provided, the rest of the diagonal is set to 0. * `.conjugate_transpose()` computes the transposed conjugate of a complex matrix. * `.conjugate_transpose_to(...)` computes the transposed conjugate of a complex matrix. The result written into a user-provided matrix. * `.transpose_to(...)` is the same as `.transpose()` but stores the result in the provided matrix. * `.conjugate_transpose_to(...)` is the same as `.conjugate_transpose()` but stores the result in the provided matrix. * Implements `IntoIterator` for `&Matrix`, `&mut Matrix` and `Matrix`. * `.mul_to(...)` multiplies two matrices and stores the result to the given buffer. * `.tr_mul_to(...)` left-multiplies `self.transpose()` to another matrix and stores the result to the given buffer. * `.add_scalar(...)` that adds a scalar to each component of a matrix. * `.add_scalar_mut(...)` that adds in-place a scalar to each component of a matrix. * `.kronecker(a, b)` computes the kronecker product (i.e. matrix tensor product) of two matrices. * `.apply(f)` replaces each component of a matrix with the results of the closure `f` called on each of them. Pure Rust implementation of some Blas operations: * `.iamax()` returns the index of the maximum value of a vector. * `.axpy(...)` computes `self = a * x + b * self`. * `.gemv(...)` computes `self = alpha * a * x + beta * self` with a matrix and vector `a` and `x`. * `.ger(...)` computes `self = alpha * x^t * y + beta * self` where `x` and `y` are vectors. * `.gemm(...)` computes `self = alpha * a * b + beta * self` where `a` and `b` are matrices. * `.gemv_symm(...)` is the same as `.gemv` except that `self` is assumed symmetric. * `.ger_symm(...)` is the same as `.ger` except that `self` is assumed symmetric. New slicing methods: * `.rows_range(...)` that retrieves a reference to a range of rows. * `.rows_range_mut(...)` that retrieves a mutable reference to a range of rows. * `.columns_range(...)` that retrieves a reference to a range of columns. * `.columns_range_mut(...)` that retrieves a mutable reference to a range of columns. Matrix decompositions implemented in pure Rust: * Cholesky, SVD, LU, QR, Hessenberg, Schur, Symmetric eigendecompositions, Bidiagonal, Symmetric tridiagonal * Computation of householder reflectors and givens rotations. Matrix edition: * `.upper_triangle()` extracts the upper triangle of a matrix, including the diagonal. * `.lower_triangle()` extracts the lower triangle of a matrix, including the diagonal. * `.fill(...)` fills the matrix with a single value. * `.fill_with_identity(...)` fills the matrix with the identity. * `.fill_diagonal(...)` fills the matrix diagonal with a single value. * `.fill_row(...)` fills a selected matrix row with a single value. * `.fill_column(...)` fills a selected matrix column with a single value. * `.set_diagonal(...)` sets the matrix diagonal. * `.set_row(...)` sets a selected row. * `.set_column(...)` sets a selected column. * `.fill_lower_triangle(...)` fills some sub-diagonals below the main diagonal with a value. * `.fill_upper_triangle(...)` fills some sub-diagonals above the main diagonal with a value. * `.swap_rows(...)` swaps two rows. * `.swap_columns(...)` swaps two columns. Column removal: * `.remove_column(...)` removes one column. * `.remove_fixed_columns(...)` removes `D` columns. * `.remove_columns(...)` removes a number of columns known at run-time. Row removal: * `.remove_row(...)` removes one row. * `.remove_fixed_rows(...)` removes `D` rows. * `.remove_rows(...)` removes a number of rows known at run-time. Column insertion: * `.insert_column(...)` adds one column at the given position. * `.insert_fixed_columns(...)` adds `D` columns at the given position. * `.insert_columns(...)` adds at the given position a number of columns known at run-time. Row insertion: * `.insert_row(...)` adds one row at the given position. * `.insert_fixed_rows(...)` adds `D` rows at the given position. * `.insert_rows(...)` adds at the given position a number of rows known at run-time. ## [0.12.0] The main change of this release is the update of the dependency serde to 1.0. ### Added * `.trace()` that computes the trace of a matrix (the sum of its diagonal elements.) ## [0.11.0] The [website](https://nalgebra.org) has been fully rewritten and gives a good overview of all the added/modified features. This version is a major rewrite of the library. Major changes are: * Algebraic traits are now defined by the [alga](https://crates.io/crates/alga) crate. All other mathematical traits, except `Axpy` have been removed from **nalgebra**. * Methods are now preferred to free functions because they do not require any trait to be used anymore. * Most algebraic entities can be parametrized by type-level integers to specify their dimensions. Using `Dynamic` instead of a type-level integer indicates that the dimension known at run-time only. * Statically-sized **rectangular** matrices. * More transformation types have been added: unit-sized complex numbers (for 2D rotations), affine/projective/general transformations with `Affine2/3`, `Projective2/3`, and `Transform2/3`. * Serde serialization is now supported instead of `rustc_serialize`. Enable it with the `serde-serialize` feature. * Matrix **slices** are now implemented. ### Added Lots of features including rectangular matrices, slices, and Serde serialization. Refer to the brand new [website](https://nalgebra.org) for more details. The following free-functions have been added as well: * `::id()` that returns the universal [identity element](https://nalgebra.org/performance_tricks/#the-id-type) of type `Id`. * `::inf_sup()` that returns both the infimum and supremum of a value at the same time. * `::partial_sort2()` that attempts to sort two values in increasing order. * `::wrap()` that moves a value to the given interval by adding or removing the interval width to it. ### Modified * `::cast` -> `::convert` * `point.as_vector()` -> `point.coords` * `na::origin` -> `P::origin()` * `na::is_zero` -> `.is_zero()` (from num::Zero) * `.transform` -> `.transform_point`/`.transform_vector` * `.translate` -> `.translate_point` * `::dimension::

` -> `::dimension::` * `::angle_between` -> `::angle` Componentwise multiplication and division has been replaced by methods: * multiplication -> `.componentwise_mul`, `.componentwise_mul_mut`. * division -> `.componentwise_div`, `.componentwise_div_mut`. The following free-functions are now replaced by methods (with the same names) only: `::cross`, `::cholesky`, `::determinant`, `::diagonal`, `::eigen_qr` (becomes `.eig`), `::hessenberg`, `::qr`, `::to_homogeneous`, `::to_rotation_matrix`, `::transpose`, `::shape`. The following free-functions are now replaced by static methods only: * `::householder_matrix` under the name `::new_householder_generic` * `::identity` * `::new_identity` under the name `::identity` * `::from_homogeneous` * `::repeat` under the name `::from_element` The following free-function are now replaced methods accessible through traits only: * `::transform` -> methods `.transform_point` and `.transform_vector` of the `alga::linear::Transformation` trait. * `::inverse_transform` -> methods `.inverse_transform_point` and `.inverse_transform_vector` of the `alga::linear::ProjectiveTransformation` trait. * `::translate`, `::inverse_translate`, `::rotate`, `::inverse_rotate` -> methods from the `alga::linear::Similarity` trait instead. Those have the same names but end with `_point` or `_vector`, e.g., `.translate_point` and `.translate_vector`. * `::orthonormal_subspace_basis` -> method with the same name from `alga::linear::FiniteDimInnerSpace`. * `::canonical_basis_element` and `::canonical_basis` -> methods with the same names from `alga::linear::FiniteDimVectorSpace`. * `::rotation_between` -> method with the same name from the `alga::linear::Rotation` trait. * `::is_zero` -> method with the same name from `num::Zero`. ### Removed * The free functions `::prepend_rotation`, `::append_rotation`, `::append_rotation_wrt_center`, `::append_rotation_wrt_point`, `::append_transformation`, and `::append_translation ` have been removed. Instead, create the rotation or translation object explicitly and use multiplication to compose it with anything else. * The free function `::outer` has been removed. Use column-vector × row-vector multiplication instead. * `::approx_eq`, `::approx_eq_eps` have been removed. Use the `relative_eq!` macro from the [approx](https://crates.io/crates/approx) crate instead. * `::covariance` has been removed. There is no replacement for now. * `::mean` has been removed. There is no replacement for now. * `::sample_sphere` has been removed. There is no replacement for now. * `::cross_matrix` has been removed. There is no replacement for now. * `::absolute_rotate` has been removed. There is no replacement for now. * `::rotation`, `::transformation`, `::translation`, `::inverse_rotation`, `::inverse_transformation`, `::inverse_translation` have been removed. Use the appropriate methods/field of each transformation type, e.g., `rotation.angle()` and `rotation.axis()`. ## [0.10.0] ### Added Binary operations are now allowed between references as well. For example `Vector3 + &Vector3` is now possible. ### Modified Removed unused parameters to methods from the `ApproxEq` trait. Those were required before rust 1.0 to help type inference. They are not needed any more since it now allowed to write for a type `T` that implements `ApproxEq`: `::approx_epsilon()`. This replaces the old form: `ApproxEq::approx_epsilon(None::)`. ## [0.9.0] ### Modified * Renamed: - `::from_col_vector` -> `::from_column_vector` - `::from_col_iter` -> `::from_column_iter` - `.col_slice` -> `.column_slice` - `.set_col` -> `.set_column` - `::canonical_basis_with_dim` -> `::canonical_basis_with_dimension` - `::from_elem` -> `::from_element` - `DiagMut` -> `DiagonalMut` - `UnitQuaternion::new` becomes `UnitQuaternion::from_scaled_axis` or `UnitQuaternion::from_axisangle`. The new `::new` method now requires a not-normalized quaternion. Method names starting with `new_with_` now start with `from_`. This is more idiomatic in Rust. The `Norm` trait now uses an associated type instead of a type parameter. Other similar trait changes are to be expected in the future, e.g., for the `Diagonal` trait. Methods marked `unsafe` for reasons unrelated to memory safety are no longer unsafe. Instead, their name end with `_unchecked`. In particular: * `Rotation3::new_with_matrix` -> `Rotation3::from_matrix_unchecked` * `PerspectiveMatrix3::new_with_matrix` -> `PerspectiveMatrix3::from_matrix_unchecked` * `OrthographicMatrix3::new_with_matrix` -> `OrthographicMatrix3::from_matrix_unchecked` ### Added - A `Unit` type that wraps normalized values. In particular, `UnitQuaternion` is now an alias for `Unit>`. - `.ln()`, `.exp()` and `.powf(..)` for quaternions and unit quaternions. - `::from_parts(...)` to build a quaternion from its scalar and vector parts. - The `Norm` trait now has a `try_normalize()` that returns `None` if the norm is too small. - The `BaseFloat` and `FloatVector` traits now inherit from `ApproxEq` as well. It is clear that performing computations with floats requires approximate equality. Still WIP: add implementations of abstract algebra traits from the `algebra` crate for vectors, rotations and points. To enable them, activate the `abstract_algebra` feature. ## [0.8.0] ### Modified * Almost everything (types, methods, and traits) now use fulls names instead of abbreviations (e.g. `Vec3` becomes `Vector3`). Most changes are obvious. Note however that: - `::sqnorm` becomes `::norm_squared`. - `::sqdist` becomes `::distance_squared`. - `::abs`, `::min`, etc. did not change as this is a common name for absolute values on, e.g., the libc. - Dynamically sized structures keep the `D` prefix, e.g., `DMat` becomes `DMatrix`. * All files with abbreviated names have been renamed to their full version, e.g., `vec.rs` becomes `vector.rs`. ## [0.7.0] ### Added * Added implementation of assignment operators (+=, -=, etc.) for everything. ### Modified * Points and vectors are now linked to each other with associated types (on the PointAsVector trait). ## [0.6.0] **Announcement:** a users forum has been created for `nalgebra`, `ncollide`, and `nphysics`. See you [there](https://users.nphysics.org)! ### Added * Added a dependency to [generic-array](https://crates.io/crates/generic-array). Feature-gated: requires `features="generic_sizes"`. * Added statically sized vectors with user-defined sizes: `VectorN`. Feature-gated: requires `features="generic_sizes"`. * Added similarity transformations (an uniform scale followed by a rotation followed by a translation): `Similarity2`, `Similarity3`. ### Removed * Removed zero-sized elements `Vector0`, `Point0`. * Removed 4-dimensional transformations `Rotation4` and `Isometry4` (which had an implementation too incomplete to be useful). ### Modified * Vectors are now multipliable with isometries. This will result into a pure rotation (this is how vectors differ from point semantically: they design directions, so they are not translatable). * `{Isometry3, Rotation3}::look_at` reimplemented and renamed to `::look_at_rh` and `::look_at_lh` to agree with the computer graphics community (in particular, the GLM library). Use the `::look_at_rh` variant to build a view matrix that may be successfully used with `Persp` and `Ortho`. * The old `{Isometry3, Rotation3}::look_at` implementations are now called `::new_observer_frame`. * Rename every `fov` on `Persp` to `fovy`. * Fixed the perspective and orthographic projection matrices. nalgebra-0.33.0/Cargo.lock0000644000001156030000000000100106530ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", "version_check", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "alga" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f823d037a7ec6ea2197046bafd4ae150e6bc36f9ca347404f46a46823fa84f2" dependencies = [ "approx 0.3.2", "num-complex 0.2.4", "num-traits", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "approx" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" dependencies = [ "num-traits", ] [[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-complex 0.4.6", "num-traits", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", "tap", "wyz", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecheck" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ "bytecheck_derive", "ptr_meta", "simdutf8", ] [[package]] name = "bytecheck_derive" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "bytemuck" version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" [[package]] name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags 1.3.2", "clap_lex", "indexmap 1.9.3", "textwrap", ] [[package]] name = "clap_lex" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] [[package]] name = "cool_asserts" version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee9f254e53f61e2688d3677fa2cbe4e9b950afd56f48819c98817417cf6b28ec" dependencies = [ "indent_write", ] [[package]] name = "cpufeatures" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "criterion" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ "anes", "atty", "cast", "ciborium", "clap", "criterion-plot", "itertools 0.10.5", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools 0.10.5", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "either" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "env_logger" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "log", "regex", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "glam" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" [[package]] name = "glam" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b" [[package]] name = "glam" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16" [[package]] name = "glam" version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776" [[package]] name = "glam" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34" [[package]] name = "glam" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca" [[package]] name = "glam" version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f" [[package]] name = "glam" version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" [[package]] name = "glam" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" [[package]] name = "glam" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" [[package]] name = "glam" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" [[package]] name = "glam" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" [[package]] name = "glam" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" [[package]] name = "glam" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "indent_write" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" [[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", ] [[package]] name = "indexmap" version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.5", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matrixcompare" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37832ba820e47c93d66b4360198dccb004b43c74abc3ac1ce1fed54e65a80445" dependencies = [ "matrixcompare-core", "num-traits", ] [[package]] name = "matrixcompare-core" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0bdabb30db18805d5290b3da7ceaccbddba795620b86c02145d688e04900a73" [[package]] name = "matrixmultiply" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" dependencies = [ "autocfg", "rawpointer", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mint" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" [[package]] name = "nalgebra" version = "0.33.0" dependencies = [ "alga", "approx 0.5.1", "bytemuck", "cool_asserts", "criterion", "glam 0.14.0", "glam 0.15.2", "glam 0.16.0", "glam 0.17.3", "glam 0.18.0", "glam 0.19.0", "glam 0.20.5", "glam 0.21.3", "glam 0.22.0", "glam 0.23.0", "glam 0.24.2", "glam 0.25.0", "glam 0.27.0", "glam 0.28.0", "itertools 0.13.0", "matrixcompare", "matrixcompare-core", "matrixmultiply", "mint", "nalgebra-macros", "num-complex 0.4.6", "num-rational", "num-traits", "pest", "pest_derive", "proptest", "quickcheck", "rand", "rand_distr", "rand_isaac", "rand_xorshift", "rayon", "rkyv", "serde", "serde_json", "simba", "trybuild", "typenum", ] [[package]] name = "nalgebra-macros" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", ] [[package]] name = "num-bigint" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-complex" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ "autocfg", "num-traits", ] [[package]] name = "num-complex" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", "serde", ] [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-rational" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pest" version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", ] [[package]] name = "pest_generator" version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", "syn 2.0.67", ] [[package]] name = "pest_meta" version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", "sha2", ] [[package]] name = "plotters" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bitflags 2.5.0", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax", "unarray", ] [[package]] name = "ptr_meta" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" dependencies = [ "ptr_meta_derive", ] [[package]] name = "ptr_meta_derive" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "quickcheck" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "env_logger", "log", "rand", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rand_distr" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", "rand", ] [[package]] name = "rand_isaac" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fac4373cd91b4f55722c553fb0f286edbb81ef3ff6eec7b99d1898a4110a0b28" dependencies = [ "rand_core", ] [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ "rand_core", ] [[package]] name = "rawpointer" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex" version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rend" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" version = "0.7.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" dependencies = [ "bitvec", "bytecheck", "bytes", "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", "tinyvec", "uuid", ] [[package]] name = "rkyv_derive" version = "0.7.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "safe_arch" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" dependencies = [ "bytemuck", ] [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "seahash" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", ] [[package]] name = "serde_json" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "simba" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" dependencies = [ "approx 0.5.1", "libm", "num-complex 0.4.6", "num-traits", "paste", "wide", ] [[package]] name = "simdutf8" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "trybuild" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" dependencies = [ "glob", "serde", "serde_derive", "serde_json", "termcolor", "toml", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "uuid" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.67", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn 2.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "wide" version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a040b111774ab63a19ef46bbc149398ab372b4ccdcfd719e9814dbd7dfd76c8" dependencies = [ "bytemuck", "safe_arch", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] [[package]] name = "wyz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] nalgebra-0.33.0/Cargo.toml0000644000000133360000000000100106760ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "nalgebra" version = "0.33.0" authors = ["Sébastien Crozet "] exclude = [ "/ci/*", "/.travis.yml", "/Makefile", ] description = "General-purpose linear algebra library with transformations and statically-sized or dynamically-sized matrices." homepage = "https://nalgebra.org" documentation = "https://www.nalgebra.org/docs" readme = "README.md" keywords = [ "linear", "algebra", "matrix", "vector", "math", ] categories = [ "science", "mathematics", "wasm", "no-std", ] license = "Apache-2.0" repository = "https://github.com/dimforge/nalgebra" resolver = "2" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [profile.bench] lto = true [lib] name = "nalgebra" path = "src/lib.rs" [[example]] name = "matrixcompare" required-features = ["compare"] [[bench]] name = "nalgebra_bench" path = "benches/lib.rs" harness = false required-features = ["rand"] [dependencies.alga] version = "0.9" optional = true default-features = false [dependencies.approx] version = "0.5" default-features = false [dependencies.bytemuck] version = "1.5" optional = true [dependencies.glam014] version = "0.14" optional = true package = "glam" [dependencies.glam015] version = "0.15" optional = true package = "glam" [dependencies.glam016] version = "0.16" optional = true package = "glam" [dependencies.glam017] version = "0.17" optional = true package = "glam" [dependencies.glam018] version = "0.18" optional = true package = "glam" [dependencies.glam019] version = "0.19" optional = true package = "glam" [dependencies.glam020] version = "0.20" optional = true package = "glam" [dependencies.glam021] version = "0.21" optional = true package = "glam" [dependencies.glam022] version = "0.22" optional = true package = "glam" [dependencies.glam023] version = "0.23" optional = true package = "glam" [dependencies.glam024] version = "0.24" optional = true package = "glam" [dependencies.glam025] version = "0.25" optional = true package = "glam" [dependencies.glam027] version = "0.27" optional = true package = "glam" [dependencies.glam028] version = "0.28" optional = true package = "glam" [dependencies.matrixcompare-core] version = "0.1" optional = true [dependencies.matrixmultiply] version = "0.3" optional = true [dependencies.mint] version = "0.5" optional = true [dependencies.nalgebra-macros] version = "0.2.2" optional = true [dependencies.num-complex] version = "0.4" default-features = false [dependencies.num-rational] version = "0.4" default-features = false [dependencies.num-traits] version = "0.2" default-features = false [dependencies.pest] version = "2" optional = true [dependencies.pest_derive] version = "2" optional = true [dependencies.proptest] version = "1" features = ["std"] optional = true default-features = false [dependencies.quickcheck] version = "1" optional = true [dependencies.rand-package] version = "0.8" optional = true default-features = false package = "rand" [dependencies.rand_distr] version = "0.4" optional = true default-features = false [dependencies.rayon] version = "1.6" optional = true [dependencies.rkyv] version = "0.7.41" optional = true default-features = false [dependencies.serde] version = "1.0" features = ["derive"] optional = true default-features = false [dependencies.simba] version = "0.9" default-features = false [dependencies.typenum] version = "1.12" [dev-dependencies.cool_asserts] version = "2.0.3" [dev-dependencies.criterion] version = "0.4" features = ["html_reports"] [dev-dependencies.itertools] version = "0.13" [dev-dependencies.matrixcompare] version = "0.3.0" [dev-dependencies.rand_isaac] version = "0.3" [dev-dependencies.rand_xorshift] version = "0.3" [dev-dependencies.serde_json] version = "1.0" [dev-dependencies.trybuild] version = "1.0.90" [features] alloc = [] arbitrary = ["quickcheck"] compare = ["matrixcompare-core"] convert-bytemuck = ["bytemuck"] convert-glam014 = ["glam014"] convert-glam015 = ["glam015"] convert-glam016 = ["glam016"] convert-glam017 = ["glam017"] convert-glam018 = ["glam018"] convert-glam019 = ["glam019"] convert-glam020 = ["glam020"] convert-glam021 = ["glam021"] convert-glam022 = ["glam022"] convert-glam023 = ["glam023"] convert-glam024 = ["glam024"] convert-glam025 = ["glam025"] convert-glam027 = ["glam027"] convert-glam028 = ["glam028"] convert-mint = ["mint"] debug = [ "approx/num-complex", "rand", ] default = [ "std", "macros", ] io = [ "pest", "pest_derive", ] libm = ["simba/libm"] libm-force = ["simba/libm_force"] macros = ["nalgebra-macros"] proptest-support = ["proptest"] rand = [ "rand-no-std", "rand-package/std", "rand-package/std_rng", "rand_distr", ] rand-no-std = ["rand-package"] rkyv-safe-deser = [ "rkyv-serialize", "rkyv/validation", ] rkyv-serialize = [ "rkyv-serialize-no-std", "rkyv/std", "rkyv/validation", ] rkyv-serialize-no-std = ["rkyv/size_32"] serde-serialize = [ "serde-serialize-no-std", "serde/std", ] serde-serialize-no-std = [ "serde", "num-complex/serde", ] slow-tests = [] sparse = [] std = [ "matrixmultiply", "num-traits/std", "num-complex/std", "num-rational/std", "approx/std", "simba/std", ] [badges.maintenance] status = "actively-developed" nalgebra-0.33.0/Cargo.toml.orig000064400000000000000000000130040072674642500143770ustar 00000000000000[package] name = "nalgebra" version = "0.33.0" authors = ["Sébastien Crozet "] description = "General-purpose linear algebra library with transformations and statically-sized or dynamically-sized matrices." documentation = "https://www.nalgebra.org/docs" homepage = "https://nalgebra.org" repository = "https://github.com/dimforge/nalgebra" readme = "README.md" categories = ["science", "mathematics", "wasm", "no-std"] keywords = ["linear", "algebra", "matrix", "vector", "math"] license = "Apache-2.0" edition = "2018" exclude = ["/ci/*", "/.travis.yml", "/Makefile"] [badges] maintenance = { status = "actively-developed" } [lib] name = "nalgebra" path = "src/lib.rs" [features] default = ["std", "macros"] std = ["matrixmultiply", "num-traits/std", "num-complex/std", "num-rational/std", "approx/std", "simba/std"] sparse = [] debug = ["approx/num-complex", "rand"] alloc = [] io = ["pest", "pest_derive"] compare = ["matrixcompare-core"] libm = ["simba/libm"] libm-force = ["simba/libm_force"] macros = ["nalgebra-macros"] # Conversion convert-mint = ["mint"] convert-bytemuck = ["bytemuck"] convert-glam014 = ["glam014"] convert-glam015 = ["glam015"] convert-glam016 = ["glam016"] convert-glam017 = ["glam017"] convert-glam018 = ["glam018"] convert-glam019 = ["glam019"] convert-glam020 = ["glam020"] convert-glam021 = ["glam021"] convert-glam022 = ["glam022"] convert-glam023 = ["glam023"] convert-glam024 = ["glam024"] convert-glam025 = ["glam025"] convert-glam027 = ["glam027"] convert-glam028 = ["glam028"] # Serialization ## To use serde in a #[no-std] environment, enable the ## `serde-serialize-no-std` feature instead of `serde-serialize`. ## Serialization of dynamically-sized matrices/vectors require ## `serde-serialize`. serde-serialize-no-std = ["serde", "num-complex/serde"] serde-serialize = ["serde-serialize-no-std", "serde/std"] rkyv-serialize-no-std = ["rkyv/size_32"] rkyv-serialize = ["rkyv-serialize-no-std", "rkyv/std", "rkyv/validation"] # Randomness ## To use rand in a #[no-std] environment, enable the ## `rand-no-std` feature instead of `rand`. rand-no-std = ["rand-package"] rand = ["rand-no-std", "rand-package/std", "rand-package/std_rng", "rand_distr"] # Tests arbitrary = ["quickcheck"] proptest-support = ["proptest"] slow-tests = [] rkyv-safe-deser = ["rkyv-serialize", "rkyv/validation"] [dependencies] nalgebra-macros = { version = "0.2.2", path = "nalgebra-macros", optional = true } typenum = "1.12" rand-package = { package = "rand", version = "0.8", optional = true, default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.4", default-features = false } num-rational = { version = "0.4", default-features = false } approx = { version = "0.5", default-features = false } simba = { version = "0.9", default-features = false } alga = { version = "0.9", default-features = false, optional = true } rand_distr = { version = "0.4", default-features = false, optional = true } matrixmultiply = { version = "0.3", optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } rkyv = { version = "0.7.41", default-features = false, optional = true } mint = { version = "0.5", optional = true } quickcheck = { version = "1", optional = true } pest = { version = "2", optional = true } pest_derive = { version = "2", optional = true } bytemuck = { version = "1.5", optional = true } matrixcompare-core = { version = "0.1", optional = true } proptest = { version = "1", optional = true, default-features = false, features = ["std"] } glam014 = { package = "glam", version = "0.14", optional = true } glam015 = { package = "glam", version = "0.15", optional = true } glam016 = { package = "glam", version = "0.16", optional = true } glam017 = { package = "glam", version = "0.17", optional = true } glam018 = { package = "glam", version = "0.18", optional = true } glam019 = { package = "glam", version = "0.19", optional = true } glam020 = { package = "glam", version = "0.20", optional = true } 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 } glam025 = { package = "glam", version = "0.25", optional = true } glam027 = { package = "glam", version = "0.27", optional = true } glam028 = { package = "glam", version = "0.28", optional = true } rayon = { version = "1.6", optional = true } [dev-dependencies] serde_json = "1.0" rand_xorshift = "0.3" rand_isaac = "0.3" criterion = { version = "0.4", features = ["html_reports"] } nalgebra = { path = ".", features = ["debug", "compare", "rand", "macros"] } # For matrix comparison macro matrixcompare = "0.3.0" itertools = "0.13" # For macro testing trybuild = "1.0.90" cool_asserts = "2.0.3" [workspace] members = ["nalgebra-lapack", "nalgebra-glm", "nalgebra-sparse", "nalgebra-macros"] resolver = "2" [[example]] name = "matrixcompare" required-features = ["compare"] [[bench]] name = "nalgebra_bench" harness = false path = "benches/lib.rs" required-features = ["rand"] #[profile.bench] #opt-level = 0 #lto = false [profile.bench] lto = true [package.metadata.docs.rs] # Enable all the features when building the docs on docs.rs all-features = true # define the configuration attribute `docsrs` rustdoc-args = ["--cfg", "docsrs"] nalgebra-0.33.0/LICENSE000064400000000000000000000264340072674642500125300ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2020 Sébastien Crozet Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. nalgebra-0.33.0/README.md000064400000000000000000000016020072674642500127700ustar 00000000000000

crates.io

crates.io

Users guide | Documentation

-----

Linear algebra library for the Rust programming language.

----- nalgebra-0.33.0/benches/common/macros.rs000064400000000000000000000065760072674642500162610ustar 00000000000000#![macro_use] macro_rules! bench_binop( ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { fn $name(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); bh.bench_function(stringify!($name), move |bh| bh.iter(|| { a.$binop(b) })); } } ); macro_rules! bench_binop_ref( ($name: ident, $t1: ty, $t2: ty, $binop: ident) => { fn $name(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); bh.bench_function(stringify!($name), move |bh| bh.iter(|| { a.$binop(&b) })); } } ); macro_rules! bench_binop_fn( ($name: ident, $t1: ty, $t2: ty, $binop: path) => { fn $name(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let a = rng.gen::<$t1>(); let b = rng.gen::<$t2>(); bh.bench_function(stringify!($name), move |bh| bh.iter(|| { $binop(&a, &b) })); } } ); macro_rules! bench_unop_na( ($name: ident, $t: ty, $unop: ident) => { fn $name(bh: &mut criterion::Criterion) { const LEN: usize = 1 << 13; use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let elems: Vec<$t> = (0usize .. LEN).map(|_| rng.gen::<$t>()).collect(); let mut i = 0; bh.bench_function(stringify!($name), move |bh| bh.iter(|| { i = (i + 1) & (LEN - 1); unsafe { std::hint::black_box(na::$unop(elems.get_unchecked(i))) } })); } } ); macro_rules! bench_unop( ($name: ident, $t: ty, $unop: ident) => { fn $name(bh: &mut criterion::Criterion) { const LEN: usize = 1 << 13; use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let mut elems: Vec<$t> = (0usize .. LEN).map(|_| rng.gen::<$t>()).collect(); let mut i = 0; bh.bench_function(stringify!($name), move |bh| bh.iter(|| { i = (i + 1) & (LEN - 1); unsafe { std::hint::black_box(elems.get_unchecked_mut(i).$unop()) } })); } } ); macro_rules! bench_construction( ($name: ident, $constructor: path, $( $args: ident: $types: ty),*) => { fn $name(bh: &mut criterion::Criterion) { const LEN: usize = 1 << 13; use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); $(let $args: Vec<$types> = (0usize .. LEN).map(|_| rng.gen::<$types>()).collect();)* let mut i = 0; bh.bench_function(stringify!($name), move |bh| bh.iter(|| { i = (i + 1) & (LEN - 1); unsafe { let res = $constructor($(*$args.get_unchecked(i),)*); std::hint::black_box(res) } })); } } ); nalgebra-0.33.0/benches/core/matrix.rs000064400000000000000000000200230072674642500157200ustar 00000000000000use na::{DMatrix, DVector, Matrix2, Matrix3, Matrix4, OMatrix, Vector2, Vector3, Vector4, U10}; use rand::Rng; use rand_isaac::IsaacRng; use std::ops::{Add, Div, Mul, Sub}; #[path = "../common/macros.rs"] mod macros; bench_binop!(mat2_mul_m, Matrix2, Matrix2, mul); bench_binop!(mat3_mul_m, Matrix3, Matrix3, mul); bench_binop!(mat4_mul_m, Matrix4, Matrix4, mul); bench_binop_ref!(mat2_tr_mul_m, Matrix2, Matrix2, tr_mul); bench_binop_ref!(mat3_tr_mul_m, Matrix3, Matrix3, tr_mul); bench_binop_ref!(mat4_tr_mul_m, Matrix4, Matrix4, tr_mul); bench_binop!(mat2_add_m, Matrix2, Matrix2, add); bench_binop!(mat3_add_m, Matrix3, Matrix3, add); bench_binop!(mat4_add_m, Matrix4, Matrix4, add); bench_binop!(mat2_sub_m, Matrix2, Matrix2, sub); bench_binop!(mat3_sub_m, Matrix3, Matrix3, sub); bench_binop!(mat4_sub_m, Matrix4, Matrix4, sub); bench_binop!(mat2_mul_v, Matrix2, Vector2, mul); bench_binop!(mat3_mul_v, Matrix3, Vector3, mul); bench_binop!(mat4_mul_v, Matrix4, Vector4, mul); bench_binop_ref!(mat2_tr_mul_v, Matrix2, Vector2, tr_mul); bench_binop_ref!(mat3_tr_mul_v, Matrix3, Vector3, tr_mul); bench_binop_ref!(mat4_tr_mul_v, Matrix4, Vector4, tr_mul); bench_binop!(mat2_mul_s, Matrix2, f32, mul); bench_binop!(mat3_mul_s, Matrix3, f32, mul); bench_binop!(mat4_mul_s, Matrix4, f32, mul); bench_binop!(mat2_div_s, Matrix2, f32, div); bench_binop!(mat3_div_s, Matrix3, f32, div); bench_binop!(mat4_div_s, Matrix4, f32, div); bench_unop!(mat2_inv, Matrix2, try_inverse); bench_unop!(mat3_inv, Matrix3, try_inverse); bench_unop!(mat4_inv, Matrix4, try_inverse); bench_unop!(mat2_transpose, Matrix2, transpose); bench_unop!(mat3_transpose, Matrix3, transpose); bench_unop!(mat4_transpose, Matrix4, transpose); fn mat_div_scalar(b: &mut criterion::Criterion) { let a = DMatrix::from_row_slice(1000, 1000, &vec![2.0; 1000000]); let n = 42.0; b.bench_function("mat_div_scalar", move |bh| { bh.iter(|| { let mut aa = a.clone(); let mut b = aa.view_mut((0, 0), (1000, 1000)); b /= n }) }); } fn mat100_add_mat100(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(100, 100); let b = DMatrix::::new_random(100, 100); bench.bench_function("mat100_add_mat100", move |bh| bh.iter(|| &a + &b)); } fn mat4_mul_mat4(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(4, 4); let b = DMatrix::::new_random(4, 4); bench.bench_function("mat4_mul_mat4", move |bh| bh.iter(|| &a * &b)); } fn mat5_mul_mat5(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(5, 5); let b = DMatrix::::new_random(5, 5); bench.bench_function("mat5_mul_mat5", move |bh| bh.iter(|| &a * &b)); } fn mat6_mul_mat6(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(6, 6); let b = DMatrix::::new_random(6, 6); bench.bench_function("mat6_mul_mat6", move |bh| bh.iter(|| &a * &b)); } fn mat7_mul_mat7(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(7, 7); let b = DMatrix::::new_random(7, 7); bench.bench_function("mat7_mul_mat7", move |bh| bh.iter(|| &a * &b)); } fn mat8_mul_mat8(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(8, 8); let b = DMatrix::::new_random(8, 8); bench.bench_function("mat8_mul_mat8", move |bh| bh.iter(|| &a * &b)); } fn mat9_mul_mat9(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(9, 9); let b = DMatrix::::new_random(9, 9); bench.bench_function("mat9_mul_mat9", move |bh| bh.iter(|| &a * &b)); } fn mat10_mul_mat10(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(10, 10); let b = DMatrix::::new_random(10, 10); bench.bench_function("mat10_mul_mat10", move |bh| bh.iter(|| &a * &b)); } fn mat10_mul_mat10_static(bench: &mut criterion::Criterion) { let a = OMatrix::::new_random(); let b = OMatrix::::new_random(); bench.bench_function("mat10_mul_mat10_static", move |bh| bh.iter(|| &a * &b)); } fn mat100_mul_mat100(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(100, 100); let b = DMatrix::::new_random(100, 100); bench.bench_function("mat100_mul_mat100", move |bh| bh.iter(|| &a * &b)); } fn mat500_mul_mat500(bench: &mut criterion::Criterion) { let a = DMatrix::::from_element(500, 500, 5f64); let b = DMatrix::::from_element(500, 500, 6f64); bench.bench_function("mat500_mul_mat500", move |bh| bh.iter(|| &a * &b)); } fn iter(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(1000, 1000); bench.bench_function("iter", move |bh| { bh.iter(|| { for value in a.iter() { std::hint::black_box(value); } }) }); } fn iter_rev(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(1000, 1000); bench.bench_function("iter_rev", move |bh| { bh.iter(|| { for value in a.iter().rev() { std::hint::black_box(value); } }) }); } fn copy_from(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(1000, 1000); let mut b = DMatrix::::new_random(1000, 1000); bench.bench_function("copy_from", move |bh| { bh.iter(|| { b.copy_from(&a); }) }); } fn axpy(bench: &mut criterion::Criterion) { let x = DVector::::from_element(100000, 2.0); let mut y = DVector::::from_element(100000, 3.0); let a = 42.0; bench.bench_function("axpy", move |bh| { bh.iter(|| { y.axpy(a, &x, 1.0); }) }); } fn tr_mul_to(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(1000, 1000); let b = DVector::::new_random(1000); let mut c = DVector::from_element(1000, 0.0); bench.bench_function("tr_mul_to", move |bh| bh.iter(|| a.tr_mul_to(&b, &mut c))); } fn mat_mul_mat(bench: &mut criterion::Criterion) { let a = DMatrix::::new_random(100, 100); let b = DMatrix::::new_random(100, 100); let mut ab = DMatrix::::from_element(100, 100, 0.0); bench.bench_function("mat_mul_mat", move |bh| { bh.iter(|| { std::hint::black_box(a.mul_to(&b, &mut ab)); }) }); } fn mat100_from_fn(bench: &mut criterion::Criterion) { bench.bench_function("mat100_from_fn", move |bh| { bh.iter(|| DMatrix::from_fn(100, 100, |a, b| a + b)) }); } fn mat500_from_fn(bench: &mut criterion::Criterion) { bench.bench_function("mat500_from_fn", move |bh| { bh.iter(|| DMatrix::from_fn(500, 500, |a, b| a + b)) }); } criterion_group!( matrix, mat2_mul_m, mat3_mul_m, mat4_mul_m, mat2_tr_mul_m, mat3_tr_mul_m, mat4_tr_mul_m, mat2_add_m, mat3_add_m, mat4_add_m, mat2_sub_m, mat3_sub_m, mat4_sub_m, mat2_mul_v, mat3_mul_v, mat4_mul_v, mat2_tr_mul_v, mat3_tr_mul_v, mat4_tr_mul_v, mat2_mul_s, mat3_mul_s, mat4_mul_s, mat2_div_s, mat3_div_s, mat4_div_s, mat2_inv, mat3_inv, mat4_inv, mat2_transpose, mat3_transpose, mat4_transpose, mat_div_scalar, mat100_add_mat100, mat4_mul_mat4, mat5_mul_mat5, mat6_mul_mat6, mat7_mul_mat7, mat8_mul_mat8, mat9_mul_mat9, mat10_mul_mat10, mat10_mul_mat10_static, mat100_mul_mat100, mat500_mul_mat500, iter, iter_rev, copy_from, axpy, tr_mul_to, mat_mul_mat, mat100_from_fn, mat500_from_fn, ); nalgebra-0.33.0/benches/core/mod.rs000064400000000000000000000001320072674642500151720ustar 00000000000000pub use self::matrix::matrix; pub use self::vector::vector; mod matrix; mod vector; nalgebra-0.33.0/benches/core/vector.rs000064400000000000000000000122560072674642500157270ustar 00000000000000use na::{DVector, SVector, Vector2, Vector3, Vector4}; use rand::Rng; use rand_isaac::IsaacRng; use std::ops::{Add, Div, Mul, Sub}; #[path = "../common/macros.rs"] mod macros; bench_binop!(vec2_add_v_f32, Vector2, Vector2, add); bench_binop!(vec3_add_v_f32, Vector3, Vector3, add); bench_binop!(vec4_add_v_f32, Vector4, Vector4, add); bench_binop!(vec2_add_v_f64, Vector2, Vector2, add); bench_binop!(vec3_add_v_f64, Vector3, Vector3, add); bench_binop!(vec4_add_v_f64, Vector4, Vector4, add); bench_binop!(vec2_sub_v, Vector2, Vector2, sub); bench_binop!(vec3_sub_v, Vector3, Vector3, sub); bench_binop!(vec4_sub_v, Vector4, Vector4, sub); bench_binop!(vec2_mul_s, Vector2, f32, mul); bench_binop!(vec3_mul_s, Vector3, f32, mul); bench_binop!(vec4_mul_s, Vector4, f32, mul); bench_binop!(vec2_div_s, Vector2, f32, div); bench_binop!(vec3_div_s, Vector3, f32, div); bench_binop!(vec4_div_s, Vector4, f32, div); bench_binop_ref!(vec2_dot_f32, Vector2, Vector2, dot); bench_binop_ref!(vec3_dot_f32, Vector3, Vector3, dot); bench_binop_ref!(vec4_dot_f32, Vector4, Vector4, dot); bench_binop_ref!(vec2_dot_f64, Vector2, Vector2, dot); bench_binop_ref!(vec3_dot_f64, Vector3, Vector3, dot); bench_binop_ref!(vec4_dot_f64, Vector4, Vector4, dot); bench_binop_ref!(vec3_cross, Vector3, Vector3, cross); bench_unop!(vec2_norm, Vector2, norm); bench_unop!(vec3_norm, Vector3, norm); bench_unop!(vec4_norm, Vector4, norm); bench_unop!(vec2_normalize, Vector2, normalize); bench_unop!(vec3_normalize, Vector3, normalize); bench_unop!(vec4_normalize, Vector4, normalize); bench_binop_ref!(vec10000_dot_f64, SVector, SVector, dot); bench_binop_ref!(vec10000_dot_f32, SVector, SVector, dot); fn vec10000_axpy_f64(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); bh.bench_function("vec10000_axpy_f64", move |bh| { bh.iter(|| a.axpy(n, &b, 1.0)) }); } fn vec10000_axpy_beta_f64(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); let beta = rng.gen::(); bh.bench_function("vec10000_axpy_beta_f64", move |bh| { bh.iter(|| a.axpy(n, &b, beta)) }); } fn vec10000_axpy_f64_slice(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); bh.bench_function("vec10000_axpy_f64_slice", move |bh| { bh.iter(|| { let mut a = a.fixed_rows_mut::<10000>(0); let b = b.fixed_rows::<10000>(0); a.axpy(n, &b, 1.0) }) }); } fn vec10000_axpy_f64_static(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let mut a = SVector::::new_random(); let b = SVector::::new_random(); let n = rng.gen::(); // NOTE: for some reasons, it is much faster if the arument are boxed (Box::new(OVector...)). bh.bench_function("vec10000_axpy_f64_static", move |bh| { bh.iter(|| a.axpy(n, &b, 1.0)) }); } fn vec10000_axpy_f32(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); bh.bench_function("vec10000_axpy_f32", move |bh| { bh.iter(|| a.axpy(n, &b, 1.0)) }); } fn vec10000_axpy_beta_f32(bh: &mut criterion::Criterion) { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); let mut a = DVector::new_random(10000); let b = DVector::new_random(10000); let n = rng.gen::(); let beta = rng.gen::(); bh.bench_function("vec10000_axpy_beta_f32", move |bh| { bh.iter(|| a.axpy(n, &b, beta)) }); } criterion_group!( vector, vec2_add_v_f32, vec3_add_v_f32, vec4_add_v_f32, vec2_add_v_f64, vec3_add_v_f64, vec4_add_v_f64, vec2_sub_v, vec3_sub_v, vec4_sub_v, vec2_mul_s, vec3_mul_s, vec4_mul_s, vec2_div_s, vec3_div_s, vec4_div_s, vec2_dot_f32, vec3_dot_f32, vec4_dot_f32, vec2_dot_f64, vec3_dot_f64, vec4_dot_f64, vec3_cross, vec2_norm, vec3_norm, vec4_norm, vec2_normalize, vec3_normalize, vec4_normalize, vec10000_dot_f64, vec10000_dot_f32, vec10000_axpy_f64, vec10000_axpy_beta_f64, vec10000_axpy_f64_slice, vec10000_axpy_f64_static, vec10000_axpy_f32, vec10000_axpy_beta_f32 ); nalgebra-0.33.0/benches/geometry/mod.rs000064400000000000000000000000720072674642500161000ustar 00000000000000pub use self::quaternion::quaternion; mod quaternion; nalgebra-0.33.0/benches/geometry/quaternion.rs000064400000000000000000000021360072674642500175110ustar 00000000000000use na::{Quaternion, UnitQuaternion, Vector3}; use rand::Rng; use rand_isaac::IsaacRng; use std::ops::{Add, Div, Mul, Sub}; #[path = "../common/macros.rs"] mod macros; bench_binop!(quaternion_add_q, Quaternion, Quaternion, add); bench_binop!(quaternion_sub_q, Quaternion, Quaternion, sub); bench_binop!(quaternion_mul_q, Quaternion, Quaternion, mul); bench_binop!( unit_quaternion_mul_v, UnitQuaternion, Vector3, mul ); bench_binop!(quaternion_mul_s, Quaternion, f32, mul); bench_binop!(quaternion_div_s, Quaternion, f32, div); bench_unop!(quaternion_inv, Quaternion, try_inverse); bench_unop!(unit_quaternion_inv, UnitQuaternion, inverse); // bench_unop_self!(quaternion_conjugate, Quaternion, conjugate); // bench_unop!(quaternion_normalize, Quaternion, normalize); criterion_group!( quaternion, quaternion_add_q, quaternion_sub_q, quaternion_mul_q, unit_quaternion_mul_v, quaternion_mul_s, quaternion_div_s, quaternion_inv, unit_quaternion_inv ); nalgebra-0.33.0/benches/lib.rs000064400000000000000000000014060072674642500142360ustar 00000000000000#![allow(unused_macros)] extern crate nalgebra as na; extern crate rand_package as rand; #[macro_use] extern crate criterion; use na::DMatrix; use rand::Rng; use rand_isaac::IsaacRng; pub mod core; pub mod geometry; pub mod linalg; fn reproductible_dmatrix(nrows: usize, ncols: usize) -> DMatrix { use rand::SeedableRng; let mut rng = IsaacRng::seed_from_u64(0); DMatrix::::from_fn(nrows, ncols, |_, _| rng.gen()) } criterion_main!( core::matrix, core::vector, geometry::quaternion, linalg::bidiagonal, linalg::cholesky, linalg::full_piv_lu, linalg::hessenberg, linalg::lu, linalg::qr, linalg::schur, linalg::solve, linalg::svd, linalg::symmetric_eigen, ); nalgebra-0.33.0/benches/linalg/bidiagonal.rs000064400000000000000000000057250072674642500170370ustar 00000000000000use na::{Bidiagonal, DMatrix, Matrix4}; #[path = "../common/macros.rs"] mod macros; // Without unpack. fn bidiagonalize_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("bidiagonalize_100x100", move |bh| { bh.iter(|| std::hint::black_box(Bidiagonal::new(m.clone()))) }); } fn bidiagonalize_100x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 500); bh.bench_function("bidiagonalize_100x500", move |bh| { bh.iter(|| std::hint::black_box(Bidiagonal::new(m.clone()))) }); } fn bidiagonalize_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("bidiagonalize_4x4", move |bh| { bh.iter(|| std::hint::black_box(Bidiagonal::new(m.clone()))) }); } fn bidiagonalize_500x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 100); bh.bench_function("bidiagonalize_500x100", move |bh| { bh.iter(|| std::hint::black_box(Bidiagonal::new(m.clone()))) }); } fn bidiagonalize_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("bidiagonalize_500x500", move |bh| { bh.iter(|| std::hint::black_box(Bidiagonal::new(m.clone()))) }); } // With unpack. fn bidiagonalize_unpack_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("bidiagonalize_unpack_100x100", move |bh| { bh.iter(|| { let bidiag = Bidiagonal::new(m.clone()); let _ = bidiag.unpack(); }) }); } fn bidiagonalize_unpack_100x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 500); bh.bench_function("bidiagonalize_unpack_100x500", move |bh| { bh.iter(|| { let bidiag = Bidiagonal::new(m.clone()); let _ = bidiag.unpack(); }) }); } fn bidiagonalize_unpack_500x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 100); bh.bench_function("bidiagonalize_unpack_500x100", move |bh| { bh.iter(|| { let bidiag = Bidiagonal::new(m.clone()); let _ = bidiag.unpack(); }) }); } fn bidiagonalize_unpack_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("bidiagonalize_unpack_500x500", move |bh| { bh.iter(|| { let bidiag = Bidiagonal::new(m.clone()); let _ = bidiag.unpack(); }) }); } criterion_group!( bidiagonal, bidiagonalize_100x100, bidiagonalize_100x500, bidiagonalize_4x4, bidiagonalize_500x100, // bidiagonalize_500x500, // too long bidiagonalize_unpack_100x100, bidiagonalize_unpack_100x500, bidiagonalize_unpack_500x100, // bidiagonalize_unpack_500x500 // too long ); nalgebra-0.33.0/benches/linalg/cholesky.rs000064400000000000000000000074600072674642500165650ustar 00000000000000use na::{Cholesky, DMatrix, DVector}; fn cholesky_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let m = &m * m.transpose(); bh.bench_function("cholesky_100x100", move |bh| { bh.iter(|| std::hint::black_box(Cholesky::new(m.clone()))) }); } fn cholesky_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let m = &m * m.transpose(); bh.bench_function("cholesky_500x500", move |bh| { bh.iter(|| std::hint::black_box(Cholesky::new(m.clone()))) }); } // With unpack. fn cholesky_decompose_unpack_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let m = &m * m.transpose(); bh.bench_function("cholesky_decompose_unpack_100x100", move |bh| { bh.iter(|| { let chol = Cholesky::new(m.clone()).unwrap(); let _ = chol.unpack(); }) }); } fn cholesky_decompose_unpack_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let m = &m * m.transpose(); bh.bench_function("cholesky_decompose_unpack_500x500", move |bh| { bh.iter(|| { let chol = Cholesky::new(m.clone()).unwrap(); let _ = chol.unpack(); }) }); } fn cholesky_solve_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let m = &m * m.transpose(); let v = DVector::::new_random(10); let chol = Cholesky::new(m.clone()).unwrap(); bh.bench_function("cholesky_solve_10x10", move |bh| { bh.iter(|| { let _ = chol.solve(&v); }) }); } fn cholesky_solve_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let m = &m * m.transpose(); let v = DVector::::new_random(100); let chol = Cholesky::new(m.clone()).unwrap(); bh.bench_function("cholesky_solve_100x100", move |bh| { bh.iter(|| { let _ = chol.solve(&v); }) }); } fn cholesky_solve_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let m = &m * m.transpose(); let v = DVector::::new_random(500); let chol = Cholesky::new(m.clone()).unwrap(); bh.bench_function("cholesky_solve_500x500", move |bh| { bh.iter(|| { let _ = chol.solve(&v); }) }); } fn cholesky_inverse_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let m = &m * m.transpose(); let chol = Cholesky::new(m.clone()).unwrap(); bh.bench_function("cholesky_inverse_10x10", move |bh| { bh.iter(|| { let _ = chol.inverse(); }) }); } fn cholesky_inverse_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let m = &m * m.transpose(); let chol = Cholesky::new(m.clone()).unwrap(); bh.bench_function("cholesky_inverse_100x100", move |bh| { bh.iter(|| { let _ = chol.inverse(); }) }); } fn cholesky_inverse_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let m = &m * m.transpose(); let chol = Cholesky::new(m.clone()).unwrap(); bh.bench_function("cholesky_inverse_500x500", move |bh| { bh.iter(|| { let _ = chol.inverse(); }) }); } criterion_group!( cholesky, cholesky_100x100, cholesky_500x500, cholesky_decompose_unpack_100x100, cholesky_decompose_unpack_500x500, cholesky_solve_10x10, cholesky_solve_100x100, cholesky_solve_500x500, cholesky_inverse_10x10, cholesky_inverse_100x100, cholesky_inverse_500x500 ); nalgebra-0.33.0/benches/linalg/eigen.rs000064400000000000000000000017510072674642500160300ustar 00000000000000use na::{DMatrix, Eigen}; fn eigen_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("eigen_100x100", move |bh| bh.iter(|| Eigen::new(m.clone(), 1.0e-7, 0))); } fn eigen_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("eigen_500x500", move |bh| bh.iter(|| Eigen::new(m.clone(), 1.0e-7, 0))); } fn eigenvalues_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("eigenvalues_100x100", move |bh| bh.iter(|| m.clone().eigenvalues(1.0e-7, 0))); } fn eigenvalues_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("eigenvalues_500x500", move |bh| bh.iter(|| m.clone().eigenvalues(1.0e-7, 0))); } criterion_group!(eigen, eigen_100x100, // eigen_500x500, eigenvalues_100x100, // eigenvalues_500x500 ); nalgebra-0.33.0/benches/linalg/full_piv_lu.rs000064400000000000000000000102110072674642500172500ustar 00000000000000use na::{DMatrix, DVector, FullPivLU}; // Without unpack. fn full_piv_lu_decompose_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); bh.bench_function("full_piv_lu_decompose_10x10", move |bh| { bh.iter(|| std::hint::black_box(FullPivLU::new(m.clone()))) }); } fn full_piv_lu_decompose_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("full_piv_lu_decompose_100x100", move |bh| { bh.iter(|| std::hint::black_box(FullPivLU::new(m.clone()))) }); } fn full_piv_lu_decompose_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("full_piv_lu_decompose_500x500", move |bh| { bh.iter(|| std::hint::black_box(FullPivLU::new(m.clone()))) }); } fn full_piv_lu_solve_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_solve_10x10", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(10, 1.0); lu.solve(&mut b); }) }); } fn full_piv_lu_solve_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_solve_100x100", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(100, 1.0); lu.solve(&mut b); }) }); } fn full_piv_lu_solve_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_solve_500x500", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(500, 1.0); lu.solve(&mut b); }) }); } fn full_piv_lu_inverse_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_inverse_10x10", move |bh| { bh.iter(|| std::hint::black_box(lu.try_inverse())) }); } fn full_piv_lu_inverse_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_inverse_100x100", move |bh| { bh.iter(|| std::hint::black_box(lu.try_inverse())) }); } fn full_piv_lu_inverse_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_inverse_500x500", move |bh| { bh.iter(|| std::hint::black_box(lu.try_inverse())) }); } fn full_piv_lu_determinant_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_determinant_10x10", move |bh| { bh.iter(|| std::hint::black_box(lu.determinant())) }); } fn full_piv_lu_determinant_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_determinant_100x100", move |bh| { bh.iter(|| std::hint::black_box(lu.determinant())) }); } fn full_piv_lu_determinant_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let lu = FullPivLU::new(m.clone()); bh.bench_function("full_piv_lu_determinant_500x500", move |bh| { bh.iter(|| std::hint::black_box(lu.determinant())) }); } criterion_group!( full_piv_lu, full_piv_lu_decompose_10x10, full_piv_lu_decompose_100x100, // full_piv_lu_decompose_500x500, full_piv_lu_solve_10x10, full_piv_lu_solve_100x100, // full_piv_lu_solve_500x500, full_piv_lu_inverse_10x10, full_piv_lu_inverse_100x100, // full_piv_lu_inverse_500x500, full_piv_lu_determinant_10x10, full_piv_lu_determinant_100x100, // full_piv_lu_determinant_500x500 ); nalgebra-0.33.0/benches/linalg/hessenberg.rs000064400000000000000000000047030072674642500170660ustar 00000000000000use na::{DMatrix, Hessenberg, Matrix4}; #[path = "../common/macros.rs"] mod macros; // Without unpack. fn hessenberg_decompose_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("hessenberg_decompose_4x4", move |bh| { bh.iter(|| std::hint::black_box(Hessenberg::new(m.clone()))) }); } fn hessenberg_decompose_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("hessenberg_decompose_100x100", move |bh| { bh.iter(|| std::hint::black_box(Hessenberg::new(m.clone()))) }); } fn hessenberg_decompose_200x200(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(200, 200); bh.bench_function("hessenberg_decompose_200x200", move |bh| { bh.iter(|| std::hint::black_box(Hessenberg::new(m.clone()))) }); } fn hessenberg_decompose_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("hessenberg_decompose_500x500", move |bh| { bh.iter(|| std::hint::black_box(Hessenberg::new(m.clone()))) }); } // With unpack. fn hessenberg_decompose_unpack_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("hessenberg_decompose_unpack_100x100", move |bh| { bh.iter(|| { let hess = Hessenberg::new(m.clone()); let _ = hess.unpack(); }) }); } fn hessenberg_decompose_unpack_200x200(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(200, 200); bh.bench_function("hessenberg_decompose_unpack_200x200", move |bh| { bh.iter(|| { let hess = Hessenberg::new(m.clone()); let _ = hess.unpack(); }) }); } fn hessenberg_decompose_unpack_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("hessenberg_decompose_unpack_500x500", move |bh| { bh.iter(|| { let hess = Hessenberg::new(m.clone()); let _ = hess.unpack(); }) }); } criterion_group!( hessenberg, hessenberg_decompose_4x4, hessenberg_decompose_100x100, hessenberg_decompose_200x200, // hessenberg_decompose_500x500, hessenberg_decompose_unpack_100x100, hessenberg_decompose_unpack_200x200, // hessenberg_decompose_unpack_500x500 ); nalgebra-0.33.0/benches/linalg/lu.rs000064400000000000000000000071730072674642500153650ustar 00000000000000use na::{DMatrix, DVector, LU}; // Without unpack. fn lu_decompose_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); bh.bench_function("lu_decompose_10x10", move |bh| { bh.iter(|| std::hint::black_box(LU::new(m.clone()))) }); } fn lu_decompose_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("lu_decompose_100x100", move |bh| { bh.iter(|| std::hint::black_box(LU::new(m.clone()))) }); } fn lu_decompose_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("lu_decompose_500x500", move |bh| { bh.iter(|| std::hint::black_box(LU::new(m.clone()))) }); } fn lu_solve_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let lu = LU::new(m.clone()); bh.bench_function("lu_solve_10x10", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(10, 1.0); lu.solve(&mut b); }) }); } fn lu_solve_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let lu = LU::new(m.clone()); bh.bench_function("lu_solve_100x100", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(100, 1.0); lu.solve(&mut b); }) }); } fn lu_solve_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let lu = LU::new(m.clone()); bh.bench_function("", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(500, 1.0); lu.solve(&mut b); }) }); } fn lu_inverse_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let lu = LU::new(m.clone()); bh.bench_function("lu_inverse_10x10", move |bh| { bh.iter(|| std::hint::black_box(lu.try_inverse())) }); } fn lu_inverse_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let lu = LU::new(m.clone()); bh.bench_function("lu_inverse_100x100", move |bh| { bh.iter(|| std::hint::black_box(lu.try_inverse())) }); } fn lu_inverse_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let lu = LU::new(m.clone()); bh.bench_function("lu_inverse_500x500", move |bh| { bh.iter(|| std::hint::black_box(lu.try_inverse())) }); } fn lu_determinant_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let lu = LU::new(m.clone()); bh.bench_function("lu_determinant_10x10", move |bh| { bh.iter(|| std::hint::black_box(lu.determinant())) }); } fn lu_determinant_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let lu = LU::new(m.clone()); bh.bench_function("lu_determinant_100x100", move |bh| { bh.iter(|| std::hint::black_box(lu.determinant())) }); } fn lu_determinant_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let lu = LU::new(m.clone()); bh.bench_function("", move |bh| { bh.iter(|| std::hint::black_box(lu.determinant())) }); } criterion_group!( lu, lu_decompose_10x10, lu_decompose_100x100, // lu_decompose_500x500, lu_solve_10x10, lu_solve_100x100, lu_inverse_10x10, lu_inverse_100x100, // lu_inverse_500x500, lu_determinant_10x10, lu_determinant_100x100 ); nalgebra-0.33.0/benches/linalg/mod.rs000064400000000000000000000007520072674642500155200ustar 00000000000000pub use self::bidiagonal::bidiagonal; pub use self::cholesky::cholesky; pub use self::full_piv_lu::full_piv_lu; pub use self::hessenberg::hessenberg; pub use self::lu::lu; pub use self::qr::qr; pub use self::schur::schur; pub use self::solve::solve; pub use self::svd::svd; pub use self::symmetric_eigen::symmetric_eigen; mod bidiagonal; mod cholesky; mod full_piv_lu; mod hessenberg; mod lu; mod qr; mod schur; mod solve; mod svd; mod symmetric_eigen; // mod eigen; nalgebra-0.33.0/benches/linalg/qr.rs000064400000000000000000000113600072674642500153600ustar 00000000000000use na::{DMatrix, DVector, Matrix4, QR}; #[path = "../common/macros.rs"] mod macros; // Without unpack. fn qr_decompose_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("qr_decompose_100x100", move |bh| { bh.iter(|| std::hint::black_box(QR::new(m.clone()))) }); } fn qr_decompose_100x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 500); bh.bench_function("qr_decompose_100x500", move |bh| { bh.iter(|| std::hint::black_box(QR::new(m.clone()))) }); } fn qr_decompose_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("qr_decompose_4x4", move |bh| { bh.iter(|| std::hint::black_box(QR::new(m.clone()))) }); } fn qr_decompose_500x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 100); bh.bench_function("qr_decompose_500x100", move |bh| { bh.iter(|| std::hint::black_box(QR::new(m.clone()))) }); } fn qr_decompose_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("qr_decompose_500x500", move |bh| { bh.iter(|| std::hint::black_box(QR::new(m.clone()))) }); } // With unpack. fn qr_decompose_unpack_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); bh.bench_function("qr_decompose_unpack_100x100", move |bh| { bh.iter(|| { let qr = QR::new(m.clone()); let _ = qr.unpack(); }) }); } fn qr_decompose_unpack_100x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 500); bh.bench_function("qr_decompose_unpack_100x500", move |bh| { bh.iter(|| { let qr = QR::new(m.clone()); let _ = qr.unpack(); }) }); } fn qr_decompose_unpack_500x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 100); bh.bench_function("qr_decompose_unpack_500x100", move |bh| { bh.iter(|| { let qr = QR::new(m.clone()); let _ = qr.unpack(); }) }); } fn qr_decompose_unpack_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); bh.bench_function("qr_decompose_unpack_500x500", move |bh| { bh.iter(|| { let qr = QR::new(m.clone()); let _ = qr.unpack(); }) }); } fn qr_solve_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let qr = QR::new(m.clone()); bh.bench_function("qr_solve_10x10", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(10, 1.0); qr.solve(&mut b); }) }); } fn qr_solve_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let qr = QR::new(m.clone()); bh.bench_function("qr_solve_100x100", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(100, 1.0); qr.solve(&mut b); }) }); } fn qr_solve_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let qr = QR::new(m.clone()); bh.bench_function("qr_solve_500x500", move |bh| { bh.iter(|| { let mut b = DVector::::from_element(500, 1.0); qr.solve(&mut b); }) }); } fn qr_inverse_10x10(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(10, 10); let qr = QR::new(m.clone()); bh.bench_function("qr_inverse_10x10", move |bh| { bh.iter(|| std::hint::black_box(qr.try_inverse())) }); } fn qr_inverse_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let qr = QR::new(m.clone()); bh.bench_function("qr_inverse_100x100", move |bh| { bh.iter(|| std::hint::black_box(qr.try_inverse())) }); } fn qr_inverse_500x500(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(500, 500); let qr = QR::new(m.clone()); bh.bench_function("qr_inverse_500x500", move |bh| { bh.iter(|| std::hint::black_box(qr.try_inverse())) }); } criterion_group!( qr, qr_decompose_100x100, qr_decompose_100x500, qr_decompose_4x4, qr_decompose_500x100, // qr_decompose_500x500, qr_decompose_unpack_100x100, qr_decompose_unpack_100x500, qr_decompose_unpack_500x100, // qr_decompose_unpack_500x500, qr_solve_10x10, qr_solve_100x100, // qr_solve_500x500, qr_inverse_10x10, qr_inverse_100x100, // qr_inverse_500x500 ); nalgebra-0.33.0/benches/linalg/schur.rs000064400000000000000000000043030072674642500160610ustar 00000000000000use na::{Matrix4, Schur}; fn schur_decompose_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("schur_decompose_4x4", move |bh| { bh.iter(|| std::hint::black_box(Schur::new(m.clone()))) }); } fn schur_decompose_10x10(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(10, 10); bh.bench_function("schur_decompose_10x10", move |bh| { bh.iter(|| std::hint::black_box(Schur::new(m.clone()))) }); } fn schur_decompose_100x100(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(100, 100); bh.bench_function("schur_decompose_100x100", move |bh| { bh.iter(|| std::hint::black_box(Schur::new(m.clone()))) }); } fn schur_decompose_200x200(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(200, 200); bh.bench_function("schur_decompose_200x200", move |bh| { bh.iter(|| std::hint::black_box(Schur::new(m.clone()))) }); } fn eigenvalues_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("eigenvalues_4x4", move |bh| { bh.iter(|| std::hint::black_box(m.complex_eigenvalues())) }); } fn eigenvalues_10x10(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(10, 10); bh.bench_function("eigenvalues_10x10", move |bh| { bh.iter(|| std::hint::black_box(m.complex_eigenvalues())) }); } fn eigenvalues_100x100(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(100, 100); bh.bench_function("eigenvalues_100x100", move |bh| { bh.iter(|| std::hint::black_box(m.complex_eigenvalues())) }); } fn eigenvalues_200x200(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(200, 200); bh.bench_function("eigenvalues_200x200", move |bh| { bh.iter(|| std::hint::black_box(m.complex_eigenvalues())) }); } criterion_group!( schur, schur_decompose_4x4, schur_decompose_10x10, schur_decompose_100x100, schur_decompose_200x200, eigenvalues_4x4, eigenvalues_10x10, eigenvalues_100x100, eigenvalues_200x200 ); nalgebra-0.33.0/benches/linalg/solve.rs000064400000000000000000000057250072674642500160760ustar 00000000000000use na::{DMatrix, DVector}; fn solve_l_triangular_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let v = DVector::::new_random(100); bh.bench_function("solve_l_triangular_100x100", move |bh| { bh.iter(|| { let _ = m.solve_lower_triangular(&v); }) }); } fn solve_l_triangular_1000x1000(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(1000, 1000); let v = DVector::::new_random(1000); bh.bench_function("solve_l_triangular_1000x1000", move |bh| { bh.iter(|| { let _ = m.solve_lower_triangular(&v); }) }); } fn tr_solve_l_triangular_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let v = DVector::::new_random(100); bh.bench_function("tr_solve_l_triangular_100x100", move |bh| { bh.iter(|| { let _ = m.tr_solve_lower_triangular(&v); }) }); } fn tr_solve_l_triangular_1000x1000(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(1000, 1000); let v = DVector::::new_random(1000); bh.bench_function("tr_solve_l_triangular_1000x1000", move |bh| { bh.iter(|| { let _ = m.tr_solve_lower_triangular(&v); }) }); } fn solve_u_triangular_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let v = DVector::::new_random(100); bh.bench_function("solve_u_triangular_100x100", move |bh| { bh.iter(|| { let _ = m.solve_upper_triangular(&v); }) }); } fn solve_u_triangular_1000x1000(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(1000, 1000); let v = DVector::::new_random(1000); bh.bench_function("solve_u_triangular_1000x1000", move |bh| { bh.iter(|| { let _ = m.solve_upper_triangular(&v); }) }); } fn tr_solve_u_triangular_100x100(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(100, 100); let v = DVector::::new_random(100); bh.bench_function("tr_solve_u_triangular_100x100", move |bh| { bh.iter(|| { let _ = m.tr_solve_upper_triangular(&v); }) }); } fn tr_solve_u_triangular_1000x1000(bh: &mut criterion::Criterion) { let m = DMatrix::::new_random(1000, 1000); let v = DVector::::new_random(1000); bh.bench_function("tr_solve_u_triangular_1000x1000", move |bh| { bh.iter(|| { let _ = m.tr_solve_upper_triangular(&v); }) }); } criterion_group!( solve, solve_l_triangular_100x100, solve_l_triangular_1000x1000, tr_solve_l_triangular_100x100, tr_solve_l_triangular_1000x1000, solve_u_triangular_100x100, solve_u_triangular_1000x1000, tr_solve_u_triangular_100x100, tr_solve_u_triangular_1000x1000 ); nalgebra-0.33.0/benches/linalg/svd.rs000064400000000000000000000115570072674642500155420ustar 00000000000000use na::{Matrix2, Matrix3, Matrix4, SVD}; fn svd_decompose_2x2_f32(bh: &mut criterion::Criterion) { let m = Matrix2::::new_random(); bh.bench_function("svd_decompose_2x2", move |bh| { bh.iter(|| std::hint::black_box(SVD::new_unordered(m.clone(), true, true))) }); } fn svd_decompose_3x3_f32(bh: &mut criterion::Criterion) { let m = Matrix3::::new_random(); bh.bench_function("svd_decompose_3x3", move |bh| { bh.iter(|| std::hint::black_box(SVD::new_unordered(m.clone(), true, true))) }); } fn svd_decompose_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("svd_decompose_4x4", move |bh| { bh.iter(|| std::hint::black_box(SVD::new(m.clone(), true, true))) }); } fn svd_decompose_10x10(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(10, 10); bh.bench_function("svd_decompose_10x10", move |bh| { bh.iter(|| std::hint::black_box(SVD::new(m.clone(), true, true))) }); } fn svd_decompose_100x100(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(100, 100); bh.bench_function("svd_decompose_100x100", move |bh| { bh.iter(|| std::hint::black_box(SVD::new(m.clone(), true, true))) }); } fn svd_decompose_200x200(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(200, 200); bh.bench_function("svd_decompose_200x200", move |bh| { bh.iter(|| std::hint::black_box(SVD::new(m.clone(), true, true))) }); } fn rank_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("rank_4x4", move |bh| { bh.iter(|| std::hint::black_box(m.rank(1.0e-10))) }); } fn rank_10x10(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(10, 10); bh.bench_function("rank_10x10", move |bh| { bh.iter(|| std::hint::black_box(m.rank(1.0e-10))) }); } fn rank_100x100(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(100, 100); bh.bench_function("rank_100x100", move |bh| { bh.iter(|| std::hint::black_box(m.rank(1.0e-10))) }); } fn rank_200x200(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(200, 200); bh.bench_function("rank_200x200", move |bh| { bh.iter(|| std::hint::black_box(m.rank(1.0e-10))) }); } fn singular_values_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("singular_values_4x4", move |bh| { bh.iter(|| std::hint::black_box(m.singular_values())) }); } fn singular_values_10x10(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(10, 10); bh.bench_function("singular_values_10x10", move |bh| { bh.iter(|| std::hint::black_box(m.singular_values())) }); } fn singular_values_100x100(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(100, 100); bh.bench_function("singular_values_100x100", move |bh| { bh.iter(|| std::hint::black_box(m.singular_values())) }); } fn singular_values_200x200(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(200, 200); bh.bench_function("singular_values_200x200", move |bh| { bh.iter(|| std::hint::black_box(m.singular_values())) }); } fn pseudo_inverse_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("pseudo_inverse_4x4", move |bh| { bh.iter(|| std::hint::black_box(m.clone().pseudo_inverse(1.0e-10))) }); } fn pseudo_inverse_10x10(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(10, 10); bh.bench_function("pseudo_inverse_10x10", move |bh| { bh.iter(|| std::hint::black_box(m.clone().pseudo_inverse(1.0e-10))) }); } fn pseudo_inverse_100x100(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(100, 100); bh.bench_function("pseudo_inverse_100x100", move |bh| { bh.iter(|| std::hint::black_box(m.clone().pseudo_inverse(1.0e-10))) }); } fn pseudo_inverse_200x200(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(200, 200); bh.bench_function("pseudo_inverse_200x200", move |bh| { bh.iter(|| std::hint::black_box(m.clone().pseudo_inverse(1.0e-10))) }); } criterion_group!( svd, svd_decompose_2x2_f32, svd_decompose_3x3_f32, svd_decompose_4x4, svd_decompose_10x10, svd_decompose_100x100, svd_decompose_200x200, rank_4x4, rank_10x10, rank_100x100, rank_200x200, singular_values_4x4, singular_values_10x10, singular_values_100x100, singular_values_200x200, pseudo_inverse_4x4, pseudo_inverse_10x10, pseudo_inverse_100x100, pseudo_inverse_200x200 ); nalgebra-0.33.0/benches/linalg/symmetric_eigen.rs000064400000000000000000000025040072674642500201210ustar 00000000000000use na::{Matrix4, SymmetricEigen}; fn symmetric_eigen_decompose_4x4(bh: &mut criterion::Criterion) { let m = Matrix4::::new_random(); bh.bench_function("symmetric_eigen_decompose_4x4", move |bh| { bh.iter(|| std::hint::black_box(SymmetricEigen::new(m.clone()))) }); } fn symmetric_eigen_decompose_10x10(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(10, 10); bh.bench_function("symmetric_eigen_decompose_10x10", move |bh| { bh.iter(|| std::hint::black_box(SymmetricEigen::new(m.clone()))) }); } fn symmetric_eigen_decompose_100x100(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(100, 100); bh.bench_function("symmetric_eigen_decompose_100x100", move |bh| { bh.iter(|| std::hint::black_box(SymmetricEigen::new(m.clone()))) }); } fn symmetric_eigen_decompose_200x200(bh: &mut criterion::Criterion) { let m = crate::reproductible_dmatrix(200, 200); bh.bench_function("symmetric_eigen_decompose_200x200", move |bh| { bh.iter(|| std::hint::black_box(SymmetricEigen::new(m.clone()))) }); } criterion_group!( symmetric_eigen, symmetric_eigen_decompose_4x4, symmetric_eigen_decompose_10x10, symmetric_eigen_decompose_100x100, symmetric_eigen_decompose_200x200 ); nalgebra-0.33.0/clippy.toml000064400000000000000000000001030072674642500137010ustar 00000000000000too-many-arguments-threshold = 8 type-complexity-threshold = 675 nalgebra-0.33.0/examples/dimensional_genericity.rs000064400000000000000000000040450072674642500204250ustar 00000000000000extern crate nalgebra as na; use na::allocator::Allocator; use na::dimension::Dim; use na::{DefaultAllocator, OVector, RealField, Unit, Vector2, Vector3}; /// Reflects a vector wrt. the hyperplane with normal `plane_normal`. fn reflect_wrt_hyperplane_with_dimensional_genericity( plane_normal: &Unit>, vector: &OVector, ) -> OVector where T: RealField, D: Dim, DefaultAllocator: Allocator, { let n = plane_normal.as_ref(); // Get the underlying V. vector - n * (n.dot(vector) * na::convert(2.0)) } /// Reflects a 2D vector wrt. the 2D line with normal `plane_normal`. fn reflect_wrt_hyperplane2(plane_normal: &Unit>, vector: &Vector2) -> Vector2 where T: RealField, { let n = plane_normal.as_ref(); // Get the underlying Vector2 vector - n * (n.dot(vector) * na::convert(2.0)) } /// Reflects a 3D vector wrt. the 3D plane with normal `plane_normal`. /// /!\ This is an exact replicate of `reflect_wrt_hyperplane2`, but for 3D. fn reflect_wrt_hyperplane3(plane_normal: &Unit>, vector: &Vector3) -> Vector3 where T: RealField, { let n = plane_normal.as_ref(); // Get the underlying Vector3 vector - n * (n.dot(vector) * na::convert(2.0)) } fn main() { let plane2 = Vector2::y_axis(); // 2D plane normal. let plane3 = Vector3::y_axis(); // 3D plane normal. let v2 = Vector2::new(1.0, 2.0); // 2D vector to be reflected. let v3 = Vector3::new(1.0, 2.0, 3.0); // 3D vector to be reflected. // We can call the same function for 2D and 3D. assert_eq!( reflect_wrt_hyperplane_with_dimensional_genericity(&plane2, &v2).y, -2.0 ); assert_eq!( reflect_wrt_hyperplane_with_dimensional_genericity(&plane3, &v3).y, -2.0 ); // Call each specific implementation depending on the dimension. assert_eq!(reflect_wrt_hyperplane2(&plane2, &v2).y, -2.0); assert_eq!(reflect_wrt_hyperplane3(&plane3, &v3).y, -2.0); } nalgebra-0.33.0/examples/homogeneous_coordinates.rs000064400000000000000000000025540072674642500206260ustar 00000000000000#[macro_use] extern crate approx; extern crate nalgebra as na; use na::{Isometry2, Point2, Vector2}; use std::f32; fn use_dedicated_types() { let iso = Isometry2::new(Vector2::new(1.0, 1.0), f32::consts::PI); let pt = Point2::new(1.0, 0.0); let vec = Vector2::x(); let transformed_pt = iso * pt; let transformed_vec = iso * vec; assert_relative_eq!(transformed_pt, Point2::new(0.0, 1.0)); assert_relative_eq!(transformed_vec, Vector2::new(-1.0, 0.0)); } fn use_homogeneous_coordinates() { let iso = Isometry2::new(Vector2::new(1.0, 1.0), f32::consts::PI); let pt = Point2::new(1.0, 0.0); let vec = Vector2::x(); // Compute using homogeneous coordinates. let hom_iso = iso.to_homogeneous(); let hom_pt = pt.to_homogeneous(); let hom_vec = vec.to_homogeneous(); let hom_transformed_pt = hom_iso * hom_pt; let hom_transformed_vec = hom_iso * hom_vec; // Convert back to the cartesian coordinates. let transformed_pt = Point2::from_homogeneous(hom_transformed_pt).unwrap(); let transformed_vec = Vector2::from_homogeneous(hom_transformed_vec).unwrap(); assert_relative_eq!(transformed_pt, Point2::new(0.0, 1.0)); assert_relative_eq!(transformed_vec, Vector2::new(-1.0, 0.0)); } fn main() { use_dedicated_types(); use_homogeneous_coordinates(); } nalgebra-0.33.0/examples/linear_system_resolution.rs000064400000000000000000000022340072674642500210400ustar 00000000000000#![cfg_attr(rustfmt, rustfmt_skip)] #[macro_use] extern crate approx; // for assert_relative_eq extern crate nalgebra as na; use na::{Matrix4, Matrix4x3, Vector4}; fn main() { let a = Matrix4::new( 1.0, 1.0, 2.0, -5.0, 2.0, 5.0, -1.0, -9.0, 2.0, 1.0, -1.0, 3.0, 1.0, 3.0, 2.0, 7.0, ); let mut b = Vector4::new(3.0, -3.0, -11.0, -5.0); let decomp = a.lu(); let x = decomp.solve(&b).expect("Linear resolution failed."); assert_relative_eq!(a * x, b); /* * It is possible to perform the resolution in-place. * This is particularly useful to avoid allocations when * `b` is a `DVector` or a `DMatrix`. */ assert!(decomp.solve_mut(&mut b), "Linear resolution failed."); assert_relative_eq!(x, b); /* * It is possible to solve multiple systems * simultaneously by using a matrix for `b`. */ let b = Matrix4x3::new( 3.0, 2.0, 0.0, -3.0, 0.0, 0.0, -11.0, 5.0, -3.0, -5.0, 10.0, 4.0, ); let x = decomp.solve(&b).expect("Linear resolution failed."); assert_relative_eq!(a * x, b); } nalgebra-0.33.0/examples/matrix_construction.rs000064400000000000000000000035240072674642500200200ustar 00000000000000extern crate nalgebra as na; use na::{DMatrix, Matrix2x3, RowVector3, Vector2}; fn main() { // All the following matrices are equal but constructed in different ways. let m = Matrix2x3::new(1.1, 1.2, 1.3, 2.1, 2.2, 2.3); let m1 = Matrix2x3::from_rows(&[ RowVector3::new(1.1, 1.2, 1.3), RowVector3::new(2.1, 2.2, 2.3), ]); let m2 = Matrix2x3::from_columns(&[ Vector2::new(1.1, 2.1), Vector2::new(1.2, 2.2), Vector2::new(1.3, 2.3), ]); let m3 = Matrix2x3::from_row_slice(&[1.1, 1.2, 1.3, 2.1, 2.2, 2.3]); let m4 = Matrix2x3::from_column_slice(&[1.1, 2.1, 1.2, 2.2, 1.3, 2.3]); let m5 = Matrix2x3::from_fn(|r, c| (r + 1) as f32 + (c + 1) as f32 / 10.0); let m6 = Matrix2x3::from_iterator([1.1f32, 2.1, 1.2, 2.2, 1.3, 2.3].iter().cloned()); assert_eq!(m, m1); assert_eq!(m, m2); assert_eq!(m, m3); assert_eq!(m, m4); assert_eq!(m, m5); assert_eq!(m, m6); // All the following matrices are equal but constructed in different ways. // This time, we used a dynamically-sized matrix to show the extra arguments // for the matrix shape. let dm = DMatrix::from_row_slice( 4, 3, &[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], ); let dm1 = DMatrix::from_diagonal_element(4, 3, 1.0); let dm2 = DMatrix::identity(4, 3); let dm3 = DMatrix::from_fn(4, 3, |r, c| if r == c { 1.0 } else { 0.0 }); let dm4 = DMatrix::from_iterator( 4, 3, [ // Components listed column-by-column. 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, ] .iter() .cloned(), ); assert_eq!(dm, dm1); assert_eq!(dm, dm2); assert_eq!(dm, dm3); assert_eq!(dm, dm4); } nalgebra-0.33.0/examples/matrixcompare.rs000064400000000000000000000043030072674642500165510ustar 00000000000000extern crate nalgebra as na; use matrixcompare::comparators::{AbsoluteElementwiseComparator, ExactElementwiseComparator}; use matrixcompare::compare_matrices; use na::{OMatrix, U3, U4}; fn compare_integers_fail() { println!("Comparing two integer matrices."); #[rustfmt::skip] let a = OMatrix::<_, U3, U4>::from_row_slice(&[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -2, 11 ]); #[rustfmt::skip] let b = OMatrix::<_, U3, U4>::from_row_slice(&[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]); if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) { println!("{}", err); } } fn compare_different_size() { println!("Comparing matrices of different size."); #[rustfmt::skip] let a = OMatrix::<_, U3, U3>::from_row_slice(&[ 0, 1, 2, 4, 5, 6, 8, 9, 10, ]); #[rustfmt::skip] let b = OMatrix::<_, U3, U4>::from_row_slice(&[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]); if let Err(err) = compare_matrices(a, b, &ExactElementwiseComparator) { println!("{}", err); } } fn compare_f64_abs_tol_fail() { println!("Comparing two f64 matrices."); #[rustfmt::skip] let a = OMatrix::::from_row_slice(&[ 0.0, 1.0, 2.0 + 1e-10, 4.0, 5.0, 6.0, 8.0, 9.0, 10.0, ]); #[rustfmt::skip] let b = OMatrix::<_, U3, U3>::from_row_slice(&[ 0.0, 1.0, 2.0, 4.0, 5.0, 6.0, 8.0, 9.0, 10.0 ]); let cmp = AbsoluteElementwiseComparator { tol: 1e-12 }; if let Err(err) = compare_matrices(a, b, &cmp) { println!("{}", err); } } fn main() { // This example mostly serves the purpose of demonstrating the kind of error messages // that are given upon comparison failure. // The more typical use case is using `assert_matrix_eq!` in tests. compare_integers_fail(); println!("======================================================"); compare_f64_abs_tol_fail(); println!("======================================================"); compare_different_size(); } nalgebra-0.33.0/examples/mvp.rs000064400000000000000000000017400072674642500145020ustar 00000000000000#![allow(unused_variables)] extern crate nalgebra as na; use na::{Isometry3, Perspective3, Point3, Vector3}; use std::f32::consts; fn main() { // Our object is translated along the x axis. let model = Isometry3::new(Vector3::x(), na::zero()); // Our camera looks toward the point (1.0, 0.0, 0.0). // It is located at (0.0, 0.0, 1.0). let eye = Point3::new(0.0, 0.0, 1.0); let target = Point3::new(1.0, 0.0, 0.0); let view = Isometry3::look_at_rh(&eye, &target, &Vector3::y()); // A perspective projection. let projection = Perspective3::new(16.0 / 9.0, consts::PI / 2.0, 1.0, 1000.0); // The combination of the model with the view is still an isometry. let model_view = view * model; // Convert everything to a `Matrix4` so that they can be combined. let mat_model_view = model_view.to_homogeneous(); // Combine everything. let model_view_projection = projection.as_matrix() * mat_model_view; } nalgebra-0.33.0/examples/point_construction.rs000064400000000000000000000014340072674642500176430ustar 00000000000000extern crate nalgebra as na; use na::{Point3, Vector3, Vector4}; fn main() { // Build using components directly. let p0 = Point3::new(2.0, 3.0, 4.0); // Build from a coordinates vector. let coords = Vector3::new(2.0, 3.0, 4.0); let p1 = Point3::from(coords); // Build by translating the origin. let translation = Vector3::new(2.0, 3.0, 4.0); let p2 = Point3::origin() + translation; // Build from homogeneous coordinates. The last component of the // vector will be removed and all other components divided by 10.0. let homogeneous_coords = Vector4::new(20.0, 30.0, 40.0, 10.0); let p3 = Point3::from_homogeneous(homogeneous_coords); assert_eq!(p0, p1); assert_eq!(p0, p2); assert_eq!(p0, p3.unwrap()); } nalgebra-0.33.0/examples/raw_pointer.rs000064400000000000000000000017750072674642500162410ustar 00000000000000extern crate nalgebra as na; use na::{Matrix3, Point3, Vector3}; fn main() { let v = Vector3::new(1.0f32, 0.0, 1.0); let p = Point3::new(1.0f32, 0.0, 1.0); let m = na::one::>(); // Convert to arrays. let v_array = v.as_slice(); let p_array = p.coords.as_slice(); let m_array = m.as_slice(); // Get data pointers. let v_pointer = v_array.as_ptr(); let p_pointer = p_array.as_ptr(); let m_pointer = m_array.as_ptr(); /* Then pass the raw pointers to some graphics API. */ #[allow(clippy::float_cmp)] unsafe { assert_eq!(*v_pointer, 1.0); assert_eq!(*v_pointer.offset(1), 0.0); assert_eq!(*v_pointer.offset(2), 1.0); assert_eq!(*p_pointer, 1.0); assert_eq!(*p_pointer.offset(1), 0.0); assert_eq!(*p_pointer.offset(2), 1.0); assert_eq!(*m_pointer, 1.0); assert_eq!(*m_pointer.offset(4), 1.0); assert_eq!(*m_pointer.offset(8), 1.0); } } nalgebra-0.33.0/examples/reshaping.rs000064400000000000000000000025070072674642500156620ustar 00000000000000#![cfg_attr(rustfmt, rustfmt_skip)] extern crate nalgebra as na; use na::{DMatrix, Dyn, Matrix2x3, Matrix3x2, Const}; fn main() { // Matrices can be reshaped in-place without moving or copying values. let m1 = Matrix2x3::new( 1.1, 1.2, 1.3, 2.1, 2.2, 2.3 ); let m2 = Matrix3x2::new( 1.1, 2.2, 2.1, 1.3, 1.2, 2.3 ); let m3 = m1.reshape_generic(Const::<3>, Const::<2>); assert_eq!(m3, m2); // Note that, for statically sized matrices, invalid reshapes will not compile: //let m4 = m3.reshape_generic(U3, U3); // If dynamically sized matrices are used, the reshaping is checked at run-time. let dm1 = DMatrix::from_row_slice( 4, 3, &[ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 ], ); let dm2 = DMatrix::from_row_slice( 6, 2, &[ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, ], ); let dm3 = dm1.reshape_generic(Dyn(6), Dyn(2)); assert_eq!(dm3, dm2); // Invalid reshapings of dynamic matrices will panic at run-time. //let dm4 = dm3.reshape_generic(Dyn(6), Dyn(6)); } nalgebra-0.33.0/examples/scalar_genericity.rs000064400000000000000000000011460072674642500173670ustar 00000000000000extern crate nalgebra as na; use na::{Scalar, Vector3}; use simba::scalar::RealField; fn print_vector(m: &Vector3) { println!("{:?}", m) } fn print_norm(v: &Vector3) { // NOTE: alternatively, nalgebra already defines `v.norm()`. let norm = v.dot(v).sqrt(); // The RealField bound implies that T is Display so we can // use "{}" instead of "{:?}" for the format string. println!("{}", norm) } fn main() { let v1 = Vector3::new(1, 2, 3); let v2 = Vector3::new(1.0, 2.0, 3.0); print_vector(&v1); print_norm(&v2); } nalgebra-0.33.0/examples/screen_to_view_coords.rs000064400000000000000000000016300072674642500202620ustar 00000000000000#![allow(unused_variables)] extern crate nalgebra as na; use na::{Perspective3, Point2, Point3, Unit}; use std::f32::consts; fn main() { let projection = Perspective3::new(800.0 / 600.0, consts::PI / 2.0, 1.0, 1000.0); let screen_point = Point2::new(10.0f32, 20.0); // Compute two points in clip-space. // "ndc" = normalized device coordinates. let near_ndc_point = Point3::new(screen_point.x / 800.0, screen_point.y / 600.0, -1.0); let far_ndc_point = Point3::new(screen_point.x / 800.0, screen_point.y / 600.0, 1.0); // Unproject them to view-space. let near_view_point = projection.unproject_point(&near_ndc_point); let far_view_point = projection.unproject_point(&far_ndc_point); // Compute the view-space line parameters. let line_location = near_view_point; let line_direction = Unit::new_normalize(far_view_point - near_view_point); } nalgebra-0.33.0/examples/transform_conversion.rs000064400000000000000000000017600072674642500201620ustar 00000000000000extern crate nalgebra as na; use na::{Isometry2, Similarity2, Vector2}; use std::f32::consts; fn main() { // Isometry -> Similarity conversion always succeeds. let iso = Isometry2::new(Vector2::new(1.0f32, 2.0), na::zero()); let _: Similarity2 = na::convert(iso); // Similarity -> Isometry conversion fails if the scaling factor is not 1.0. let sim_without_scaling = Similarity2::new(Vector2::new(1.0f32, 2.0), consts::PI, 1.0); let sim_with_scaling = Similarity2::new(Vector2::new(1.0f32, 2.0), consts::PI, 2.0); let iso_success: Option> = na::try_convert(sim_without_scaling); let iso_fail: Option> = na::try_convert(sim_with_scaling); assert!(iso_success.is_some()); assert!(iso_fail.is_none()); // Similarity -> Isometry conversion can be forced at your own risks. let iso_forced: Isometry2 = na::convert_unchecked(sim_with_scaling); assert_eq!(iso_success.unwrap(), iso_forced); } nalgebra-0.33.0/examples/transform_matrix4.rs000064400000000000000000000031470072674642500173660ustar 00000000000000#[macro_use] extern crate approx; extern crate nalgebra as na; use na::{Matrix4, Point3, Vector3}; use std::f32::consts; fn main() { // Create a uniform scaling matrix with scaling factor 2. let mut m = Matrix4::new_scaling(2.0); assert_eq!(m.transform_vector(&Vector3::x()), Vector3::x() * 2.0); assert_eq!(m.transform_vector(&Vector3::y()), Vector3::y() * 2.0); assert_eq!(m.transform_vector(&Vector3::z()), Vector3::z() * 2.0); // Append a nonuniform scaling in-place. m.append_nonuniform_scaling_mut(&Vector3::new(1.0, 2.0, 3.0)); assert_eq!(m.transform_vector(&Vector3::x()), Vector3::x() * 2.0); assert_eq!(m.transform_vector(&Vector3::y()), Vector3::y() * 4.0); assert_eq!(m.transform_vector(&Vector3::z()), Vector3::z() * 6.0); // Append a translation out-of-place. let m2 = m.append_translation(&Vector3::new(42.0, 0.0, 0.0)); assert_eq!( m2.transform_point(&Point3::new(1.0, 1.0, 1.0)), Point3::new(42.0 + 2.0, 4.0, 6.0) ); // Create rotation. let rot = Matrix4::from_scaled_axis(Vector3::x() * consts::PI); let rot_then_m = m * rot; // Right-multiplication is equivalent to prepending `rot` to `m`. let m_then_rot = rot * m; // Left-multiplication is equivalent to appending `rot` to `m`. let pt = Point3::new(1.0, 2.0, 3.0); assert_relative_eq!( m.transform_point(&rot.transform_point(&pt)), rot_then_m.transform_point(&pt) ); assert_relative_eq!( rot.transform_point(&m.transform_point(&pt)), m_then_rot.transform_point(&pt) ); } nalgebra-0.33.0/examples/transform_vector_point.rs000064400000000000000000000013340072674642500205050ustar 00000000000000#[macro_use] extern crate approx; extern crate nalgebra as na; use na::{Isometry2, Point2, Vector2}; use std::f32; fn main() { let t = Isometry2::new(Vector2::new(1.0, 1.0), f32::consts::PI); let p = Point2::new(1.0, 0.0); // Will be affected by te rotation and the translation. let v = Vector2::x(); // Will *not* be affected by the translation. assert_relative_eq!(t * p, Point2::new(-1.0 + 1.0, 1.0)); // ^^^^ │ ^^^^^^^^ // rotated │ translated assert_relative_eq!(t * v, Vector2::new(-1.0, 0.0)); // ^^^^^ // rotated only } nalgebra-0.33.0/examples/transform_vector_point3.rs000064400000000000000000000017640072674642500205770ustar 00000000000000extern crate nalgebra as na; use na::{Matrix4, Point3, Vector3, Vector4}; fn main() { let mut m = Matrix4::new_rotation_wrt_point(Vector3::x() * 1.57, Point3::new(1.0, 2.0, 1.0)); m.append_scaling_mut(2.0); let point1 = Point3::new(2.0, 3.0, 4.0); let homogeneous_point2 = Vector4::new(2.0, 3.0, 4.0, 1.0); // First option: use the dedicated `.transform_point(...)` method. let transformed_point1 = m.transform_point(&point1); // Second option: use the homogeneous coordinates of the point. let transformed_homogeneous_point2 = m * homogeneous_point2; // Recover the 3D point from its 4D homogeneous coordinates. let transformed_point2 = Point3::from_homogeneous(transformed_homogeneous_point2); // Check that transforming the 3D point with the `.transform_point` method is // indeed equivalent to multiplying its 4D homogeneous coordinates by the 4x4 // matrix. assert_eq!(transformed_point1, transformed_point2.unwrap()); } nalgebra-0.33.0/examples/transformation_pointer.rs000064400000000000000000000014440072674642500205070ustar 00000000000000extern crate nalgebra as na; use na::{Isometry3, Vector3}; fn main() { let iso = Isometry3::new(Vector3::new(1.0f32, 0.0, 1.0), na::zero()); // Compute the homogeneous coordinates first. let iso_matrix = iso.to_homogeneous(); let iso_array = iso_matrix.as_slice(); let iso_pointer = iso_array.as_ptr(); /* Then pass the raw pointer to some graphics API. */ #[allow(clippy::float_cmp)] unsafe { assert_eq!(*iso_pointer, 1.0); assert_eq!(*iso_pointer.offset(5), 1.0); assert_eq!(*iso_pointer.offset(10), 1.0); assert_eq!(*iso_pointer.offset(15), 1.0); assert_eq!(*iso_pointer.offset(12), 1.0); assert_eq!(*iso_pointer.offset(13), 0.0); assert_eq!(*iso_pointer.offset(14), 1.0); } } nalgebra-0.33.0/examples/unit_wrapper.rs000064400000000000000000000016000072674642500164120ustar 00000000000000#![allow(clippy::float_cmp)] extern crate nalgebra as na; use na::{Unit, Vector3}; fn length_on_direction_with_unit(v: &Vector3, dir: &Unit>) -> f32 { // No need to normalize `dir`: we know that it is non-zero and normalized. v.dot(dir.as_ref()) } fn length_on_direction_without_unit(v: &Vector3, dir: &Vector3) -> f32 { // Obligatory normalization of the direction vector (and test, for robustness). if let Some(unit_dir) = dir.try_normalize(1.0e-6) { v.dot(&unit_dir) } else { // Normalization failed because the norm was too small. panic!("Invalid input direction.") } } fn main() { let v = Vector3::new(1.0, 2.0, 3.0); let l1 = length_on_direction_with_unit(&v, &Vector3::y_axis()); let l2 = length_on_direction_without_unit(&v, &Vector3::y()); assert_eq!(l1, l2) } nalgebra-0.33.0/rustfmt.toml000064400000000000000000000001150072674642500141100ustar 00000000000000edition = "2018" use_try_shorthand = true use_field_init_shorthand = true nalgebra-0.33.0/src/base/alias.rs000064400000000000000000000444570072674642500146700ustar 00000000000000#[cfg(any(feature = "alloc", feature = "std"))] use crate::base::dimension::Dyn; use crate::base::dimension::{U1, U2, U3, U4, U5, U6}; use crate::base::storage::Owned; #[cfg(any(feature = "std", feature = "alloc"))] use crate::base::vec_storage::VecStorage; use crate::base::{ArrayStorage, Const, Matrix, Unit}; use crate::storage::OwnedUninit; use std::mem::MaybeUninit; /* * * * Column-major matrices. * * */ /// An owned matrix column-major matrix with `R` rows and `C` columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type OMatrix = Matrix>; /// An owned matrix with uninitialized data. pub type UninitMatrix = Matrix, R, C, OwnedUninit>; /// An owned matrix column-major matrix with `R` rows and `C` columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated( note = "use SMatrix for a statically-sized matrix using integer dimensions, or OMatrix for an owned matrix using types as dimensions." )] pub type MatrixMN = Matrix>; /// An owned matrix column-major matrix with `D` columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated(note = "use OMatrix or SMatrix instead.")] pub type MatrixN = Matrix>; /// A statically sized column-major matrix with `R` rows and `C` columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type SMatrix = Matrix, Const, ArrayStorage>; /// A dynamically sized column-major matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type DMatrix = Matrix>; /// A heap-allocated, column-major, matrix with a dynamic number of rows and 1 columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type MatrixXx1 = Matrix>; /// A heap-allocated, column-major, matrix with a dynamic number of rows and 2 columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type MatrixXx2 = Matrix>; /// A heap-allocated, column-major, matrix with a dynamic number of rows and 3 columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type MatrixXx3 = Matrix>; /// A heap-allocated, column-major, matrix with a dynamic number of rows and 4 columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type MatrixXx4 = Matrix>; /// A heap-allocated, column-major, matrix with a dynamic number of rows and 5 columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type MatrixXx5 = Matrix>; /// A heap-allocated, column-major, matrix with a dynamic number of rows and 6 columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type MatrixXx6 = Matrix>; /// A heap-allocated, column-major, matrix with 1 rows and a dynamic number of columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type Matrix1xX = Matrix>; /// A heap-allocated, column-major, matrix with 2 rows and a dynamic number of columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type Matrix2xX = Matrix>; /// A heap-allocated, column-major, matrix with 3 rows and a dynamic number of columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type Matrix3xX = Matrix>; /// A heap-allocated, column-major, matrix with 4 rows and a dynamic number of columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type Matrix4xX = Matrix>; /// A heap-allocated, column-major, matrix with 5 rows and a dynamic number of columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type Matrix5xX = Matrix>; /// A heap-allocated, column-major, matrix with 6 rows and a dynamic number of columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[cfg(any(feature = "std", feature = "alloc"))] pub type Matrix6xX = Matrix>; /// A stack-allocated, column-major, 1x1 square matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix1 = Matrix>; /// A stack-allocated, column-major, 2x2 square matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix2 = Matrix>; /// A stack-allocated, column-major, 3x3 square matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix3 = Matrix>; /// A stack-allocated, column-major, 4x4 square matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix4 = Matrix>; /// A stack-allocated, column-major, 5x5 square matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix5 = Matrix>; /// A stack-allocated, column-major, 6x6 square matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix6 = Matrix>; /// A stack-allocated, column-major, 1x2 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix1x2 = Matrix>; /// A stack-allocated, column-major, 1x3 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix1x3 = Matrix>; /// A stack-allocated, column-major, 1x4 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix1x4 = Matrix>; /// A stack-allocated, column-major, 1x5 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix1x5 = Matrix>; /// A stack-allocated, column-major, 1x6 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix1x6 = Matrix>; /// A stack-allocated, column-major, 2x3 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix2x3 = Matrix>; /// A stack-allocated, column-major, 2x4 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix2x4 = Matrix>; /// A stack-allocated, column-major, 2x5 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix2x5 = Matrix>; /// A stack-allocated, column-major, 2x6 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix2x6 = Matrix>; /// A stack-allocated, column-major, 3x4 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix3x4 = Matrix>; /// A stack-allocated, column-major, 3x5 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix3x5 = Matrix>; /// A stack-allocated, column-major, 3x6 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix3x6 = Matrix>; /// A stack-allocated, column-major, 4x5 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix4x5 = Matrix>; /// A stack-allocated, column-major, 4x6 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix4x6 = Matrix>; /// A stack-allocated, column-major, 5x6 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix5x6 = Matrix>; /// A stack-allocated, column-major, 2x1 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix2x1 = Matrix>; /// A stack-allocated, column-major, 3x1 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix3x1 = Matrix>; /// A stack-allocated, column-major, 4x1 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix4x1 = Matrix>; /// A stack-allocated, column-major, 5x1 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix5x1 = Matrix>; /// A stack-allocated, column-major, 6x1 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix6x1 = Matrix>; /// A stack-allocated, column-major, 3x2 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix3x2 = Matrix>; /// A stack-allocated, column-major, 4x2 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix4x2 = Matrix>; /// A stack-allocated, column-major, 5x2 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix5x2 = Matrix>; /// A stack-allocated, column-major, 6x2 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix6x2 = Matrix>; /// A stack-allocated, column-major, 4x3 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix4x3 = Matrix>; /// A stack-allocated, column-major, 5x3 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix5x3 = Matrix>; /// A stack-allocated, column-major, 6x3 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix6x3 = Matrix>; /// A stack-allocated, column-major, 5x4 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix5x4 = Matrix>; /// A stack-allocated, column-major, 6x4 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix6x4 = Matrix>; /// A stack-allocated, column-major, 6x5 matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type Matrix6x5 = Matrix>; /* * * * Column vectors. * * */ /// A dynamically sized column vector. #[cfg(any(feature = "std", feature = "alloc"))] pub type DVector = Matrix>; /// An owned D-dimensional column vector. pub type OVector = Matrix>; /// A statically sized D-dimensional column vector. pub type SVector = Matrix, U1, ArrayStorage>; // Owned, U1>>; /// An owned matrix with uninitialized data. pub type UninitVector = Matrix, D, U1, OwnedUninit>; /// An owned matrix column-major matrix with `R` rows and `C` columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated( note = "use SVector for a statically-sized matrix using integer dimensions, or OVector for an owned matrix using types as dimensions." )] pub type VectorN = Matrix>; /// A stack-allocated, 1-dimensional column vector. pub type Vector1 = Matrix>; /// A stack-allocated, 2-dimensional column vector. pub type Vector2 = Matrix>; /// A stack-allocated, 3-dimensional column vector. pub type Vector3 = Matrix>; /// A stack-allocated, 4-dimensional column vector. pub type Vector4 = Matrix>; /// A stack-allocated, 5-dimensional column vector. pub type Vector5 = Matrix>; /// A stack-allocated, 6-dimensional column vector. pub type Vector6 = Matrix>; /* * * * Row vectors. * * */ /// A dynamically sized row vector. #[cfg(any(feature = "std", feature = "alloc"))] pub type RowDVector = Matrix>; /// An owned D-dimensional row vector. pub type RowOVector = Matrix>; /// A statically sized D-dimensional row vector. pub type RowSVector = Matrix, ArrayStorage>; /// A stack-allocated, 1-dimensional row vector. pub type RowVector1 = Matrix>; /// A stack-allocated, 2-dimensional row vector. pub type RowVector2 = Matrix>; /// A stack-allocated, 3-dimensional row vector. pub type RowVector3 = Matrix>; /// A stack-allocated, 4-dimensional row vector. pub type RowVector4 = Matrix>; /// A stack-allocated, 5-dimensional row vector. pub type RowVector5 = Matrix>; /// A stack-allocated, 6-dimensional row vector. pub type RowVector6 = Matrix>; /* * * * Unit Vector. * * */ /// A stack-allocated, 1-dimensional unit vector. pub type UnitVector1 = Unit>>; /// A stack-allocated, 2-dimensional unit vector. pub type UnitVector2 = Unit>>; /// A stack-allocated, 3-dimensional unit vector. pub type UnitVector3 = Unit>>; /// A stack-allocated, 4-dimensional unit vector. pub type UnitVector4 = Unit>>; /// A stack-allocated, 5-dimensional unit vector. pub type UnitVector5 = Unit>>; /// A stack-allocated, 6-dimensional unit vector. pub type UnitVector6 = Unit>>; nalgebra-0.33.0/src/base/alias_slice.rs000064400000000000000000001213420072674642500160340ustar 00000000000000use crate::base::dimension::{Dyn, U1, U2, U3, U4, U5, U6}; use crate::base::matrix_view::{ViewStorage, ViewStorageMut}; use crate::base::{Const, Matrix}; use crate::slice_deprecation_note; /* * * * Matrix slice aliases. * * */ // NOTE: we can't provide defaults for the strides because it's not supported yet by min_const_generics. /// A column-major matrix slice with dimensions known at compile-time. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(SMatrixView)] pub type SMatrixSlice<'a, T, const R: usize, const C: usize> = Matrix, Const, ViewStorage<'a, T, Const, Const, Const<1>, Const>>; /// A column-major matrix slice dynamic numbers of rows and columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(DMatrixView)] pub type DMatrixSlice<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major 1x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView1)] pub type MatrixSlice1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 2x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView2)] pub type MatrixSlice2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 3x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView3)] pub type MatrixSlice3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 4x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView4)] pub type MatrixSlice4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 5x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView5)] pub type MatrixSlice5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 6x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView6)] pub type MatrixSlice6<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 1x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView1x2)] pub type MatrixSlice1x2<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView1x3)] pub type MatrixSlice1x3<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView1x4)] pub type MatrixSlice1x4<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView1x5)] pub type MatrixSlice1x5<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView1x6)] pub type MatrixSlice1x6<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 2x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView2x1)] pub type MatrixSlice2x1<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView2x3)] pub type MatrixSlice2x3<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView2x4)] pub type MatrixSlice2x4<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView2x5)] pub type MatrixSlice2x5<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView2x6)] pub type MatrixSlice2x6<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 3x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView3x1)] pub type MatrixSlice3x1<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView3x2)] pub type MatrixSlice3x2<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView3x4)] pub type MatrixSlice3x4<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView3x5)] pub type MatrixSlice3x5<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView3x6)] pub type MatrixSlice3x6<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 4x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView4x1)] pub type MatrixSlice4x1<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView4x2)] pub type MatrixSlice4x2<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView4x3)] pub type MatrixSlice4x3<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView4x5)] pub type MatrixSlice4x5<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView4x6)] pub type MatrixSlice4x6<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 5x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView5x1)] pub type MatrixSlice5x1<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView5x2)] pub type MatrixSlice5x2<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView5x3)] pub type MatrixSlice5x3<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView5x4)] pub type MatrixSlice5x4<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView5x6)] pub type MatrixSlice5x6<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 6x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView6x1)] pub type MatrixSlice6x1<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView6x2)] pub type MatrixSlice6x2<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView6x3)] pub type MatrixSlice6x3<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView6x4)] pub type MatrixSlice6x4<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixView6x5)] pub type MatrixSlice6x5<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major matrix slice with 1 row and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixView1xX)] pub type MatrixSlice1xX<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major matrix slice with 2 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixView2xX)] pub type MatrixSlice2xX<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major matrix slice with 3 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixView3xX)] pub type MatrixSlice3xX<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major matrix slice with 4 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixView4xX)] pub type MatrixSlice4xX<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major matrix slice with 5 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixView5xX)] pub type MatrixSlice5xX<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major matrix slice with 6 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixView6xX)] pub type MatrixSlice6xX<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 1 column. #[deprecated = slice_deprecation_note!(MatrixViewXx1)] pub type MatrixSliceXx1<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 2 columns. #[deprecated = slice_deprecation_note!(MatrixViewXx2)] pub type MatrixSliceXx2<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 3 columns. #[deprecated = slice_deprecation_note!(MatrixViewXx3)] pub type MatrixSliceXx3<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 4 columns. #[deprecated = slice_deprecation_note!(MatrixViewXx4)] pub type MatrixSliceXx4<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 5 columns. #[deprecated = slice_deprecation_note!(MatrixViewXx5)] pub type MatrixSliceXx5<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 6 columns. #[deprecated = slice_deprecation_note!(MatrixViewXx6)] pub type MatrixSliceXx6<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column vector slice with dimensions known at compile-time. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorView)] pub type VectorSlice<'a, T, D, RStride = U1, CStride = D> = Matrix>; /// A column vector slice with dimensions known at compile-time. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(SVectorView)] pub type SVectorSlice<'a, T, const D: usize> = Matrix, Const<1>, ViewStorage<'a, T, Const, Const<1>, Const<1>, Const>>; /// A column vector slice dynamic numbers of rows and columns. #[deprecated = slice_deprecation_note!(DVectorView)] pub type DVectorSlice<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A 1D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorView1)] pub type VectorSlice1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A 2D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorView2)] pub type VectorSlice2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A 3D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorView3)] pub type VectorSlice3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A 4D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorView4)] pub type VectorSlice4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A 5D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorView5)] pub type VectorSlice5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A 6D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorView6)] pub type VectorSlice6<'a, T, RStride = U1, CStride = U6> = Matrix>; /* * * * Same thing, but for mutable slices. * * */ /// A column-major matrix slice with `R` rows and `C` columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = "Use MatrixViewMut instead, which has an identical definition."] pub type MatrixSliceMutMN<'a, T, R, C, RStride = U1, CStride = R> = Matrix>; /// A column-major matrix slice with `D` rows and columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = "Use MatrixViewMut instead."] pub type MatrixSliceMutN<'a, T, D, RStride = U1, CStride = D> = Matrix>; /// A column-major matrix slice with dimensions known at compile-time. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(SMatrixViewMut)] pub type SMatrixSliceMut<'a, T, const R: usize, const C: usize> = Matrix, Const, ViewStorageMut<'a, T, Const, Const, Const<1>, Const>>; /// A column-major matrix slice dynamic numbers of rows and columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(DMatrixViewMut)] pub type DMatrixSliceMut<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major 1x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut1)] pub type MatrixSliceMut1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 2x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut2)] pub type MatrixSliceMut2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 3x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut3)] pub type MatrixSliceMut3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 4x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut4)] pub type MatrixSliceMut4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 5x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut5)] pub type MatrixSliceMut5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 6x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut6)] pub type MatrixSliceMut6<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 1x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut1x2)] pub type MatrixSliceMut1x2<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut1x3)] pub type MatrixSliceMut1x3<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut1x4)] pub type MatrixSliceMut1x4<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut1x5)] pub type MatrixSliceMut1x5<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 1x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut1x6)] pub type MatrixSliceMut1x6<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major 2x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut2x1)] pub type MatrixSliceMut2x1<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut2x3)] pub type MatrixSliceMut2x3<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut2x4)] pub type MatrixSliceMut2x4<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut2x5)] pub type MatrixSliceMut2x5<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 2x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut2x6)] pub type MatrixSliceMut2x6<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major 3x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut3x1)] pub type MatrixSliceMut3x1<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut3x2)] pub type MatrixSliceMut3x2<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut3x4)] pub type MatrixSliceMut3x4<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut3x5)] pub type MatrixSliceMut3x5<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 3x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut3x6)] pub type MatrixSliceMut3x6<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major 4x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut4x1)] pub type MatrixSliceMut4x1<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut4x2)] pub type MatrixSliceMut4x2<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut4x3)] pub type MatrixSliceMut4x3<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut4x5)] pub type MatrixSliceMut4x5<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 4x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut4x6)] pub type MatrixSliceMut4x6<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major 5x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut5x1)] pub type MatrixSliceMut5x1<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut5x2)] pub type MatrixSliceMut5x2<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut5x3)] pub type MatrixSliceMut5x3<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut5x4)] pub type MatrixSliceMut5x4<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 5x6 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut5x6)] pub type MatrixSliceMut5x6<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major 6x1 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut6x1)] pub type MatrixSliceMut6x1<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x2 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut6x2)] pub type MatrixSliceMut6x2<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x3 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut6x3)] pub type MatrixSliceMut6x3<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x4 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut6x4)] pub type MatrixSliceMut6x4<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major 6x5 matrix slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(MatrixViewMut6x5)] pub type MatrixSliceMut6x5<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major matrix slice with 1 row and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixViewMut1xX)] pub type MatrixSliceMut1xX<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A column-major matrix slice with 2 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixViewMut2xX)] pub type MatrixSliceMut2xX<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A column-major matrix slice with 3 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixViewMut3xX)] pub type MatrixSliceMut3xX<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A column-major matrix slice with 4 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixViewMut4xX)] pub type MatrixSliceMut4xX<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A column-major matrix slice with 5 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixViewMut5xX)] pub type MatrixSliceMut5xX<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A column-major matrix slice with 6 rows and a number of columns chosen at runtime. #[deprecated = slice_deprecation_note!(MatrixViewMut6xX)] pub type MatrixSliceMut6xX<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 1 column. #[deprecated = slice_deprecation_note!(MatrixViewMutXx1)] pub type MatrixSliceMutXx1<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 2 columns. #[deprecated = slice_deprecation_note!(MatrixViewMutXx2)] pub type MatrixSliceMutXx2<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 3 columns. #[deprecated = slice_deprecation_note!(MatrixViewMutXx3)] pub type MatrixSliceMutXx3<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 4 columns. #[deprecated = slice_deprecation_note!(MatrixViewMutXx4)] pub type MatrixSliceMutXx4<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 5 columns. #[deprecated = slice_deprecation_note!(MatrixViewMutXx5)] pub type MatrixSliceMutXx5<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column-major matrix slice with a number of rows chosen at runtime and 6 columns. #[deprecated = slice_deprecation_note!(MatrixViewMutXx6)] pub type MatrixSliceMutXx6<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A column vector slice with dimensions known at compile-time. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorViewMut)] pub type VectorSliceMut<'a, T, D, RStride = U1, CStride = D> = Matrix>; /// A column vector slice with dimensions known at compile-time. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(SVectorViewMut)] pub type SVectorSliceMut<'a, T, const D: usize> = Matrix, Const<1>, ViewStorageMut<'a, T, Const, Const<1>, Const<1>, Const>>; /// A column vector slice dynamic numbers of rows and columns. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(DVectorViewMut)] pub type DVectorSliceMut<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A 1D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorViewMut1)] pub type VectorSliceMut1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A 2D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorViewMut2)] pub type VectorSliceMut2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A 3D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorViewMut3)] pub type VectorSliceMut3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A 4D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorViewMut4)] pub type VectorSliceMut4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A 5D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorViewMut5)] pub type VectorSliceMut5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A 6D column vector slice. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** #[deprecated = slice_deprecation_note!(VectorViewMut6)] pub type VectorSliceMut6<'a, T, RStride = U1, CStride = U6> = Matrix>; nalgebra-0.33.0/src/base/alias_view.rs000064400000000000000000001327060072674642500157150ustar 00000000000000use crate::base::dimension::{Dyn, U1, U2, U3, U4, U5, U6}; use crate::base::matrix_view::{ViewStorage, ViewStorageMut}; use crate::base::{Const, Matrix}; /* * * * Matrix view aliases. * * */ // NOTE: we can't provide defaults for the strides because it's not supported yet by min_const_generics. /// An immutable column-major matrix view with dimensions known at compile-time. /// /// See [`SMatrixViewMut`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type SMatrixView<'a, T, const R: usize, const C: usize> = Matrix, Const, ViewStorage<'a, T, Const, Const, Const<1>, Const>>; /// An immutable column-major matrix view dynamic numbers of rows and columns. /// /// See [`DMatrixViewMut`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type DMatrixView<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable column-major 1x1 matrix view. /// /// See [`MatrixViewMut1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable column-major 2x2 matrix view. /// /// See [`MatrixViewMut2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable column-major 3x3 matrix view. /// /// See [`MatrixViewMut3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable column-major 4x4 matrix view. /// /// See [`MatrixViewMut4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable column-major 5x5 matrix view. /// /// See [`MatrixViewMut5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable column-major 6x6 matrix view. /// /// See [`MatrixViewMut6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView6<'a, T, RStride = U1, CStride = U6> = Matrix>; /// An immutable column-major 1x2 matrix view. /// /// See [`MatrixViewMut1x2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView1x2<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable column-major 1x3 matrix view. /// /// See [`MatrixViewMut1x3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView1x3<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable column-major 1x4 matrix view. /// /// See [`MatrixViewMut1x4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView1x4<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable column-major 1x5 matrix view. /// /// See [`MatrixViewMut1x5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView1x5<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable column-major 1x6 matrix view. /// /// See [`MatrixViewMut1x6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView1x6<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable column-major 2x1 matrix view. /// /// See [`MatrixViewMut2x1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView2x1<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable column-major 2x3 matrix view. /// /// See [`MatrixViewMut2x3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView2x3<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable column-major 2x4 matrix view. /// /// See [`MatrixViewMut2x4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView2x4<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable column-major 2x5 matrix view. /// /// See [`MatrixViewMut2x5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView2x5<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable column-major 2x6 matrix view. /// /// See [`MatrixViewMut2x6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView2x6<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable column-major 3x1 matrix view. /// /// See [`MatrixViewMut3x1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView3x1<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable column-major 3x2 matrix view. /// /// See [`MatrixViewMut3x2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView3x2<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable column-major 3x4 matrix view. /// /// See [`MatrixViewMut3x4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView3x4<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable column-major 3x5 matrix view. /// /// See [`MatrixViewMut3x5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView3x5<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable column-major 3x6 matrix view. /// /// See [`MatrixViewMut3x6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView3x6<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable column-major 4x1 matrix view. /// /// See [`MatrixViewMut4x1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView4x1<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable column-major 4x2 matrix view. /// /// See [`MatrixViewMut4x2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView4x2<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable column-major 4x3 matrix view. /// /// See [`MatrixViewMut4x3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView4x3<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable column-major 4x5 matrix view. /// /// See [`MatrixViewMut4x5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView4x5<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable column-major 4x6 matrix view. /// /// See [`MatrixViewMut4x6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView4x6<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable column-major 5x1 matrix view. /// /// See [`MatrixViewMut5x1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView5x1<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable column-major 5x2 matrix view. /// /// See [`MatrixViewMut5x2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView5x2<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable column-major 5x3 matrix view. /// /// See [`MatrixViewMut5x3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView5x3<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable column-major 5x4 matrix view. /// /// See [`MatrixViewMut5x4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView5x4<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable column-major 5x6 matrix view. /// /// See [`MatrixViewMut5x6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView5x6<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable column-major 6x1 matrix view. /// /// See [`MatrixViewMut6x1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView6x1<'a, T, RStride = U1, CStride = U6> = Matrix>; /// An immutable column-major 6x2 matrix view. /// /// See [`MatrixViewMut6x2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView6x2<'a, T, RStride = U1, CStride = U6> = Matrix>; /// An immutable column-major 6x3 matrix view. /// /// See [`MatrixViewMut6x3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView6x3<'a, T, RStride = U1, CStride = U6> = Matrix>; /// An immutable column-major 6x4 matrix view. /// /// See [`MatrixViewMut6x4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView6x4<'a, T, RStride = U1, CStride = U6> = Matrix>; /// An immutable column-major 6x5 matrix view. /// /// See [`MatrixViewMut6x5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView6x5<'a, T, RStride = U1, CStride = U6> = Matrix>; /// An immutable column-major matrix view with 1 row and a number of columns chosen at runtime. /// /// See [`MatrixViewMut1xX`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView1xX<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable column-major matrix view with 2 rows and a number of columns chosen at runtime. /// /// See [`MatrixViewMut2xX`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView2xX<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable column-major matrix view with 3 rows and a number of columns chosen at runtime. /// /// See [`MatrixViewMut3xX`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView3xX<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable column-major matrix view with 4 rows and a number of columns chosen at runtime. /// /// See [`MatrixViewMut4xX`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView4xX<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable column-major matrix view with 5 rows and a number of columns chosen at runtime. /// /// See [`MatrixViewMut5xX`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView5xX<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable column-major matrix view with 6 rows and a number of columns chosen at runtime. /// /// See [`MatrixViewMut6xX`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixView6xX<'a, T, RStride = U1, CStride = U6> = Matrix>; /// An immutable column-major matrix view with a number of rows chosen at runtime and 1 column. /// /// See [`MatrixViewMutXx1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewXx1<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable column-major matrix view with a number of rows chosen at runtime and 2 columns. /// /// See [`MatrixViewMutXx2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewXx2<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable column-major matrix view with a number of rows chosen at runtime and 3 columns. /// /// See [`MatrixViewMutXx3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewXx3<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable column-major matrix view with a number of rows chosen at runtime and 4 columns. /// /// See [`MatrixViewMutXx4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewXx4<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable column-major matrix view with a number of rows chosen at runtime and 5 columns. /// /// See [`MatrixViewMutXx5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewXx5<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable column-major matrix view with a number of rows chosen at runtime and 6 columns. /// /// See [`MatrixViewMutXx6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewXx6<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable column vector view with dimensions known at compile-time. /// /// See [`VectorViewMut`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorView<'a, T, D, RStride = U1, CStride = D> = Matrix>; /// An immutable column vector view with dimensions known at compile-time. /// /// See [`SVectorViewMut`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type SVectorView<'a, T, const D: usize> = Matrix, Const<1>, ViewStorage<'a, T, Const, Const<1>, Const<1>, Const>>; /// An immutable column vector view dynamic numbers of rows and columns. /// /// See [`DVectorViewMut`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type DVectorView<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// An immutable 1D column vector view. /// /// See [`VectorViewMut1`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorView1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// An immutable 2D column vector view. /// /// See [`VectorViewMut2`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorView2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// An immutable 3D column vector view. /// /// See [`VectorViewMut3`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorView3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// An immutable 4D column vector view. /// /// See [`VectorViewMut4`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorView4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// An immutable 5D column vector view. /// /// See [`VectorViewMut5`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorView5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// An immutable 6D column vector view. /// /// See [`VectorViewMut6`] for a mutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorView6<'a, T, RStride = U1, CStride = U6> = Matrix>; /* * * * Same thing, but for mutable views. * * */ /// A mutable column-major matrix view with dimensions known at compile-time. /// /// See [`SMatrixView`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type SMatrixViewMut<'a, T, const R: usize, const C: usize> = Matrix, Const, ViewStorageMut<'a, T, Const, Const, Const<1>, Const>>; /// A mutable column-major matrix view dynamic numbers of rows and columns. /// /// See [`DMatrixView`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type DMatrixViewMut<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable column-major 1x1 matrix view. /// /// See [`MatrixView1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable column-major 2x2 matrix view. /// /// See [`MatrixView2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable column-major 3x3 matrix view. /// /// See [`MatrixView3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable column-major 4x4 matrix view. /// /// See [`MatrixView4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable column-major 5x5 matrix view. /// /// See [`MatrixView5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable column-major 6x6 matrix view. /// /// See [`MatrixView6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut6<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A mutable column-major 1x2 matrix view. /// /// See [`MatrixView1x2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut1x2<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable column-major 1x3 matrix view. /// /// See [`MatrixView1x3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut1x3<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable column-major 1x4 matrix view. /// /// See [`MatrixView1x4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut1x4<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable column-major 1x5 matrix view. /// /// See [`MatrixView1x5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut1x5<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable column-major 1x6 matrix view. /// /// See [`MatrixView1x6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut1x6<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable column-major 2x1 matrix view. /// /// See [`MatrixView2x1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut2x1<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable column-major 2x3 matrix view. /// /// See [`MatrixView2x3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut2x3<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable column-major 2x4 matrix view. /// /// See [`MatrixView2x4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut2x4<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable column-major 2x5 matrix view. /// /// See [`MatrixView2x5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut2x5<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable column-major 2x6 matrix view. /// /// See [`MatrixView2x6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut2x6<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable column-major 3x1 matrix view. /// /// See [`MatrixView3x1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut3x1<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable column-major 3x2 matrix view. /// /// See [`MatrixView3x2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut3x2<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable column-major 3x4 matrix view. /// /// See [`MatrixView3x4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut3x4<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable column-major 3x5 matrix view. /// /// See [`MatrixView3x5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut3x5<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable column-major 3x6 matrix view. /// /// See [`MatrixView3x6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut3x6<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable column-major 4x1 matrix view. /// /// See [`MatrixView4x1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut4x1<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable column-major 4x2 matrix view. /// /// See [`MatrixView4x2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut4x2<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable column-major 4x3 matrix view. /// /// See [`MatrixView4x3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut4x3<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable column-major 4x5 matrix view. /// /// See [`MatrixView4x5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut4x5<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable column-major 4x6 matrix view. /// /// See [`MatrixView4x6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut4x6<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable column-major 5x1 matrix view. /// /// See [`MatrixView5x1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut5x1<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable column-major 5x2 matrix view. /// /// See [`MatrixView5x2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut5x2<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable column-major 5x3 matrix view. /// /// See [`MatrixView5x3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut5x3<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable column-major 5x4 matrix view. /// /// See [`MatrixView5x4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut5x4<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable column-major 5x6 matrix view. /// /// See [`MatrixView5x6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut5x6<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable column-major 6x1 matrix view. /// /// See [`MatrixView6x1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut6x1<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A mutable column-major 6x2 matrix view. /// /// See [`MatrixView6x2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut6x2<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A mutable column-major 6x3 matrix view. /// /// See [`MatrixView6x3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut6x3<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A mutable column-major 6x4 matrix view. /// /// See [`MatrixView6x4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut6x4<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A mutable column-major 6x5 matrix view. /// /// See [`MatrixView6x5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut6x5<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A mutable column-major matrix view with 1 row and a number of columns chosen at runtime. /// /// See [`MatrixView1xX`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut1xX<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable column-major matrix view with 2 rows and a number of columns chosen at runtime. /// /// See [`MatrixView2xX`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut2xX<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable column-major matrix view with 3 rows and a number of columns chosen at runtime. /// /// See [`MatrixView3xX`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut3xX<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable column-major matrix view with 4 rows and a number of columns chosen at runtime. /// /// See [`MatrixView4xX`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut4xX<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable column-major matrix view with 5 rows and a number of columns chosen at runtime. /// /// See [`MatrixView5xX`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut5xX<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable column-major matrix view with 6 rows and a number of columns chosen at runtime. /// /// See [`MatrixView6xX`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMut6xX<'a, T, RStride = U1, CStride = U6> = Matrix>; /// A mutable column-major matrix view with a number of rows chosen at runtime and 1 column. /// /// See [`MatrixViewXx1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMutXx1<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable column-major matrix view with a number of rows chosen at runtime and 2 columns. /// /// See [`MatrixViewXx2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMutXx2<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable column-major matrix view with a number of rows chosen at runtime and 3 columns. /// /// See [`MatrixViewXx3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMutXx3<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable column-major matrix view with a number of rows chosen at runtime and 4 columns. /// /// See [`MatrixViewXx4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMutXx4<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable column-major matrix view with a number of rows chosen at runtime and 5 columns. /// /// See [`MatrixViewXx5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMutXx5<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable column-major matrix view with a number of rows chosen at runtime and 6 columns. /// /// See [`MatrixViewXx6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type MatrixViewMutXx6<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable column vector view with dimensions known at compile-time. /// /// See [`VectorView`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorViewMut<'a, T, D, RStride = U1, CStride = D> = Matrix>; /// A mutable column vector view with dimensions known at compile-time. /// /// See [`SVectorView`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type SVectorViewMut<'a, T, const D: usize> = Matrix, Const<1>, ViewStorageMut<'a, T, Const, Const<1>, Const<1>, Const>>; /// A mutable column vector view dynamic numbers of rows and columns. /// /// See [`DVectorView`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type DVectorViewMut<'a, T, RStride = U1, CStride = Dyn> = Matrix>; /// A mutable 1D column vector view. /// /// See [`VectorView1`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorViewMut1<'a, T, RStride = U1, CStride = U1> = Matrix>; /// A mutable 2D column vector view. /// /// See [`VectorView2`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorViewMut2<'a, T, RStride = U1, CStride = U2> = Matrix>; /// A mutable 3D column vector view. /// /// See [`VectorView3`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorViewMut3<'a, T, RStride = U1, CStride = U3> = Matrix>; /// A mutable 4D column vector view. /// /// See [`VectorView4`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorViewMut4<'a, T, RStride = U1, CStride = U4> = Matrix>; /// A mutable 5D column vector view. /// /// See [`VectorView5`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorViewMut5<'a, T, RStride = U1, CStride = U5> = Matrix>; /// A mutable 6D column vector view. /// /// See [`VectorView6`] for an immutable version of this type. /// /// **Because this is an alias, not all its methods are listed here. See the [`Matrix`](crate::base::Matrix) type too.** pub type VectorViewMut6<'a, T, RStride = U1, CStride = U6> = Matrix>; nalgebra-0.33.0/src/base/allocator.rs000064400000000000000000000132310072674642500155410ustar 00000000000000//! Abstract definition of a matrix data storage allocator. use std::any::Any; use crate::base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use crate::base::dimension::{Dim, U1}; use crate::base::{DefaultAllocator, Scalar}; use crate::storage::{IsContiguous, RawStorageMut}; use crate::StorageMut; use std::fmt::Debug; use std::mem::MaybeUninit; /// A matrix allocator of a memory buffer that may contain `R::to_usize() * C::to_usize()` /// elements of type `T`. /// /// An allocator is said to be: /// − static: if `R` and `C` both implement `DimName`. /// − dynamic: if either one (or both) of `R` or `C` is equal to `Dyn`. /// /// Every allocator must be both static and dynamic. Though not all implementations may share the /// same `Buffer` type. pub trait Allocator: Any + Sized { /// The type of buffer this allocator can instantiate. type Buffer: StorageMut + IsContiguous + Clone + Debug; /// The type of buffer with uninitialized components this allocator can instantiate. type BufferUninit: RawStorageMut, R, C> + IsContiguous; /// Allocates a buffer with the given number of rows and columns without initializing its content. fn allocate_uninit(nrows: R, ncols: C) -> Self::BufferUninit; /// Assumes a data buffer to be initialized. /// /// # Safety /// The user must make sure that every single entry of the buffer has been initialized, /// or Undefined Behavior will immediately occur. unsafe fn assume_init(uninit: Self::BufferUninit) -> Self::Buffer; /// Allocates a buffer initialized with the content of the given iterator. fn allocate_from_iterator>( nrows: R, ncols: C, iter: I, ) -> Self::Buffer; #[inline] /// Allocates a buffer initialized with the content of the given row-major order iterator. fn allocate_from_row_iterator>( nrows: R, ncols: C, iter: I, ) -> Self::Buffer { let mut res = Self::allocate_uninit(nrows, ncols); let mut count = 0; unsafe { // OK because the allocated buffer is guaranteed to be contiguous. let res_ptr = res.as_mut_slice_unchecked(); for (k, e) in iter .into_iter() .take(ncols.value() * nrows.value()) .enumerate() { let i = k / ncols.value(); let j = k % ncols.value(); // result[(i, j)] = e; *res_ptr.get_unchecked_mut(i + j * nrows.value()) = MaybeUninit::new(e); count += 1; } assert!( count == nrows.value() * ncols.value(), "Matrix init. from row iterator: iterator not long enough." ); >::assume_init(res) } } } /// A matrix reallocator. Changes the size of the memory buffer that initially contains (`RFrom` × /// `CFrom`) elements to a smaller or larger size (`RTo`, `CTo`). pub trait Reallocator: Allocator + Allocator { /// Reallocates a buffer of shape `(RTo, CTo)`, possibly reusing a previously allocated buffer /// `buf`. Data stored by `buf` are linearly copied to the output: /// /// # Safety /// The following invariants must be respected by the implementors of this method: /// * The copy is performed as if both were just arrays (without taking into account the matrix structure). /// * If the underlying buffer is being shrunk, the removed elements must **not** be dropped /// by this method. Dropping them is the responsibility of the caller. unsafe fn reallocate_copy( nrows: RTo, ncols: CTo, buf: >::Buffer, ) -> >::BufferUninit; } /// The number of rows of the result of a componentwise operation on two matrices. pub type SameShapeR = >::Representative; /// The number of columns of the result of a componentwise operation on two matrices. pub type SameShapeC = >::Representative; // TODO: Bad name. /// Restricts the given number of rows and columns to be respectively the same. pub trait SameShapeAllocator: Allocator + Allocator, SameShapeC> where R1: Dim, R2: Dim, C1: Dim, C2: Dim, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { } impl SameShapeAllocator for DefaultAllocator where R1: Dim, R2: Dim, C1: Dim, C2: Dim, DefaultAllocator: Allocator + Allocator, SameShapeC>, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { } // XXX: Bad name. /// Restricts the given number of rows to be equal. pub trait SameShapeVectorAllocator: Allocator + Allocator> + SameShapeAllocator where R1: Dim, R2: Dim, ShapeConstraint: SameNumberOfRows, { } impl SameShapeVectorAllocator for DefaultAllocator where R1: Dim, R2: Dim, DefaultAllocator: Allocator + Allocator>, ShapeConstraint: SameNumberOfRows, { } nalgebra-0.33.0/src/base/array_storage.rs000064400000000000000000000205660072674642500164340ustar 00000000000000use std::fmt::{self, Debug, Formatter}; // use std::hash::{Hash, Hasher}; use std::ops::Mul; #[cfg(feature = "serde-serialize-no-std")] use serde::de::{Error, SeqAccess, Visitor}; #[cfg(feature = "serde-serialize-no-std")] use serde::ser::SerializeTuple; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde-serialize-no-std")] use std::marker::PhantomData; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; use crate::base::allocator::Allocator; use crate::base::default_allocator::DefaultAllocator; use crate::base::dimension::{Const, ToTypenum}; use crate::base::storage::{IsContiguous, Owned, RawStorage, RawStorageMut, ReshapableStorage}; use crate::base::Scalar; use crate::Storage; use std::mem; /* * * Static RawStorage. * */ /// An array-based statically sized matrix data storage. #[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "ArrayStorage", bound(archive = " T: rkyv::Archive, [[T; R]; C]: rkyv::Archive ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] pub struct ArrayStorage(pub [[T; R]; C]); impl ArrayStorage { /// Converts this array storage to a slice. #[inline] pub fn as_slice(&self) -> &[T] { // SAFETY: this is OK because ArrayStorage is contiguous. unsafe { self.as_slice_unchecked() } } /// Converts this array storage to a mutable slice. #[inline] pub fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: this is OK because ArrayStorage is contiguous. unsafe { self.as_mut_slice_unchecked() } } } // TODO: remove this once the stdlib implements Default for arrays. impl Default for ArrayStorage where [[T; R]; C]: Default, { #[inline] fn default() -> Self { Self(Default::default()) } } impl Debug for ArrayStorage { #[inline] fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { self.0.fmt(fmt) } } unsafe impl RawStorage, Const> for ArrayStorage { type RStride = Const<1>; type CStride = Const; #[inline] fn ptr(&self) -> *const T { self.0.as_ptr() as *const T } #[inline] fn shape(&self) -> (Const, Const) { (Const, Const) } #[inline] fn strides(&self) -> (Self::RStride, Self::CStride) { (Const, Const) } #[inline] fn is_contiguous(&self) -> bool { true } #[inline] unsafe fn as_slice_unchecked(&self) -> &[T] { std::slice::from_raw_parts(self.ptr(), R * C) } } unsafe impl Storage, Const> for ArrayStorage where DefaultAllocator: Allocator, Const, Buffer = Self>, { #[inline] fn into_owned(self) -> Owned, Const> where DefaultAllocator: Allocator, Const>, { self } #[inline] fn clone_owned(&self) -> Owned, Const> where DefaultAllocator: Allocator, Const>, { self.clone() } #[inline] fn forget_elements(self) { // No additional cleanup required. std::mem::forget(self); } } unsafe impl RawStorageMut, Const> for ArrayStorage { #[inline] fn ptr_mut(&mut self) -> *mut T { self.0.as_mut_ptr() as *mut T } #[inline] unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { std::slice::from_raw_parts_mut(self.ptr_mut(), R * C) } } unsafe impl IsContiguous for ArrayStorage {} impl ReshapableStorage, Const, Const, Const> for ArrayStorage where T: Scalar, Const: ToTypenum, Const: ToTypenum, Const: ToTypenum, Const: ToTypenum, as ToTypenum>::Typenum: Mul< as ToTypenum>::Typenum>, as ToTypenum>::Typenum: Mul< as ToTypenum>::Typenum, Output = typenum::Prod< as ToTypenum>::Typenum, as ToTypenum>::Typenum, >, >, { type Output = ArrayStorage; fn reshape_generic(self, _: Const, _: Const) -> Self::Output { unsafe { let data: [[T; R2]; C2] = mem::transmute_copy(&self.0); mem::forget(self.0); ArrayStorage(data) } } } /* * * Serialization. * */ // XXX: open an issue for serde so that it allows the serialization/deserialization of all arrays? #[cfg(feature = "serde-serialize-no-std")] impl Serialize for ArrayStorage where T: Scalar + Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut serializer = serializer.serialize_tuple(R * C)?; for e in self.as_slice().iter() { serializer.serialize_element(e)?; } serializer.end() } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T, const R: usize, const C: usize> Deserialize<'a> for ArrayStorage where T: Scalar + Deserialize<'a>, { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a>, { deserializer.deserialize_tuple(R * C, ArrayStorageVisitor::new()) } } #[cfg(feature = "serde-serialize-no-std")] /// A visitor that produces a matrix array. struct ArrayStorageVisitor { marker: PhantomData, } #[cfg(feature = "serde-serialize-no-std")] impl ArrayStorageVisitor where T: Scalar, { /// Construct a new sequence visitor. pub fn new() -> Self { ArrayStorageVisitor { marker: PhantomData, } } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T, const R: usize, const C: usize> Visitor<'a> for ArrayStorageVisitor where T: Scalar + Deserialize<'a>, { type Value = ArrayStorage; fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { formatter.write_str("a matrix array") } #[inline] fn visit_seq(self, mut visitor: V) -> Result, V::Error> where V: SeqAccess<'a>, { let mut out: ArrayStorage, R, C> = >::allocate_uninit(Const::, Const::); let mut curr = 0; while let Some(value) = visitor.next_element()? { *out.as_mut_slice() .get_mut(curr) .ok_or_else(|| V::Error::invalid_length(curr, &self))? = core::mem::MaybeUninit::new(value); curr += 1; } if curr == R * C { // Safety: all the elements have been initialized. unsafe { Ok(, Const>>::assume_init(out)) } } else { for i in 0..curr { // Safety: // - We couldn’t initialize the whole storage. Drop the ones we initialized. unsafe { std::ptr::drop_in_place(out.as_mut_slice()[i].as_mut_ptr()) }; } Err(V::Error::invalid_length(curr, &self)) } } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for ArrayStorage { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for ArrayStorage { } nalgebra-0.33.0/src/base/blas.rs000064400000000000000000001267510072674642500145160ustar 00000000000000use crate::{RawStorage, SimdComplexField}; use num::{One, Zero}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; use crate::base::allocator::Allocator; use crate::base::blas_uninit::{axcpy_uninit, gemm_uninit, gemv_uninit}; use crate::base::constraint::{ AreMultipliable, DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint, }; use crate::base::dimension::{Const, Dim, Dyn, U1, U2, U3, U4}; use crate::base::storage::{Storage, StorageMut}; use crate::base::uninit::Init; use crate::base::{ DVectorView, DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector, VectorView, }; /// # Dot/scalar product impl> Matrix where T: Scalar + Zero + ClosedAddAssign + ClosedMulAssign, { #[inline(always)] fn dotx( &self, rhs: &Matrix, conjugate: impl Fn(T) -> T, ) -> T where SB: RawStorage, ShapeConstraint: DimEq + DimEq, { assert!( self.nrows() == rhs.nrows(), "Dot product dimensions mismatch for shapes {:?} and {:?}: left rows != right rows.", self.shape(), rhs.shape(), ); assert!( self.ncols() == rhs.ncols(), "Dot product dimensions mismatch for shapes {:?} and {:?}: left cols != right cols.", self.shape(), rhs.shape(), ); // So we do some special cases for common fixed-size vectors of dimension lower than 8 // because the `for` loop below won't be very efficient on those. if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { let a = conjugate(self.get_unchecked((0, 0)).clone()) * rhs.get_unchecked((0, 0)).clone(); let b = conjugate(self.get_unchecked((1, 0)).clone()) * rhs.get_unchecked((1, 0)).clone(); return a + b; } } if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { let a = conjugate(self.get_unchecked((0, 0)).clone()) * rhs.get_unchecked((0, 0)).clone(); let b = conjugate(self.get_unchecked((1, 0)).clone()) * rhs.get_unchecked((1, 0)).clone(); let c = conjugate(self.get_unchecked((2, 0)).clone()) * rhs.get_unchecked((2, 0)).clone(); return a + b + c; } } if (R::is::() || R2::is::()) && (C::is::() || C2::is::()) { unsafe { let mut a = conjugate(self.get_unchecked((0, 0)).clone()) * rhs.get_unchecked((0, 0)).clone(); let mut b = conjugate(self.get_unchecked((1, 0)).clone()) * rhs.get_unchecked((1, 0)).clone(); let c = conjugate(self.get_unchecked((2, 0)).clone()) * rhs.get_unchecked((2, 0)).clone(); let d = conjugate(self.get_unchecked((3, 0)).clone()) * rhs.get_unchecked((3, 0)).clone(); a += c; b += d; return a + b; } } // All this is inspired from the "unrolled version" discussed in: // https://blog.theincredibleholk.org/blog/2012/12/10/optimizing-dot-product/ // // And this comment from bluss: // https://users.rust-lang.org/t/how-to-zip-two-slices-efficiently/2048/12 let mut res = T::zero(); // We have to define them outside of the loop (and not inside at first assignment) // otherwise vectorization won't kick in for some reason. let mut acc0; let mut acc1; let mut acc2; let mut acc3; let mut acc4; let mut acc5; let mut acc6; let mut acc7; for j in 0..self.ncols() { let mut i = 0; acc0 = T::zero(); acc1 = T::zero(); acc2 = T::zero(); acc3 = T::zero(); acc4 = T::zero(); acc5 = T::zero(); acc6 = T::zero(); acc7 = T::zero(); while self.nrows() - i >= 8 { acc0 += unsafe { conjugate(self.get_unchecked((i, j)).clone()) * rhs.get_unchecked((i, j)).clone() }; acc1 += unsafe { conjugate(self.get_unchecked((i + 1, j)).clone()) * rhs.get_unchecked((i + 1, j)).clone() }; acc2 += unsafe { conjugate(self.get_unchecked((i + 2, j)).clone()) * rhs.get_unchecked((i + 2, j)).clone() }; acc3 += unsafe { conjugate(self.get_unchecked((i + 3, j)).clone()) * rhs.get_unchecked((i + 3, j)).clone() }; acc4 += unsafe { conjugate(self.get_unchecked((i + 4, j)).clone()) * rhs.get_unchecked((i + 4, j)).clone() }; acc5 += unsafe { conjugate(self.get_unchecked((i + 5, j)).clone()) * rhs.get_unchecked((i + 5, j)).clone() }; acc6 += unsafe { conjugate(self.get_unchecked((i + 6, j)).clone()) * rhs.get_unchecked((i + 6, j)).clone() }; acc7 += unsafe { conjugate(self.get_unchecked((i + 7, j)).clone()) * rhs.get_unchecked((i + 7, j)).clone() }; i += 8; } res += acc0 + acc4; res += acc1 + acc5; res += acc2 + acc6; res += acc3 + acc7; for k in i..self.nrows() { res += unsafe { conjugate(self.get_unchecked((k, j)).clone()) * rhs.get_unchecked((k, j)).clone() } } } res } /// The dot product between two vectors or matrices (seen as vectors). /// /// This is equal to `self.transpose() * rhs`. For the sesquilinear complex dot product, use /// `self.dotc(rhs)`. /// /// Note that this is **not** the matrix multiplication as in, e.g., numpy. For matrix /// multiplication, use one of: `.gemm`, `.mul_to`, `.mul`, the `*` operator. /// /// # Example /// ``` /// # use nalgebra::{Vector3, Matrix2x3}; /// let vec1 = Vector3::new(1.0, 2.0, 3.0); /// let vec2 = Vector3::new(0.1, 0.2, 0.3); /// assert_eq!(vec1.dot(&vec2), 1.4); /// /// let mat1 = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// let mat2 = Matrix2x3::new(0.1, 0.2, 0.3, /// 0.4, 0.5, 0.6); /// assert_eq!(mat1.dot(&mat2), 9.1); /// ``` /// #[inline] #[must_use] pub fn dot(&self, rhs: &Matrix) -> T where SB: RawStorage, ShapeConstraint: DimEq + DimEq, { self.dotx(rhs, |e| e) } /// The conjugate-linear dot product between two vectors or matrices (seen as vectors). /// /// This is equal to `self.adjoint() * rhs`. /// For real vectors, this is identical to `self.dot(&rhs)`. /// Note that this is **not** the matrix multiplication as in, e.g., numpy. For matrix /// multiplication, use one of: `.gemm`, `.mul_to`, `.mul`, the `*` operator. /// /// # Example /// ``` /// # use nalgebra::{Vector2, Complex}; /// let vec1 = Vector2::new(Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)); /// let vec2 = Vector2::new(Complex::new(0.4, 0.3), Complex::new(0.2, 0.1)); /// assert_eq!(vec1.dotc(&vec2), Complex::new(2.0, -1.0)); /// /// // Note that for complex vectors, we generally have: /// // vec1.dotc(&vec2) != vec2.dot(&vec2) /// assert_ne!(vec1.dotc(&vec2), vec1.dot(&vec2)); /// ``` #[inline] #[must_use] pub fn dotc(&self, rhs: &Matrix) -> T where T: SimdComplexField, SB: RawStorage, ShapeConstraint: DimEq + DimEq, { self.dotx(rhs, T::simd_conjugate) } /// The dot product between the transpose of `self` and `rhs`. /// /// # Example /// ``` /// # use nalgebra::{Vector3, RowVector3, Matrix2x3, Matrix3x2}; /// let vec1 = Vector3::new(1.0, 2.0, 3.0); /// let vec2 = RowVector3::new(0.1, 0.2, 0.3); /// assert_eq!(vec1.tr_dot(&vec2), 1.4); /// /// let mat1 = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// let mat2 = Matrix3x2::new(0.1, 0.4, /// 0.2, 0.5, /// 0.3, 0.6); /// assert_eq!(mat1.tr_dot(&mat2), 9.1); /// ``` #[inline] #[must_use] pub fn tr_dot(&self, rhs: &Matrix) -> T where SB: RawStorage, ShapeConstraint: DimEq + DimEq, { let (nrows, ncols) = self.shape(); assert_eq!( (ncols, nrows), rhs.shape(), "Transposed dot product dimension mismatch." ); let mut res = T::zero(); for j in 0..self.nrows() { for i in 0..self.ncols() { res += unsafe { self.get_unchecked((j, i)).clone() * rhs.get_unchecked((i, j)).clone() } } } res } } /// # BLAS functions impl Vector where T: Scalar + Zero + ClosedAddAssign + ClosedMulAssign, S: StorageMut, { /// Computes `self = a * x * c + b * self`. /// /// If `b` is zero, `self` is never read from. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let mut vec1 = Vector3::new(1.0, 2.0, 3.0); /// let vec2 = Vector3::new(0.1, 0.2, 0.3); /// vec1.axcpy(5.0, &vec2, 2.0, 5.0); /// assert_eq!(vec1, Vector3::new(6.0, 12.0, 18.0)); /// ``` #[inline] #[allow(clippy::many_single_char_names)] pub fn axcpy(&mut self, a: T, x: &Vector, c: T, b: T) where SB: Storage, ShapeConstraint: DimEq, { unsafe { axcpy_uninit(Init, self, a, x, c, b) }; } /// Computes `self = a * x + b * self`. /// /// If `b` is zero, `self` is never read from. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let mut vec1 = Vector3::new(1.0, 2.0, 3.0); /// let vec2 = Vector3::new(0.1, 0.2, 0.3); /// vec1.axpy(10.0, &vec2, 5.0); /// assert_eq!(vec1, Vector3::new(6.0, 12.0, 18.0)); /// ``` #[inline] pub fn axpy(&mut self, a: T, x: &Vector, b: T) where T: One, SB: Storage, ShapeConstraint: DimEq, { assert_eq!(self.nrows(), x.nrows(), "Axpy: mismatched vector shapes."); self.axcpy(a, x, T::one(), b) } /// Computes `self = alpha * a * x + beta * self`, where `a` is a matrix, `x` a vector, and /// `alpha, beta` two scalars. /// /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # use nalgebra::{Matrix2, Vector2}; /// let mut vec1 = Vector2::new(1.0, 2.0); /// let vec2 = Vector2::new(0.1, 0.2); /// let mat = Matrix2::new(1.0, 2.0, /// 3.0, 4.0); /// vec1.gemv(10.0, &mat, &vec2, 5.0); /// assert_eq!(vec1, Vector2::new(10.0, 21.0)); /// ``` #[inline] pub fn gemv( &mut self, alpha: T, a: &Matrix, x: &Vector, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + AreMultipliable, { // Safety: this is safe because we are passing Status == Init. unsafe { gemv_uninit(Init, self, alpha, a, x, beta) } } #[inline(always)] fn xxgemv( &mut self, alpha: T, a: &SquareMatrix, x: &Vector, beta: T, dot: impl Fn( &DVectorView<'_, T, SB::RStride, SB::CStride>, &DVectorView<'_, T, SC::RStride, SC::CStride>, ) -> T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + AreMultipliable, { let dim1 = self.nrows(); let dim2 = a.nrows(); let dim3 = x.nrows(); assert!( a.is_square(), "Symmetric cgemv: the input matrix must be square." ); assert!( dim2 == dim3 && dim1 == dim2, "Symmetric cgemv: dimensions mismatch." ); if dim2 == 0 { return; } // TODO: avoid bound checks. let col2 = a.column(0); let val = unsafe { x.vget_unchecked(0).clone() }; self.axpy(alpha.clone() * val, &col2, beta); self[0] += alpha.clone() * dot(&a.view_range(1.., 0), &x.rows_range(1..)); for j in 1..dim2 { let col2 = a.column(j); let dot = dot(&col2.rows_range(j..), &x.rows_range(j..)); let val; unsafe { val = x.vget_unchecked(j).clone(); *self.vget_unchecked_mut(j) += alpha.clone() * dot; } self.rows_range_mut(j + 1..).axpy( alpha.clone() * val, &col2.rows_range(j + 1..), T::one(), ); } } /// Computes `self = alpha * a * x + beta * self`, where `a` is a **symmetric** matrix, `x` a /// vector, and `alpha, beta` two scalars. /// /// For hermitian matrices, use `.hegemv` instead. /// If `beta` is zero, `self` is never read. If `self` is read, only its lower-triangular part /// (including the diagonal) is actually read. /// /// # Examples /// ``` /// # use nalgebra::{Matrix2, Vector2}; /// let mat = Matrix2::new(1.0, 2.0, /// 2.0, 4.0); /// let mut vec1 = Vector2::new(1.0, 2.0); /// let vec2 = Vector2::new(0.1, 0.2); /// vec1.sygemv(10.0, &mat, &vec2, 5.0); /// assert_eq!(vec1, Vector2::new(10.0, 20.0)); /// /// /// // The matrix upper-triangular elements can be garbage because it is never /// // read by this method. Therefore, it is not necessary for the caller to /// // fill the matrix struct upper-triangle. /// let mat = Matrix2::new(1.0, 9999999.9999999, /// 2.0, 4.0); /// let mut vec1 = Vector2::new(1.0, 2.0); /// vec1.sygemv(10.0, &mat, &vec2, 5.0); /// assert_eq!(vec1, Vector2::new(10.0, 20.0)); /// ``` #[inline] pub fn sygemv( &mut self, alpha: T, a: &SquareMatrix, x: &Vector, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + AreMultipliable, { self.xxgemv(alpha, a, x, beta, |a, b| a.dot(b)) } /// Computes `self = alpha * a * x + beta * self`, where `a` is an **hermitian** matrix, `x` a /// vector, and `alpha, beta` two scalars. /// /// If `beta` is zero, `self` is never read. If `self` is read, only its lower-triangular part /// (including the diagonal) is actually read. /// /// # Examples /// ``` /// # use nalgebra::{Matrix2, Vector2, Complex}; /// let mat = Matrix2::new(Complex::new(1.0, 0.0), Complex::new(2.0, -0.1), /// Complex::new(2.0, 1.0), Complex::new(4.0, 0.0)); /// let mut vec1 = Vector2::new(Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)); /// let vec2 = Vector2::new(Complex::new(0.1, 0.2), Complex::new(0.3, 0.4)); /// vec1.sygemv(Complex::new(10.0, 20.0), &mat, &vec2, Complex::new(5.0, 15.0)); /// assert_eq!(vec1, Vector2::new(Complex::new(-48.0, 44.0), Complex::new(-75.0, 110.0))); /// /// /// // The matrix upper-triangular elements can be garbage because it is never /// // read by this method. Therefore, it is not necessary for the caller to /// // fill the matrix struct upper-triangle. /// /// let mat = Matrix2::new(Complex::new(1.0, 0.0), Complex::new(99999999.9, 999999999.9), /// Complex::new(2.0, 1.0), Complex::new(4.0, 0.0)); /// let mut vec1 = Vector2::new(Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)); /// let vec2 = Vector2::new(Complex::new(0.1, 0.2), Complex::new(0.3, 0.4)); /// vec1.sygemv(Complex::new(10.0, 20.0), &mat, &vec2, Complex::new(5.0, 15.0)); /// assert_eq!(vec1, Vector2::new(Complex::new(-48.0, 44.0), Complex::new(-75.0, 110.0))); /// ``` #[inline] pub fn hegemv( &mut self, alpha: T, a: &SquareMatrix, x: &Vector, beta: T, ) where T: SimdComplexField, SB: Storage, SC: Storage, ShapeConstraint: DimEq + AreMultipliable, { self.xxgemv(alpha, a, x, beta, |a, b| a.dotc(b)) } #[inline(always)] fn gemv_xx( &mut self, alpha: T, a: &Matrix, x: &Vector, beta: T, dot: impl Fn(&VectorView<'_, T, R2, SB::RStride, SB::CStride>, &Vector) -> T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + AreMultipliable, { let dim1 = self.nrows(); let (nrows2, ncols2) = a.shape(); let dim3 = x.nrows(); assert!( nrows2 == dim3 && dim1 == ncols2, "Gemv: dimensions mismatch." ); if ncols2 == 0 { return; } if beta.is_zero() { for j in 0..ncols2 { let val = unsafe { self.vget_unchecked_mut(j) }; *val = alpha.clone() * dot(&a.column(j), x) } } else { for j in 0..ncols2 { let val = unsafe { self.vget_unchecked_mut(j) }; *val = alpha.clone() * dot(&a.column(j), x) + beta.clone() * val.clone(); } } } /// Computes `self = alpha * a.transpose() * x + beta * self`, where `a` is a matrix, `x` a vector, and /// `alpha, beta` two scalars. /// /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # use nalgebra::{Matrix2, Vector2}; /// let mat = Matrix2::new(1.0, 3.0, /// 2.0, 4.0); /// let mut vec1 = Vector2::new(1.0, 2.0); /// let vec2 = Vector2::new(0.1, 0.2); /// let expected = mat.transpose() * vec2 * 10.0 + vec1 * 5.0; /// /// vec1.gemv_tr(10.0, &mat, &vec2, 5.0); /// assert_eq!(vec1, expected); /// ``` #[inline] pub fn gemv_tr( &mut self, alpha: T, a: &Matrix, x: &Vector, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + AreMultipliable, { self.gemv_xx(alpha, a, x, beta, |a, b| a.dot(b)) } /// Computes `self = alpha * a.adjoint() * x + beta * self`, where `a` is a matrix, `x` a vector, and /// `alpha, beta` two scalars. /// /// For real matrices, this is the same as `.gemv_tr`. /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # use nalgebra::{Matrix2, Vector2, Complex}; /// let mat = Matrix2::new(Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), /// Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)); /// let mut vec1 = Vector2::new(Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)); /// let vec2 = Vector2::new(Complex::new(0.1, 0.2), Complex::new(0.3, 0.4)); /// let expected = mat.adjoint() * vec2 * Complex::new(10.0, 20.0) + vec1 * Complex::new(5.0, 15.0); /// /// vec1.gemv_ad(Complex::new(10.0, 20.0), &mat, &vec2, Complex::new(5.0, 15.0)); /// assert_eq!(vec1, expected); /// ``` #[inline] pub fn gemv_ad( &mut self, alpha: T, a: &Matrix, x: &Vector, beta: T, ) where T: SimdComplexField, SB: Storage, SC: Storage, ShapeConstraint: DimEq + AreMultipliable, { self.gemv_xx(alpha, a, x, beta, |a, b| a.dotc(b)) } } impl> Matrix where T: Scalar + Zero + ClosedAddAssign + ClosedMulAssign, { #[inline(always)] fn gerx( &mut self, alpha: T, x: &Vector, y: &Vector, beta: T, conjugate: impl Fn(T) -> T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + DimEq, { let (nrows1, ncols1) = self.shape(); let dim2 = x.nrows(); let dim3 = y.nrows(); assert!( nrows1 == dim2 && ncols1 == dim3, "ger: dimensions mismatch." ); for j in 0..ncols1 { // TODO: avoid bound checks. let val = unsafe { conjugate(y.vget_unchecked(j).clone()) }; self.column_mut(j) .axpy(alpha.clone() * val, x, beta.clone()); } } /// Computes `self = alpha * x * y.transpose() + beta * self`. /// /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector2, Vector3}; /// let mut mat = Matrix2x3::repeat(4.0); /// let vec1 = Vector2::new(1.0, 2.0); /// let vec2 = Vector3::new(0.1, 0.2, 0.3); /// let expected = vec1 * vec2.transpose() * 10.0 + mat * 5.0; /// /// mat.ger(10.0, &vec1, &vec2, 5.0); /// assert_eq!(mat, expected); /// ``` #[inline] pub fn ger( &mut self, alpha: T, x: &Vector, y: &Vector, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + DimEq, { self.gerx(alpha, x, y, beta, |e| e) } /// Computes `self = alpha * x * y.adjoint() + beta * self`. /// /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix2x3, Vector2, Vector3, Complex}; /// let mut mat = Matrix2x3::repeat(Complex::new(4.0, 5.0)); /// let vec1 = Vector2::new(Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)); /// let vec2 = Vector3::new(Complex::new(0.6, 0.5), Complex::new(0.4, 0.5), Complex::new(0.2, 0.1)); /// let expected = vec1 * vec2.adjoint() * Complex::new(10.0, 20.0) + mat * Complex::new(5.0, 15.0); /// /// mat.gerc(Complex::new(10.0, 20.0), &vec1, &vec2, Complex::new(5.0, 15.0)); /// assert_eq!(mat, expected); /// ``` #[inline] pub fn gerc( &mut self, alpha: T, x: &Vector, y: &Vector, beta: T, ) where T: SimdComplexField, SB: Storage, SC: Storage, ShapeConstraint: DimEq + DimEq, { self.gerx(alpha, x, y, beta, SimdComplexField::simd_conjugate) } /// Computes `self = alpha * a * b + beta * self`, where `a, b, self` are matrices. /// `alpha` and `beta` are scalar. /// /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix2x3, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// let mat3 = Matrix3x4::new(0.1, 0.2, 0.3, 0.4, /// 0.5, 0.6, 0.7, 0.8, /// 0.9, 1.0, 1.1, 1.2); /// let expected = mat2 * mat3 * 10.0 + mat1 * 5.0; /// /// mat1.gemm(10.0, &mat2, &mat3, 5.0); /// assert_relative_eq!(mat1, expected); /// ``` #[inline] pub fn gemm( &mut self, alpha: T, a: &Matrix, b: &Matrix, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + AreMultipliable, { // SAFETY: this is valid because our matrices are initialized and // we are using status = Init. unsafe { gemm_uninit(Init, self, alpha, a, b, beta) } } /// Computes `self = alpha * a.transpose() * b + beta * self`, where `a, b, self` are matrices. /// `alpha` and `beta` are scalar. /// /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix3x2, Matrix3x4, Matrix2x4}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix3x2::new(1.0, 4.0, /// 2.0, 5.0, /// 3.0, 6.0); /// let mat3 = Matrix3x4::new(0.1, 0.2, 0.3, 0.4, /// 0.5, 0.6, 0.7, 0.8, /// 0.9, 1.0, 1.1, 1.2); /// let expected = mat2.transpose() * mat3 * 10.0 + mat1 * 5.0; /// /// mat1.gemm_tr(10.0, &mat2, &mat3, 5.0); /// assert_eq!(mat1, expected); /// ``` #[inline] pub fn gemm_tr( &mut self, alpha: T, a: &Matrix, b: &Matrix, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + AreMultipliable, { let (nrows1, ncols1) = self.shape(); let (nrows2, ncols2) = a.shape(); let (nrows3, ncols3) = b.shape(); assert_eq!( nrows2, nrows3, "gemm: dimensions mismatch for multiplication." ); assert_eq!( (nrows1, ncols1), (ncols2, ncols3), "gemm: dimensions mismatch for addition." ); for j1 in 0..ncols1 { // TODO: avoid bound checks. self.column_mut(j1) .gemv_tr(alpha.clone(), a, &b.column(j1), beta.clone()); } } /// Computes `self = alpha * a.adjoint() * b + beta * self`, where `a, b, self` are matrices. /// `alpha` and `beta` are scalar. /// /// If `beta` is zero, `self` is never read. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix3x2, Matrix3x4, Matrix2x4, Complex}; /// let mut mat1 = Matrix2x4::identity(); /// let mat2 = Matrix3x2::new(Complex::new(1.0, 4.0), Complex::new(7.0, 8.0), /// Complex::new(2.0, 5.0), Complex::new(9.0, 10.0), /// Complex::new(3.0, 6.0), Complex::new(11.0, 12.0)); /// let mat3 = Matrix3x4::new(Complex::new(0.1, 1.3), Complex::new(0.2, 1.4), Complex::new(0.3, 1.5), Complex::new(0.4, 1.6), /// Complex::new(0.5, 1.7), Complex::new(0.6, 1.8), Complex::new(0.7, 1.9), Complex::new(0.8, 2.0), /// Complex::new(0.9, 2.1), Complex::new(1.0, 2.2), Complex::new(1.1, 2.3), Complex::new(1.2, 2.4)); /// let expected = mat2.adjoint() * mat3 * Complex::new(10.0, 20.0) + mat1 * Complex::new(5.0, 15.0); /// /// mat1.gemm_ad(Complex::new(10.0, 20.0), &mat2, &mat3, Complex::new(5.0, 15.0)); /// assert_eq!(mat1, expected); /// ``` #[inline] pub fn gemm_ad( &mut self, alpha: T, a: &Matrix, b: &Matrix, beta: T, ) where T: SimdComplexField, SB: Storage, SC: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + AreMultipliable, { let (nrows1, ncols1) = self.shape(); let (nrows2, ncols2) = a.shape(); let (nrows3, ncols3) = b.shape(); assert_eq!( nrows2, nrows3, "gemm: dimensions mismatch for multiplication." ); assert_eq!( (nrows1, ncols1), (ncols2, ncols3), "gemm: dimensions mismatch for addition." ); for j1 in 0..ncols1 { // TODO: avoid bound checks. self.column_mut(j1) .gemv_ad(alpha.clone(), a, &b.column(j1), beta.clone()); } } } impl> Matrix where T: Scalar + Zero + ClosedAddAssign + ClosedMulAssign, { #[inline(always)] fn xxgerx( &mut self, alpha: T, x: &Vector, y: &Vector, beta: T, conjugate: impl Fn(T) -> T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + DimEq, { let dim1 = self.nrows(); let dim2 = x.nrows(); let dim3 = y.nrows(); assert!( self.is_square(), "Symmetric ger: the input matrix must be square." ); assert!(dim1 == dim2 && dim1 == dim3, "ger: dimensions mismatch."); for j in 0..dim1 { let val = unsafe { conjugate(y.vget_unchecked(j).clone()) }; let subdim = Dyn(dim1 - j); // TODO: avoid bound checks. self.generic_view_mut((j, j), (subdim, Const::<1>)).axpy( alpha.clone() * val, &x.rows_range(j..), beta.clone(), ); } } /// Computes `self = alpha * x * y.transpose() + beta * self`, where `self` is a **symmetric** /// matrix. /// /// If `beta` is zero, `self` is never read. The result is symmetric. Only the lower-triangular /// (including the diagonal) part of `self` is read/written. /// /// # Example /// ``` /// # use nalgebra::{Matrix2, Vector2}; /// let mut mat = Matrix2::identity(); /// let vec1 = Vector2::new(1.0, 2.0); /// let vec2 = Vector2::new(0.1, 0.2); /// let expected = vec1 * vec2.transpose() * 10.0 + mat * 5.0; /// mat.m12 = 99999.99999; // This component is on the upper-triangular part and will not be read/written. /// /// mat.ger_symm(10.0, &vec1, &vec2, 5.0); /// assert_eq!(mat.lower_triangle(), expected.lower_triangle()); /// assert_eq!(mat.m12, 99999.99999); // This was untouched. /// ``` #[inline] #[deprecated(note = "This is renamed `syger` to match the original BLAS terminology.")] pub fn ger_symm( &mut self, alpha: T, x: &Vector, y: &Vector, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + DimEq, { self.syger(alpha, x, y, beta) } /// Computes `self = alpha * x * y.transpose() + beta * self`, where `self` is a **symmetric** /// matrix. /// /// For hermitian complex matrices, use `.hegerc` instead. /// If `beta` is zero, `self` is never read. The result is symmetric. Only the lower-triangular /// (including the diagonal) part of `self` is read/written. /// /// # Example /// ``` /// # use nalgebra::{Matrix2, Vector2}; /// let mut mat = Matrix2::identity(); /// let vec1 = Vector2::new(1.0, 2.0); /// let vec2 = Vector2::new(0.1, 0.2); /// let expected = vec1 * vec2.transpose() * 10.0 + mat * 5.0; /// mat.m12 = 99999.99999; // This component is on the upper-triangular part and will not be read/written. /// /// mat.syger(10.0, &vec1, &vec2, 5.0); /// assert_eq!(mat.lower_triangle(), expected.lower_triangle()); /// assert_eq!(mat.m12, 99999.99999); // This was untouched. /// ``` #[inline] pub fn syger( &mut self, alpha: T, x: &Vector, y: &Vector, beta: T, ) where T: One, SB: Storage, SC: Storage, ShapeConstraint: DimEq + DimEq, { self.xxgerx(alpha, x, y, beta, |e| e) } /// Computes `self = alpha * x * y.adjoint() + beta * self`, where `self` is an **hermitian** /// matrix. /// /// If `beta` is zero, `self` is never read. The result is symmetric. Only the lower-triangular /// (including the diagonal) part of `self` is read/written. /// /// # Example /// ``` /// # use nalgebra::{Matrix2, Vector2, Complex}; /// let mut mat = Matrix2::identity(); /// let vec1 = Vector2::new(Complex::new(1.0, 3.0), Complex::new(2.0, 4.0)); /// let vec2 = Vector2::new(Complex::new(0.2, 0.4), Complex::new(0.1, 0.3)); /// let expected = vec1 * vec2.adjoint() * Complex::new(10.0, 20.0) + mat * Complex::new(5.0, 15.0); /// mat.m12 = Complex::new(99999.99999, 88888.88888); // This component is on the upper-triangular part and will not be read/written. /// /// mat.hegerc(Complex::new(10.0, 20.0), &vec1, &vec2, Complex::new(5.0, 15.0)); /// assert_eq!(mat.lower_triangle(), expected.lower_triangle()); /// assert_eq!(mat.m12, Complex::new(99999.99999, 88888.88888)); // This was untouched. /// ``` #[inline] pub fn hegerc( &mut self, alpha: T, x: &Vector, y: &Vector, beta: T, ) where T: SimdComplexField, SB: Storage, SC: Storage, ShapeConstraint: DimEq + DimEq, { self.xxgerx(alpha, x, y, beta, SimdComplexField::simd_conjugate) } } impl> SquareMatrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, { /// Computes the quadratic form `self = alpha * lhs * mid * lhs.transpose() + beta * self`. /// /// This uses the provided workspace `work` to avoid allocations for intermediate results. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the /// // workspace is actually useful (assuming the same workspace is re-used for /// // several computations) because it avoids repeated dynamic allocations. /// let mut mat = DMatrix::identity(2, 2); /// let lhs = DMatrix::from_row_slice(2, 3, &[1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0]); /// let mid = DMatrix::from_row_slice(3, 3, &[0.1, 0.2, 0.3, /// 0.5, 0.6, 0.7, /// 0.9, 1.0, 1.1]); /// // The random shows that values on the workspace do not /// // matter as they will be overwritten. /// let mut workspace = DVector::new_random(2); /// let expected = &lhs * &mid * lhs.transpose() * 10.0 + &mat * 5.0; /// /// mat.quadform_tr_with_workspace(&mut workspace, 10.0, &lhs, &mid, 5.0); /// assert_relative_eq!(mat, expected); /// ``` pub fn quadform_tr_with_workspace( &mut self, work: &mut Vector, alpha: T, lhs: &Matrix, mid: &SquareMatrix, beta: T, ) where D2: Dim, R3: Dim, C3: Dim, D4: Dim, S2: StorageMut, S3: Storage, S4: Storage, ShapeConstraint: DimEq + DimEq + DimEq + DimEq, { work.gemv(T::one(), lhs, &mid.column(0), T::zero()); self.ger(alpha.clone(), work, &lhs.column(0), beta); for j in 1..mid.ncols() { work.gemv(T::one(), lhs, &mid.column(j), T::zero()); self.ger(alpha.clone(), work, &lhs.column(j), T::one()); } } /// Computes the quadratic form `self = alpha * lhs * mid * lhs.transpose() + beta * self`. /// /// This allocates a workspace vector of dimension D1 for intermediate results. /// If `D1` is a type-level integer, then the allocation is performed on the stack. /// Use `.quadform_tr_with_workspace(...)` instead to avoid allocations. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix2, Matrix3, Matrix2x3, Vector2}; /// let mut mat = Matrix2::identity(); /// let lhs = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// let mid = Matrix3::new(0.1, 0.2, 0.3, /// 0.5, 0.6, 0.7, /// 0.9, 1.0, 1.1); /// let expected = lhs * mid * lhs.transpose() * 10.0 + mat * 5.0; /// /// mat.quadform_tr(10.0, &lhs, &mid, 5.0); /// assert_relative_eq!(mat, expected); /// ``` pub fn quadform_tr( &mut self, alpha: T, lhs: &Matrix, mid: &SquareMatrix, beta: T, ) where R3: Dim, C3: Dim, D4: Dim, S3: Storage, S4: Storage, ShapeConstraint: DimEq + DimEq + DimEq, DefaultAllocator: Allocator, { // TODO: would it be useful to avoid the zero-initialization of the workspace data? let mut work = Matrix::zeros_generic(self.shape_generic().0, Const::<1>); self.quadform_tr_with_workspace(&mut work, alpha, lhs, mid, beta) } /// Computes the quadratic form `self = alpha * rhs.transpose() * mid * rhs + beta * self`. /// /// This uses the provided workspace `work` to avoid allocations for intermediate results. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{DMatrix, DVector}; /// // Note that all those would also work with statically-sized matrices. /// // We use DMatrix/DVector since that's the only case where pre-allocating the /// // workspace is actually useful (assuming the same workspace is re-used for /// // several computations) because it avoids repeated dynamic allocations. /// let mut mat = DMatrix::identity(2, 2); /// let rhs = DMatrix::from_row_slice(3, 2, &[1.0, 2.0, /// 3.0, 4.0, /// 5.0, 6.0]); /// let mid = DMatrix::from_row_slice(3, 3, &[0.1, 0.2, 0.3, /// 0.5, 0.6, 0.7, /// 0.9, 1.0, 1.1]); /// // The random shows that values on the workspace do not /// // matter as they will be overwritten. /// let mut workspace = DVector::new_random(3); /// let expected = rhs.transpose() * &mid * &rhs * 10.0 + &mat * 5.0; /// /// mat.quadform_with_workspace(&mut workspace, 10.0, &mid, &rhs, 5.0); /// assert_relative_eq!(mat, expected); /// ``` pub fn quadform_with_workspace( &mut self, work: &mut Vector, alpha: T, mid: &SquareMatrix, rhs: &Matrix, beta: T, ) where D2: Dim, D3: Dim, R4: Dim, C4: Dim, S2: StorageMut, S3: Storage, S4: Storage, ShapeConstraint: DimEq + DimEq + DimEq + AreMultipliable, { work.gemv(T::one(), mid, &rhs.column(0), T::zero()); self.column_mut(0) .gemv_tr(alpha.clone(), rhs, work, beta.clone()); for j in 1..rhs.ncols() { work.gemv(T::one(), mid, &rhs.column(j), T::zero()); self.column_mut(j) .gemv_tr(alpha.clone(), rhs, work, beta.clone()); } } /// Computes the quadratic form `self = alpha * rhs.transpose() * mid * rhs + beta * self`. /// /// This allocates a workspace vector of dimension D2 for intermediate results. /// If `D2` is a type-level integer, then the allocation is performed on the stack. /// Use `.quadform_with_workspace(...)` instead to avoid allocations. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix2, Matrix3x2, Matrix3}; /// let mut mat = Matrix2::identity(); /// let rhs = Matrix3x2::new(1.0, 2.0, /// 3.0, 4.0, /// 5.0, 6.0); /// let mid = Matrix3::new(0.1, 0.2, 0.3, /// 0.5, 0.6, 0.7, /// 0.9, 1.0, 1.1); /// let expected = rhs.transpose() * mid * rhs * 10.0 + mat * 5.0; /// /// mat.quadform(10.0, &mid, &rhs, 5.0); /// assert_relative_eq!(mat, expected); /// ``` pub fn quadform( &mut self, alpha: T, mid: &SquareMatrix, rhs: &Matrix, beta: T, ) where D2: Dim, R3: Dim, C3: Dim, S2: Storage, S3: Storage, ShapeConstraint: DimEq + DimEq + AreMultipliable, DefaultAllocator: Allocator, { // TODO: would it be useful to avoid the zero-initialization of the workspace data? let mut work = Vector::zeros_generic(mid.shape_generic().0, Const::<1>); self.quadform_with_workspace(&mut work, alpha, mid, rhs, beta) } } nalgebra-0.33.0/src/base/blas_uninit.rs000064400000000000000000000246440072674642500161020ustar 00000000000000/* * This file implements some BLAS operations in such a way that they work * even if the first argument (the output parameter) is an uninitialized matrix. * * Because doing this makes the code harder to read, we only implemented the operations that we * know would benefit from this performance-wise, namely, GEMM (which we use for our matrix * multiplication code). If we identify other operations like that in the future, we could add * them here. */ #[cfg(feature = "std")] use matrixmultiply; use num::{One, Zero}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; #[cfg(feature = "std")] use std::{any::TypeId, mem}; use crate::base::constraint::{ AreMultipliable, DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint, }; #[cfg(feature = "std")] use crate::base::dimension::Dyn; use crate::base::dimension::{Dim, U1}; use crate::base::storage::{RawStorage, RawStorageMut}; use crate::base::uninit::InitStatus; use crate::base::{Matrix, Scalar, Vector}; // # Safety // The content of `y` must only contain values for which // `Status::assume_init_mut` is sound. #[allow(clippy::too_many_arguments)] unsafe fn array_axcpy( _: Status, y: &mut [Status::Value], a: T, x: &[T], c: T, beta: T, stride1: usize, stride2: usize, len: usize, ) where Status: InitStatus, T: Scalar + Zero + ClosedAddAssign + ClosedMulAssign, { for i in 0..len { let y = Status::assume_init_mut(y.get_unchecked_mut(i * stride1)); *y = a.clone() * x.get_unchecked(i * stride2).clone() * c.clone() + beta.clone() * y.clone(); } } fn array_axc( _: Status, y: &mut [Status::Value], a: T, x: &[T], c: T, stride1: usize, stride2: usize, len: usize, ) where Status: InitStatus, T: Scalar + Zero + ClosedAddAssign + ClosedMulAssign, { for i in 0..len { unsafe { Status::init( y.get_unchecked_mut(i * stride1), a.clone() * x.get_unchecked(i * stride2).clone() * c.clone(), ); } } } /// Computes `y = a * x * c + b * y`. /// /// If `b` is zero, `y` is never read from and may be uninitialized. /// /// # Safety /// This is UB if b != 0 and any component of `y` is uninitialized. #[inline(always)] #[allow(clippy::many_single_char_names)] pub unsafe fn axcpy_uninit( status: Status, y: &mut Vector, a: T, x: &Vector, c: T, b: T, ) where T: Scalar + Zero + ClosedAddAssign + ClosedMulAssign, SA: RawStorageMut, SB: RawStorage, ShapeConstraint: DimEq, Status: InitStatus, { assert_eq!(y.nrows(), x.nrows(), "Axcpy: mismatched vector shapes."); let rstride1 = y.strides().0; let rstride2 = x.strides().0; // SAFETY: the conversion to slices is OK because we access the // elements taking the strides into account. let y = y.data.as_mut_slice_unchecked(); let x = x.data.as_slice_unchecked(); if !b.is_zero() { array_axcpy(status, y, a, x, c, b, rstride1, rstride2, x.len()); } else { array_axc(status, y, a, x, c, rstride1, rstride2, x.len()); } } /// Computes `y = alpha * a * x + beta * y`, where `a` is a matrix, `x` a vector, and /// `alpha, beta` two scalars. /// /// If `beta` is zero, `y` is never read from and may be uninitialized. /// /// # Safety /// This is UB if beta != 0 and any component of `y` is uninitialized. #[inline(always)] pub unsafe fn gemv_uninit( status: Status, y: &mut Vector, alpha: T, a: &Matrix, x: &Vector, beta: T, ) where Status: InitStatus, T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SA: RawStorageMut, SB: RawStorage, SC: RawStorage, ShapeConstraint: DimEq + AreMultipliable, { let dim1 = y.nrows(); let (nrows2, ncols2) = a.shape(); let dim3 = x.nrows(); assert!( ncols2 == dim3 && dim1 == nrows2, "Gemv: dimensions mismatch." ); if ncols2 == 0 { if beta.is_zero() { y.apply(|e| Status::init(e, T::zero())); } else { // SAFETY: this is UB if y is uninitialized. y.apply(|e| *Status::assume_init_mut(e) *= beta.clone()); } return; } // TODO: avoid bound checks. let col2 = a.column(0); let val = x.vget_unchecked(0).clone(); // SAFETY: this is the call that makes this method unsafe: it is UB if Status = Uninit and beta != 0. axcpy_uninit(status, y, alpha.clone(), &col2, val, beta); for j in 1..ncols2 { let col2 = a.column(j); let val = x.vget_unchecked(j).clone(); // SAFETY: safe because y was initialized above. axcpy_uninit(status, y, alpha.clone(), &col2, val, T::one()); } } /// Computes `y = alpha * a * b + beta * y`, where `a, b, y` are matrices. /// `alpha` and `beta` are scalar. /// /// If `beta` is zero, `y` is never read from and may be uninitialized. /// /// # Safety /// This is UB if beta != 0 and any component of `y` is uninitialized. #[inline(always)] pub unsafe fn gemm_uninit< Status, T, R1: Dim, C1: Dim, R2: Dim, C2: Dim, R3: Dim, C3: Dim, SA, SB, SC, >( status: Status, y: &mut Matrix, alpha: T, a: &Matrix, b: &Matrix, beta: T, ) where Status: InitStatus, T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SA: RawStorageMut, SB: RawStorage, SC: RawStorage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + AreMultipliable, { let ncols1 = y.ncols(); #[cfg(feature = "std")] { // We assume large matrices will be Dyn but small matrices static. // We could use matrixmultiply for large statically-sized matrices but the performance // threshold to activate it would be different from SMALL_DIM because our code optimizes // better for statically-sized matrices. if R1::is::() || C1::is::() || R2::is::() || C2::is::() || R3::is::() || C3::is::() { // matrixmultiply can be used only if the std feature is available. let nrows1 = y.nrows(); let (nrows2, ncols2) = a.shape(); let (nrows3, ncols3) = b.shape(); // Threshold determined empirically. const SMALL_DIM: usize = 5; if nrows1 > SMALL_DIM && ncols1 > SMALL_DIM && nrows2 > SMALL_DIM && ncols2 > SMALL_DIM { assert_eq!( ncols2, nrows3, "gemm: dimensions mismatch for multiplication." ); assert_eq!( (nrows1, ncols1), (nrows2, ncols3), "gemm: dimensions mismatch for addition." ); // NOTE: this case should never happen because we enter this // codepath only when ncols2 > SMALL_DIM. Though we keep this // here just in case if in the future we change the conditions to // enter this codepath. if ncols2 == 0 { // NOTE: we can't just always multiply by beta // because we documented the guaranty that `self` is // never read if `beta` is zero. if beta.is_zero() { y.apply(|e| Status::init(e, T::zero())); } else { // SAFETY: this is UB if Status = Uninit y.apply(|e| *Status::assume_init_mut(e) *= beta.clone()); } return; } if TypeId::of::() == TypeId::of::() { let (rsa, csa) = a.strides(); let (rsb, csb) = b.strides(); let (rsc, csc) = y.strides(); matrixmultiply::sgemm( nrows2, ncols2, ncols3, mem::transmute_copy(&alpha), a.data.ptr() as *const f32, rsa as isize, csa as isize, b.data.ptr() as *const f32, rsb as isize, csb as isize, mem::transmute_copy(&beta), y.data.ptr_mut() as *mut f32, rsc as isize, csc as isize, ); return; } else if TypeId::of::() == TypeId::of::() { let (rsa, csa) = a.strides(); let (rsb, csb) = b.strides(); let (rsc, csc) = y.strides(); matrixmultiply::dgemm( nrows2, ncols2, ncols3, mem::transmute_copy(&alpha), a.data.ptr() as *const f64, rsa as isize, csa as isize, b.data.ptr() as *const f64, rsb as isize, csb as isize, mem::transmute_copy(&beta), y.data.ptr_mut() as *mut f64, rsc as isize, csc as isize, ); return; } } } } for j1 in 0..ncols1 { // TODO: avoid bound checks. // SAFETY: this is UB if Status = Uninit && beta != 0 gemv_uninit( status, &mut y.column_mut(j1), alpha.clone(), a, &b.column(j1), beta.clone(), ); } } nalgebra-0.33.0/src/base/cg.rs000064400000000000000000000400740072674642500141570ustar 00000000000000/* * * Computer-graphics specific implementations. * Currently, it is mostly implemented for homogeneous matrices in 2- and 3-space. * */ use num::{One, Zero}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimName, DimNameDiff, DimNameSub, U1}; use crate::base::storage::{Storage, StorageMut}; use crate::base::{ Const, DefaultAllocator, Matrix3, Matrix4, OMatrix, OVector, Scalar, SquareMatrix, Unit, Vector, Vector2, Vector3, }; use crate::geometry::{ Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point2, Point3, Rotation2, Rotation3, }; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, RealField}; /// # Translation and scaling in any dimension impl OMatrix where T: Scalar + Zero + One, DefaultAllocator: Allocator, { /// Creates a new homogeneous matrix that applies the same scaling factor on each dimension. #[inline] pub fn new_scaling(scaling: T) -> Self { let mut res = Self::from_diagonal_element(scaling); res[(D::dim() - 1, D::dim() - 1)] = T::one(); res } /// Creates a new homogeneous matrix that applies a distinct scaling factor for each dimension. #[inline] pub fn new_nonuniform_scaling(scaling: &Vector, SB>) -> Self where D: DimNameSub, SB: Storage>, { let mut res = Self::identity(); for i in 0..scaling.len() { res[(i, i)] = scaling[i].clone(); } res } /// Creates a new homogeneous matrix that applies a pure translation. #[inline] pub fn new_translation(translation: &Vector, SB>) -> Self where D: DimNameSub, SB: Storage>, { let mut res = Self::identity(); res.generic_view_mut( (0, D::dim() - 1), (DimNameDiff::::name(), Const::<1>), ) .copy_from(translation); res } } /// # 2D transformations as a Matrix3 impl Matrix3 { /// Builds a 2 dimensional homogeneous rotation matrix from an angle in radian. #[inline] pub fn new_rotation(angle: T) -> Self { Rotation2::new(angle).to_homogeneous() } /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point. /// /// Can be used to implement `zoom_to` functionality. #[inline] pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector2, pt: &Point2) -> Self { let zero = T::zero(); let one = T::one(); Matrix3::new( scaling.x.clone(), zero.clone(), pt.x.clone() - pt.x.clone() * scaling.x.clone(), zero.clone(), scaling.y.clone(), pt.y.clone() - pt.y.clone() * scaling.y.clone(), zero.clone(), zero, one, ) } } /// # 3D transformations as a Matrix4 impl Matrix4 { /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together). /// /// Returns the identity matrix if the given argument is zero. #[inline] pub fn new_rotation(axisangle: Vector3) -> Self { Rotation3::new(axisangle).to_homogeneous() } /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together). /// /// Returns the identity matrix if the given argument is zero. #[inline] pub fn new_rotation_wrt_point(axisangle: Vector3, pt: Point3) -> Self { let rot = Rotation3::from_scaled_axis(axisangle); Isometry::rotation_wrt_point(rot, pt).to_homogeneous() } /// Creates a new homogeneous matrix that applies a scaling factor for each dimension with respect to point. /// /// Can be used to implement `zoom_to` functionality. #[inline] pub fn new_nonuniform_scaling_wrt_point(scaling: &Vector3, pt: &Point3) -> Self { let zero = T::zero(); let one = T::one(); Matrix4::new( scaling.x.clone(), zero.clone(), zero.clone(), pt.x.clone() - pt.x.clone() * scaling.x.clone(), zero.clone(), scaling.y.clone(), zero.clone(), pt.y.clone() - pt.y.clone() * scaling.y.clone(), zero.clone(), zero.clone(), scaling.z.clone(), pt.z.clone() - pt.z.clone() * scaling.z.clone(), zero.clone(), zero.clone(), zero, one, ) } /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together). /// /// Returns the identity matrix if the given argument is zero. /// This is identical to `Self::new_rotation`. #[inline] pub fn from_scaled_axis(axisangle: Vector3) -> Self { Rotation3::from_scaled_axis(axisangle).to_homogeneous() } /// Creates a new rotation from Euler angles. /// /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw. pub fn from_euler_angles(roll: T, pitch: T, yaw: T) -> Self { Rotation3::from_euler_angles(roll, pitch, yaw).to_homogeneous() } /// Builds a 3D homogeneous rotation matrix from an axis and a rotation angle. pub fn from_axis_angle(axis: &Unit>, angle: T) -> Self { Rotation3::from_axis_angle(axis, angle).to_homogeneous() } /// Creates a new homogeneous matrix for an orthographic projection. #[inline] pub fn new_orthographic(left: T, right: T, bottom: T, top: T, znear: T, zfar: T) -> Self { Orthographic3::new(left, right, bottom, top, znear, zfar).into_inner() } /// Creates a new homogeneous matrix for a perspective projection. #[inline] pub fn new_perspective(aspect: T, fovy: T, znear: T, zfar: T) -> Self { Perspective3::new(aspect, fovy, znear, zfar).into_inner() } /// Creates an isometry that corresponds to the local frame of an observer standing at the /// point `eye` and looking toward `target`. /// /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the /// `eye`. #[inline] pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { IsometryMatrix3::face_towards(eye, target, up).to_homogeneous() } /// Deprecated: Use [`Matrix4::face_towards`] instead. #[deprecated(note = "renamed to `face_towards`")] pub fn new_observer_frame(eye: &Point3, target: &Point3, up: &Vector3) -> Self { Matrix4::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. #[inline] pub fn look_at_rh(eye: &Point3, target: &Point3, up: &Vector3) -> Self { IsometryMatrix3::look_at_rh(eye, target, up).to_homogeneous() } /// Builds a left-handed look-at view matrix. #[inline] pub fn look_at_lh(eye: &Point3, target: &Point3, up: &Vector3) -> Self { IsometryMatrix3::look_at_lh(eye, target, up).to_homogeneous() } } /// # Append/prepend translation and scaling impl< T: Scalar + Zero + One + ClosedMulAssign + ClosedAddAssign, D: DimName, S: Storage, > SquareMatrix { /// Computes the transformation equal to `self` followed by an uniform scaling factor. #[inline] #[must_use = "Did you mean to use append_scaling_mut()?"] pub fn append_scaling(&self, scaling: T) -> OMatrix where D: DimNameSub, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.append_scaling_mut(scaling); res } /// Computes the transformation equal to an uniform scaling factor followed by `self`. #[inline] #[must_use = "Did you mean to use prepend_scaling_mut()?"] pub fn prepend_scaling(&self, scaling: T) -> OMatrix where D: DimNameSub, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.prepend_scaling_mut(scaling); res } /// Computes the transformation equal to `self` followed by a non-uniform scaling factor. #[inline] #[must_use = "Did you mean to use append_nonuniform_scaling_mut()?"] pub fn append_nonuniform_scaling( &self, scaling: &Vector, SB>, ) -> OMatrix where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.append_nonuniform_scaling_mut(scaling); res } /// Computes the transformation equal to a non-uniform scaling factor followed by `self`. #[inline] #[must_use = "Did you mean to use prepend_nonuniform_scaling_mut()?"] pub fn prepend_nonuniform_scaling( &self, scaling: &Vector, SB>, ) -> OMatrix where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.prepend_nonuniform_scaling_mut(scaling); res } /// Computes the transformation equal to `self` followed by a translation. #[inline] #[must_use = "Did you mean to use append_translation_mut()?"] pub fn append_translation( &self, shift: &Vector, SB>, ) -> OMatrix where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.append_translation_mut(shift); res } /// Computes the transformation equal to a translation followed by `self`. #[inline] #[must_use = "Did you mean to use prepend_translation_mut()?"] pub fn prepend_translation( &self, shift: &Vector, SB>, ) -> OMatrix where D: DimNameSub, SB: Storage>, DefaultAllocator: Allocator + Allocator>, { let mut res = self.clone_owned(); res.prepend_translation_mut(shift); res } /// Computes in-place the transformation equal to `self` followed by an uniform scaling factor. #[inline] pub fn append_scaling_mut(&mut self, scaling: T) where S: StorageMut, D: DimNameSub, { let mut to_scale = self.rows_generic_mut(0, DimNameDiff::::name()); to_scale *= scaling; } /// Computes in-place the transformation equal to an uniform scaling factor followed by `self`. #[inline] pub fn prepend_scaling_mut(&mut self, scaling: T) where S: StorageMut, D: DimNameSub, { let mut to_scale = self.columns_generic_mut(0, DimNameDiff::::name()); to_scale *= scaling; } /// Computes in-place the transformation equal to `self` followed by a non-uniform scaling factor. #[inline] pub fn append_nonuniform_scaling_mut(&mut self, scaling: &Vector, SB>) where S: StorageMut, D: DimNameSub, SB: Storage>, { for i in 0..scaling.len() { let mut to_scale = self.fixed_rows_mut::<1>(i); to_scale *= scaling[i].clone(); } } /// Computes in-place the transformation equal to a non-uniform scaling factor followed by `self`. #[inline] pub fn prepend_nonuniform_scaling_mut( &mut self, scaling: &Vector, SB>, ) where S: StorageMut, D: DimNameSub, SB: Storage>, { for i in 0..scaling.len() { let mut to_scale = self.fixed_columns_mut::<1>(i); to_scale *= scaling[i].clone(); } } /// Computes the transformation equal to `self` followed by a translation. #[inline] pub fn append_translation_mut(&mut self, shift: &Vector, SB>) where S: StorageMut, D: DimNameSub, SB: Storage>, { for i in 0..D::dim() { for j in 0..D::dim() - 1 { let add = shift[j].clone() * self[(D::dim() - 1, i)].clone(); self[(j, i)] += add; } } } /// Computes the transformation equal to a translation followed by `self`. #[inline] pub fn prepend_translation_mut(&mut self, shift: &Vector, SB>) where D: DimNameSub, S: StorageMut, SB: Storage>, DefaultAllocator: Allocator>, { let scale = self .generic_view( (D::dim() - 1, 0), (Const::<1>, DimNameDiff::::name()), ) .tr_dot(shift); let post_translation = self.generic_view( (0, 0), (DimNameDiff::::name(), DimNameDiff::::name()), ) * shift; self[(D::dim() - 1, D::dim() - 1)] += scale; let mut translation = self.generic_view_mut( (0, D::dim() - 1), (DimNameDiff::::name(), Const::<1>), ); translation += post_translation; } } /// # Transformation of vectors and points impl, S: Storage> SquareMatrix where DefaultAllocator: Allocator + Allocator> + Allocator, DimNameDiff>, { /// Transforms the given vector, assuming the matrix `self` uses homogeneous coordinates. #[inline] pub fn transform_vector( &self, v: &OVector>, ) -> OVector> { let transform = self.generic_view( (0, 0), (DimNameDiff::::name(), DimNameDiff::::name()), ); let normalizer = self.generic_view( (D::dim() - 1, 0), (Const::<1>, DimNameDiff::::name()), ); let n = normalizer.tr_dot(v); if !n.is_zero() { return transform * (v / n); } transform * v } } impl, Const<3>>> SquareMatrix, S> { /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates. #[inline] pub fn transform_point(&self, pt: &Point) -> Point { let transform = self.fixed_view::<2, 2>(0, 0); let translation = self.fixed_view::<2, 1>(0, 2); let normalizer = self.fixed_view::<1, 2>(2, 0); let n = normalizer.tr_dot(&pt.coords) + unsafe { self.get_unchecked((2, 2)).clone() }; if !n.is_zero() { (transform * pt + translation) / n } else { transform * pt + translation } } } impl, Const<4>>> SquareMatrix, S> { /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates. #[inline] pub fn transform_point(&self, pt: &Point) -> Point { let transform = self.fixed_view::<3, 3>(0, 0); let translation = self.fixed_view::<3, 1>(0, 3); let normalizer = self.fixed_view::<1, 3>(3, 0); let n = normalizer.tr_dot(&pt.coords) + unsafe { self.get_unchecked((3, 3)).clone() }; if !n.is_zero() { (transform * pt + translation) / n } else { transform * pt + translation } } } nalgebra-0.33.0/src/base/componentwise.rs000064400000000000000000000272300072674642500164570ustar 00000000000000// Non-conventional component-wise operators. use num::{Signed, Zero}; use std::ops::{Add, Mul}; use simba::scalar::{ClosedDivAssign, ClosedMulAssign}; use simba::simd::SimdPartialOrd; use crate::base::allocator::{Allocator, SameShapeAllocator}; use crate::base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use crate::base::dimension::Dim; use crate::base::storage::{Storage, StorageMut}; use crate::base::{DefaultAllocator, Matrix, MatrixSum, OMatrix, Scalar}; use crate::ClosedAddAssign; /// The type of the result of a matrix component-wise operation. pub type MatrixComponentOp = MatrixSum; impl> Matrix { /// Computes the component-wise absolute value. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2; /// let a = Matrix2::new(0.0, 1.0, /// -2.0, -3.0); /// assert_eq!(a.abs(), Matrix2::new(0.0, 1.0, 2.0, 3.0)) /// ``` #[inline] #[must_use] pub fn abs(&self) -> OMatrix where T: Signed, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); for e in res.iter_mut() { *e = e.abs(); } res } // TODO: add other operators like component_ln, component_pow, etc. ? } macro_rules! component_binop_impl( ($($binop: ident, $binop_mut: ident, $binop_assign: ident, $cmpy: ident, $Trait: ident . $op: ident . $op_assign: ident, $desc:expr, $desc_cmpy:expr, $desc_mut:expr);* $(;)*) => {$( #[doc = $desc] #[inline] #[must_use] pub fn $binop(&self, rhs: &Matrix) -> MatrixComponentOp where T: $Trait, R2: Dim, C2: Dim, SB: Storage, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { assert_eq!(self.shape(), rhs.shape(), "Componentwise mul/div: mismatched matrix dimensions."); let mut res = self.clone_owned_sum(); for j in 0 .. res.ncols() { for i in 0 .. res.nrows() { unsafe { res.get_unchecked_mut((i, j)).$op_assign(rhs.get_unchecked((i, j)).clone()); } } } res } // componentwise binop plus Y. #[doc = $desc_cmpy] #[inline] pub fn $cmpy(&mut self, alpha: T, a: &Matrix, b: &Matrix, beta: T) where T: $Trait + Zero + Mul + Add, R2: Dim, C2: Dim, R3: Dim, C3: Dim, SA: StorageMut, SB: Storage, SC: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + SameNumberOfRows + SameNumberOfColumns { assert_eq!(self.shape(), a.shape(), "Componentwise mul/div: mismatched matrix dimensions."); assert_eq!(self.shape(), b.shape(), "Componentwise mul/div: mismatched matrix dimensions."); if beta.is_zero() { for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { let res = alpha.clone() * a.get_unchecked((i, j)).clone().$op(b.get_unchecked((i, j)).clone()); *self.get_unchecked_mut((i, j)) = res; } } } } else { for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { let res = alpha.clone() * a.get_unchecked((i, j)).clone().$op(b.get_unchecked((i, j)).clone()); *self.get_unchecked_mut((i, j)) = beta.clone() * self.get_unchecked((i, j)).clone() + res; } } } } } #[doc = $desc_mut] #[inline] pub fn $binop_assign(&mut self, rhs: &Matrix) where T: $Trait, R2: Dim, C2: Dim, SA: StorageMut, SB: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { assert_eq!(self.shape(), rhs.shape(), "Componentwise mul/div: mismatched matrix dimensions."); for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { self.get_unchecked_mut((i, j)).$op_assign(rhs.get_unchecked((i, j)).clone()); } } } } #[doc = $desc_mut] #[inline] #[deprecated(note = "This is renamed using the `_assign` suffix instead of the `_mut` suffix.")] pub fn $binop_mut(&mut self, rhs: &Matrix) where T: $Trait, R2: Dim, C2: Dim, SA: StorageMut, SB: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { self.$binop_assign(rhs) } )*} ); /// # Componentwise operations impl> Matrix { component_binop_impl!( component_mul, component_mul_mut, component_mul_assign, cmpy, ClosedMulAssign.mul.mul_assign, r" Componentwise matrix or vector multiplication. # Example ``` # use nalgebra::Matrix2; let a = Matrix2::new(0.0, 1.0, 2.0, 3.0); let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); let expected = Matrix2::new(0.0, 5.0, 12.0, 21.0); assert_eq!(a.component_mul(&b), expected); ``` ", r" Computes componentwise `self[i] = alpha * a[i] * b[i] + beta * self[i]`. # Example ``` # use nalgebra::Matrix2; let mut m = Matrix2::new(0.0, 1.0, 2.0, 3.0); let a = Matrix2::new(0.0, 1.0, 2.0, 3.0); let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); let expected = (a.component_mul(&b) * 5.0) + m * 10.0; m.cmpy(5.0, &a, &b, 10.0); assert_eq!(m, expected); ``` ", r" Inplace componentwise matrix or vector multiplication. # Example ``` # use nalgebra::Matrix2; let mut a = Matrix2::new(0.0, 1.0, 2.0, 3.0); let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); let expected = Matrix2::new(0.0, 5.0, 12.0, 21.0); a.component_mul_assign(&b); assert_eq!(a, expected); ``` "; component_div, component_div_mut, component_div_assign, cdpy, ClosedDivAssign.div.div_assign, r" Componentwise matrix or vector division. # Example ``` # use nalgebra::Matrix2; let a = Matrix2::new(0.0, 1.0, 2.0, 3.0); let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); let expected = Matrix2::new(0.0, 1.0 / 5.0, 2.0 / 6.0, 3.0 / 7.0); assert_eq!(a.component_div(&b), expected); ``` ", r" Computes componentwise `self[i] = alpha * a[i] / b[i] + beta * self[i]`. # Example ``` # use nalgebra::Matrix2; let mut m = Matrix2::new(0.0, 1.0, 2.0, 3.0); let a = Matrix2::new(4.0, 5.0, 6.0, 7.0); let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); let expected = (a.component_div(&b) * 5.0) + m * 10.0; m.cdpy(5.0, &a, &b, 10.0); assert_eq!(m, expected); ``` ", r" Inplace componentwise matrix or vector division. # Example ``` # use nalgebra::Matrix2; let mut a = Matrix2::new(0.0, 1.0, 2.0, 3.0); let b = Matrix2::new(4.0, 5.0, 6.0, 7.0); let expected = Matrix2::new(0.0, 1.0 / 5.0, 2.0 / 6.0, 3.0 / 7.0); a.component_div_assign(&b); assert_eq!(a, expected); ``` "; // TODO: add other operators like bitshift, etc. ? ); /// Computes the infimum (aka. componentwise min) of two matrices/vectors. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2; /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0); /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0); /// let expected = Matrix2::new(2.0, 2.0, -2.0, -2.0); /// assert_eq!(u.inf(&v), expected) /// ``` #[inline] #[must_use] pub fn inf(&self, other: &Self) -> OMatrix where T: SimdPartialOrd, DefaultAllocator: Allocator, { self.zip_map(other, |a, b| a.simd_min(b)) } /// Computes the supremum (aka. componentwise max) of two matrices/vectors. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2; /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0); /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0); /// let expected = Matrix2::new(4.0, 4.0, 1.0, 1.0); /// assert_eq!(u.sup(&v), expected) /// ``` #[inline] #[must_use] pub fn sup(&self, other: &Self) -> OMatrix where T: SimdPartialOrd, DefaultAllocator: Allocator, { self.zip_map(other, |a, b| a.simd_max(b)) } /// Computes the (infimum, supremum) of two matrices/vectors. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2; /// let u = Matrix2::new(4.0, 2.0, 1.0, -2.0); /// let v = Matrix2::new(2.0, 4.0, -2.0, 1.0); /// let expected = (Matrix2::new(2.0, 2.0, -2.0, -2.0), Matrix2::new(4.0, 4.0, 1.0, 1.0)); /// assert_eq!(u.inf_sup(&v), expected) /// ``` #[inline] #[must_use] pub fn inf_sup(&self, other: &Self) -> (OMatrix, OMatrix) where T: SimdPartialOrd, DefaultAllocator: Allocator, { // TODO: can this be optimized? (self.inf(other), self.sup(other)) } /// Adds a scalar to `self`. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2; /// let u = Matrix2::new(1.0, 2.0, 3.0, 4.0); /// let s = 10.0; /// let expected = Matrix2::new(11.0, 12.0, 13.0, 14.0); /// assert_eq!(u.add_scalar(s), expected) /// ``` #[inline] #[must_use = "Did you mean to use add_scalar_mut()?"] pub fn add_scalar(&self, rhs: T) -> OMatrix where T: ClosedAddAssign, DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.add_scalar_mut(rhs); res } /// Adds a scalar to `self` in-place. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2; /// let mut u = Matrix2::new(1.0, 2.0, 3.0, 4.0); /// let s = 10.0; /// u.add_scalar_mut(s); /// let expected = Matrix2::new(11.0, 12.0, 13.0, 14.0); /// assert_eq!(u, expected) /// ``` #[inline] pub fn add_scalar_mut(&mut self, rhs: T) where T: ClosedAddAssign, SA: StorageMut, { for e in self.iter_mut() { *e += rhs.clone() } } } nalgebra-0.33.0/src/base/constraint.rs000064400000000000000000000070540072674642500157530ustar 00000000000000//! Compatibility constraints between matrix shapes, e.g., for addition or multiplication. use crate::base::dimension::{Dim, DimName, Dyn}; /// A type used in `where` clauses for enforcing constraints. #[derive(Copy, Clone, Debug)] pub struct ShapeConstraint; /// Constrains `C1` and `R2` to be equivalent. pub trait AreMultipliable: DimEq {} impl AreMultipliable for ShapeConstraint where ShapeConstraint: DimEq { } /// Constrains `D1` and `D2` to be equivalent. pub trait DimEq { /// This is either equal to `D1` or `D2`, always choosing the one (if any) which is a type-level /// constant. type Representative: Dim; /// This constructs a value of type `Representative` with the /// correct value fn representative(d1: D1, d2: D2) -> Option { if d1.value() != d2.value() { None } else { Some(Self::Representative::from_usize(d1.value())) } } } impl DimEq for ShapeConstraint { type Representative = D; } impl DimEq for ShapeConstraint { type Representative = D; } impl DimEq for ShapeConstraint { type Representative = D; } macro_rules! equality_trait_decl( ($($doc: expr, $Trait: ident),* $(,)*) => {$( // XXX: we can't do something like `DimEq for D2` because we would require a blanket impl… #[doc = $doc] pub trait $Trait: DimEq + DimEq { /// This is either equal to `D1` or `D2`, always choosing the one (if any) which is a type-level /// constant. type Representative: Dim; /// Returns a representative dimension instance if the two are equal, /// otherwise `None`. fn representative(d1: D1, d2: D2) -> Option<>::Representative> { >::representative(d1, d2) .map(|common_dim| >::Representative::from_usize(common_dim.value())) } } impl $Trait for ShapeConstraint { type Representative = D; } impl $Trait for ShapeConstraint { type Representative = D; } impl $Trait for ShapeConstraint { type Representative = D; } )*} ); equality_trait_decl!( "Constrains `D1` and `D2` to be equivalent. \ They are both assumed to be the number of \ rows of a matrix.", SameNumberOfRows, "Constrains `D1` and `D2` to be equivalent. \ They are both assumed to be the number of \ columns of a matrix.", SameNumberOfColumns ); /// Constrains D1 and D2 to be equivalent, where they both designate dimensions of algebraic /// entities (e.g. square matrices). pub trait SameDimension: SameNumberOfRows + SameNumberOfColumns { /// This is either equal to `D1` or `D2`, always choosing the one (if any) which is a type-level /// constant. type Representative: Dim; } impl SameDimension for ShapeConstraint { type Representative = D; } impl SameDimension for ShapeConstraint { type Representative = D; } impl SameDimension for ShapeConstraint { type Representative = D; } nalgebra-0.33.0/src/base/construction.rs000064400000000000000000001265510072674642500163250ustar 00000000000000#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; #[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::{Bounded, One, Zero}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use std::iter; use typenum::{self, Cmp, Greater}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; use crate::base::allocator::Allocator; use crate::base::dimension::{Dim, DimName, Dyn, ToTypenum}; use crate::base::storage::RawStorage; use crate::base::{ ArrayStorage, Const, DefaultAllocator, Matrix, OMatrix, OVector, Scalar, Unit, Vector, }; use crate::UninitMatrix; use std::mem::MaybeUninit; impl UninitMatrix where DefaultAllocator: Allocator, { /// Builds a matrix with uninitialized elements of type `MaybeUninit`. #[inline(always)] pub fn uninit(nrows: R, ncols: C) -> Self { // SAFETY: this is OK because the dimension automatically match the storage // because we are building an owned storage. unsafe { Self::from_data_statically_unchecked(DefaultAllocator::allocate_uninit(nrows, ncols)) } } } /// # Generic constructors /// This set of matrix and vector construction functions are all generic /// with-regard to the matrix dimensions. They all expect to be given /// the dimension as inputs. /// /// These functions should only be used when working on dimension-generic code. impl OMatrix where DefaultAllocator: Allocator, { /// Creates a matrix with all its elements set to `elem`. #[inline] pub fn from_element_generic(nrows: R, ncols: C, elem: T) -> Self { let len = nrows.value() * ncols.value(); Self::from_iterator_generic(nrows, ncols, iter::repeat(elem).take(len)) } /// Creates a matrix with all its elements set to `elem`. /// /// Same as `from_element_generic`. #[inline] pub fn repeat_generic(nrows: R, ncols: C, elem: T) -> Self { let len = nrows.value() * ncols.value(); Self::from_iterator_generic(nrows, ncols, iter::repeat(elem).take(len)) } /// Creates a matrix with all its elements set to 0. #[inline] pub fn zeros_generic(nrows: R, ncols: C) -> Self where T: Zero, { Self::from_element_generic(nrows, ncols, T::zero()) } /// Creates a matrix with all its elements filled by an iterator. #[inline] pub fn from_iterator_generic(nrows: R, ncols: C, iter: I) -> Self where I: IntoIterator, { Self::from_data(DefaultAllocator::allocate_from_iterator(nrows, ncols, iter)) } /// Creates a matrix with all its elements filled by an row-major order iterator. #[inline] pub fn from_row_iterator_generic(nrows: R, ncols: C, iter: I) -> Self where I: IntoIterator, { Self::from_data(DefaultAllocator::allocate_from_row_iterator( nrows, ncols, iter, )) } /// Creates a matrix with its elements filled with the components provided by a slice in /// row-major order. /// /// The order of elements in the slice must follow the usual mathematic writing, i.e., /// row-by-row. #[inline] pub fn from_row_slice_generic(nrows: R, ncols: C, slice: &[T]) -> Self { assert!( slice.len() == nrows.value() * ncols.value(), "Matrix init. error: the slice did not contain the right number of elements." ); let mut res = Matrix::uninit(nrows, ncols); let mut iter = slice.iter(); unsafe { for i in 0..nrows.value() { for j in 0..ncols.value() { *res.get_unchecked_mut((i, j)) = MaybeUninit::new(iter.next().unwrap().clone()) } } // SAFETY: the result has been fully initialized above. res.assume_init() } } /// Creates a matrix with its elements filled with the components provided by a slice. The /// components must have the same layout as the matrix data storage (i.e. column-major). #[inline] pub fn from_column_slice_generic(nrows: R, ncols: C, slice: &[T]) -> Self { Self::from_iterator_generic(nrows, ncols, slice.iter().cloned()) } /// Creates a matrix filled with the results of a function applied to each of its component /// coordinates. #[inline] pub fn from_fn_generic(nrows: R, ncols: C, mut f: F) -> Self where F: FnMut(usize, usize) -> T, { let mut res = Matrix::uninit(nrows, ncols); unsafe { for j in 0..ncols.value() { for i in 0..nrows.value() { *res.get_unchecked_mut((i, j)) = MaybeUninit::new(f(i, j)); } } // SAFETY: the result has been fully initialized above. res.assume_init() } } /// Creates a new identity matrix. /// /// If the matrix is not square, the largest square submatrix starting at index `(0, 0)` is set /// to the identity matrix. All other entries are set to zero. #[inline] pub fn identity_generic(nrows: R, ncols: C) -> Self where T: Zero + One, { Self::from_diagonal_element_generic(nrows, ncols, T::one()) } /// Creates a new matrix with its diagonal filled with copies of `elt`. /// /// If the matrix is not square, the largest square submatrix starting at index `(0, 0)` is set /// to the identity matrix. All other entries are set to zero. #[inline] pub fn from_diagonal_element_generic(nrows: R, ncols: C, elt: T) -> Self where T: Zero + One, { let mut res = Self::zeros_generic(nrows, ncols); for i in 0..crate::min(nrows.value(), ncols.value()) { unsafe { *res.get_unchecked_mut((i, i)) = elt.clone() } } res } /// Creates a new matrix that may be rectangular. The first `elts.len()` diagonal elements are /// filled with the content of `elts`. Others are set to 0. /// /// Panics if `elts.len()` is larger than the minimum among `nrows` and `ncols`. #[inline] pub fn from_partial_diagonal_generic(nrows: R, ncols: C, elts: &[T]) -> Self where T: Zero, { let mut res = Self::zeros_generic(nrows, ncols); assert!( elts.len() <= crate::min(nrows.value(), ncols.value()), "Too many diagonal elements provided." ); for (i, elt) in elts.iter().enumerate() { unsafe { *res.get_unchecked_mut((i, i)) = elt.clone() } } res } /// Builds a new matrix from its rows. /// /// Panics if not enough rows are provided (for statically-sized matrices), or if all rows do /// not have the same dimensions. /// /// # Example /// ``` /// # use nalgebra::{RowVector3, Matrix3}; /// # use std::iter; /// /// let m = Matrix3::from_rows(&[ RowVector3::new(1.0, 2.0, 3.0), RowVector3::new(4.0, 5.0, 6.0), RowVector3::new(7.0, 8.0, 9.0) ]); /// /// assert!(m.m11 == 1.0 && m.m12 == 2.0 && m.m13 == 3.0 && /// m.m21 == 4.0 && m.m22 == 5.0 && m.m23 == 6.0 && /// m.m31 == 7.0 && m.m32 == 8.0 && m.m33 == 9.0); /// ``` #[inline] pub fn from_rows(rows: &[Matrix, C, SB>]) -> Self where SB: RawStorage, C>, { assert!(!rows.is_empty(), "At least one row must be given."); let nrows = R::try_to_usize().unwrap_or(rows.len()); let ncols = rows[0].len(); assert!( rows.len() == nrows, "Invalid number of rows provided to build this matrix." ); if C::try_to_usize().is_none() { assert!( rows.iter().all(|r| r.len() == ncols), "The provided rows must all have the same dimension." ); } // TODO: optimize that. Self::from_fn_generic(R::from_usize(nrows), C::from_usize(ncols), |i, j| { rows[i][(0, j)].clone() }) } /// Builds a new matrix from its columns. /// /// Panics if not enough columns are provided (for statically-sized matrices), or if all /// columns do not have the same dimensions. /// /// # Example /// ``` /// # use nalgebra::{Vector3, Matrix3}; /// # use std::iter; /// /// let m = Matrix3::from_columns(&[ Vector3::new(1.0, 2.0, 3.0), Vector3::new(4.0, 5.0, 6.0), Vector3::new(7.0, 8.0, 9.0) ]); /// /// assert!(m.m11 == 1.0 && m.m12 == 4.0 && m.m13 == 7.0 && /// m.m21 == 2.0 && m.m22 == 5.0 && m.m23 == 8.0 && /// m.m31 == 3.0 && m.m32 == 6.0 && m.m33 == 9.0); /// ``` #[inline] pub fn from_columns(columns: &[Vector]) -> Self where SB: RawStorage, { assert!(!columns.is_empty(), "At least one column must be given."); let ncols = C::try_to_usize().unwrap_or(columns.len()); let nrows = columns[0].len(); assert!( columns.len() == ncols, "Invalid number of columns provided to build this matrix." ); if R::try_to_usize().is_none() { assert!( columns.iter().all(|r| r.len() == nrows), "The columns provided must all have the same dimension." ); } // TODO: optimize that. Self::from_fn_generic(R::from_usize(nrows), C::from_usize(ncols), |i, j| { columns[j][i].clone() }) } /// Creates a matrix filled with random values. #[inline] #[cfg(feature = "rand")] pub fn new_random_generic(nrows: R, ncols: C) -> Self where Standard: Distribution, { let mut rng = rand::thread_rng(); Self::from_fn_generic(nrows, ncols, |_, _| rng.gen()) } /// Creates a matrix filled with random values from the given distribution. #[inline] #[cfg(feature = "rand-no-std")] pub fn from_distribution_generic + ?Sized, G: Rng + ?Sized>( nrows: R, ncols: C, distribution: &Distr, rng: &mut G, ) -> Self { Self::from_fn_generic(nrows, ncols, |_, _| distribution.sample(rng)) } /// Creates a matrix backed by a given `Vec`. /// /// The output matrix is filled column-by-column. /// /// # Example /// ``` /// # use nalgebra::{Dyn, DMatrix, Matrix, Const}; /// /// let vec = vec![0, 1, 2, 3, 4, 5]; /// let vec_ptr = vec.as_ptr(); /// /// let matrix = Matrix::from_vec_generic(Dyn(vec.len()), Const::<1>, vec); /// let matrix_storage_ptr = matrix.data.as_vec().as_ptr(); /// /// // `matrix` is backed by exactly the same `Vec` as it was constructed from. /// assert_eq!(matrix_storage_ptr, vec_ptr); /// ``` #[inline] #[cfg(any(feature = "std", feature = "alloc"))] pub fn from_vec_generic(nrows: R, ncols: C, data: Vec) -> Self { Self::from_iterator_generic(nrows, ncols, data) } } impl OMatrix where T: Scalar, DefaultAllocator: Allocator, { /// Creates a square matrix with its diagonal set to `diag` and all other entries set to 0. /// /// # Example /// ``` /// # use nalgebra::{Vector3, DVector, Matrix3, DMatrix}; /// # use std::iter; /// /// let m = Matrix3::from_diagonal(&Vector3::new(1.0, 2.0, 3.0)); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_diagonal(&DVector::from_row_slice(&[1.0, 2.0, 3.0])); /// /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && /// m.m21 == 0.0 && m.m22 == 2.0 && m.m23 == 0.0 && /// m.m31 == 0.0 && m.m32 == 0.0 && m.m33 == 3.0); /// assert!(dm[(0, 0)] == 1.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 0.0 && /// dm[(2, 0)] == 0.0 && dm[(2, 1)] == 0.0 && dm[(2, 2)] == 3.0); /// ``` #[inline] pub fn from_diagonal>(diag: &Vector) -> Self where T: Zero, { let (dim, _) = diag.shape_generic(); let mut res = Self::zeros_generic(dim, dim); for i in 0..diag.len() { unsafe { *res.get_unchecked_mut((i, i)) = diag.vget_unchecked(i).clone(); } } res } } /* * * Generate constructors with varying number of arguments, depending on the object type. * */ macro_rules! impl_constructors( ($($Dims: ty),*; $(=> $DimIdent: ident: $DimBound: ident),*; $($gargs: expr),*; $($args: ident),*) => { /// Creates a matrix or vector with all its elements set to `elem`. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// /// let v = Vector3::from_element(2.0); /// // The additional argument represents the vector dimension. /// let dv = DVector::from_element(3, 2.0); /// let m = Matrix2x3::from_element(2.0); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_element(2, 3, 2.0); /// /// assert!(v.x == 2.0 && v.y == 2.0 && v.z == 2.0); /// assert!(dv[0] == 2.0 && dv[1] == 2.0 && dv[2] == 2.0); /// assert!(m.m11 == 2.0 && m.m12 == 2.0 && m.m13 == 2.0 && /// m.m21 == 2.0 && m.m22 == 2.0 && m.m23 == 2.0); /// assert!(dm[(0, 0)] == 2.0 && dm[(0, 1)] == 2.0 && dm[(0, 2)] == 2.0 && /// dm[(1, 0)] == 2.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 2.0); /// ``` #[inline] pub fn from_element($($args: usize,)* elem: T) -> Self { Self::from_element_generic($($gargs, )* elem) } /// Creates a matrix or vector with all its elements set to `elem`. /// /// Same as `.from_element`. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// /// let v = Vector3::repeat(2.0); /// // The additional argument represents the vector dimension. /// let dv = DVector::repeat(3, 2.0); /// let m = Matrix2x3::repeat(2.0); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::repeat(2, 3, 2.0); /// /// assert!(v.x == 2.0 && v.y == 2.0 && v.z == 2.0); /// assert!(dv[0] == 2.0 && dv[1] == 2.0 && dv[2] == 2.0); /// assert!(m.m11 == 2.0 && m.m12 == 2.0 && m.m13 == 2.0 && /// m.m21 == 2.0 && m.m22 == 2.0 && m.m23 == 2.0); /// assert!(dm[(0, 0)] == 2.0 && dm[(0, 1)] == 2.0 && dm[(0, 2)] == 2.0 && /// dm[(1, 0)] == 2.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 2.0); /// ``` #[inline] pub fn repeat($($args: usize,)* elem: T) -> Self { Self::repeat_generic($($gargs, )* elem) } /// Creates a matrix or vector with all its elements set to `0`. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// /// let v = Vector3::::zeros(); /// // The argument represents the vector dimension. /// let dv = DVector::::zeros(3); /// let m = Matrix2x3::::zeros(); /// // The two arguments represent the matrix dimensions. /// let dm = DMatrix::::zeros(2, 3); /// /// assert!(v.x == 0.0 && v.y == 0.0 && v.z == 0.0); /// assert!(dv[0] == 0.0 && dv[1] == 0.0 && dv[2] == 0.0); /// assert!(m.m11 == 0.0 && m.m12 == 0.0 && m.m13 == 0.0 && /// m.m21 == 0.0 && m.m22 == 0.0 && m.m23 == 0.0); /// assert!(dm[(0, 0)] == 0.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 0.0 && dm[(1, 2)] == 0.0); /// ``` #[inline] pub fn zeros($($args: usize),*) -> Self where T: Zero { Self::zeros_generic($($gargs),*) } /// Creates a matrix or vector with all its elements filled by an iterator. /// /// The output matrix is filled column-by-column. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// # use std::iter; /// /// let v = Vector3::from_iterator((0..3).into_iter()); /// // The additional argument represents the vector dimension. /// let dv = DVector::from_iterator(3, (0..3).into_iter()); /// let m = Matrix2x3::from_iterator((0..6).into_iter()); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_iterator(2, 3, (0..6).into_iter()); /// /// assert!(v.x == 0 && v.y == 1 && v.z == 2); /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); /// ``` #[inline] pub fn from_iterator($($args: usize,)* iter: I) -> Self where I: IntoIterator { Self::from_iterator_generic($($gargs, )* iter) } /// Creates a matrix or vector with all its elements filled by a row-major iterator. /// /// The output matrix is filled row-by-row. /// /// ## Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// # use std::iter; /// /// let v = Vector3::from_row_iterator((0..3).into_iter()); /// // The additional argument represents the vector dimension. /// let dv = DVector::from_row_iterator(3, (0..3).into_iter()); /// let m = Matrix2x3::from_row_iterator((0..6).into_iter()); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_row_iterator(2, 3, (0..6).into_iter()); /// /// // For Vectors from_row_iterator is identical to from_iterator /// assert!(v.x == 0 && v.y == 1 && v.z == 2); /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); /// ``` #[inline] pub fn from_row_iterator($($args: usize,)* iter: I) -> Self where I: IntoIterator { Self::from_row_iterator_generic($($gargs, )* iter) } /// Creates a matrix or vector filled with the results of a function applied to each of its /// component coordinates. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// # use std::iter; /// /// let v = Vector3::from_fn(|i, _| i); /// // The additional argument represents the vector dimension. /// let dv = DVector::from_fn(3, |i, _| i); /// let m = Matrix2x3::from_fn(|i, j| i * 3 + j); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_fn(2, 3, |i, j| i * 3 + j); /// /// assert!(v.x == 0 && v.y == 1 && v.z == 2); /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); /// ``` #[inline] pub fn from_fn($($args: usize,)* f: F) -> Self where F: FnMut(usize, usize) -> T { Self::from_fn_generic($($gargs, )* f) } /// Creates an identity matrix. If the matrix is not square, the largest square /// submatrix (starting at the first row and column) is set to the identity while all /// other entries are set to zero. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, DMatrix}; /// # use std::iter; /// /// let m = Matrix2x3::::identity(); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::::identity(2, 3); /// /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && /// m.m21 == 0.0 && m.m22 == 1.0 && m.m23 == 0.0); /// assert!(dm[(0, 0)] == 1.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 1.0 && dm[(1, 2)] == 0.0); /// ``` #[inline] pub fn identity($($args: usize,)*) -> Self where T: Zero + One { Self::identity_generic($($gargs),* ) } /// Creates a matrix filled with its diagonal filled with `elt` and all other /// components set to zero. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, DMatrix}; /// # use std::iter; /// /// let m = Matrix2x3::from_diagonal_element(5.0); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_diagonal_element(2, 3, 5.0); /// /// assert!(m.m11 == 5.0 && m.m12 == 0.0 && m.m13 == 0.0 && /// m.m21 == 0.0 && m.m22 == 5.0 && m.m23 == 0.0); /// assert!(dm[(0, 0)] == 5.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 5.0 && dm[(1, 2)] == 0.0); /// ``` #[inline] pub fn from_diagonal_element($($args: usize,)* elt: T) -> Self where T: Zero + One { Self::from_diagonal_element_generic($($gargs, )* elt) } /// Creates a new matrix that may be rectangular. The first `elts.len()` diagonal /// elements are filled with the content of `elts`. Others are set to 0. /// /// Panics if `elts.len()` is larger than the minimum among `nrows` and `ncols`. /// /// # Example /// ``` /// # use nalgebra::{Matrix3, DMatrix}; /// # use std::iter; /// /// let m = Matrix3::from_partial_diagonal(&[1.0, 2.0]); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_partial_diagonal(3, 3, &[1.0, 2.0]); /// /// assert!(m.m11 == 1.0 && m.m12 == 0.0 && m.m13 == 0.0 && /// m.m21 == 0.0 && m.m22 == 2.0 && m.m23 == 0.0 && /// m.m31 == 0.0 && m.m32 == 0.0 && m.m33 == 0.0); /// assert!(dm[(0, 0)] == 1.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 0.0 && /// dm[(1, 0)] == 0.0 && dm[(1, 1)] == 2.0 && dm[(1, 2)] == 0.0 && /// dm[(2, 0)] == 0.0 && dm[(2, 1)] == 0.0 && dm[(2, 2)] == 0.0); /// ``` #[inline] pub fn from_partial_diagonal($($args: usize,)* elts: &[T]) -> Self where T: Zero { Self::from_partial_diagonal_generic($($gargs, )* elts) } /// Creates a matrix or vector filled with random values from the given distribution. #[inline] #[cfg(feature = "rand-no-std")] pub fn from_distribution + ?Sized, G: Rng + ?Sized>( $($args: usize,)* distribution: &Distr, rng: &mut G, ) -> Self { Self::from_distribution_generic($($gargs, )* distribution, rng) } /// Creates a matrix filled with random values. #[inline] #[cfg(feature = "rand")] pub fn new_random($($args: usize),*) -> Self where Standard: Distribution { Self::new_random_generic($($gargs),*) } } ); /// # Constructors of statically-sized vectors or statically-sized matrices impl OMatrix where DefaultAllocator: Allocator, { // TODO: this is not very pretty. We could find a better call syntax. impl_constructors!(R, C; // Arguments for Matrix => R: DimName, => C: DimName; // Type parameters for impl R::name(), C::name(); // Arguments for `_generic` constructors. ); // Arguments for non-generic constructors. } /// # Constructors of matrices with a dynamic number of columns impl OMatrix where DefaultAllocator: Allocator, { impl_constructors!(R, Dyn; => R: DimName; R::name(), Dyn(ncols); ncols); } /// # Constructors of dynamic vectors and matrices with a dynamic number of rows impl OMatrix where DefaultAllocator: Allocator, { impl_constructors!(Dyn, C; => C: DimName; Dyn(nrows), C::name(); nrows); } /// # Constructors of fully dynamic matrices #[cfg(any(feature = "std", feature = "alloc"))] impl OMatrix where DefaultAllocator: Allocator, { impl_constructors!(Dyn, Dyn; ; Dyn(nrows), Dyn(ncols); nrows, ncols); } /* * * Constructors that don't necessarily require all dimensions * to be specified when one dimension is already known. * */ macro_rules! impl_constructors_from_data( ($data: ident; $($Dims: ty),*; $(=> $DimIdent: ident: $DimBound: ident),*; $($gargs: expr),*; $($args: ident),*) => { impl OMatrix where DefaultAllocator: Allocator<$($Dims),*> { /// Creates a matrix with its elements filled with the components provided by a slice /// in row-major order. /// /// The order of elements in the slice must follow the usual mathematic writing, i.e., /// row-by-row. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// # use std::iter; /// /// let v = Vector3::from_row_slice(&[0, 1, 2]); /// // The additional argument represents the vector dimension. /// let dv = DVector::from_row_slice(&[0, 1, 2]); /// let m = Matrix2x3::from_row_slice(&[0, 1, 2, 3, 4, 5]); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_row_slice(2, 3, &[0, 1, 2, 3, 4, 5]); /// /// assert!(v.x == 0 && v.y == 1 && v.z == 2); /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); /// assert!(m.m11 == 0 && m.m12 == 1 && m.m13 == 2 && /// m.m21 == 3 && m.m22 == 4 && m.m23 == 5); /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 1 && dm[(0, 2)] == 2 && /// dm[(1, 0)] == 3 && dm[(1, 1)] == 4 && dm[(1, 2)] == 5); /// ``` #[inline] pub fn from_row_slice($($args: usize,)* $data: &[T]) -> Self { Self::from_row_slice_generic($($gargs, )* $data) } /// Creates a matrix with its elements filled with the components provided by a slice /// in column-major order. /// /// # Example /// ``` /// # use nalgebra::{Matrix2x3, Vector3, DVector, DMatrix}; /// # use std::iter; /// /// let v = Vector3::from_column_slice(&[0, 1, 2]); /// // The additional argument represents the vector dimension. /// let dv = DVector::from_column_slice(&[0, 1, 2]); /// let m = Matrix2x3::from_column_slice(&[0, 1, 2, 3, 4, 5]); /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_column_slice(2, 3, &[0, 1, 2, 3, 4, 5]); /// /// assert!(v.x == 0 && v.y == 1 && v.z == 2); /// assert!(dv[0] == 0 && dv[1] == 1 && dv[2] == 2); /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); /// ``` #[inline] pub fn from_column_slice($($args: usize,)* $data: &[T]) -> Self { Self::from_column_slice_generic($($gargs, )* $data) } /// Creates a matrix backed by a given `Vec`. /// /// The output matrix is filled column-by-column. /// /// # Example /// ``` /// # use nalgebra::{DMatrix, Matrix2x3}; /// /// let m = Matrix2x3::from_vec(vec![0, 1, 2, 3, 4, 5]); /// /// assert!(m.m11 == 0 && m.m12 == 2 && m.m13 == 4 && /// m.m21 == 1 && m.m22 == 3 && m.m23 == 5); /// /// /// // The two additional arguments represent the matrix dimensions. /// let dm = DMatrix::from_vec(2, 3, vec![0, 1, 2, 3, 4, 5]); /// /// assert!(dm[(0, 0)] == 0 && dm[(0, 1)] == 2 && dm[(0, 2)] == 4 && /// dm[(1, 0)] == 1 && dm[(1, 1)] == 3 && dm[(1, 2)] == 5); /// ``` #[inline] #[cfg(any(feature = "std", feature = "alloc"))] pub fn from_vec($($args: usize,)* $data: Vec) -> Self { Self::from_vec_generic($($gargs, )* $data) } } } ); // TODO: this is not very pretty. We could find a better call syntax. impl_constructors_from_data!(data; R, C; // Arguments for Matrix => R: DimName, => C: DimName; // Type parameters for impl R::name(), C::name(); // Arguments for `_generic` constructors. ); // Arguments for non-generic constructors. impl_constructors_from_data!(data; R, Dyn; => R: DimName; R::name(), Dyn(data.len() / R::dim()); ); impl_constructors_from_data!(data; Dyn, C; => C: DimName; Dyn(data.len() / C::dim()), C::name(); ); #[cfg(any(feature = "std", feature = "alloc"))] impl_constructors_from_data!(data; Dyn, Dyn; ; Dyn(nrows), Dyn(ncols); nrows, ncols); /* * * Zero, One, Rand traits. * */ impl Zero for OMatrix where T: Scalar + Zero + ClosedAddAssign, DefaultAllocator: Allocator, { #[inline] fn zero() -> Self { Self::from_element(T::zero()) } #[inline] fn is_zero(&self) -> bool { self.iter().all(|e| e.is_zero()) } } impl One for OMatrix where T: Scalar + Zero + One + ClosedMulAssign + ClosedAddAssign, DefaultAllocator: Allocator, { #[inline] fn one() -> Self { Self::identity() } } impl Bounded for OMatrix where T: Scalar + Bounded, DefaultAllocator: Allocator, { #[inline] fn max_value() -> Self { Self::from_element(T::max_value()) } #[inline] fn min_value() -> Self { Self::from_element(T::min_value()) } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where DefaultAllocator: Allocator, Standard: Distribution, { #[inline] fn sample(&self, rng: &mut G) -> OMatrix { let nrows = R::try_to_usize().unwrap_or_else(|| rng.gen_range(0..10)); let ncols = C::try_to_usize().unwrap_or_else(|| rng.gen_range(0..10)); OMatrix::from_fn_generic(R::from_usize(nrows), C::from_usize(ncols), |_, _| rng.gen()) } } #[cfg(feature = "arbitrary")] impl Arbitrary for OMatrix where R: Dim, C: Dim, T: Scalar + Arbitrary + Send, DefaultAllocator: Allocator, Owned: Clone + Send, { #[inline] fn arbitrary(g: &mut Gen) -> Self { let nrows = R::try_to_usize().unwrap_or(usize::arbitrary(g) % 10); let ncols = C::try_to_usize().unwrap_or(usize::arbitrary(g) % 10); Self::from_fn_generic(R::from_usize(nrows), C::from_usize(ncols), |_, _| { T::arbitrary(g) }) } } // TODO(specialization): faster impls possible for D≤4 (see rand_distr::{UnitCircle, UnitSphere}) #[cfg(feature = "rand")] impl Distribution>> for Standard where DefaultAllocator: Allocator, rand_distr::StandardNormal: Distribution, { /// Generate a uniformly distributed random unit vector. #[inline] fn sample(&self, rng: &mut G) -> Unit> { Unit::new_normalize(OVector::from_distribution_generic( D::name(), Const::<1>, &rand_distr::StandardNormal, rng, )) } } /* * * Constructors for small matrices and vectors. * */ macro_rules! transpose_array( [$($a: ident),*;] => { [$([$a]),*] }; [$($a: ident),*; $($b: ident),*;] => { [$([$a, $b]),*] }; [$($a: ident),*; $($b: ident),*; $($c: ident),*;] => { [$([$a, $b, $c]),*] }; [$($a: ident),*; $($b: ident),*; $($c: ident),*; $($d: ident),*;] => { [$([$a, $b, $c, $d]),*] }; [$($a: ident),*; $($b: ident),*; $($c: ident),*; $($d: ident),*; $($e: ident),*;] => { [$([$a, $b, $c, $d, $e]),*] }; [$($a: ident),*; $($b: ident),*; $($c: ident),*; $($d: ident),*; $($e: ident),*; $($f: ident),*;] => { [$([$a, $b, $c, $d, $e, $f]),*] }; ); macro_rules! componentwise_constructors_impl( ($($R: expr, $C: expr, [$($($args: ident),*);*] $(;)*)*) => {$( impl Matrix, Const<$C>, ArrayStorage> { /// Initializes this matrix from its components. #[inline] #[allow(clippy::too_many_arguments)] pub const fn new($($($args: T),*),*) -> Self { unsafe { Self::from_data_statically_unchecked( ArrayStorage( transpose_array![ $( $($args),* ;)* ] ) ) } } } )*} ); componentwise_constructors_impl!( /* * Square matrices 1 .. 6. */ 2, 2, [m11, m12; m21, m22]; 3, 3, [m11, m12, m13; m21, m22, m23; m31, m32, m33]; 4, 4, [m11, m12, m13, m14; m21, m22, m23, m24; m31, m32, m33, m34; m41, m42, m43, m44]; 5, 5, [m11, m12, m13, m14, m15; m21, m22, m23, m24, m25; m31, m32, m33, m34, m35; m41, m42, m43, m44, m45; m51, m52, m53, m54, m55]; 6, 6, [m11, m12, m13, m14, m15, m16; m21, m22, m23, m24, m25, m26; m31, m32, m33, m34, m35, m36; m41, m42, m43, m44, m45, m46; m51, m52, m53, m54, m55, m56; m61, m62, m63, m64, m65, m66]; /* * Rectangular matrices with 2 rows. */ 2, 3, [m11, m12, m13; m21, m22, m23]; 2, 4, [m11, m12, m13, m14; m21, m22, m23, m24]; 2, 5, [m11, m12, m13, m14, m15; m21, m22, m23, m24, m25]; 2, 6, [m11, m12, m13, m14, m15, m16; m21, m22, m23, m24, m25, m26]; /* * Rectangular matrices with 3 rows. */ 3, 2, [m11, m12; m21, m22; m31, m32]; 3, 4, [m11, m12, m13, m14; m21, m22, m23, m24; m31, m32, m33, m34]; 3, 5, [m11, m12, m13, m14, m15; m21, m22, m23, m24, m25; m31, m32, m33, m34, m35]; 3, 6, [m11, m12, m13, m14, m15, m16; m21, m22, m23, m24, m25, m26; m31, m32, m33, m34, m35, m36]; /* * Rectangular matrices with 4 rows. */ 4, 2, [m11, m12; m21, m22; m31, m32; m41, m42]; 4, 3, [m11, m12, m13; m21, m22, m23; m31, m32, m33; m41, m42, m43]; 4, 5, [m11, m12, m13, m14, m15; m21, m22, m23, m24, m25; m31, m32, m33, m34, m35; m41, m42, m43, m44, m45]; 4, 6, [m11, m12, m13, m14, m15, m16; m21, m22, m23, m24, m25, m26; m31, m32, m33, m34, m35, m36; m41, m42, m43, m44, m45, m46]; /* * Rectangular matrices with 5 rows. */ 5, 2, [m11, m12; m21, m22; m31, m32; m41, m42; m51, m52]; 5, 3, [m11, m12, m13; m21, m22, m23; m31, m32, m33; m41, m42, m43; m51, m52, m53]; 5, 4, [m11, m12, m13, m14; m21, m22, m23, m24; m31, m32, m33, m34; m41, m42, m43, m44; m51, m52, m53, m54]; 5, 6, [m11, m12, m13, m14, m15, m16; m21, m22, m23, m24, m25, m26; m31, m32, m33, m34, m35, m36; m41, m42, m43, m44, m45, m46; m51, m52, m53, m54, m55, m56]; /* * Rectangular matrices with 6 rows. */ 6, 2, [m11, m12; m21, m22; m31, m32; m41, m42; m51, m52; m61, m62]; 6, 3, [m11, m12, m13; m21, m22, m23; m31, m32, m33; m41, m42, m43; m51, m52, m53; m61, m62, m63]; 6, 4, [m11, m12, m13, m14; m21, m22, m23, m24; m31, m32, m33, m34; m41, m42, m43, m44; m51, m52, m53, m54; m61, m62, m63, m64]; 6, 5, [m11, m12, m13, m14, m15; m21, m22, m23, m24, m25; m31, m32, m33, m34, m35; m41, m42, m43, m44, m45; m51, m52, m53, m54, m55; m61, m62, m63, m64, m65]; /* * Row vectors 1 .. 6. */ 1, 1, [x]; 1, 2, [x, y]; 1, 3, [x, y, z]; 1, 4, [x, y, z, w]; 1, 5, [x, y, z, w, a]; 1, 6, [x, y, z, w, a, b]; /* * Column vectors 1 .. 6. */ 2, 1, [x; y]; 3, 1, [x; y; z]; 4, 1, [x; y; z; w]; 5, 1, [x; y; z; w; a]; 6, 1, [x; y; z; w; a; b]; ); /* * * Axis constructors. * */ impl OVector where R: ToTypenum, T: Scalar + Zero + One, DefaultAllocator: Allocator, { /// The column vector with `val` as its i-th component. #[inline] pub fn ith(i: usize, val: T) -> Self { let mut res = Self::zeros(); res[i] = val; res } /// The column unit vector with `T::one()` as its i-th component. #[inline] pub fn ith_axis(i: usize) -> Unit { Unit::new_unchecked(Self::ith(i, T::one())) } /// The column vector with a 1 as its first component, and zero elsewhere. #[inline] pub fn x() -> Self where R::Typenum: Cmp, { let mut res = Self::zeros(); unsafe { *res.vget_unchecked_mut(0) = T::one(); } res } /// The column vector with a 1 as its second component, and zero elsewhere. #[inline] pub fn y() -> Self where R::Typenum: Cmp, { let mut res = Self::zeros(); unsafe { *res.vget_unchecked_mut(1) = T::one(); } res } /// The column vector with a 1 as its third component, and zero elsewhere. #[inline] pub fn z() -> Self where R::Typenum: Cmp, { let mut res = Self::zeros(); unsafe { *res.vget_unchecked_mut(2) = T::one(); } res } /// The column vector with a 1 as its fourth component, and zero elsewhere. #[inline] pub fn w() -> Self where R::Typenum: Cmp, { let mut res = Self::zeros(); unsafe { *res.vget_unchecked_mut(3) = T::one(); } res } /// The column vector with a 1 as its fifth component, and zero elsewhere. #[inline] pub fn a() -> Self where R::Typenum: Cmp, { let mut res = Self::zeros(); unsafe { *res.vget_unchecked_mut(4) = T::one(); } res } /// The column vector with a 1 as its sixth component, and zero elsewhere. #[inline] pub fn b() -> Self where R::Typenum: Cmp, { let mut res = Self::zeros(); unsafe { *res.vget_unchecked_mut(5) = T::one(); } res } /// The unit column vector with a 1 as its first component, and zero elsewhere. #[inline] pub fn x_axis() -> Unit where R::Typenum: Cmp, { Unit::new_unchecked(Self::x()) } /// The unit column vector with a 1 as its second component, and zero elsewhere. #[inline] pub fn y_axis() -> Unit where R::Typenum: Cmp, { Unit::new_unchecked(Self::y()) } /// The unit column vector with a 1 as its third component, and zero elsewhere. #[inline] pub fn z_axis() -> Unit where R::Typenum: Cmp, { Unit::new_unchecked(Self::z()) } /// The unit column vector with a 1 as its fourth component, and zero elsewhere. #[inline] pub fn w_axis() -> Unit where R::Typenum: Cmp, { Unit::new_unchecked(Self::w()) } /// The unit column vector with a 1 as its fifth component, and zero elsewhere. #[inline] pub fn a_axis() -> Unit where R::Typenum: Cmp, { Unit::new_unchecked(Self::a()) } /// The unit column vector with a 1 as its sixth component, and zero elsewhere. #[inline] pub fn b_axis() -> Unit where R::Typenum: Cmp, { Unit::new_unchecked(Self::b()) } } nalgebra-0.33.0/src/base/construction_view.rs000064400000000000000000000331360072674642500173530ustar 00000000000000use crate::base::dimension::{Const, Dim, DimName, Dyn}; use crate::base::matrix_view::{ViewStorage, ViewStorageMut}; use crate::base::{MatrixView, MatrixViewMut, Scalar}; use num_rational::Ratio; /// # Creating matrix views from `&[T]` impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> MatrixView<'a, T, R, C, RStride, CStride> { /// Creates, without bounds checking, a matrix view from an array and with dimensions and strides specified by generic types instances. /// /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub unsafe fn from_slice_with_strides_generic_unchecked( data: &'a [T], start: usize, nrows: R, ncols: C, rstride: RStride, cstride: CStride, ) -> Self { let data = ViewStorage::from_raw_parts( data.as_ptr().add(start), (nrows, ncols), (rstride, cstride), ); Self::from_data(data) } /// Creates a matrix view from an array and with dimensions and strides specified by generic types instances. /// /// Panics if the input data array dose not contain enough elements. /// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub fn from_slice_with_strides_generic( data: &'a [T], nrows: R, ncols: C, rstride: RStride, cstride: CStride, ) -> Self { // NOTE: The assertion implements the following formula, but without subtractions to avoid // underflow panics: // len >= (ncols - 1) * cstride + (nrows - 1) * rstride + 1 assert!( data.len() + cstride.value() + rstride.value() >= ncols.value() * cstride.value() + nrows.value() * rstride.value() + 1, "Matrix view: input data buffer too small." ); unsafe { Self::from_slice_with_strides_generic_unchecked(data, 0, nrows, ncols, rstride, cstride) } } } impl<'a, T: Scalar, R: Dim, C: Dim> MatrixView<'a, T, R, C> { /// Creates, without bound-checking, a matrix view from an array and with dimensions specified by generic types instances. /// /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub unsafe fn from_slice_generic_unchecked( data: &'a [T], start: usize, nrows: R, ncols: C, ) -> Self { Self::from_slice_with_strides_generic_unchecked( data, start, nrows, ncols, Const::<1>, nrows, ) } /// Creates a matrix view from an array and with dimensions and strides specified by generic types instances. /// /// Panics if the input data array dose not contain enough elements. /// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub fn from_slice_generic(data: &'a [T], nrows: R, ncols: C) -> Self { Self::from_slice_with_strides_generic(data, nrows, ncols, Const::<1>, nrows) } } macro_rules! impl_constructors( ($($Dims: ty),*; $(=> $DimIdent: ident: $DimBound: ident),*; $($gargs: expr),*; $($args: ident),*) => { impl<'a, T: Scalar, $($DimIdent: $DimBound),*> MatrixView<'a, T, $($Dims),*> { /// Creates a new matrix view from the given data array. /// /// Panics if `data` does not contain enough elements. #[inline] pub fn from_slice(data: &'a [T], $($args: usize),*) -> Self { Self::from_slice_generic(data, $($gargs),*) } /// 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),*) } } impl<'a, T: Scalar, $($DimIdent: $DimBound, )*> MatrixView<'a, T, $($Dims,)* Dyn, Dyn> { /// Creates a new matrix view with the specified strides from the given data array. /// /// Panics if `data` does not contain enough elements. #[inline] pub fn from_slice_with_strides(data: &'a [T], $($args: usize,)* rstride: usize, cstride: usize) -> Self { Self::from_slice_with_strides_generic(data, $($gargs,)* Dyn(rstride), Dyn(cstride)) } /// 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)) } } } ); // TODO: this is not very pretty. We could find a better call syntax. impl_constructors!(R, C; // Arguments for Matrix => R: DimName, => C: DimName; // Type parameters for impl R::name(), C::name(); // Arguments for `_generic` constructors. ); // Arguments for non-generic constructors. impl_constructors!(R, Dyn; => R: DimName; R::name(), Dyn(ncols); ncols); impl_constructors!(Dyn, C; => C: DimName; Dyn(nrows), C::name(); nrows); impl_constructors!(Dyn, Dyn; ; Dyn(nrows), Dyn(ncols); nrows, ncols); /// # Creating mutable matrix views from `&mut [T]` impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> MatrixViewMut<'a, T, R, C, RStride, CStride> { /// Creates, without bound-checking, a mutable matrix view from an array and with dimensions and strides specified by generic types instances. /// /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub unsafe fn from_slice_with_strides_generic_unchecked( data: &'a mut [T], start: usize, nrows: R, ncols: C, rstride: RStride, cstride: CStride, ) -> Self { let data = ViewStorageMut::from_raw_parts( data.as_mut_ptr().add(start), (nrows, ncols), (rstride, cstride), ); Self::from_data(data) } /// Creates a mutable matrix view from an array and with dimensions and strides specified by generic types instances. /// /// Panics if the input data array dose not contain enough elements. /// The generic types `R`, `C`, `RStride`, `CStride` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub fn from_slice_with_strides_generic( data: &'a mut [T], nrows: R, ncols: C, rstride: RStride, cstride: CStride, ) -> Self { // NOTE: The assertion implements the following formula, but without subtractions to avoid // underflow panics: // len >= (ncols - 1) * cstride + (nrows - 1) * rstride + 1 assert!( data.len() + cstride.value() + rstride.value() >= ncols.value() * cstride.value() + nrows.value() * rstride.value() + 1, "Matrix view: input data buffer too small." ); assert!( { let nrows = nrows.value(); let ncols = ncols.value(); let rstride = rstride.value(); let cstride = cstride.value(); nrows * ncols <= 1 || match (rstride, cstride) { (0, 0) => false, // otherwise: matrix[(0, 0)] == index[(nrows - 1, ncols - 1)], (0, _) => nrows <= 1, // otherwise: matrix[(0, 0)] == index[(nrows - 1, 0)], (_, 0) => ncols <= 1, // otherwise: matrix[(0, 0)] == index[(0, ncols - 1)], (_, _) => { // otherwise: matrix[(0, numer)] == index[(denom, 0)] let ratio = Ratio::new(rstride, cstride); nrows <= *ratio.denom() || ncols <= *ratio.numer() } } }, "Matrix view: dimensions and strides result in aliased indices." ); unsafe { Self::from_slice_with_strides_generic_unchecked(data, 0, nrows, ncols, rstride, cstride) } } } impl<'a, T: Scalar, R: Dim, C: Dim> MatrixViewMut<'a, T, R, C> { /// Creates, without bound-checking, a mutable matrix view from an array and with dimensions specified by generic types instances. /// /// # Safety /// This method is unsafe because the input data array is not checked to contain enough elements. /// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub unsafe fn from_slice_generic_unchecked( data: &'a mut [T], start: usize, nrows: R, ncols: C, ) -> Self { Self::from_slice_with_strides_generic_unchecked( data, start, nrows, ncols, Const::<1>, nrows, ) } /// Creates a mutable matrix view from an array and with dimensions and strides specified by generic types instances. /// /// Panics if the input data array dose not contain enough elements. /// The generic types `R` and `C` can either be type-level integers or integers wrapped with `Dyn()`. #[inline] pub fn from_slice_generic(data: &'a mut [T], nrows: R, ncols: C) -> Self { Self::from_slice_with_strides_generic(data, nrows, ncols, Const::<1>, nrows) } } macro_rules! impl_constructors_mut( ($($Dims: ty),*; $(=> $DimIdent: ident: $DimBound: ident),*; $($gargs: expr),*; $($args: ident),*) => { impl<'a, T: Scalar, $($DimIdent: $DimBound),*> MatrixViewMut<'a, T, $($Dims),*> { /// Creates a new mutable matrix view from the given data array. /// /// Panics if `data` does not contain enough elements. #[inline] pub fn from_slice(data: &'a mut [T], $($args: usize),*) -> Self { Self::from_slice_generic(data, $($gargs),*) } /// 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),*) } } impl<'a, T: Scalar, $($DimIdent: $DimBound, )*> MatrixViewMut<'a, T, $($Dims,)* Dyn, Dyn> { /// Creates a new mutable matrix view with the specified strides from the given data array. /// /// Panics if `data` does not contain enough elements. #[inline] pub fn from_slice_with_strides_mut(data: &'a mut [T], $($args: usize,)* rstride: usize, cstride: usize) -> Self { Self::from_slice_with_strides_generic( data, $($gargs,)* Dyn(rstride), Dyn(cstride)) } /// 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( data, start, $($gargs,)* Dyn(rstride), Dyn(cstride)) } } } ); // TODO: this is not very pretty. We could find a better call syntax. impl_constructors_mut!(R, C; // Arguments for Matrix => R: DimName, => C: DimName; // Type parameters for impl R::name(), C::name(); // Arguments for `_generic` constructors. ); // Arguments for non-generic constructors. impl_constructors_mut!(R, Dyn; => R: DimName; R::name(), Dyn(ncols); ncols); impl_constructors_mut!(Dyn, C; => C: DimName; Dyn(nrows), C::name(); nrows); impl_constructors_mut!(Dyn, Dyn; ; Dyn(nrows), Dyn(ncols); nrows, ncols); nalgebra-0.33.0/src/base/conversion.rs000064400000000000000000000515100072674642500157500ustar 00000000000000#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; use simba::scalar::{SubsetOf, SupersetOf}; use std::borrow::{Borrow, BorrowMut}; use std::convert::{AsMut, AsRef, From, Into}; use simba::simd::{PrimitiveSimdValue, SimdValue}; use crate::base::allocator::{Allocator, SameShapeAllocator}; use crate::base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use crate::base::dimension::{ Const, Dim, U1, U10, U11, U12, U13, U14, U15, U16, U2, U3, U4, U5, U6, U7, U8, U9, }; #[cfg(any(feature = "std", feature = "alloc"))] use crate::base::dimension::{DimName, Dyn}; use crate::base::iter::{MatrixIter, MatrixIterMut}; use crate::base::storage::{IsContiguous, RawStorage, RawStorageMut}; use crate::base::{ ArrayStorage, DVectorView, DVectorViewMut, DefaultAllocator, Matrix, MatrixView, MatrixViewMut, OMatrix, Scalar, }; #[cfg(any(feature = "std", feature = "alloc"))] use crate::base::{DVector, RowDVector, VecStorage}; use crate::base::{ViewStorage, ViewStorageMut}; use crate::constraint::DimEq; use crate::{IsNotStaticOne, RowSVector, SMatrix, SVector, VectorView, VectorViewMut}; use std::mem::MaybeUninit; // TODO: too bad this won't work for slice conversions. impl SubsetOf> for OMatrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, T1: Scalar, T2: Scalar + SupersetOf, DefaultAllocator: Allocator + Allocator + SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { #[inline] fn to_superset(&self) -> OMatrix { let (nrows, ncols) = self.shape(); let nrows2 = R2::from_usize(nrows); let ncols2 = C2::from_usize(ncols); let mut res = Matrix::uninit(nrows2, ncols2); for i in 0..nrows { for j in 0..ncols { // Safety: all indices are in range. unsafe { *res.get_unchecked_mut((i, j)) = MaybeUninit::new(T2::from_subset(self.get_unchecked((i, j)))); } } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } #[inline] fn is_in_subset(m: &OMatrix) -> bool { m.iter().all(|e| e.is_in_subset()) } #[inline] fn from_superset_unchecked(m: &OMatrix) -> Self { let (nrows2, ncols2) = m.shape(); let nrows = R1::from_usize(nrows2); let ncols = C1::from_usize(ncols2); let mut res = Matrix::uninit(nrows, ncols); for i in 0..nrows2 { for j in 0..ncols2 { // Safety: all indices are in range. unsafe { *res.get_unchecked_mut((i, j)) = MaybeUninit::new(m.get_unchecked((i, j)).to_subset_unchecked()) } } } unsafe { res.assume_init() } } } impl<'a, T: Scalar, R: Dim, C: Dim, S: RawStorage> IntoIterator for &'a Matrix { type Item = &'a T; type IntoIter = MatrixIter<'a, T, R, C, S>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> IntoIterator for Matrix> { type Item = &'a T; type IntoIter = MatrixIter<'a, T, R, C, ViewStorage<'a, T, R, C, RStride, CStride>>; #[inline] fn into_iter(self) -> Self::IntoIter { MatrixIter::new_owned(self.data) } } impl<'a, T: Scalar, R: Dim, C: Dim, S: RawStorageMut> IntoIterator for &'a mut Matrix { type Item = &'a mut T; type IntoIter = MatrixIterMut<'a, T, R, C, S>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> IntoIterator for Matrix> { type Item = &'a mut T; type IntoIter = MatrixIterMut<'a, T, R, C, ViewStorageMut<'a, T, R, C, RStride, CStride>>; #[inline] fn into_iter(self) -> Self::IntoIter { MatrixIterMut::new_owned_mut(self.data) } } impl From<[T; D]> for SVector { #[inline] fn from(arr: [T; D]) -> Self { unsafe { Self::from_data_statically_unchecked(ArrayStorage([arr; 1])) } } } impl From> for [T; D] { #[inline] fn from(vec: SVector) -> Self { // TODO: unfortunately, we must clone because we can move out of an array. vec.data.0[0].clone() } } impl<'a, T: Scalar, RStride: Dim, CStride: Dim, const D: usize> From, RStride, CStride>> for [T; D] { #[inline] fn from(vec: VectorView<'a, T, Const, RStride, CStride>) -> Self { vec.into_owned().into() } } impl<'a, T: Scalar, RStride: Dim, CStride: Dim, const D: usize> From, RStride, CStride>> for [T; D] { #[inline] fn from(vec: VectorViewMut<'a, T, Const, RStride, CStride>) -> Self { vec.into_owned().into() } } impl From<[T; D]> for RowSVector where Const: IsNotStaticOne, { #[inline] fn from(arr: [T; D]) -> Self { SVector::::from(arr).transpose() } } impl From> for [T; D] where Const: IsNotStaticOne, { #[inline] fn from(vec: RowSVector) -> [T; D] { vec.transpose().into() } } macro_rules! impl_from_into_asref_1D( ($(($NRows: ident, $NCols: ident) => $SZ: expr);* $(;)*) => {$( impl AsRef<[T; $SZ]> for Matrix where T: Scalar, S: RawStorage + IsContiguous { #[inline] fn as_ref(&self) -> &[T; $SZ] { // Safety: this is OK thanks to the IsContiguous trait. unsafe { &*(self.data.ptr() as *const [T; $SZ]) } } } impl AsMut<[T; $SZ]> for Matrix where T: Scalar, S: RawStorageMut + IsContiguous { #[inline] fn as_mut(&mut self) -> &mut [T; $SZ] { // Safety: this is OK thanks to the IsContiguous trait. unsafe { &mut *(self.data.ptr_mut() as *mut [T; $SZ]) } } } )*} ); // Implement for vectors of dimension 1 .. 16. impl_from_into_asref_1D!( // Row vectors. (U1, U1 ) => 1; (U1, U2 ) => 2; (U1, U3 ) => 3; (U1, U4 ) => 4; (U1, U5 ) => 5; (U1, U6 ) => 6; (U1, U7 ) => 7; (U1, U8 ) => 8; (U1, U9 ) => 9; (U1, U10) => 10; (U1, U11) => 11; (U1, U12) => 12; (U1, U13) => 13; (U1, U14) => 14; (U1, U15) => 15; (U1, U16) => 16; // Column vectors. (U2 , U1) => 2; (U3 , U1) => 3; (U4 , U1) => 4; (U5 , U1) => 5; (U6 , U1) => 6; (U7 , U1) => 7; (U8 , U1) => 8; (U9 , U1) => 9; (U10, U1) => 10; (U11, U1) => 11; (U12, U1) => 12; (U13, U1) => 13; (U14, U1) => 14; (U15, U1) => 15; (U16, U1) => 16; ); impl From<[[T; R]; C]> for SMatrix { #[inline] fn from(arr: [[T; R]; C]) -> Self { unsafe { Self::from_data_statically_unchecked(ArrayStorage(arr)) } } } impl From> for [[T; R]; C] { #[inline] fn from(mat: SMatrix) -> Self { mat.data.0 } } impl<'a, T: Scalar, RStride: Dim, CStride: Dim, const R: usize, const C: usize> From, Const, RStride, CStride>> for [[T; R]; C] { #[inline] fn from(mat: MatrixView<'a, T, Const, Const, RStride, CStride>) -> Self { mat.into_owned().into() } } impl<'a, T: Scalar, RStride: Dim, CStride: Dim, const R: usize, const C: usize> From, Const, RStride, CStride>> for [[T; R]; C] { #[inline] fn from(mat: MatrixViewMut<'a, T, Const, Const, RStride, CStride>) -> Self { mat.into_owned().into() } } macro_rules! impl_from_into_asref_borrow_2D( //does the impls on one case for either AsRef/AsMut and Borrow/BorrowMut ( ($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr); $Ref:ident.$ref:ident(), $Mut:ident.$mut:ident() ) => { impl $Ref<[[T; $SZRows]; $SZCols]> for Matrix where S: RawStorage + IsContiguous { #[inline] fn $ref(&self) -> &[[T; $SZRows]; $SZCols] { // Safety: OK thanks to the IsContiguous trait. unsafe { &*(self.data.ptr() as *const [[T; $SZRows]; $SZCols]) } } } impl $Mut<[[T; $SZRows]; $SZCols]> for Matrix where S: RawStorageMut + IsContiguous { #[inline] fn $mut(&mut self) -> &mut [[T; $SZRows]; $SZCols] { // Safety: OK thanks to the IsContiguous trait. unsafe { &mut *(self.data.ptr_mut() as *mut [[T; $SZRows]; $SZCols]) } } } }; //collects the mappings from typenum pairs to consts ($(($NRows: ty, $NCols: ty) => ($SZRows: expr, $SZCols: expr));* $(;)*) => {$( impl_from_into_asref_borrow_2D!( ($NRows, $NCols) => ($SZRows, $SZCols); AsRef.as_ref(), AsMut.as_mut() ); impl_from_into_asref_borrow_2D!( ($NRows, $NCols) => ($SZRows, $SZCols); Borrow.borrow(), BorrowMut.borrow_mut() ); )*} ); // Implement for matrices with shape 2x2 .. 6x6. impl_from_into_asref_borrow_2D!( (U2, U2) => (2, 2); (U2, U3) => (2, 3); (U2, U4) => (2, 4); (U2, U5) => (2, 5); (U2, U6) => (2, 6); (U3, U2) => (3, 2); (U3, U3) => (3, 3); (U3, U4) => (3, 4); (U3, U5) => (3, 5); (U3, U6) => (3, 6); (U4, U2) => (4, 2); (U4, U3) => (4, 3); (U4, U4) => (4, 4); (U4, U5) => (4, 5); (U4, U6) => (4, 6); (U5, U2) => (5, 2); (U5, U3) => (5, 3); (U5, U4) => (5, 4); (U5, U5) => (5, 5); (U5, U6) => (5, 6); (U6, U2) => (6, 2); (U6, U3) => (6, 3); (U6, U4) => (6, 4); (U6, U5) => (6, 5); (U6, U6) => (6, 6); ); impl<'a, T, RStride, CStride, const R: usize, const C: usize> From, Const, RStride, CStride>> for Matrix, Const, ArrayStorage> where T: Scalar, RStride: Dim, CStride: Dim, { fn from(matrix_view: MatrixView<'a, T, Const, Const, RStride, CStride>) -> Self { matrix_view.into_owned() } } #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, T, C, RStride, CStride> From> for Matrix> where T: Scalar, C: Dim, RStride: Dim, CStride: Dim, { fn from(matrix_view: MatrixView<'a, T, Dyn, C, RStride, CStride>) -> Self { matrix_view.into_owned() } } #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, T, R, RStride, CStride> From> for Matrix> where T: Scalar, R: DimName, RStride: Dim, CStride: Dim, { fn from(matrix_view: MatrixView<'a, T, R, Dyn, RStride, CStride>) -> Self { matrix_view.into_owned() } } impl<'a, T, RStride, CStride, const R: usize, const C: usize> From, Const, RStride, CStride>> for Matrix, Const, ArrayStorage> where T: Scalar, RStride: Dim, CStride: Dim, { fn from(matrix_view: MatrixViewMut<'a, T, Const, Const, RStride, CStride>) -> Self { matrix_view.into_owned() } } #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, T, C, RStride, CStride> From> for Matrix> where T: Scalar, C: Dim, RStride: Dim, CStride: Dim, { fn from(matrix_view: MatrixViewMut<'a, T, Dyn, C, RStride, CStride>) -> Self { matrix_view.into_owned() } } #[cfg(any(feature = "std", feature = "alloc"))] impl<'a, T, R, RStride, CStride> From> for Matrix> where T: Scalar, R: DimName, RStride: Dim, CStride: Dim, { fn from(matrix_view: MatrixViewMut<'a, T, R, Dyn, RStride, CStride>) -> Self { matrix_view.into_owned() } } impl<'a, T, R, C, RView, CView, RStride, CStride, S> From<&'a Matrix> for MatrixView<'a, T, RView, CView, RStride, CStride> where R: Dim, C: Dim, RView: Dim, CView: Dim, RStride: Dim, CStride: Dim, S: RawStorage, ShapeConstraint: DimEq + DimEq + DimEq + DimEq, { fn from(m: &'a Matrix) -> Self { let (row, col) = m.shape_generic(); let rows_result = RView::from_usize(row.value()); let cols_result = CView::from_usize(col.value()); let (rstride, cstride) = m.strides(); let rstride_result = RStride::from_usize(rstride); let cstride_result = CStride::from_usize(cstride); unsafe { let data = ViewStorage::from_raw_parts( m.data.ptr(), (rows_result, cols_result), (rstride_result, cstride_result), ); Matrix::from_data_statically_unchecked(data) } } } impl<'a, T, R, C, RView, CView, RStride, CStride, S> From<&'a mut Matrix> for MatrixView<'a, T, RView, CView, RStride, CStride> where R: Dim, C: Dim, RView: Dim, CView: Dim, RStride: Dim, CStride: Dim, S: RawStorage, ShapeConstraint: DimEq + DimEq + DimEq + DimEq, { fn from(m: &'a mut Matrix) -> Self { let (row, col) = m.shape_generic(); let rows_result = RView::from_usize(row.value()); let cols_result = CView::from_usize(col.value()); let (rstride, cstride) = m.strides(); let rstride_result = RStride::from_usize(rstride); let cstride_result = CStride::from_usize(cstride); unsafe { let data = ViewStorage::from_raw_parts( m.data.ptr(), (rows_result, cols_result), (rstride_result, cstride_result), ); Matrix::from_data_statically_unchecked(data) } } } impl<'a, T, R, C, RView, CView, RStride, CStride, S> From<&'a mut Matrix> for MatrixViewMut<'a, T, RView, CView, RStride, CStride> where R: Dim, C: Dim, RView: Dim, CView: Dim, RStride: Dim, CStride: Dim, S: RawStorageMut, ShapeConstraint: DimEq + DimEq + DimEq + DimEq, { fn from(m: &'a mut Matrix) -> Self { let (row, col) = m.shape_generic(); let rows_result = RView::from_usize(row.value()); let cols_result = CView::from_usize(col.value()); let (rstride, cstride) = m.strides(); let rstride_result = RStride::from_usize(rstride); let cstride_result = CStride::from_usize(cstride); unsafe { let data = ViewStorageMut::from_raw_parts( m.data.ptr_mut(), (rows_result, cols_result), (rstride_result, cstride_result), ); Matrix::from_data_statically_unchecked(data) } } } #[cfg(any(feature = "std", feature = "alloc"))] impl From> for DVector { #[inline] fn from(vec: Vec) -> Self { Self::from_vec(vec) } } #[cfg(any(feature = "std", feature = "alloc"))] impl From> for RowDVector { #[inline] fn from(vec: Vec) -> Self { Self::from_vec(vec) } } impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: RawStorage + IsContiguous> From<&'a Matrix> for &'a [T] { #[inline] fn from(matrix: &'a Matrix) -> Self { matrix.as_slice() } } impl<'a, T: Scalar + Copy, R: Dim, C: Dim, S: RawStorageMut + IsContiguous> From<&'a mut Matrix> for &'a mut [T] { #[inline] fn from(matrix: &'a mut Matrix) -> Self { matrix.as_mut_slice() } } impl<'a, T: Scalar + Copy> From<&'a [T]> for DVectorView<'a, T> { #[inline] fn from(slice: &'a [T]) -> Self { Self::from_slice(slice, slice.len()) } } impl<'a, T: Scalar> From> for &'a [T] { fn from(vec: DVectorView<'a, T>) -> &'a [T] { vec.data.into_slice() } } impl<'a, T: Scalar + Copy> From<&'a mut [T]> for DVectorViewMut<'a, T> { #[inline] fn from(slice: &'a mut [T]) -> Self { Self::from_slice(slice, slice.len()) } } impl<'a, T: Scalar> From> for &'a mut [T] { fn from(vec: DVectorViewMut<'a, T>) -> &'a mut [T] { vec.data.into_slice_mut() } } impl From<[OMatrix; 2]> for OMatrix where T: From<[::Element; 2]>, T::Element: Scalar + SimdValue, DefaultAllocator: Allocator, { #[inline] fn from(arr: [OMatrix; 2]) -> Self { let (nrows, ncols) = arr[0].shape_generic(); Self::from_fn_generic(nrows, ncols, |i, j| { [arr[0][(i, j)].clone(), arr[1][(i, j)].clone()].into() }) } } impl From<[OMatrix; 4]> for OMatrix where T: From<[::Element; 4]>, T::Element: Scalar + SimdValue, DefaultAllocator: Allocator, { #[inline] fn from(arr: [OMatrix; 4]) -> Self { let (nrows, ncols) = arr[0].shape_generic(); Self::from_fn_generic(nrows, ncols, |i, j| { [ arr[0][(i, j)].clone(), arr[1][(i, j)].clone(), arr[2][(i, j)].clone(), arr[3][(i, j)].clone(), ] .into() }) } } impl From<[OMatrix; 8]> for OMatrix where T: From<[::Element; 8]>, T::Element: Scalar + SimdValue, DefaultAllocator: Allocator, { #[inline] fn from(arr: [OMatrix; 8]) -> Self { let (nrows, ncols) = arr[0].shape_generic(); Self::from_fn_generic(nrows, ncols, |i, j| { [ arr[0][(i, j)].clone(), arr[1][(i, j)].clone(), arr[2][(i, j)].clone(), arr[3][(i, j)].clone(), arr[4][(i, j)].clone(), arr[5][(i, j)].clone(), arr[6][(i, j)].clone(), arr[7][(i, j)].clone(), ] .into() }) } } impl From<[OMatrix; 16]> for OMatrix where T: From<[::Element; 16]>, T::Element: Scalar + SimdValue, DefaultAllocator: Allocator, { fn from(arr: [OMatrix; 16]) -> Self { let (nrows, ncols) = arr[0].shape_generic(); Self::from_fn_generic(nrows, ncols, |i, j| { [ arr[0][(i, j)].clone(), arr[1][(i, j)].clone(), arr[2][(i, j)].clone(), arr[3][(i, j)].clone(), arr[4][(i, j)].clone(), arr[5][(i, j)].clone(), arr[6][(i, j)].clone(), arr[7][(i, j)].clone(), arr[8][(i, j)].clone(), arr[9][(i, j)].clone(), arr[10][(i, j)].clone(), arr[11][(i, j)].clone(), arr[12][(i, j)].clone(), arr[13][(i, j)].clone(), arr[14][(i, j)].clone(), arr[15][(i, j)].clone(), ] .into() }) } } nalgebra-0.33.0/src/base/coordinates.rs000064400000000000000000000165130072674642500161010ustar 00000000000000#![allow(missing_docs)] //! Structures to which matrices and vector can be auto-dereferenced (through `Deref`) to access //! components using their names. For example, if `v` is a 3D vector, one can write `v.z` instead //! of `v[2]`. use std::ops::{Deref, DerefMut}; use crate::base::dimension::{U1, U2, U3, U4, U5, U6}; use crate::base::storage::{IsContiguous, RawStorage, RawStorageMut}; use crate::base::{Matrix, Scalar}; /* * * Give coordinates to owned Vector{1 .. 6} and Matrix{1 .. 6} * */ macro_rules! coords_impl( ($T: ident; $($comps: ident),*) => { /// Data structure used to provide access to matrix and vector coordinates with the dot /// notation, e.g., `v.x` is the same as `v[0]` for a vector. #[repr(C)] #[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)] #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] pub struct $T { $(pub $comps: T),* } } ); macro_rules! deref_impl( ($R: ty, $C: ty; $Target: ident) => { impl Deref for Matrix where S: RawStorage + IsContiguous { type Target = $Target; #[inline] fn deref(&self) -> &Self::Target { // Safety: this is OK because of the IsContiguous trait. unsafe { &*(self.data.ptr() as *const Self::Target) } } } impl DerefMut for Matrix where S: RawStorageMut + IsContiguous { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { // Safety: this is OK because of the IsContiguous trait. unsafe { &mut *(self.data.ptr_mut() as *mut Self::Target) } } } } ); /* * * Vector coordinates. * */ coords_impl!(X; x); coords_impl!(XY; x, y); coords_impl!(XYZ; x, y, z); coords_impl!(XYZW; x, y, z, w); coords_impl!(XYZWA; x, y, z, w, a); coords_impl!(XYZWAB; x, y, z, w, a, b); coords_impl!(IJKW; i, j, k, w); /* * Rectangular matrices with 2 rows. */ coords_impl!(M2x2; m11, m21, m12, m22); coords_impl!(M2x3; m11, m21, m12, m22, m13, m23); coords_impl!(M2x4; m11, m21, m12, m22, m13, m23, m14, m24); coords_impl!(M2x5; m11, m21, m12, m22, m13, m23, m14, m24, m15, m25); coords_impl!(M2x6; m11, m21, m12, m22, m13, m23, m14, m24, m15, m25, m16, m26); /* * Rectangular matrices with 3 rows. */ coords_impl!(M3x2; m11, m21, m31, m12, m22, m32); coords_impl!(M3x3; m11, m21, m31, m12, m22, m32, m13, m23, m33); coords_impl!(M3x4; m11, m21, m31, m12, m22, m32, m13, m23, m33, m14, m24, m34); coords_impl!(M3x5; m11, m21, m31, m12, m22, m32, m13, m23, m33, m14, m24, m34, m15, m25, m35); coords_impl!(M3x6; m11, m21, m31, m12, m22, m32, m13, m23, m33, m14, m24, m34, m15, m25, m35, m16, m26, m36); /* * Rectangular matrices with 4 rows. */ coords_impl!(M4x2; m11, m21, m31, m41, m12, m22, m32, m42); coords_impl!(M4x3; m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43); coords_impl!(M4x4; m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44); coords_impl!(M4x5; m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44, m15, m25, m35, m45); coords_impl!(M4x6; m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44, m15, m25, m35, m45, m16, m26, m36, m46); /* * Rectangular matrices with 5 rows. */ coords_impl!(M5x2; m11, m21, m31, m41, m51, m12, m22, m32, m42, m52); coords_impl!(M5x3; m11, m21, m31, m41, m51, m12, m22, m32, m42, m52, m13, m23, m33, m43, m53); coords_impl!(M5x4; m11, m21, m31, m41, m51, m12, m22, m32, m42, m52, m13, m23, m33, m43, m53, m14, m24, m34, m44, m54); coords_impl!(M5x5; m11, m21, m31, m41, m51, m12, m22, m32, m42, m52, m13, m23, m33, m43, m53, m14, m24, m34, m44, m54, m15, m25, m35, m45, m55); coords_impl!(M5x6; m11, m21, m31, m41, m51, m12, m22, m32, m42, m52, m13, m23, m33, m43, m53, m14, m24, m34, m44, m54, m15, m25, m35, m45, m55, m16, m26, m36, m46, m56); /* * Rectangular matrices with 6 rows. */ coords_impl!(M6x2; m11, m21, m31, m41, m51, m61, m12, m22, m32, m42, m52, m62); coords_impl!(M6x3; m11, m21, m31, m41, m51, m61, m12, m22, m32, m42, m52, m62, m13, m23, m33, m43, m53, m63); coords_impl!(M6x4; m11, m21, m31, m41, m51, m61, m12, m22, m32, m42, m52, m62, m13, m23, m33, m43, m53, m63, m14, m24, m34, m44, m54, m64); coords_impl!(M6x5; m11, m21, m31, m41, m51, m61, m12, m22, m32, m42, m52, m62, m13, m23, m33, m43, m53, m63, m14, m24, m34, m44, m54, m64, m15, m25, m35, m45, m55, m65); coords_impl!(M6x6; m11, m21, m31, m41, m51, m61, m12, m22, m32, m42, m52, m62, m13, m23, m33, m43, m53, m63, m14, m24, m34, m44, m54, m64, m15, m25, m35, m45, m55, m65, m16, m26, m36, m46, m56, m66); /* * * Attach coordinates to matrices. * */ deref_impl!(U1, U1; X); deref_impl!(U2, U1; XY); deref_impl!(U3, U1; XYZ); deref_impl!(U4, U1; XYZW); deref_impl!(U5, U1; XYZWA); deref_impl!(U6, U1; XYZWAB); deref_impl!(U1, U2; XY); deref_impl!(U1, U3; XYZ); deref_impl!(U1, U4; XYZW); deref_impl!(U1, U5; XYZWA); deref_impl!(U1, U6; XYZWAB); deref_impl!(U2, U2; M2x2); deref_impl!(U2, U3; M2x3); deref_impl!(U2, U4; M2x4); deref_impl!(U2, U5; M2x5); deref_impl!(U2, U6; M2x6); deref_impl!(U3, U2; M3x2); deref_impl!(U3, U3; M3x3); deref_impl!(U3, U4; M3x4); deref_impl!(U3, U5; M3x5); deref_impl!(U3, U6; M3x6); deref_impl!(U4, U2; M4x2); deref_impl!(U4, U3; M4x3); deref_impl!(U4, U4; M4x4); deref_impl!(U4, U5; M4x5); deref_impl!(U4, U6; M4x6); deref_impl!(U5, U2; M5x2); deref_impl!(U5, U3; M5x3); deref_impl!(U5, U4; M5x4); deref_impl!(U5, U5; M5x5); deref_impl!(U5, U6; M5x6); deref_impl!(U6, U2; M6x2); deref_impl!(U6, U3; M6x3); deref_impl!(U6, U4; M6x4); deref_impl!(U6, U5; M6x5); deref_impl!(U6, U6; M6x6); nalgebra-0.33.0/src/base/default_allocator.rs000064400000000000000000000271240072674642500172530ustar 00000000000000//! The default matrix data storage allocator. //! //! This will use stack-allocated buffers for matrices with dimensions known at compile-time, and //! heap-allocated buffers for matrices with at least one dimension unknown at compile-time. use std::cmp; use std::ptr; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; use super::Const; use crate::base::allocator::{Allocator, Reallocator}; use crate::base::array_storage::ArrayStorage; use crate::base::dimension::Dim; #[cfg(any(feature = "alloc", feature = "std"))] use crate::base::dimension::{DimName, Dyn}; use crate::base::storage::{RawStorage, RawStorageMut, Storage}; #[cfg(any(feature = "std", feature = "alloc"))] use crate::base::vec_storage::VecStorage; use crate::base::Scalar; #[cfg(any(feature = "std", feature = "alloc"))] use std::mem::ManuallyDrop; use std::mem::MaybeUninit; /* * * Allocator. * */ /// An allocator based on [`ArrayStorage`] and [`VecStorage`] for statically-sized and dynamically-sized /// matrices respectively. #[derive(Copy, Clone, Debug)] pub struct DefaultAllocator; // Static - Static impl Allocator, Const> for DefaultAllocator { type Buffer = ArrayStorage; type BufferUninit = ArrayStorage, R, C>; #[inline(always)] fn allocate_uninit(_: Const, _: Const) -> ArrayStorage, R, C> { // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid. let array: [[MaybeUninit; R]; C] = unsafe { MaybeUninit::uninit().assume_init() }; ArrayStorage(array) } #[inline(always)] unsafe fn assume_init( uninit: ArrayStorage, R, C>, ) -> ArrayStorage { // Safety: // * The caller guarantees that all elements of the array are initialized // * `MaybeUninit` and T are guaranteed to have the same layout // * `MaybeUninit` does not drop, so there are no double-frees // And thus the conversion is safe ArrayStorage((&uninit as *const _ as *const [_; C]).read()) } #[inline] fn allocate_from_iterator>( nrows: Const, ncols: Const, iter: I, ) -> Self::Buffer { let mut res = Self::allocate_uninit(nrows, ncols); let mut count = 0; // Safety: conversion to a slice is OK because the Buffer is known to be contiguous. let res_slice = unsafe { res.as_mut_slice_unchecked() }; for (res, e) in res_slice.iter_mut().zip(iter.into_iter()) { *res = MaybeUninit::new(e); count += 1; } assert!( count == nrows.value() * ncols.value(), "Matrix init. from iterator: iterator not long enough." ); // Safety: the assertion above made sure that the iterator // yielded enough elements to initialize our matrix. unsafe { , Const>>::assume_init(res) } } } // Dyn - Static // Dyn - Dyn #[cfg(any(feature = "std", feature = "alloc"))] impl Allocator for DefaultAllocator { type Buffer = VecStorage; type BufferUninit = VecStorage, Dyn, C>; #[inline] fn allocate_uninit(nrows: Dyn, ncols: C) -> VecStorage, Dyn, C> { let mut data = Vec::new(); let length = nrows.value() * ncols.value(); data.reserve_exact(length); data.resize_with(length, MaybeUninit::uninit); VecStorage::new(nrows, ncols, data) } #[inline] unsafe fn assume_init( uninit: VecStorage, Dyn, C>, ) -> VecStorage { // Avoids a double-drop. let (nrows, ncols) = uninit.shape(); let vec: Vec<_> = uninit.into(); let mut md = ManuallyDrop::new(vec); // Safety: // - MaybeUninit has the same alignment and layout as T. // - The length and capacity come from a valid vector. let new_data = Vec::from_raw_parts(md.as_mut_ptr() as *mut _, md.len(), md.capacity()); VecStorage::new(nrows, ncols, new_data) } #[inline] fn allocate_from_iterator>( nrows: Dyn, ncols: C, iter: I, ) -> Self::Buffer { let it = iter.into_iter(); let res: Vec = it.collect(); assert!(res.len() == nrows.value() * ncols.value(), "Allocation from iterator error: the iterator did not yield the correct number of elements."); VecStorage::new(nrows, ncols, res) } } // Static - Dyn #[cfg(any(feature = "std", feature = "alloc"))] impl Allocator for DefaultAllocator { type Buffer = VecStorage; type BufferUninit = VecStorage, R, Dyn>; #[inline] fn allocate_uninit(nrows: R, ncols: Dyn) -> VecStorage, R, Dyn> { let mut data = Vec::new(); let length = nrows.value() * ncols.value(); data.reserve_exact(length); data.resize_with(length, MaybeUninit::uninit); VecStorage::new(nrows, ncols, data) } #[inline] unsafe fn assume_init( uninit: VecStorage, R, Dyn>, ) -> VecStorage { // Avoids a double-drop. let (nrows, ncols) = uninit.shape(); let vec: Vec<_> = uninit.into(); let mut md = ManuallyDrop::new(vec); // Safety: // - MaybeUninit has the same alignment and layout as T. // - The length and capacity come from a valid vector. let new_data = Vec::from_raw_parts(md.as_mut_ptr() as *mut _, md.len(), md.capacity()); VecStorage::new(nrows, ncols, new_data) } #[inline] fn allocate_from_iterator>( nrows: R, ncols: Dyn, iter: I, ) -> Self::Buffer { let it = iter.into_iter(); let res: Vec = it.collect(); assert!(res.len() == nrows.value() * ncols.value(), "Allocation from iterator error: the iterator did not yield the correct number of elements."); VecStorage::new(nrows, ncols, res) } } /* * * Reallocator. * */ // Anything -> Static × Static impl Reallocator, Const> for DefaultAllocator where RFrom: Dim, CFrom: Dim, Self: Allocator, { #[inline] unsafe fn reallocate_copy( rto: Const, cto: Const, buf: >::Buffer, ) -> ArrayStorage, RTO, CTO> { let mut res = , Const>>::allocate_uninit(rto, cto); let (rfrom, cfrom) = buf.shape(); let len_from = rfrom.value() * cfrom.value(); let len_to = rto.value() * cto.value(); let len_copied = cmp::min(len_from, len_to); ptr::copy_nonoverlapping(buf.ptr(), res.ptr_mut() as *mut T, len_copied); // Safety: // - We don’t care about dropping elements because the caller is responsible for dropping things. // - We forget `buf` so that we don’t drop the other elements, but ensure the buffer itself is cleaned up. buf.forget_elements(); res } } // Static × Static -> Dyn × Any #[cfg(any(feature = "std", feature = "alloc"))] impl Reallocator, Const, Dyn, CTo> for DefaultAllocator where CTo: Dim, { #[inline] unsafe fn reallocate_copy( rto: Dyn, cto: CTo, buf: ArrayStorage, ) -> VecStorage, Dyn, CTo> { let mut res = >::allocate_uninit(rto, cto); let (rfrom, cfrom) = buf.shape(); let len_from = rfrom.value() * cfrom.value(); let len_to = rto.value() * cto.value(); let len_copied = cmp::min(len_from, len_to); ptr::copy_nonoverlapping(buf.ptr(), res.ptr_mut() as *mut T, len_copied); // Safety: // - We don’t care about dropping elements because the caller is responsible for dropping things. // - We forget `buf` so that we don’t drop the other elements, but ensure the buffer itself is cleaned up. buf.forget_elements(); res } } // Static × Static -> Static × Dyn #[cfg(any(feature = "std", feature = "alloc"))] impl Reallocator, Const, RTo, Dyn> for DefaultAllocator where RTo: DimName, { #[inline] unsafe fn reallocate_copy( rto: RTo, cto: Dyn, buf: ArrayStorage, ) -> VecStorage, RTo, Dyn> { let mut res = >::allocate_uninit(rto, cto); let (rfrom, cfrom) = buf.shape(); let len_from = rfrom.value() * cfrom.value(); let len_to = rto.value() * cto.value(); let len_copied = cmp::min(len_from, len_to); ptr::copy_nonoverlapping(buf.ptr(), res.ptr_mut() as *mut T, len_copied); // Safety: // - We don’t care about dropping elements because the caller is responsible for dropping things. // - We forget `buf` so that we don’t drop the other elements, but ensure the buffer itself is cleaned up. buf.forget_elements(); res } } // All conversion from a dynamic buffer to a dynamic buffer. #[cfg(any(feature = "std", feature = "alloc"))] impl Reallocator for DefaultAllocator { #[inline] unsafe fn reallocate_copy( rto: Dyn, cto: CTo, buf: VecStorage, ) -> VecStorage, Dyn, CTo> { let new_buf = buf.resize(rto.value() * cto.value()); VecStorage::new(rto, cto, new_buf) } } #[cfg(any(feature = "std", feature = "alloc"))] impl Reallocator for DefaultAllocator { #[inline] unsafe fn reallocate_copy( rto: RTo, cto: Dyn, buf: VecStorage, ) -> VecStorage, RTo, Dyn> { let new_buf = buf.resize(rto.value() * cto.value()); VecStorage::new(rto, cto, new_buf) } } #[cfg(any(feature = "std", feature = "alloc"))] impl Reallocator for DefaultAllocator { #[inline] unsafe fn reallocate_copy( rto: Dyn, cto: CTo, buf: VecStorage, ) -> VecStorage, Dyn, CTo> { let new_buf = buf.resize(rto.value() * cto.value()); VecStorage::new(rto, cto, new_buf) } } #[cfg(any(feature = "std", feature = "alloc"))] impl Reallocator for DefaultAllocator { #[inline] unsafe fn reallocate_copy( rto: RTo, cto: Dyn, buf: VecStorage, ) -> VecStorage, RTo, Dyn> { let new_buf = buf.resize(rto.value() * cto.value()); VecStorage::new(rto, cto, new_buf) } } nalgebra-0.33.0/src/base/dimension.rs000064400000000000000000000236260072674642500155570ustar 00000000000000#![allow(missing_docs)] //! Traits and tags for identifying the dimension of all algebraic entities. use std::any::{Any, TypeId}; use std::cmp; use std::fmt::Debug; use std::ops::{Add, Div, Mul, Sub}; use typenum::{self, Diff, Max, Maximum, Min, Minimum, Prod, Quot, Sum, Unsigned}; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// Dim of dynamically-sized algebraic entities. #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) )] #[cfg_attr( feature = "rkyv-serialize", archive_attr(derive(bytecheck::CheckBytes)) )] pub struct Dyn(pub usize); #[deprecated(note = "use Dyn instead.")] pub type Dynamic = Dyn; impl Dyn { /// A dynamic size equal to `value`. #[inline] #[deprecated(note = "use Dyn(value) instead.")] pub const fn new(value: usize) -> Self { Self(value) } } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Dyn { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.0.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'de> Deserialize<'de> for Dyn { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { usize::deserialize(deserializer).map(|x| Dyn(x)) } } /// Trait implemented by `Dyn`. pub trait IsDynamic {} /// Trait implemented by `Dyn` and type-level integers different from `U1`. pub trait IsNotStaticOne {} impl IsDynamic for Dyn {} 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() -> bool { TypeId::of::() == TypeId::of::() } /// Gets the compile-time value of `Self`. Returns `None` if it is not known, i.e., if `Self = /// Dyn`. fn try_to_usize() -> Option; /// Gets the run-time value of `self`. For type-level integers, this is the same as /// `Self::try_to_usize().unwrap()`. fn value(&self) -> usize; /// Builds an instance of `Self` from a run-time value. Panics if `Self` is a type-level /// integer and `dim != Self::try_to_usize().unwrap()`. fn from_usize(dim: usize) -> Self; } unsafe impl Dim for Dyn { #[inline] fn try_to_usize() -> Option { None } #[inline] fn from_usize(dim: usize) -> Self { Self(dim) } #[inline] fn value(&self) -> usize { self.0 } } impl Add for Dyn { type Output = Dyn; #[inline] fn add(self, rhs: usize) -> Self { Self(self.0 + rhs) } } impl Sub for Dyn { type Output = Dyn; #[inline] fn sub(self, rhs: usize) -> Self { Self(self.0 - rhs) } } /* * * Operations. * */ macro_rules! dim_ops( ($($DimOp: ident, $DimNameOp: ident, $Op: ident, $op: ident, $op_path: path, $DimResOp: ident, $DimNameResOp: ident, $ResOp: ident);* $(;)*) => {$( pub type $DimResOp = >::Output; pub trait $DimOp: Dim { type Output: Dim; fn $op(self, other: D) -> Self::Output; } impl $DimOp> for Const where Const: ToTypenum, Const: ToTypenum, as ToTypenum>::Typenum: $Op< as ToTypenum>::Typenum>, $ResOp< as ToTypenum>::Typenum, as ToTypenum>::Typenum>: ToConst, { type Output = <$ResOp< as ToTypenum>::Typenum, as ToTypenum>::Typenum> as ToConst>::Const; fn $op(self, _: Const) -> Self::Output { Self::Output::name() } } impl $DimOp for Dyn { type Output = Dyn; #[inline] fn $op(self, other: D) -> Dyn { Dyn($op_path(self.value(), other.value())) } } // TODO: use Const instead of D: DimName? impl $DimOp for D { type Output = Dyn; #[inline] fn $op(self, other: Dyn) -> Dyn { Dyn($op_path(self.value(), other.value())) } } pub type $DimNameResOp = >::Output; pub trait $DimNameOp: DimName { type Output: DimName; fn $op(self, other: D) -> Self::Output; } impl $DimNameOp> for Const where Const: ToTypenum, Const: ToTypenum, as ToTypenum>::Typenum: $Op< as ToTypenum>::Typenum>, $ResOp< as ToTypenum>::Typenum, as ToTypenum>::Typenum>: ToConst, { type Output = <$ResOp< as ToTypenum>::Typenum, as ToTypenum>::Typenum> as ToConst>::Const; fn $op(self, _: Const) -> Self::Output { Self::Output::name() } } )*} ); dim_ops!( DimAdd, DimNameAdd, Add, add, Add::add, DimSum, DimNameSum, Sum; DimMul, DimNameMul, Mul, mul, Mul::mul, DimProd, DimNameProd, Prod; DimSub, DimNameSub, Sub, sub, Sub::sub, DimDiff, DimNameDiff, Diff; DimDiv, DimNameDiv, Div, div, Div::div, DimQuot, DimNameQuot, Quot; DimMin, DimNameMin, Min, min, cmp::min, DimMinimum, DimNameMinimum, Minimum; DimMax, DimNameMax, Max, max, cmp::max, DimMaximum, DimNameMaximum, Maximum; ); #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(as = "Self") )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] pub struct Const; /// Trait implemented exclusively by type-level integers. pub trait DimName: Dim { const USIZE: usize; /// The name of this dimension, i.e., the singleton `Self`. fn name() -> Self; // TODO: this is not a very idiomatic name. /// The value of this dimension. fn dim() -> usize; } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Const { fn serialize(&self, serializer: S) -> Result where S: Serializer, { ().serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'de, const D: usize> Deserialize<'de> for Const { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'de>, { <()>::deserialize(deserializer).map(|_| Const::) } } pub trait ToConst { type Const: DimName; } pub trait ToTypenum { type Typenum: Unsigned; } unsafe impl Dim for Const { #[inline] fn try_to_usize() -> Option { Some(T) } #[inline] fn value(&self) -> usize { T } #[inline] fn from_usize(dim: usize) -> Self { assert_eq!(dim, T); Self } } impl DimName for Const { const USIZE: usize = T; #[inline] fn name() -> Self { Self } #[inline] fn dim() -> usize { T } } pub type U1 = Const<1>; impl ToTypenum for Const<1> { type Typenum = typenum::U1; } impl ToConst for typenum::U1 { type Const = Const<1>; } macro_rules! from_to_typenum ( ($($D: ident, $VAL: expr);* $(;)*) => {$( pub type $D = Const<$VAL>; impl ToTypenum for Const<$VAL> { type Typenum = typenum::$D; } impl ToConst for typenum::$D { type Const = Const<$VAL>; } impl IsNotStaticOne for $D { } /// The constant dimension #[doc = stringify!($VAL)] /// . pub const $D: $D = Const::<$VAL>; )*} ); from_to_typenum!( U0, 0; /*U1,1;*/ U2, 2; U3, 3; U4, 4; U5, 5; U6, 6; U7, 7; U8, 8; U9, 9; U10, 10; U11, 11; U12, 12; U13, 13; U14, 14; U15, 15; U16, 16; U17, 17; U18, 18; U19, 19; U20, 20; U21, 21; U22, 22; U23, 23; U24, 24; U25, 25; U26, 26; U27, 27; U28, 28; U29, 29; U30, 30; U31, 31; U32, 32; U33, 33; U34, 34; U35, 35; U36, 36; U37, 37; U38, 38; U39, 39; U40, 40; U41, 41; U42, 42; U43, 43; U44, 44; U45, 45; U46, 46; U47, 47; U48, 48; U49, 49; U50, 50; U51, 51; U52, 52; U53, 53; U54, 54; U55, 55; U56, 56; U57, 57; U58, 58; U59, 59; U60, 60; U61, 61; U62, 62; U63, 63; U64, 64; U65, 65; U66, 66; U67, 67; U68, 68; U69, 69; U70, 70; U71, 71; U72, 72; U73, 73; U74, 74; U75, 75; U76, 76; U77, 77; U78, 78; U79, 79; U80, 80; U81, 81; U82, 82; U83, 83; U84, 84; U85, 85; U86, 86; U87, 87; U88, 88; U89, 89; U90, 90; U91, 91; U92, 92; U93, 93; U94, 94; U95, 95; U96, 96; U97, 97; U98, 98; U99, 99; U100, 100; U101, 101; U102, 102; U103, 103; U104, 104; U105, 105; U106, 106; U107, 107; U108, 108; U109, 109; U110, 110; U111, 111; U112, 112; U113, 113; U114, 114; U115, 115; U116, 116; U117, 117; U118, 118; U119, 119; U120, 120; U121, 121; U122, 122; U123, 123; U124, 124; U125, 125; U126, 126; U127, 127 ); /// The constant dimension 1. // Note: We add U1 separately since it's not covered by the from_to_typenum! macro. pub const U1: U1 = Const::<1>; nalgebra-0.33.0/src/base/edition.rs000064400000000000000000001337120072674642500152230ustar 00000000000000use num::{One, Zero}; use std::cmp; #[cfg(any(feature = "std", feature = "alloc"))] use std::iter::ExactSizeIterator; use std::ptr; use crate::base::allocator::{Allocator, Reallocator}; use crate::base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; #[cfg(any(feature = "std", feature = "alloc"))] use crate::base::dimension::Dyn; use crate::base::dimension::{Const, Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimSub, DimSum, U1}; use crate::base::storage::{RawStorage, RawStorageMut, ReshapableStorage}; use crate::base::{DefaultAllocator, Matrix, OMatrix, RowVector, Scalar, Vector}; use crate::{Storage, UninitMatrix}; use std::mem::MaybeUninit; /// # Triangular matrix extraction impl> Matrix { /// Extracts the upper triangular part of this matrix (including the diagonal). #[inline] #[must_use] pub fn upper_triangle(&self) -> OMatrix where DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.fill_lower_triangle(T::zero(), 1); res } /// Extracts the lower triangular part of this matrix (including the diagonal). #[inline] #[must_use] pub fn lower_triangle(&self) -> OMatrix where DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.fill_upper_triangle(T::zero(), 1); res } } /// # Rows and columns extraction impl> Matrix { /// Creates a new matrix by extracting the given set of rows from `self`. #[cfg(any(feature = "std", feature = "alloc"))] #[must_use] pub fn select_rows<'a, I>(&self, irows: I) -> OMatrix where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone, DefaultAllocator: Allocator, { let irows = irows.into_iter(); let ncols = self.shape_generic().1; let mut res = Matrix::uninit(Dyn(irows.len()), ncols); // First, check that all the indices from irows are valid. // This will allow us to use unchecked access in the inner loop. for i in irows.clone() { assert!(*i < self.nrows(), "Row index out of bounds.") } for j in 0..ncols.value() { // TODO: use unchecked column indexing let mut res = res.column_mut(j); let src = self.column(j); for (destination, source) in irows.clone().enumerate() { // Safety: all indices are in range. unsafe { *res.vget_unchecked_mut(destination) = MaybeUninit::new(src.vget_unchecked(*source).clone()); } } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Creates a new matrix by extracting the given set of columns from `self`. #[cfg(any(feature = "std", feature = "alloc"))] #[must_use] pub fn select_columns<'a, I>(&self, icols: I) -> OMatrix where I: IntoIterator, I::IntoIter: ExactSizeIterator, DefaultAllocator: Allocator, { let icols = icols.into_iter(); let nrows = self.shape_generic().0; let mut res = Matrix::uninit(nrows, Dyn(icols.len())); for (destination, source) in icols.enumerate() { // NOTE: this is basically a copy_frow but wrapping the values insnide of MaybeUninit. res.column_mut(destination) .zip_apply(&self.column(*source), |out, e| *out = MaybeUninit::new(e)); } // Safety: res is now fully initialized. unsafe { res.assume_init() } } } /// # Set rows, columns, and diagonal impl> Matrix { /// Fills the diagonal of this matrix with the content of the given vector. #[inline] pub fn set_diagonal(&mut self, diag: &Vector) where R: DimMin, S2: RawStorage, ShapeConstraint: DimEq, R2>, { let (nrows, ncols) = self.shape(); let min_nrows_ncols = cmp::min(nrows, ncols); assert_eq!(diag.len(), min_nrows_ncols, "Mismatched dimensions."); for i in 0..min_nrows_ncols { unsafe { *self.get_unchecked_mut((i, i)) = diag.vget_unchecked(i).clone() } } } /// Fills the diagonal of this matrix with the content of the given iterator. /// /// This will fill as many diagonal elements as the iterator yields, up to the /// minimum of the number of rows and columns of `self`, and starting with the /// diagonal element at index (0, 0). #[inline] pub fn set_partial_diagonal(&mut self, diag: impl Iterator) { let (nrows, ncols) = self.shape(); let min_nrows_ncols = cmp::min(nrows, ncols); for (i, val) in diag.enumerate().take(min_nrows_ncols) { unsafe { *self.get_unchecked_mut((i, i)) = val } } } /// Fills the selected row of this matrix with the content of the given vector. #[inline] pub fn set_row(&mut self, i: usize, row: &RowVector) where S2: RawStorage, ShapeConstraint: SameNumberOfColumns, { self.row_mut(i).copy_from(row); } /// Fills the selected column of this matrix with the content of the given vector. #[inline] pub fn set_column(&mut self, i: usize, column: &Vector) where S2: RawStorage, ShapeConstraint: SameNumberOfRows, { self.column_mut(i).copy_from(column); } } /// # In-place filling impl> Matrix { /// Sets all the elements of this matrix to the value returned by the closure. #[inline] pub fn fill_with(&mut self, val: impl Fn() -> T) { for e in self.iter_mut() { *e = val() } } /// Sets all the elements of this matrix to `val`. #[inline] pub fn fill(&mut self, val: T) where T: Scalar, { for e in self.iter_mut() { *e = val.clone() } } /// Fills `self` with the identity matrix. #[inline] pub fn fill_with_identity(&mut self) where T: Scalar + Zero + One, { self.fill(T::zero()); self.fill_diagonal(T::one()); } /// Sets all the diagonal elements of this matrix to `val`. #[inline] pub fn fill_diagonal(&mut self, val: T) where T: Scalar, { let (nrows, ncols) = self.shape(); let n = cmp::min(nrows, ncols); for i in 0..n { unsafe { *self.get_unchecked_mut((i, i)) = val.clone() } } } /// Sets all the elements of the selected row to `val`. #[inline] pub fn fill_row(&mut self, i: usize, val: T) where T: Scalar, { assert!(i < self.nrows(), "Row index out of bounds."); for j in 0..self.ncols() { unsafe { *self.get_unchecked_mut((i, j)) = val.clone() } } } /// Sets all the elements of the selected column to `val`. #[inline] pub fn fill_column(&mut self, j: usize, val: T) where T: Scalar, { assert!(j < self.ncols(), "Row index out of bounds."); for i in 0..self.nrows() { unsafe { *self.get_unchecked_mut((i, j)) = val.clone() } } } /// Sets all the elements of the lower-triangular part of this matrix to `val`. /// /// The parameter `shift` allows some subdiagonals to be left untouched: /// * If `shift = 0` then the diagonal is overwritten as well. /// * If `shift = 1` then the diagonal is left untouched. /// * If `shift > 1`, then the diagonal and the first `shift - 1` subdiagonals are left /// untouched. #[inline] pub fn fill_lower_triangle(&mut self, val: T, shift: usize) where T: Scalar, { for j in 0..self.ncols() { for i in (j + shift)..self.nrows() { unsafe { *self.get_unchecked_mut((i, j)) = val.clone() } } } } /// Sets all the elements of the lower-triangular part of this matrix to `val`. /// /// The parameter `shift` allows some superdiagonals to be left untouched: /// * If `shift = 0` then the diagonal is overwritten as well. /// * If `shift = 1` then the diagonal is left untouched. /// * If `shift > 1`, then the diagonal and the first `shift - 1` superdiagonals are left /// untouched. #[inline] pub fn fill_upper_triangle(&mut self, val: T, shift: usize) where T: Scalar, { for j in shift..self.ncols() { // TODO: is there a more efficient way to avoid the min ? // (necessary for rectangular matrices) for i in 0..cmp::min(j + 1 - shift, self.nrows()) { unsafe { *self.get_unchecked_mut((i, j)) = val.clone() } } } } } impl> Matrix { /// Copies the upper-triangle of this matrix to its lower-triangular part. /// /// This makes the matrix symmetric. Panics if the matrix is not square. pub fn fill_lower_triangle_with_upper_triangle(&mut self) { assert!(self.is_square(), "The input matrix should be square."); let dim = self.nrows(); for j in 0..dim { for i in j + 1..dim { unsafe { *self.get_unchecked_mut((i, j)) = self.get_unchecked((j, i)).clone(); } } } } /// Copies the upper-triangle of this matrix to its upper-triangular part. /// /// This makes the matrix symmetric. Panics if the matrix is not square. pub fn fill_upper_triangle_with_lower_triangle(&mut self) { assert!(self.is_square(), "The input matrix should be square."); for j in 1..self.ncols() { for i in 0..j { unsafe { *self.get_unchecked_mut((i, j)) = self.get_unchecked((j, i)).clone(); } } } } } /// # In-place swapping impl> Matrix { /// Swaps two rows in-place. #[inline] pub fn swap_rows(&mut self, irow1: usize, irow2: usize) { assert!(irow1 < self.nrows() && irow2 < self.nrows()); if irow1 != irow2 { // TODO: optimize that. for i in 0..self.ncols() { unsafe { self.swap_unchecked((irow1, i), (irow2, i)) } } } // Otherwise do nothing. } /// Swaps two columns in-place. #[inline] pub fn swap_columns(&mut self, icol1: usize, icol2: usize) { assert!(icol1 < self.ncols() && icol2 < self.ncols()); if icol1 != icol2 { // TODO: optimize that. for i in 0..self.nrows() { unsafe { self.swap_unchecked((i, icol1), (i, icol2)) } } } // Otherwise do nothing. } } /* * * TODO: specialize all the following for slices. * */ /// # Rows and columns removal impl> Matrix { /* * * Column removal. * */ /// Removes the `i`-th column from this matrix. #[inline] pub fn remove_column(self, i: usize) -> OMatrix> where C: DimSub, DefaultAllocator: Reallocator>, { self.remove_fixed_columns::<1>(i) } /// Removes all columns in `indices` #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_columns_at(self, indices: &[usize]) -> OMatrix where C: DimSub, DefaultAllocator: Reallocator, { let mut m = self.into_owned(); let (nrows, ncols) = m.shape_generic(); let mut offset: usize = 0; let mut target: usize = 0; while offset + target < ncols.value() { if indices.contains(&(target + offset)) { // Safety: the resulting pointer is within range. let col_ptr = unsafe { m.data.ptr_mut().add((target + offset) * nrows.value()) }; // Drop every element in the column we are about to overwrite. // We use the a similar technique as in `Vec::truncate`. let s = ptr::slice_from_raw_parts_mut(col_ptr, nrows.value()); // Safety: we drop the column in-place, which is OK because we will overwrite these // entries later in the loop, or discard them with the `reallocate_copy` // afterwards. unsafe { ptr::drop_in_place(s) }; offset += 1; } else { unsafe { let ptr_source = m.data.ptr().add((target + offset) * nrows.value()); let ptr_target = m.data.ptr_mut().add(target * nrows.value()); // Copy the data, overwriting what we dropped. ptr::copy(ptr_source, ptr_target, nrows.value()); target += 1; } } } // Safety: The new size is smaller than the old size, so // DefaultAllocator::reallocate_copy will initialize // every element of the new matrix which can then // be assumed to be initialized. unsafe { let new_data = DefaultAllocator::reallocate_copy( nrows, ncols.sub(Dyn::from_usize(offset)), m.data, ); Matrix::from_data(new_data).assume_init() } } /// Removes all rows in `indices` #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_rows_at(self, indices: &[usize]) -> OMatrix where R: DimSub, DefaultAllocator: Reallocator, { let mut m = self.into_owned(); let (nrows, ncols) = m.shape_generic(); let mut offset: usize = 0; let mut target: usize = 0; while offset + target < nrows.value() * ncols.value() { if indices.contains(&((target + offset) % nrows.value())) { // Safety: the resulting pointer is within range. unsafe { let elt_ptr = m.data.ptr_mut().add(target + offset); // Safety: we drop the component in-place, which is OK because we will overwrite these // entries later in the loop, or discard them with the `reallocate_copy` // afterwards. ptr::drop_in_place(elt_ptr) }; offset += 1; } else { unsafe { let ptr_source = m.data.ptr().add(target + offset); let ptr_target = m.data.ptr_mut().add(target); // Copy the data, overwriting what we dropped in the previous iterations. ptr::copy(ptr_source, ptr_target, 1); target += 1; } } } // Safety: The new size is smaller than the old size, so // DefaultAllocator::reallocate_copy will initialize // every element of the new matrix which can then // be assumed to be initialized. unsafe { let new_data = DefaultAllocator::reallocate_copy( nrows.sub(Dyn::from_usize(offset / ncols.value())), ncols, m.data, ); Matrix::from_data(new_data).assume_init() } } /// Removes `D::dim()` consecutive columns from this matrix, starting with the `i`-th /// (included). #[inline] pub fn remove_fixed_columns( self, i: usize, ) -> OMatrix>> where C: DimSub>, DefaultAllocator: Reallocator>>, { self.remove_columns_generic(i, Const::) } /// Removes `n` consecutive columns from this matrix, starting with the `i`-th (included). #[inline] #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_columns(self, i: usize, n: usize) -> OMatrix where C: DimSub, DefaultAllocator: Reallocator, { self.remove_columns_generic(i, Dyn(n)) } /// Removes `nremove.value()` columns from this matrix, starting with the `i`-th (included). /// /// This is the generic implementation of `.remove_columns(...)` and /// `.remove_fixed_columns(...)` which have nicer API interfaces. #[inline] pub fn remove_columns_generic(self, i: usize, nremove: D) -> OMatrix> where D: Dim, C: DimSub, DefaultAllocator: Reallocator>, { let mut m = self.into_owned(); let (nrows, ncols) = m.shape_generic(); assert!( i + nremove.value() <= ncols.value(), "Column index out of range." ); let need_column_shifts = nremove.value() != 0 && i + nremove.value() < ncols.value(); if need_column_shifts { // The first `deleted_i * nrows` are left untouched. let copied_value_start = i + nremove.value(); unsafe { let ptr_in = m.data.ptr().add(copied_value_start * nrows.value()); let ptr_out = m.data.ptr_mut().add(i * nrows.value()); // Drop all the elements of the columns we are about to overwrite. // We use the a similar technique as in `Vec::truncate`. let s = ptr::slice_from_raw_parts_mut(ptr_out, nremove.value() * nrows.value()); // Safety: we drop the column in-place, which is OK because we will overwrite these // entries with `ptr::copy` afterward. ptr::drop_in_place(s); ptr::copy( ptr_in, ptr_out, (ncols.value() - copied_value_start) * nrows.value(), ); } } else { // All the columns to remove are at the end of the buffer. Drop them. unsafe { let ptr = m.data.ptr_mut().add(i * nrows.value()); let s = ptr::slice_from_raw_parts_mut(ptr, nremove.value() * nrows.value()); ptr::drop_in_place(s) }; } // Safety: The new size is smaller than the old size, so // DefaultAllocator::reallocate_copy will initialize // every element of the new matrix which can then // be assumed to be initialized. unsafe { let new_data = DefaultAllocator::reallocate_copy(nrows, ncols.sub(nremove), m.data); Matrix::from_data(new_data).assume_init() } } /* * * Row removal. * */ /// Removes the `i`-th row from this matrix. #[inline] pub fn remove_row(self, i: usize) -> OMatrix, C> where R: DimSub, DefaultAllocator: Reallocator, C>, { self.remove_fixed_rows::<1>(i) } /// Removes `D::dim()` consecutive rows from this matrix, starting with the `i`-th (included). #[inline] pub fn remove_fixed_rows(self, i: usize) -> OMatrix>, C> where R: DimSub>, DefaultAllocator: Reallocator>, C>, { self.remove_rows_generic(i, Const::) } /// Removes `n` consecutive rows from this matrix, starting with the `i`-th (included). #[inline] #[cfg(any(feature = "std", feature = "alloc"))] pub fn remove_rows(self, i: usize, n: usize) -> OMatrix where R: DimSub, DefaultAllocator: Reallocator, { self.remove_rows_generic(i, Dyn(n)) } /// Removes `nremove.value()` rows from this matrix, starting with the `i`-th (included). /// /// This is the generic implementation of `.remove_rows(...)` and `.remove_fixed_rows(...)` /// which have nicer API interfaces. #[inline] pub fn remove_rows_generic(self, i: usize, nremove: D) -> OMatrix, C> where D: Dim, R: DimSub, DefaultAllocator: Reallocator, C>, { let mut m = self.into_owned(); let (nrows, ncols) = m.shape_generic(); assert!( i + nremove.value() <= nrows.value(), "Row index out of range." ); if nremove.value() != 0 { unsafe { compress_rows( m.as_mut_slice(), nrows.value(), ncols.value(), i, nremove.value(), ); } } // Safety: The new size is smaller than the old size, so // DefaultAllocator::reallocate_copy will initialize // every element of the new matrix which can then // be assumed to be initialized. unsafe { let new_data = DefaultAllocator::reallocate_copy(nrows.sub(nremove), ncols, m.data); Matrix::from_data(new_data).assume_init() } } } /// # Rows and columns insertion impl> Matrix { /* * * Columns insertion. * */ /// Inserts a column filled with `val` at the `i-th` position. #[inline] pub fn insert_column(self, i: usize, val: T) -> OMatrix> where C: DimAdd, DefaultAllocator: Reallocator>, { self.insert_fixed_columns::<1>(i, val) } /// Inserts `D` columns filled with `val` starting at the `i-th` position. #[inline] pub fn insert_fixed_columns( self, i: usize, val: T, ) -> OMatrix>> where C: DimAdd>, DefaultAllocator: Reallocator>>, { let mut res = unsafe { self.insert_columns_generic_uninitialized(i, Const::) }; res.fixed_columns_mut::(i) .fill_with(|| MaybeUninit::new(val.clone())); // Safety: the result is now fully initialized. The added columns have // been initialized by the `fill_with` above, and the rest have // been initialized by `insert_columns_generic_uninitialized`. unsafe { res.assume_init() } } /// Inserts `n` columns filled with `val` starting at the `i-th` position. #[inline] #[cfg(any(feature = "std", feature = "alloc"))] pub fn insert_columns(self, i: usize, n: usize, val: T) -> OMatrix where C: DimAdd, DefaultAllocator: Reallocator, { let mut res = unsafe { self.insert_columns_generic_uninitialized(i, Dyn(n)) }; res.columns_mut(i, n) .fill_with(|| MaybeUninit::new(val.clone())); // Safety: the result is now fully initialized. The added columns have // been initialized by the `fill_with` above, and the rest have // been initialized by `insert_columns_generic_uninitialized`. unsafe { res.assume_init() } } /// Inserts `ninsert.value()` columns starting at the `i-th` place of this matrix. /// /// # Safety /// The output matrix has all its elements initialized except for the the components of the /// added columns. #[inline] pub unsafe fn insert_columns_generic_uninitialized( self, i: usize, ninsert: D, ) -> UninitMatrix> where D: Dim, C: DimAdd, DefaultAllocator: Reallocator>, { let m = self.into_owned(); let (nrows, ncols) = m.shape_generic(); let mut res = Matrix::from_data(DefaultAllocator::reallocate_copy( nrows, ncols.add(ninsert), m.data, )); assert!(i <= ncols.value(), "Column insertion index out of range."); if ninsert.value() != 0 && i != ncols.value() { let ptr_in = res.data.ptr().add(i * nrows.value()); let ptr_out = res .data .ptr_mut() .add((i + ninsert.value()) * nrows.value()); ptr::copy(ptr_in, ptr_out, (ncols.value() - i) * nrows.value()) } res } /* * * Rows insertion. * */ /// Inserts a row filled with `val` at the `i-th` position. #[inline] pub fn insert_row(self, i: usize, val: T) -> OMatrix, C> where R: DimAdd, DefaultAllocator: Reallocator, C>, { self.insert_fixed_rows::<1>(i, val) } /// Inserts `D::dim()` rows filled with `val` starting at the `i-th` position. #[inline] pub fn insert_fixed_rows( self, i: usize, val: T, ) -> OMatrix>, C> where R: DimAdd>, DefaultAllocator: Reallocator>, C>, { let mut res = unsafe { self.insert_rows_generic_uninitialized(i, Const::) }; res.fixed_rows_mut::(i) .fill_with(|| MaybeUninit::new(val.clone())); // Safety: the result is now fully initialized. The added rows have // been initialized by the `fill_with` above, and the rest have // been initialized by `insert_rows_generic_uninitialized`. unsafe { res.assume_init() } } /// Inserts `n` rows filled with `val` starting at the `i-th` position. #[inline] #[cfg(any(feature = "std", feature = "alloc"))] pub fn insert_rows(self, i: usize, n: usize, val: T) -> OMatrix where R: DimAdd, DefaultAllocator: Reallocator, { let mut res = unsafe { self.insert_rows_generic_uninitialized(i, Dyn(n)) }; res.rows_mut(i, n) .fill_with(|| MaybeUninit::new(val.clone())); // Safety: the result is now fully initialized. The added rows have // been initialized by the `fill_with` above, and the rest have // been initialized by `insert_rows_generic_uninitialized`. unsafe { res.assume_init() } } /// Inserts `ninsert.value()` rows at the `i-th` place of this matrix. /// /// # Safety /// The added rows values are not initialized. /// This is the generic implementation of `.insert_rows(...)` and /// `.insert_fixed_rows(...)` which have nicer API interfaces. #[inline] pub unsafe fn insert_rows_generic_uninitialized( self, i: usize, ninsert: D, ) -> UninitMatrix, C> where D: Dim, R: DimAdd, DefaultAllocator: Reallocator, C>, { let m = self.into_owned(); let (nrows, ncols) = m.shape_generic(); let mut res = Matrix::from_data(DefaultAllocator::reallocate_copy( nrows.add(ninsert), ncols, m.data, )); assert!(i <= nrows.value(), "Row insertion index out of range."); if ninsert.value() != 0 { extend_rows( res.as_mut_slice(), nrows.value(), ncols.value(), i, ninsert.value(), ); } res } } /// # Resizing and reshaping impl> Matrix { /// Resizes this matrix so that it contains `new_nrows` rows and `new_ncols` columns. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// rows and/or columns than `self`, then the extra rows or columns are filled with `val`. #[cfg(any(feature = "std", feature = "alloc"))] pub fn resize(self, new_nrows: usize, new_ncols: usize, val: T) -> OMatrix where DefaultAllocator: Reallocator, { self.resize_generic(Dyn(new_nrows), Dyn(new_ncols), val) } /// Resizes this matrix vertically, i.e., so that it contains `new_nrows` rows while keeping the same number of columns. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// rows than `self`, then the extra rows are filled with `val`. #[cfg(any(feature = "std", feature = "alloc"))] pub fn resize_vertically(self, new_nrows: usize, val: T) -> OMatrix where DefaultAllocator: Reallocator, { let ncols = self.shape_generic().1; self.resize_generic(Dyn(new_nrows), ncols, val) } /// Resizes this matrix horizontally, i.e., so that it contains `new_ncolumns` columns while keeping the same number of columns. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// columns than `self`, then the extra columns are filled with `val`. #[cfg(any(feature = "std", feature = "alloc"))] pub fn resize_horizontally(self, new_ncols: usize, val: T) -> OMatrix where DefaultAllocator: Reallocator, { let nrows = self.shape_generic().0; self.resize_generic(nrows, Dyn(new_ncols), val) } /// Resizes this matrix so that it contains `R2::value()` rows and `C2::value()` columns. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// rows and/or columns than `self`, then the extra rows or columns are filled with `val`. pub fn fixed_resize( self, val: T, ) -> OMatrix, Const> where DefaultAllocator: Reallocator, Const>, { self.resize_generic(Const::, Const::, val) } /// Resizes `self` such that it has dimensions `new_nrows × new_ncols`. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// rows and/or columns than `self`, then the extra rows or columns are filled with `val`. #[inline] pub fn resize_generic( self, new_nrows: R2, new_ncols: C2, val: T, ) -> OMatrix where DefaultAllocator: Reallocator, { let (nrows, ncols) = self.shape(); let mut data = self.into_owned(); if new_nrows.value() == nrows { if new_ncols.value() < ncols { unsafe { let num_cols_to_delete = ncols - new_ncols.value(); let col_ptr = data.data.ptr_mut().add(new_ncols.value() * nrows); let s = ptr::slice_from_raw_parts_mut(col_ptr, num_cols_to_delete * nrows); // Safety: drop the elements of the deleted columns. // these are the elements that will be truncated // by the `reallocate_copy` afterward. ptr::drop_in_place(s) }; } let res = unsafe { DefaultAllocator::reallocate_copy(new_nrows, new_ncols, data.data) }; let mut res = Matrix::from_data(res); if new_ncols.value() > ncols { res.columns_range_mut(ncols..) .fill_with(|| MaybeUninit::new(val.clone())); } // Safety: the result is now fully initialized by `reallocate_copy` and // `fill_with` (if the output has more columns than the input). unsafe { res.assume_init() } } else { let mut res; unsafe { if new_nrows.value() < nrows { compress_rows( data.as_mut_slice(), nrows, ncols, new_nrows.value(), nrows - new_nrows.value(), ); res = Matrix::from_data(DefaultAllocator::reallocate_copy( new_nrows, new_ncols, data.data, )); } else { res = Matrix::from_data(DefaultAllocator::reallocate_copy( new_nrows, new_ncols, data.data, )); extend_rows( res.as_mut_slice(), nrows, new_ncols.value(), nrows, new_nrows.value() - nrows, ); } } if new_ncols.value() > ncols { res.columns_range_mut(ncols..) .fill_with(|| MaybeUninit::new(val.clone())); } if new_nrows.value() > nrows { res.view_range_mut(nrows.., ..cmp::min(ncols, new_ncols.value())) .fill_with(|| MaybeUninit::new(val.clone())); } // Safety: the result is now fully initialized by `reallocate_copy` and // `fill_with` (whenever applicable). unsafe { res.assume_init() } } } /// Reshapes `self` such that it has dimensions `new_nrows × new_ncols`. /// /// This will reinterpret `self` as if it is a matrix with `new_nrows` rows and `new_ncols` /// columns. The arrangements of the component in the output matrix are the same as what /// would be obtained by `Matrix::from_slice_generic(self.as_slice(), new_nrows, new_ncols)`. /// /// If `self` is a dynamically-sized matrix, then its components are neither copied nor moved. /// If `self` is staticyll-sized, then a copy may happen in some situations. /// This function will panic if the given dimensions are such that the number of elements of /// the input matrix are not equal to the number of elements of the output matrix. /// /// # Examples /// /// ``` /// # use nalgebra::{Matrix3x2, Matrix2x3, DMatrix, Const, Dyn}; /// /// let m1 = Matrix2x3::new( /// 1.1, 1.2, 1.3, /// 2.1, 2.2, 2.3 /// ); /// let m2 = Matrix3x2::new( /// 1.1, 2.2, /// 2.1, 1.3, /// 1.2, 2.3 /// ); /// let reshaped = m1.reshape_generic(Const::<3>, Const::<2>); /// assert_eq!(reshaped, m2); /// /// let dm1 = DMatrix::from_row_slice( /// 4, /// 3, /// &[ /// 1.0, 0.0, 0.0, /// 0.0, 0.0, 1.0, /// 0.0, 0.0, 0.0, /// 0.0, 1.0, 0.0 /// ], /// ); /// let dm2 = DMatrix::from_row_slice( /// 6, /// 2, /// &[ /// 1.0, 0.0, /// 0.0, 1.0, /// 0.0, 0.0, /// 0.0, 1.0, /// 0.0, 0.0, /// 0.0, 0.0, /// ], /// ); /// let reshaped = dm1.reshape_generic(Dyn(6), Dyn(2)); /// assert_eq!(reshaped, dm2); /// ``` pub fn reshape_generic( self, new_nrows: R2, new_ncols: C2, ) -> Matrix where R2: Dim, C2: Dim, S: ReshapableStorage, { let data = self.data.reshape_generic(new_nrows, new_ncols); Matrix::from_data(data) } } /// # In-place resizing #[cfg(any(feature = "std", feature = "alloc"))] impl OMatrix { /// Resizes this matrix in-place. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// rows and/or columns than `self`, then the extra rows or columns are filled with `val`. /// /// Defined only for owned fully-dynamic matrices, i.e., `DMatrix`. pub fn resize_mut(&mut self, new_nrows: usize, new_ncols: usize, val: T) where DefaultAllocator: Reallocator, { // TODO: avoid the clone. *self = self.clone().resize(new_nrows, new_ncols, val); } } #[cfg(any(feature = "std", feature = "alloc"))] impl OMatrix where DefaultAllocator: Allocator, { /// Changes the number of rows of this matrix in-place. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// rows than `self`, then the extra rows are filled with `val`. /// /// Defined only for owned matrices with a dynamic number of rows (for example, `DVector`). #[cfg(any(feature = "std", feature = "alloc"))] pub fn resize_vertically_mut(&mut self, new_nrows: usize, val: T) where DefaultAllocator: Reallocator, { // TODO: avoid the clone. *self = self.clone().resize_vertically(new_nrows, val); } } #[cfg(any(feature = "std", feature = "alloc"))] impl OMatrix where DefaultAllocator: Allocator, { /// Changes the number of column of this matrix in-place. /// /// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more /// columns than `self`, then the extra columns are filled with `val`. /// /// Defined only for owned matrices with a dynamic number of columns (for example, `DVector`). #[cfg(any(feature = "std", feature = "alloc"))] pub fn resize_horizontally_mut(&mut self, new_ncols: usize, val: T) where DefaultAllocator: Reallocator, { // TODO: avoid the clone. *self = self.clone().resize_horizontally(new_ncols, val); } } // Move the elements of `data` in such a way that the matrix with // the rows `[i, i + nremove[` deleted is represented in a contiguous // way in `data` after this method completes. // Every deleted element are manually dropped by this method. unsafe fn compress_rows( data: &mut [T], nrows: usize, ncols: usize, i: usize, nremove: usize, ) { let new_nrows = nrows - nremove; if nremove == 0 { return; // Nothing to remove or drop. } if new_nrows == 0 || ncols == 0 { // The output matrix is empty, drop everything. ptr::drop_in_place(data); return; } // Safety: because `nremove != 0`, the pointers given to `ptr::copy` // won’t alias. let ptr_in = data.as_ptr(); let ptr_out = data.as_mut_ptr(); let mut curr_i = i; for k in 0..ncols - 1 { // Safety: we drop the row elements in-place because we will overwrite these // entries later with the `ptr::copy`. let s = ptr::slice_from_raw_parts_mut(ptr_out.add(curr_i), nremove); ptr::drop_in_place(s); ptr::copy( ptr_in.add(curr_i + (k + 1) * nremove), ptr_out.add(curr_i), new_nrows, ); curr_i += new_nrows; } /* * Deal with the last column from which less values have to be copied. */ // Safety: we drop the row elements in-place because we will overwrite these // entries later with the `ptr::copy`. let s = ptr::slice_from_raw_parts_mut(ptr_out.add(curr_i), nremove); ptr::drop_in_place(s); let remaining_len = nrows - i - nremove; ptr::copy( ptr_in.add(nrows * ncols - remaining_len), ptr_out.add(curr_i), remaining_len, ); } // Moves entries of a matrix buffer to make place for `ninsert` empty rows starting at the `i-th` row index. // The `data` buffer is assumed to contained at least `(nrows + ninsert) * ncols` elements. unsafe fn extend_rows(data: &mut [T], nrows: usize, ncols: usize, i: usize, ninsert: usize) { let new_nrows = nrows + ninsert; if new_nrows == 0 || ncols == 0 { return; // Nothing to do as the output matrix is empty. } let ptr_in = data.as_ptr(); let ptr_out = data.as_mut_ptr(); let remaining_len = nrows - i; let mut curr_i = new_nrows * ncols - remaining_len; // Deal with the last column from which less values have to be copied. ptr::copy( ptr_in.add(nrows * ncols - remaining_len), ptr_out.add(curr_i), remaining_len, ); for k in (0..ncols - 1).rev() { curr_i -= new_nrows; ptr::copy(ptr_in.add(k * nrows + i), ptr_out.add(curr_i), nrows); } } /// Extend the number of columns of the `Matrix` with elements from /// a given iterator. #[cfg(any(feature = "std", feature = "alloc"))] impl Extend for Matrix where T: Scalar, R: Dim, S: Extend, { /// Extend the number of columns of the `Matrix` with elements /// from the given iterator. /// /// # Example /// ``` /// # use nalgebra::{DMatrix, Dyn, Matrix, OMatrix, Matrix3}; /// /// let data = vec![0, 1, 2, // column 1 /// 3, 4, 5]; // column 2 /// /// let mut matrix = DMatrix::from_vec(3, 2, data); /// /// matrix.extend(vec![6, 7, 8]); // column 3 /// /// assert!(matrix.eq(&Matrix3::new(0, 3, 6, /// 1, 4, 7, /// 2, 5, 8))); /// ``` /// /// # Panics /// This function panics if the number of elements yielded by the /// given iterator is not a multiple of the number of rows of the /// `Matrix`. /// /// ```should_panic /// # use nalgebra::{DMatrix, Dyn, OMatrix}; /// let data = vec![0, 1, 2, // column 1 /// 3, 4, 5]; // column 2 /// /// let mut matrix = DMatrix::from_vec(3, 2, data); /// /// // The following panics because the vec length is not a multiple of 3. /// matrix.extend(vec![6, 7, 8, 9]); /// ``` fn extend>(&mut self, iter: I) { self.data.extend(iter); } } /// Extend the number of rows of the `Vector` with elements from /// a given iterator. #[cfg(any(feature = "std", feature = "alloc"))] impl Extend for Matrix where T: Scalar, S: Extend, { /// Extend the number of rows of a `Vector` with elements /// from the given iterator. /// /// # Example /// ``` /// # use nalgebra::DVector; /// let mut vector = DVector::from_vec(vec![0, 1, 2]); /// vector.extend(vec![3, 4, 5]); /// assert!(vector.eq(&DVector::from_vec(vec![0, 1, 2, 3, 4, 5]))); /// ``` fn extend>(&mut self, iter: I) { self.data.extend(iter); } } #[cfg(any(feature = "std", feature = "alloc"))] impl Extend> for Matrix where T: Scalar, R: Dim, S: Extend>, RV: Dim, SV: RawStorage, ShapeConstraint: SameNumberOfRows, { /// Extends the number of columns of a `Matrix` with `Vector`s /// from a given iterator. /// /// # Example /// ``` /// # use nalgebra::{DMatrix, Vector3, Matrix3x4}; /// /// let data = vec![0, 1, 2, // column 1 /// 3, 4, 5]; // column 2 /// /// let mut matrix = DMatrix::from_vec(3, 2, data); /// /// matrix.extend( /// vec![Vector3::new(6, 7, 8), // column 3 /// Vector3::new(9, 10, 11)]); // column 4 /// /// assert!(matrix.eq(&Matrix3x4::new(0, 3, 6, 9, /// 1, 4, 7, 10, /// 2, 5, 8, 11))); /// ``` /// /// # Panics /// This function panics if the dimension of each `Vector` yielded /// by the given iterator is not equal to the number of rows of /// this `Matrix`. /// /// ```should_panic /// # use nalgebra::{DMatrix, Vector2, Matrix3x4}; /// let mut matrix = /// DMatrix::from_vec(3, 2, /// vec![0, 1, 2, // column 1 /// 3, 4, 5]); // column 2 /// /// // The following panics because this matrix can only be extended with 3-dimensional vectors. /// matrix.extend( /// vec![Vector2::new(6, 7)]); // too few dimensions! /// ``` /// /// ```should_panic /// # use nalgebra::{DMatrix, Vector4, Matrix3x4}; /// let mut matrix = /// DMatrix::from_vec(3, 2, /// vec![0, 1, 2, // column 1 /// 3, 4, 5]); // column 2 /// /// // The following panics because this matrix can only be extended with 3-dimensional vectors. /// matrix.extend( /// vec![Vector4::new(6, 7, 8, 9)]); // too few dimensions! /// ``` fn extend>>(&mut self, iter: I) { self.data.extend(iter); } } nalgebra-0.33.0/src/base/helper.rs000064400000000000000000000013620072674642500150420ustar 00000000000000#[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; /// Simple helper function for rejection sampling #[cfg(feature = "arbitrary")] #[doc(hidden)] #[inline] pub fn reject bool, T: Arbitrary>(g: &mut Gen, f: F) -> T { use std::iter; iter::repeat(()) .map(|_| Arbitrary::arbitrary(g)) .find(f) .unwrap() } #[doc(hidden)] #[inline] #[cfg(feature = "rand-no-std")] pub fn reject_rand bool, T>(g: &mut G, f: F) -> T where Standard: Distribution, { use std::iter; iter::repeat(()).map(|_| g.gen()).find(f).unwrap() } nalgebra-0.33.0/src/base/indexing.rs000064400000000000000000000537700072674642500154020ustar 00000000000000//! Indexing #![allow(clippy::reversed_empty_ranges)] use crate::base::storage::{RawStorage, RawStorageMut}; use crate::base::{ Const, Dim, DimDiff, DimName, DimSub, Dyn, Matrix, MatrixView, MatrixViewMut, Scalar, U1, }; use std::ops; // N.B.: Not a public trait! trait DimRange { /// The number of elements indexed by this range. type Length: Dim; /// The lower bound of the range, inclusive. fn lower(&self, dimension: D) -> usize; /// The number of elements included in the range. fn length(&self, dimension: D) -> Self::Length; /// Produces true if `Self` is contained within `dimension`. fn contained_by(&self, dimension: D) -> bool; } impl DimRange for usize { type Length = U1; #[inline(always)] fn lower(&self, _: D) -> usize { *self } #[inline(always)] fn length(&self, _: D) -> Self::Length { Const::<1> } #[inline(always)] fn contained_by(&self, dimension: D) -> bool { *self < dimension.value() } } #[test] fn dimrange_usize() { assert!(!DimRange::contained_by(&0, Const::<0>)); assert!(DimRange::contained_by(&0, Const::<1>)); } impl DimRange for ops::Range { type Length = Dyn; #[inline(always)] fn lower(&self, _: D) -> usize { self.start } #[inline(always)] fn length(&self, _: D) -> Self::Length { Dyn(self.end.saturating_sub(self.start)) } #[inline(always)] fn contained_by(&self, dimension: D) -> bool { (self.start < dimension.value()) && (self.end <= dimension.value()) } } #[test] fn dimrange_range_usize() { assert!(!DimRange::contained_by(&(0..0), Const::<0>)); assert!(!DimRange::contained_by(&(0..1), Const::<0>)); assert!(DimRange::contained_by(&(0..1), Const::<1>)); assert!(DimRange::contained_by( &((usize::MAX - 1)..usize::MAX), Dyn(usize::MAX) )); assert_eq!( DimRange::length(&((usize::MAX - 1)..usize::MAX), Dyn(usize::MAX)), Dyn(1) ); assert_eq!( DimRange::length(&(usize::MAX..(usize::MAX - 1)), Dyn(usize::MAX)), Dyn(0) ); assert_eq!( DimRange::length(&(usize::MAX..usize::MAX), Dyn(usize::MAX)), Dyn(0) ); } impl DimRange for ops::RangeFrom { type Length = Dyn; #[inline(always)] fn lower(&self, _: D) -> usize { self.start } #[inline(always)] fn length(&self, dimension: D) -> Self::Length { (self.start..dimension.value()).length(dimension) } #[inline(always)] fn contained_by(&self, dimension: D) -> bool { self.start < dimension.value() } } #[test] fn dimrange_rangefrom_usize() { assert!(!DimRange::contained_by(&(0..), Const::<0>)); assert!(!DimRange::contained_by(&(0..), Const::<0>)); assert!(DimRange::contained_by(&(0..), Const::<1>)); assert!(DimRange::contained_by( &((usize::MAX - 1)..), Dyn(usize::MAX) )); assert_eq!( DimRange::length(&((usize::MAX - 1)..), Dyn(usize::MAX)), Dyn(1) ); assert_eq!(DimRange::length(&(usize::MAX..), Dyn(usize::MAX)), Dyn(0)); } impl DimRange for ops::RangeFrom where D: DimSub, { type Length = DimDiff; #[inline(always)] fn lower(&self, _: D) -> usize { self.start.value() } #[inline(always)] fn length(&self, dimension: D) -> Self::Length { dimension.sub(self.start) } #[inline(always)] fn contained_by(&self, _: D) -> bool { true } } #[test] fn dimrange_rangefrom_dimname() { assert_eq!(DimRange::length(&(Const::<1>..), Const::<5>), Const::<4>); } impl DimRange for ops::RangeFull { type Length = D; #[inline(always)] fn lower(&self, _: D) -> usize { 0 } #[inline(always)] fn length(&self, dimension: D) -> Self::Length { dimension } #[inline(always)] fn contained_by(&self, _: D) -> bool { true } } #[test] fn dimrange_rangefull() { assert!(DimRange::contained_by(&(..), Const::<0>)); assert_eq!(DimRange::length(&(..), Const::<1>), Const::<1>); } impl DimRange for ops::RangeInclusive { type Length = Dyn; #[inline(always)] fn lower(&self, _: D) -> usize { *self.start() } #[inline(always)] fn length(&self, _: D) -> Self::Length { Dyn(if self.end() < self.start() { 0 } else { self.end().wrapping_sub(self.start().wrapping_sub(1)) }) } #[inline(always)] fn contained_by(&self, dimension: D) -> bool { (*self.start() < dimension.value()) && (*self.end() < dimension.value()) } } #[test] fn dimrange_rangeinclusive_usize() { assert!(!DimRange::contained_by(&(0..=0), Const::<0>)); assert!(DimRange::contained_by(&(0..=0), Const::<1>)); assert!(!DimRange::contained_by( &(usize::MAX..=usize::MAX), Dyn(usize::MAX) )); assert!(!DimRange::contained_by( &((usize::MAX - 1)..=usize::MAX), Dyn(usize::MAX) )); assert!(DimRange::contained_by( &((usize::MAX - 1)..=(usize::MAX - 1)), Dyn(usize::MAX) )); assert_eq!(DimRange::length(&(0..=0), Const::<1>), Dyn(1)); assert_eq!( DimRange::length(&((usize::MAX - 1)..=usize::MAX), Dyn(usize::MAX)), Dyn(2) ); assert_eq!( DimRange::length(&(usize::MAX..=(usize::MAX - 1)), Dyn(usize::MAX)), Dyn(0) ); assert_eq!( DimRange::length(&(usize::MAX..=usize::MAX), Dyn(usize::MAX)), Dyn(1) ); } impl DimRange for ops::RangeTo { type Length = Dyn; #[inline(always)] fn lower(&self, _: D) -> usize { 0 } #[inline(always)] fn length(&self, _: D) -> Self::Length { Dyn(self.end) } #[inline(always)] fn contained_by(&self, dimension: D) -> bool { self.end <= dimension.value() } } #[test] fn dimrange_rangeto_usize() { assert!(DimRange::contained_by(&(..0), Const::<0>)); assert!(!DimRange::contained_by(&(..1), Const::<0>)); assert!(DimRange::contained_by(&(..0), Const::<1>)); assert!(DimRange::contained_by( &(..(usize::MAX - 1)), Dyn(usize::MAX) )); assert_eq!( DimRange::length(&(..(usize::MAX - 1)), Dyn(usize::MAX)), Dyn(usize::MAX - 1) ); assert_eq!( DimRange::length(&(..usize::MAX), Dyn(usize::MAX)), Dyn(usize::MAX) ); } impl DimRange for ops::RangeToInclusive { type Length = Dyn; #[inline(always)] fn lower(&self, _: D) -> usize { 0 } #[inline(always)] fn length(&self, _: D) -> Self::Length { Dyn(self.end + 1) } #[inline(always)] fn contained_by(&self, dimension: D) -> bool { self.end < dimension.value() } } #[test] fn dimrange_rangetoinclusive_usize() { assert!(!DimRange::contained_by(&(..=0), Const::<0>)); assert!(!DimRange::contained_by(&(..=1), Const::<0>)); assert!(DimRange::contained_by(&(..=0), Const::<1>)); assert!(!DimRange::contained_by(&(..=(usize::MAX)), Dyn(usize::MAX))); assert!(DimRange::contained_by( &(..=(usize::MAX - 1)), Dyn(usize::MAX) )); assert_eq!( DimRange::length(&(..=(usize::MAX - 1)), Dyn(usize::MAX)), Dyn(usize::MAX) ); } /// A helper trait used for indexing operations. pub trait MatrixIndex<'a, T, R: Dim, C: Dim, S: RawStorage>: Sized { /// The output type returned by methods. type Output: 'a; /// Produces true if the given matrix is contained by this index. #[doc(hidden)] fn contained_by(&self, matrix: &Matrix) -> bool; /// Produces a shared view of the data at this location if in bounds, /// or `None`, otherwise. #[doc(hidden)] #[inline(always)] fn get(self, matrix: &'a Matrix) -> Option { if self.contained_by(matrix) { Some(unsafe { self.get_unchecked(matrix) }) } else { None } } /// Produces a shared view of the data at this location if in bounds /// without any bounds checking. #[doc(hidden)] unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output; /// Produces a shared view to the data at this location, or panics /// if out of bounds. #[doc(hidden)] #[inline(always)] fn index(self, matrix: &'a Matrix) -> Self::Output { self.get(matrix).expect("Index out of bounds.") } } /// A helper trait used for indexing operations. pub trait MatrixIndexMut<'a, T, R: Dim, C: Dim, S: RawStorageMut>: MatrixIndex<'a, T, R, C, S> { /// The output type returned by methods. type OutputMut: 'a; /// Produces a mutable view of the data at this location, without /// performing any bounds checking. #[doc(hidden)] unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut; /// Produces a mutable view of the data at this location, if in /// bounds. #[doc(hidden)] #[inline(always)] fn get_mut(self, matrix: &'a mut Matrix) -> Option { if self.contained_by(matrix) { Some(unsafe { self.get_unchecked_mut(matrix) }) } else { None } } /// Produces a mutable view of the data at this location, or panics /// if out of bounds. #[doc(hidden)] #[inline(always)] fn index_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut { self.get_mut(matrix).expect("Index out of bounds.") } } /// # Views based on ranges /// ## Indices to Individual Elements /// ### Two-Dimensional Indices /// ``` /// # use nalgebra::*; /// let matrix = Matrix2::new(0, 2, /// 1, 3); /// /// assert_eq!(matrix.index((0, 0)), &0); /// assert_eq!(matrix.index((1, 0)), &1); /// assert_eq!(matrix.index((0, 1)), &2); /// assert_eq!(matrix.index((1, 1)), &3); /// ``` /// /// ### Linear Address Indexing /// ``` /// # use nalgebra::*; /// let matrix = Matrix2::new(0, 2, /// 1, 3); /// /// assert_eq!(matrix.get(0), Some(&0)); /// assert_eq!(matrix.get(1), Some(&1)); /// assert_eq!(matrix.get(2), Some(&2)); /// assert_eq!(matrix.get(3), Some(&3)); /// ``` /// /// ## Indices to Individual Rows and Columns /// ### Index to a Row /// ``` /// # use nalgebra::*; /// let matrix = Matrix2::new(0, 2, /// 1, 3); /// /// assert!(matrix.index((0, ..)) /// .eq(&Matrix1x2::new(0, 2))); /// ``` /// /// ### Index to a Column /// ``` /// # use nalgebra::*; /// let matrix = Matrix2::new(0, 2, /// 1, 3); /// /// assert!(matrix.index((.., 0)) /// .eq(&Matrix2x1::new(0, /// 1))); /// ``` /// /// ## Indices to Parts of Individual Rows and Columns /// ### Index to a Partial Row /// ``` /// # use nalgebra::*; /// let matrix = Matrix3::new(0, 3, 6, /// 1, 4, 7, /// 2, 5, 8); /// /// assert!(matrix.index((0, ..2)) /// .eq(&Matrix1x2::new(0, 3))); /// ``` /// /// ### Index to a Partial Column /// ``` /// # use nalgebra::*; /// let matrix = Matrix3::new(0, 3, 6, /// 1, 4, 7, /// 2, 5, 8); /// /// assert!(matrix.index((..2, 0)) /// .eq(&Matrix2x1::new(0, /// 1))); /// /// assert!(matrix.index((Const::<1>.., 0)) /// .eq(&Matrix2x1::new(1, /// 2))); /// ``` /// ## Indices to Ranges of Rows and Columns /// ### Index to a Range of Rows /// ``` /// # use nalgebra::*; /// let matrix = Matrix3::new(0, 3, 6, /// 1, 4, 7, /// 2, 5, 8); /// /// assert!(matrix.index((1..3, ..)) /// .eq(&Matrix2x3::new(1, 4, 7, /// 2, 5, 8))); /// ``` /// ### Index to a Range of Columns /// ``` /// # use nalgebra::*; /// let matrix = Matrix3::new(0, 3, 6, /// 1, 4, 7, /// 2, 5, 8); /// /// assert!(matrix.index((.., 1..3)) /// .eq(&Matrix3x2::new(3, 6, /// 4, 7, /// 5, 8))); /// ``` impl> Matrix { /// Produces a view of the data at the given index, or /// `None` if the index is out of bounds. #[inline] #[must_use] pub fn get<'a, I>(&'a self, index: I) -> Option where I: MatrixIndex<'a, T, R, C, S>, { index.get(self) } /// Produces a mutable view of the data at the given index, or /// `None` if the index is out of bounds. #[inline] #[must_use] pub fn get_mut<'a, I>(&'a mut self, index: I) -> Option where S: RawStorageMut, I: MatrixIndexMut<'a, T, R, C, S>, { index.get_mut(self) } /// Produces a view of the data at the given index, or /// panics if the index is out of bounds. #[inline] #[must_use] pub fn index<'a, I>(&'a self, index: I) -> I::Output where I: MatrixIndex<'a, T, R, C, S>, { index.index(self) } /// Produces a mutable view of the data at the given index, or /// panics if the index is out of bounds. #[inline] pub fn index_mut<'a, I>(&'a mut self, index: I) -> I::OutputMut where S: RawStorageMut, I: MatrixIndexMut<'a, T, R, C, S>, { index.index_mut(self) } /// 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 where I: MatrixIndex<'a, T, R, C, S>, { index.get_unchecked(self) } /// 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 where S: RawStorageMut, I: MatrixIndexMut<'a, T, R, C, S>, { index.get_unchecked_mut(self) } } // EXTRACT A SINGLE ELEMENT BY 1D LINEAR ADDRESS impl<'a, T, R, C, S> MatrixIndex<'a, T, R, C, S> for usize where T: Scalar, R: Dim, C: Dim, S: RawStorage, { type Output = &'a T; #[doc(hidden)] #[inline(always)] fn contained_by(&self, matrix: &Matrix) -> bool { *self < matrix.len() } #[doc(hidden)] #[inline(always)] unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { let nrows = matrix.shape().0; let row = self % nrows; let col = self / nrows; matrix.data.get_unchecked(row, col) } } impl<'a, T, R, C, S> MatrixIndexMut<'a, T, R, C, S> for usize where T: Scalar, R: Dim, C: Dim, S: RawStorageMut, { type OutputMut = &'a mut T; #[doc(hidden)] #[inline(always)] unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut where S: RawStorageMut, { let nrows = matrix.shape().0; let row = self % nrows; let col = self / nrows; matrix.data.get_unchecked_mut(row, col) } } // EXTRACT A SINGLE ELEMENT BY 2D COORDINATES impl<'a, T: 'a, R, C, S> MatrixIndex<'a, T, R, C, S> for (usize, usize) where R: Dim, C: Dim, S: RawStorage, { type Output = &'a T; #[doc(hidden)] #[inline(always)] fn contained_by(&self, matrix: &Matrix) -> bool { let (rows, cols) = self; let (nrows, ncols) = matrix.shape_generic(); DimRange::contained_by(rows, nrows) && DimRange::contained_by(cols, ncols) } #[doc(hidden)] #[inline(always)] unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { let (row, col) = self; matrix.data.get_unchecked(row, col) } } impl<'a, T: 'a, R, C, S> MatrixIndexMut<'a, T, R, C, S> for (usize, usize) where R: Dim, C: Dim, S: RawStorageMut, { type OutputMut = &'a mut T; #[doc(hidden)] #[inline(always)] unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut where S: RawStorageMut, { let (row, col) = self; matrix.data.get_unchecked_mut(row, col) } } macro_rules! impl_index_pair { ( $R: ident, $C: ident, [<$($RTyP: ident : $RTyPB: ty,)*> usize => $ROut: ty $(where $RConstraintType: ty: $RConstraintBound: ident<$($RConstraintBoundParams: ty $( = $REqBound: ty )*),*>)*], [<$($CTyP: ident : $CTyPB: ty,)*> usize => $COut: ty $(where $CConstraintType: ty: $CConstraintBound: ident<$($CConstraintBoundParams: ty $( = $CEqBound: ty )*),*>)*] ) => {}; ( $R: ident, $C: ident, [<$($RTyP: ident: $RTyPB: tt),*> $RIdx: ty => $ROut: ty $(where $RConstraintType: ty: $RConstraintBound: ident $(<$($RConstraintBoundParams: ty $( = $REqBound: ty )*),*>)* )*], [<$($CTyP: ident: $CTyPB: tt),*> $CIdx: ty => $COut: ty $(where $CConstraintType: ty: $CConstraintBound: ident $(<$($CConstraintBoundParams: ty $( = $CEqBound: ty )*),*>)* )*] ) => { impl<'a, T, $R, $C, S, $($RTyP : $RTyPB,)* $($CTyP : $CTyPB),*> MatrixIndex<'a, T, $R, $C, S> for ($RIdx, $CIdx) where T: Scalar, $R: Dim, $C: Dim, S: RawStorage, $( $RConstraintType: $RConstraintBound $(<$( $RConstraintBoundParams $( = $REqBound )*),*>)* ,)* $( $CConstraintType: $CConstraintBound $(<$( $CConstraintBoundParams $( = $CEqBound )*),*>)* ),* { type Output = MatrixView<'a, T, $ROut, $COut, S::RStride, S::CStride>; #[doc(hidden)] #[inline(always)] fn contained_by(&self, matrix: &Matrix) -> bool { let (rows, cols) = self; let (nrows, ncols) = matrix.shape_generic(); DimRange::contained_by(rows, nrows) && DimRange::contained_by(cols, ncols) } #[doc(hidden)] #[inline(always)] unsafe fn get_unchecked(self, matrix: &'a Matrix) -> Self::Output { use crate::base::ViewStorage; let (rows, cols) = self; let (nrows, ncols) = matrix.shape_generic(); let data = ViewStorage::new_unchecked(&matrix.data, (rows.lower(nrows), cols.lower(ncols)), (rows.length(nrows), cols.length(ncols))); Matrix::from_data_statically_unchecked(data) } } impl<'a, T, $R, $C, S, $($RTyP : $RTyPB,)* $($CTyP : $CTyPB),*> MatrixIndexMut<'a, T, $R, $C, S> for ($RIdx, $CIdx) where T: Scalar, $R: Dim, $C: Dim, S: RawStorageMut, $( $RConstraintType: $RConstraintBound $(<$( $RConstraintBoundParams $( = $REqBound )*),*>)* ,)* $( $CConstraintType: $CConstraintBound $(<$( $CConstraintBoundParams $( = $CEqBound )*),*>)* ),* { type OutputMut = MatrixViewMut<'a, T, $ROut, $COut, S::RStride, S::CStride>; #[doc(hidden)] #[inline(always)] unsafe fn get_unchecked_mut(self, matrix: &'a mut Matrix) -> Self::OutputMut { use crate::base::ViewStorageMut; let (rows, cols) = self; let (nrows, ncols) = matrix.shape_generic(); let data = ViewStorageMut::new_unchecked(&mut matrix.data, (rows.lower(nrows), cols.lower(ncols)), (rows.length(nrows), cols.length(ncols))); Matrix::from_data_statically_unchecked(data) } } } } macro_rules! impl_index_pairs { (index $R: ident with {} index $C: ident with {$($r: tt,)* }) => {}; (index $R: ident with {$lh : tt, $($lt : tt,)*} index $C: ident with { $($r: tt,)* }) => { $( impl_index_pair!{$R, $C, $lh, $r} )* impl_index_pairs!{index $R with {$($lt,)*} index $C with {$($r,)*}} } } impl_index_pairs! { index R with { [<> usize => U1], [<> ops::Range => Dyn], [<> ops::RangeFrom => Dyn], [<> ops::RangeFull => R], [<> ops::RangeInclusive => Dyn], [<> ops::RangeTo => Dyn], [<> ops::RangeToInclusive => Dyn], [ ops::RangeFrom => DimDiff where R: DimSub], } index C with { [<> usize => U1], [<> ops::Range => Dyn], [<> ops::RangeFrom => Dyn], [<> ops::RangeFull => C], [<> ops::RangeInclusive => Dyn], [<> ops::RangeTo => Dyn], [<> ops::RangeToInclusive => Dyn], [ ops::RangeFrom => DimDiff where C: DimSub], } } nalgebra-0.33.0/src/base/interpolation.rs000064400000000000000000000100470072674642500164520ustar 00000000000000use crate::storage::Storage; use crate::{ Allocator, DefaultAllocator, Dim, OVector, One, RealField, Scalar, Unit, Vector, Zero, }; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, ClosedSubAssign}; /// # Interpolation impl< T: Scalar + Zero + One + ClosedAddAssign + ClosedSubAssign + ClosedMulAssign, D: Dim, S: Storage, > Vector { /// Returns `self * (1.0 - t) + rhs * t`, i.e., the linear blend of the vectors x and y using the scalar value a. /// /// The value for a is not restricted to the range `[0, 1]`. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let x = Vector3::new(1.0, 2.0, 3.0); /// let y = Vector3::new(10.0, 20.0, 30.0); /// assert_eq!(x.lerp(&y, 0.1), Vector3::new(1.9, 3.8, 5.7)); /// ``` #[must_use] pub fn lerp>(&self, rhs: &Vector, t: T) -> OVector where DefaultAllocator: Allocator, { let mut res = self.clone_owned(); res.axpy(t.clone(), rhs, T::one() - t); res } /// Computes the spherical linear interpolation between two non-zero vectors. /// /// The result is a unit vector. /// /// # Examples: /// /// ``` /// # use nalgebra::{Unit, Vector2}; /// /// let v1 =Vector2::new(1.0, 2.0); /// let v2 = Vector2::new(2.0, -3.0); /// /// let v = v1.slerp(&v2, 1.0); /// /// assert_eq!(v, v2.normalize()); /// ``` #[must_use] pub fn slerp>(&self, rhs: &Vector, t: T) -> OVector where T: RealField, DefaultAllocator: Allocator, { let me = Unit::new_normalize(self.clone_owned()); let rhs = Unit::new_normalize(rhs.clone_owned()); me.slerp(&rhs, t).into_inner() } } /// # Interpolation between two unit vectors impl> Unit> { /// Computes the spherical linear interpolation between two unit vectors. /// /// # Examples: /// /// ``` /// # use nalgebra::{Unit, Vector2}; /// /// let v1 = Unit::new_normalize(Vector2::new(1.0, 2.0)); /// let v2 = Unit::new_normalize(Vector2::new(2.0, -3.0)); /// /// let v = v1.slerp(&v2, 1.0); /// /// assert_eq!(v, v2); /// ``` #[must_use] pub fn slerp>( &self, rhs: &Unit>, t: T, ) -> Unit> where DefaultAllocator: Allocator, { // TODO: the result is wrong when self and rhs are collinear with opposite direction. self.try_slerp(rhs, t, T::default_epsilon()) .unwrap_or_else(|| Unit::new_unchecked(self.clone_owned())) } /// Computes the spherical linear interpolation between two unit vectors. /// /// Returns `None` if the two vectors are almost collinear and with opposite direction /// (in this case, there is an infinity of possible results). #[must_use] pub fn try_slerp>( &self, rhs: &Unit>, t: T, epsilon: T, ) -> Option>> where DefaultAllocator: Allocator, { let c_hang = self.dot(rhs); // self == other if c_hang >= T::one() { return Some(Unit::new_unchecked(self.clone_owned())); } let hang = c_hang.clone().acos(); let s_hang = (T::one() - c_hang.clone() * c_hang).sqrt(); // TODO: what if s_hang is 0.0 ? The result is not well-defined. if relative_eq!(s_hang, T::zero(), epsilon = epsilon) { None } else { let ta = ((T::one() - t.clone()) * hang.clone()).sin() / s_hang.clone(); let tb = (t * hang).sin() / s_hang; let mut res = self.scale(ta); res.axpy(tb, &**rhs, T::one()); Some(Unit::new_unchecked(res)) } } } nalgebra-0.33.0/src/base/iter.rs000064400000000000000000000446240072674642500145360ustar 00000000000000//! Matrix iterators. // only enables the `doc_cfg` feature when // the `docsrs` configuration attribute is defined #![cfg_attr(docsrs, feature(doc_cfg))] use core::fmt::Debug; use core::ops::Range; use std::iter::FusedIterator; use std::marker::PhantomData; use std::mem; use crate::base::dimension::{Dim, U1}; use crate::base::storage::{RawStorage, RawStorageMut}; use crate::base::{Matrix, MatrixView, MatrixViewMut, Scalar, ViewStorage, ViewStorageMut}; #[derive(Clone, Debug)] struct RawIter { ptr: Ptr, inner_ptr: Ptr, inner_end: Ptr, size: usize, strides: (RStride, CStride), _phantoms: PhantomData<(fn() -> T, R, C)>, } macro_rules! iterator { (struct $Name:ident for $Storage:ident.$ptr: ident -> $Ptr:ty, $Ref:ty, $SRef: ty, $($derives:ident),* $(,)?) => { // TODO: we need to specialize for the case where the matrix storage is owned (in which // case the iterator is trivial because it does not have any stride). impl RawIter<$Ptr, T, R, C, RStride, CStride> { /// Creates a new iterator for the given matrix storage. fn new<'a, S: $Storage>( storage: $SRef, ) -> Self { let shape = storage.shape(); let strides = storage.strides(); let inner_offset = shape.0.value() * strides.0.value(); let size = shape.0.value() * shape.1.value(); let ptr = storage.$ptr(); // If we have a size of 0, 'ptr' must be // dangling. However, 'inner_offset' might // not be zero if only one dimension is zero, so // we don't want to call 'offset'. // This pointer will never actually get used // if our size is '0', so it's fine to use // 'ptr' for both the start and end. let inner_end = if size == 0 { ptr } else { // Safety: // If 'size' is non-zero, we know that 'ptr' // is not dangling, and 'inner_offset' must lie // within the allocation unsafe { ptr.add(inner_offset) } }; RawIter { ptr, inner_ptr: ptr, inner_end, size: shape.0.value() * shape.1.value(), strides, _phantoms: PhantomData, } } } impl Iterator for RawIter<$Ptr, T, R, C, RStride, CStride> { type Item = $Ptr; #[inline] fn next(&mut self) -> Option { unsafe { if self.size == 0 { None } else { self.size -= 1; // Jump to the next outer dimension if needed. if self.ptr == self.inner_end { let stride = self.strides.1.value() as isize; // This might go past the end of the allocation, // depending on the value of 'size'. We use // `wrapping_offset` to avoid UB self.inner_end = self.ptr.wrapping_offset(stride); // This will always be in bounds, since // we're going to dereference it self.ptr = self.inner_ptr.offset(stride); self.inner_ptr = self.ptr; } // Go to the next element. let old = self.ptr; // Don't offset `self.ptr` for the last element, // as this will be out of bounds. Iteration is done // at this point (the next call to `next` will return `None`) // so this is not observable. if self.size != 0 { let stride = self.strides.0.value(); self.ptr = self.ptr.add(stride); } Some(old) } } } #[inline] fn size_hint(&self) -> (usize, Option) { (self.size, Some(self.size)) } #[inline] fn count(self) -> usize { self.size_hint().0 } } impl DoubleEndedIterator for RawIter<$Ptr, T, R, C, RStride, CStride> { #[inline] fn next_back(&mut self) -> Option { unsafe { if self.size == 0 { None } else { // Pre-decrement `size` such that it now counts to the // element we want to return. self.size -= 1; // Fetch strides let inner_stride = self.strides.0.value(); let outer_stride = self.strides.1.value(); // Compute number of rows // Division should be exact let inner_raw_size = self.inner_end.offset_from(self.inner_ptr) as usize; let inner_size = inner_raw_size / inner_stride; // Compute rows and cols remaining let outer_remaining = self.size / inner_size; let inner_remaining = self.size % inner_size; // Compute pointer to last element let last = self .ptr .add((outer_remaining * outer_stride + inner_remaining * inner_stride)); Some(last) } } } } impl ExactSizeIterator for RawIter<$Ptr, T, R, C, RStride, CStride> { #[inline] fn len(&self) -> usize { self.size } } impl FusedIterator for RawIter<$Ptr, T, R, C, RStride, CStride> { } /// An iterator through a dense matrix with arbitrary strides matrix. #[derive($($derives),*)] pub struct $Name<'a, T, R: Dim, C: Dim, S: 'a + $Storage> { inner: RawIter<$Ptr, T, R, C, S::RStride, S::CStride>, _marker: PhantomData<$Ref>, } impl<'a, T, R: Dim, C: Dim, S: 'a + $Storage> $Name<'a, T, R, C, S> { /// Creates a new iterator for the given matrix storage. pub fn new(storage: $SRef) -> Self { Self { inner: RawIter::<$Ptr, T, R, C, S::RStride, S::CStride>::new(storage), _marker: PhantomData, } } } impl<'a, T, R: Dim, C: Dim, S: 'a + $Storage> Iterator for $Name<'a, T, R, C, S> { type Item = $Ref; #[inline(always)] fn next(&mut self) -> Option { // We want either `& *last` or `&mut *last` here, depending // on the mutability of `$Ref`. #[allow(clippy::transmute_ptr_to_ref)] self.inner.next().map(|ptr| unsafe { mem::transmute(ptr) }) } #[inline(always)] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } #[inline(always)] fn count(self) -> usize { self.inner.count() } } impl<'a, T, R: Dim, C: Dim, S: 'a + $Storage> DoubleEndedIterator for $Name<'a, T, R, C, S> { #[inline(always)] fn next_back(&mut self) -> Option { // We want either `& *last` or `&mut *last` here, depending // on the mutability of `$Ref`. #[allow(clippy::transmute_ptr_to_ref)] self.inner .next_back() .map(|ptr| unsafe { mem::transmute(ptr) }) } } impl<'a, T, R: Dim, C: Dim, S: 'a + $Storage> ExactSizeIterator for $Name<'a, T, R, C, S> { #[inline(always)] fn len(&self) -> usize { self.inner.len() } } impl<'a, T, R: Dim, C: Dim, S: 'a + $Storage> FusedIterator for $Name<'a, T, R, C, S> { } }; } iterator!(struct MatrixIter for RawStorage.ptr -> *const T, &'a T, &'a S, Clone, Debug); iterator!(struct MatrixIterMut for RawStorageMut.ptr_mut -> *mut T, &'a mut T, &'a mut S, Debug); impl<'a, T, R: Dim, C: Dim, RStride: Dim, CStride: Dim> MatrixIter<'a, T, R, C, ViewStorage<'a, T, R, C, RStride, CStride>> { /// Creates a new iterator for the given matrix storage view. pub fn new_owned(storage: ViewStorage<'a, T, R, C, RStride, CStride>) -> Self { Self { inner: RawIter::<*const T, T, R, C, RStride, CStride>::new(&storage), _marker: PhantomData, } } } impl<'a, T, R: Dim, C: Dim, RStride: Dim, CStride: Dim> MatrixIterMut<'a, T, R, C, ViewStorageMut<'a, T, R, C, RStride, CStride>> { /// Creates a new iterator for the given matrix storage view. pub fn new_owned_mut(mut storage: ViewStorageMut<'a, T, R, C, RStride, CStride>) -> Self { Self { inner: RawIter::<*mut T, T, R, C, RStride, CStride>::new(&mut storage), _marker: PhantomData, } } } /* * * Row iterators. * */ #[derive(Clone, Debug)] /// An iterator through the rows of a matrix. pub struct RowIter<'a, T, R: Dim, C: Dim, S: RawStorage> { mat: &'a Matrix, curr: usize, } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorage> RowIter<'a, T, R, C, S> { pub(crate) fn new(mat: &'a Matrix) -> Self { RowIter { mat, curr: 0 } } } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorage> Iterator for RowIter<'a, T, R, C, S> { type Item = MatrixView<'a, T, U1, C, S::RStride, S::CStride>; #[inline] fn next(&mut self) -> Option { if self.curr < self.mat.nrows() { let res = self.mat.row(self.curr); self.curr += 1; Some(res) } else { None } } #[inline] fn size_hint(&self) -> (usize, Option) { ( self.mat.nrows() - self.curr, Some(self.mat.nrows() - self.curr), ) } #[inline] fn count(self) -> usize { self.mat.nrows() - self.curr } } impl<'a, T: Scalar, R: Dim, C: Dim, S: 'a + RawStorage> ExactSizeIterator for RowIter<'a, T, R, C, S> { #[inline] fn len(&self) -> usize { self.mat.nrows() - self.curr } } /// An iterator through the mutable rows of a matrix. #[derive(Debug)] pub struct RowIterMut<'a, T, R: Dim, C: Dim, S: RawStorageMut> { mat: *mut Matrix, curr: usize, phantom: PhantomData<&'a mut Matrix>, } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorageMut> RowIterMut<'a, T, R, C, S> { pub(crate) fn new(mat: &'a mut Matrix) -> Self { RowIterMut { mat, curr: 0, phantom: PhantomData, } } fn nrows(&self) -> usize { unsafe { (*self.mat).nrows() } } } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorageMut> Iterator for RowIterMut<'a, T, R, C, S> { type Item = MatrixViewMut<'a, T, U1, C, S::RStride, S::CStride>; #[inline] fn next(&mut self) -> Option { if self.curr < self.nrows() { let res = unsafe { (*self.mat).row_mut(self.curr) }; self.curr += 1; Some(res) } else { None } } #[inline] fn size_hint(&self) -> (usize, Option) { (self.nrows() - self.curr, Some(self.nrows() - self.curr)) } #[inline] fn count(self) -> usize { self.nrows() - self.curr } } impl<'a, T: Scalar, R: Dim, C: Dim, S: 'a + RawStorageMut> ExactSizeIterator for RowIterMut<'a, T, R, C, S> { #[inline] fn len(&self) -> usize { self.nrows() - self.curr } } /* * Column iterators. * */ #[derive(Clone, Debug)] /// An iterator through the columns of a matrix. pub struct ColumnIter<'a, T, R: Dim, C: Dim, S: RawStorage> { mat: &'a Matrix, range: Range, } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorage> ColumnIter<'a, T, R, C, S> { /// a new column iterator covering all columns of the matrix pub(crate) fn new(mat: &'a Matrix) -> Self { ColumnIter { mat, range: 0..mat.ncols(), } } #[cfg(feature = "rayon")] pub(crate) fn split_at(self, index: usize) -> (Self, Self) { // SAFETY: this makes sure the generated ranges are valid. let split_pos = (self.range.start + index).min(self.range.end); let left_iter = ColumnIter { mat: self.mat, range: self.range.start..split_pos, }; let right_iter = ColumnIter { mat: self.mat, range: split_pos..self.range.end, }; (left_iter, right_iter) } } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorage> Iterator for ColumnIter<'a, T, R, C, S> { type Item = MatrixView<'a, T, R, U1, S::RStride, S::CStride>; #[inline] fn next(&mut self) -> Option { debug_assert!(self.range.start <= self.range.end); if self.range.start < self.range.end { let res = self.mat.column(self.range.start); self.range.start += 1; Some(res) } else { None } } #[inline] fn size_hint(&self) -> (usize, Option) { let hint = self.range.len(); (hint, Some(hint)) } #[inline] fn count(self) -> usize { self.range.len() } } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorage> DoubleEndedIterator for ColumnIter<'a, T, R, C, S> { fn next_back(&mut self) -> Option { debug_assert!(self.range.start <= self.range.end); if !self.range.is_empty() { self.range.end -= 1; debug_assert!(self.range.end < self.mat.ncols()); debug_assert!(self.range.end >= self.range.start); Some(self.mat.column(self.range.end)) } else { None } } } impl<'a, T: Scalar, R: Dim, C: Dim, S: 'a + RawStorage> ExactSizeIterator for ColumnIter<'a, T, R, C, S> { #[inline] fn len(&self) -> usize { self.range.end - self.range.start } } /// An iterator through the mutable columns of a matrix. #[derive(Debug)] pub struct ColumnIterMut<'a, T, R: Dim, C: Dim, S: RawStorageMut> { mat: *mut Matrix, range: Range, phantom: PhantomData<&'a mut Matrix>, } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorageMut> ColumnIterMut<'a, T, R, C, S> { pub(crate) fn new(mat: &'a mut Matrix) -> Self { let range = 0..mat.ncols(); ColumnIterMut { mat, range, phantom: Default::default(), } } #[cfg(feature = "rayon")] pub(crate) fn split_at(self, index: usize) -> (Self, Self) { // SAFETY: this makes sure the generated ranges are valid. let split_pos = (self.range.start + index).min(self.range.end); let left_iter = ColumnIterMut { mat: self.mat, range: self.range.start..split_pos, phantom: Default::default(), }; let right_iter = ColumnIterMut { mat: self.mat, range: split_pos..self.range.end, phantom: Default::default(), }; (left_iter, right_iter) } fn ncols(&self) -> usize { unsafe { (*self.mat).ncols() } } } impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorageMut> Iterator for ColumnIterMut<'a, T, R, C, S> { type Item = MatrixViewMut<'a, T, R, U1, S::RStride, S::CStride>; #[inline] fn next(&'_ mut self) -> Option { debug_assert!(self.range.start <= self.range.end); if self.range.start < self.range.end { let res = unsafe { (*self.mat).column_mut(self.range.start) }; self.range.start += 1; Some(res) } else { None } } #[inline] fn size_hint(&self) -> (usize, Option) { let hint = self.range.len(); (hint, Some(hint)) } #[inline] fn count(self) -> usize { self.range.len() } } impl<'a, T: Scalar, R: Dim, C: Dim, S: 'a + RawStorageMut> ExactSizeIterator for ColumnIterMut<'a, T, R, C, S> { #[inline] fn len(&self) -> usize { self.range.len() } } impl<'a, T: Scalar, R: Dim, C: Dim, S: 'a + RawStorageMut> DoubleEndedIterator for ColumnIterMut<'a, T, R, C, S> { fn next_back(&mut self) -> Option { debug_assert!(self.range.start <= self.range.end); if !self.range.is_empty() { self.range.end -= 1; debug_assert!(self.range.end < self.ncols()); debug_assert!(self.range.end >= self.range.start); Some(unsafe { (*self.mat).column_mut(self.range.end) }) } else { None } } } nalgebra-0.33.0/src/base/matrix.rs000064400000000000000000002356310072674642500150770ustar 00000000000000use num::{One, Zero}; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::any::TypeId; use std::cmp::Ordering; use std::fmt; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::mem; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "rkyv-serialize-no-std")] use super::rkyv_wrappers::CustomPhantom; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; #[cfg(feature = "rkyv-serialize-no-std")] use rkyv::{with::With, Archive, Archived}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, ClosedSubAssign, Field, SupersetOf}; use simba::simd::SimdPartialOrd; use crate::base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR}; use crate::base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use crate::base::dimension::{Dim, DimAdd, DimSum, IsNotStaticOne, U1, U2, U3}; use crate::base::iter::{ ColumnIter, ColumnIterMut, MatrixIter, MatrixIterMut, RowIter, RowIterMut, }; use crate::base::storage::{Owned, RawStorage, RawStorageMut, SameShapeStorage}; use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit}; use crate::{ArrayStorage, SMatrix, SimdComplexField, Storage, UninitMatrix}; use crate::storage::IsContiguous; use crate::uninit::{Init, InitStatus, Uninit}; #[cfg(any(feature = "std", feature = "alloc"))] use crate::{DMatrix, DVector, Dyn, RowDVector, VecStorage}; use std::mem::MaybeUninit; /// A square matrix. pub type SquareMatrix = Matrix; /// A matrix with one column and `D` rows. pub type Vector = Matrix; /// A matrix with one row and `D` columns . pub type RowVector = Matrix; /// The type of the result of a matrix sum. pub type MatrixSum = Matrix, SameShapeC, SameShapeStorage>; /// The type of the result of a matrix sum. pub type VectorSum = Matrix, U1, SameShapeStorage>; /// The type of the result of a matrix cross product. pub type MatrixCross = Matrix, SameShapeC, SameShapeStorage>; /// The most generic column-major matrix (and vector) type. /// /// # Methods summary /// Because `Matrix` is the most generic types used as a common representation of all matrices and /// vectors of **nalgebra** this documentation page contains every single matrix/vector-related /// method. In order to make browsing this page simpler, the next subsections contain direct links /// to groups of methods related to a specific topic. /// /// #### Vector and matrix construction /// - [Constructors of statically-sized vectors or statically-sized matrices](#constructors-of-statically-sized-vectors-or-statically-sized-matrices) /// (`Vector3`, `Matrix3x6`…) /// - [Constructors of fully dynamic matrices](#constructors-of-fully-dynamic-matrices) (`DMatrix`) /// - [Constructors of dynamic vectors and matrices with a dynamic number of rows](#constructors-of-dynamic-vectors-and-matrices-with-a-dynamic-number-of-rows) /// (`DVector`, `MatrixXx3`…) /// - [Constructors of matrices with a dynamic number of columns](#constructors-of-matrices-with-a-dynamic-number-of-columns) /// (`Matrix2xX`…) /// - [Generic constructors](#generic-constructors) /// (For code generic wrt. the vectors or matrices dimensions.) /// /// #### Computer graphics utilities for transformations /// - [2D transformations as a Matrix3 `new_rotation`…](#2d-transformations-as-a-matrix3) /// - [3D transformations as a Matrix4 `new_rotation`, `new_perspective`, `look_at_rh`…](#3d-transformations-as-a-matrix4) /// - [Translation and scaling in any dimension `new_scaling`, `new_translation`…](#translation-and-scaling-in-any-dimension) /// - [Append/prepend translation and scaling `append_scaling`, `prepend_translation_mut`…](#appendprepend-translation-and-scaling) /// - [Transformation of vectors and points `transform_vector`, `transform_point`…](#transformation-of-vectors-and-points) /// /// #### Common math operations /// - [Componentwise operations `component_mul`, `component_div`, `inf`…](#componentwise-operations) /// - [Special multiplications `tr_mul`, `ad_mul`, `kronecker`…](#special-multiplications) /// - [Dot/scalar product `dot`, `dotc`, `tr_dot`…](#dotscalar-product) /// - [Cross product `cross`, `perp`…](#cross-product) /// - [Magnitude and norms `norm`, `normalize`, `metric_distance`…](#magnitude-and-norms) /// - [In-place normalization `normalize_mut`, `try_normalize_mut`…](#in-place-normalization) /// - [Interpolation `lerp`, `slerp`…](#interpolation) /// - [BLAS functions `gemv`, `gemm`, `syger`…](#blas-functions) /// - [Swizzling `xx`, `yxz`…](#swizzling) /// - [Triangular matrix extraction `upper_triangle`, `lower_triangle`](#triangular-matrix-extraction) /// /// #### Statistics /// - [Common operations `row_sum`, `column_mean`, `variance`…](#common-statistics-operations) /// - [Find the min and max components `min`, `max`, `amin`, `amax`, `camin`, `cmax`…](#find-the-min-and-max-components) /// - [Find the min and max components (vector-specific methods) `argmin`, `argmax`, `icamin`, `icamax`…](#find-the-min-and-max-components-vector-specific-methods) /// /// #### Iteration, map, and fold /// - [Iteration on components, rows, and columns `iter`, `column_iter`…](#iteration-on-components-rows-and-columns) /// - [Parallel iterators using rayon `par_column_iter`, `par_column_iter_mut`…](#parallel-iterators-using-rayon) /// - [Elementwise mapping and folding `map`, `fold`, `zip_map`…](#elementwise-mapping-and-folding) /// - [Folding or columns and rows `compress_rows`, `compress_columns`…](#folding-on-columns-and-rows) /// /// #### Vector and matrix views /// - [Creating matrix views from `&[T]` `from_slice`, `from_slice_with_strides`…](#creating-matrix-views-from-t) /// - [Creating mutable matrix views from `&mut [T]` `from_slice_mut`, `from_slice_with_strides_mut`…](#creating-mutable-matrix-views-from-mut-t) /// - [Views based on index and length `row`, `columns`, `view`…](#views-based-on-index-and-length) /// - [Mutable views based on index and length `row_mut`, `columns_mut`, `view_mut`…](#mutable-views-based-on-index-and-length) /// - [Views based on ranges `rows_range`, `columns_range`…](#views-based-on-ranges) /// - [Mutable views based on ranges `rows_range_mut`, `columns_range_mut`…](#mutable-views-based-on-ranges) /// /// #### In-place modification of a single matrix or vector /// - [In-place filling `fill`, `fill_diagonal`, `fill_with_identity`…](#in-place-filling) /// - [In-place swapping `swap`, `swap_columns`…](#in-place-swapping) /// - [Set rows, columns, and diagonal `set_column`, `set_diagonal`…](#set-rows-columns-and-diagonal) /// /// #### Vector and matrix size modification /// - [Rows and columns insertion `insert_row`, `insert_column`…](#rows-and-columns-insertion) /// - [Rows and columns removal `remove_row`, `remove column`…](#rows-and-columns-removal) /// - [Rows and columns extraction `select_rows`, `select_columns`…](#rows-and-columns-extraction) /// - [Resizing and reshaping `resize`, `reshape_generic`…](#resizing-and-reshaping) /// - [In-place resizing `resize_mut`, `resize_vertically_mut`…](#in-place-resizing) /// /// #### Matrix decomposition /// - [Rectangular matrix decomposition `qr`, `lu`, `svd`…](#rectangular-matrix-decomposition) /// - [Square matrix decomposition `cholesky`, `symmetric_eigen`…](#square-matrix-decomposition) /// /// #### Vector basis computation /// - [Basis and orthogonalization `orthonormal_subspace_basis`, `orthonormalize`…](#basis-and-orthogonalization) /// /// # Type parameters /// The generic `Matrix` type has four type parameters: /// - `T`: for the matrix components scalar type. /// - `R`: for the matrix number of rows. /// - `C`: for the matrix number of columns. /// - `S`: for the matrix data storage, i.e., the buffer that actually contains the matrix /// components. /// /// The matrix dimensions parameters `R` and `C` can either be: /// - type-level unsigned integer constants (e.g. `U1`, `U124`) from the `nalgebra::` root module. /// All numbers from 0 to 127 are defined that way. /// - type-level unsigned integer constants (e.g. `U1024`, `U10000`) from the `typenum::` crate. /// Using those, you will not get error messages as nice as for numbers smaller than 128 defined on /// the `nalgebra::` module. /// - the special value `Dyn` from the `nalgebra::` root module. This indicates that the /// specified dimension is not known at compile-time. Note that this will generally imply that the /// matrix data storage `S` performs a dynamic allocation and contains extra metadata for the /// matrix shape. /// /// Note that mixing `Dyn` with type-level unsigned integers is allowed. Actually, a /// dynamically-sized column vector should be represented as a `Matrix` (given /// some concrete types for `T` and a compatible data storage type `S`). #[repr(C)] #[derive(Clone, Copy)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Matrix", bound(archive = " T: Archive, S: Archive, With, CustomPhantom<(Archived, R, C)>>: Archive, R, C)>> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] pub struct Matrix { /// The data storage that contains all the matrix components. Disappointed? /// /// Well, if you came here to see how you can access the matrix components, /// you may be in luck: you can access the individual components of all vectors with compile-time /// dimensions <= 6 using field notation like this: /// `vec.x`, `vec.y`, `vec.z`, `vec.w`, `vec.a`, `vec.b`. Reference and assignation work too: /// ``` /// # use nalgebra::Vector3; /// let mut vec = Vector3::new(1.0, 2.0, 3.0); /// vec.x = 10.0; /// vec.y += 30.0; /// assert_eq!(vec.x, 10.0); /// assert_eq!(vec.y + 100.0, 132.0); /// ``` /// Similarly, for matrices with compile-time dimensions <= 6, you can use field notation /// like this: `mat.m11`, `mat.m42`, etc. The first digit identifies the row to address /// and the second digit identifies the column to address. So `mat.m13` identifies the component /// at the first row and third column (note that the count of rows and columns start at 1 instead /// of 0 here. This is so we match the mathematical notation). /// /// For all matrices and vectors, independently from their size, individual components can /// be accessed and modified using indexing: `vec[20]`, `mat[(20, 19)]`. Here the indexing /// starts at 0 as you would expect. pub data: S, // NOTE: the fact that this field is private is important because // this prevents the user from constructing a matrix with // dimensions R, C that don't match the dimension of the // storage S. Instead they have to use the unsafe function // from_data_statically_unchecked. // Note that it would probably make sense to just have // the type `Matrix`, and have `T, R, C` be associated-types // of the `RawStorage` trait. However, because we don't have // specialization, this is not possible because these `T, R, C` // allows us to desambiguate a lot of configurations. #[cfg_attr(feature = "rkyv-serialize-no-std", with(CustomPhantom<(T::Archived, R, C)>))] _phantoms: PhantomData<(T, R, C)>, } impl fmt::Debug for Matrix { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.data.fmt(formatter) } } impl Default for Matrix where T: Scalar, R: Dim, C: Dim, S: Default, { fn default() -> Self { Matrix { data: Default::default(), _phantoms: PhantomData, } } } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Matrix where T: Scalar, R: Dim, C: Dim, S: Serialize, { fn serialize(&self, serializer: Ser) -> Result where Ser: Serializer, { self.data.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'de, T, R, C, S> Deserialize<'de> for Matrix where T: Scalar, R: Dim, C: Dim, S: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { S::deserialize(deserializer).map(|x| Matrix { data: x, _phantoms: PhantomData, }) } } #[cfg(feature = "compare")] impl> matrixcompare_core::Matrix for Matrix { fn rows(&self) -> usize { self.nrows() } fn cols(&self) -> usize { self.ncols() } fn access(&self) -> matrixcompare_core::Access<'_, T> { matrixcompare_core::Access::Dense(self) } } #[cfg(feature = "compare")] impl> matrixcompare_core::DenseAccess for Matrix { fn fetch_single(&self, row: usize, col: usize) -> T { self.index((row, col)).clone() } } #[cfg(feature = "bytemuck")] unsafe impl> bytemuck::Zeroable for Matrix where S: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl> bytemuck::Pod for Matrix where S: bytemuck::Pod, Self: Copy, { } impl Matrix { /// 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 { Matrix { data, _phantoms: PhantomData, } } } impl SMatrix { /// Creates a new statically-allocated matrix from the given [`ArrayStorage`]. /// /// This method exists primarily as a workaround for the fact that `from_data` can not /// work in `const fn` contexts. #[inline(always)] pub const fn from_array_storage(storage: ArrayStorage) -> Self { // This is sound because the row and column types are exactly the same as that of the // storage, so there can be no mismatch unsafe { Self::from_data_statically_unchecked(storage) } } } // TODO: Consider removing/deprecating `from_vec_storage` once we are able to make // `from_data` const fn compatible #[cfg(any(feature = "std", feature = "alloc"))] impl DMatrix { /// Creates a new heap-allocated matrix from the given [`VecStorage`]. /// /// This method exists primarily as a workaround for the fact that `from_data` can not /// work in `const fn` contexts. pub const fn from_vec_storage(storage: VecStorage) -> Self { // This is sound because the dimensions of the matrix and the storage are guaranteed // to be the same unsafe { Self::from_data_statically_unchecked(storage) } } } // TODO: Consider removing/deprecating `from_vec_storage` once we are able to make // `from_data` const fn compatible #[cfg(any(feature = "std", feature = "alloc"))] impl DVector { /// Creates a new heap-allocated matrix from the given [`VecStorage`]. /// /// This method exists primarily as a workaround for the fact that `from_data` can not /// work in `const fn` contexts. pub const fn from_vec_storage(storage: VecStorage) -> Self { // This is sound because the dimensions of the matrix and the storage are guaranteed // to be the same unsafe { Self::from_data_statically_unchecked(storage) } } } // TODO: Consider removing/deprecating `from_vec_storage` once we are able to make // `from_data` const fn compatible #[cfg(any(feature = "std", feature = "alloc"))] impl RowDVector { /// Creates a new heap-allocated matrix from the given [`VecStorage`]. /// /// This method exists primarily as a workaround for the fact that `from_data` can not /// work in `const fn` contexts. pub const fn from_vec_storage(storage: VecStorage) -> Self { // This is sound because the dimensions of the matrix and the storage are guaranteed // to be the same unsafe { Self::from_data_statically_unchecked(storage) } } } impl UninitMatrix where DefaultAllocator: Allocator, { /// Assumes a matrix's entries to be initialized. This operation should be near zero-cost. /// /// # Safety /// The user must make sure that every single entry of the buffer has been initialized, /// or Undefined Behavior will immediately occur. #[inline(always)] pub unsafe fn assume_init(self) -> OMatrix { OMatrix::from_data(>::assume_init( self.data, )) } } impl> Matrix { /// Creates a new matrix with the given data. #[inline(always)] pub fn from_data(data: S) -> Self { unsafe { Self::from_data_statically_unchecked(data) } } /// The shape of this matrix returned as the tuple (number of rows, number of columns). /// /// # Example /// ``` /// # use nalgebra::Matrix3x4; /// let mat = Matrix3x4::::zeros(); /// assert_eq!(mat.shape(), (3, 4)); /// ``` #[inline] #[must_use] pub fn shape(&self) -> (usize, usize) { let (nrows, ncols) = self.shape_generic(); (nrows.value(), ncols.value()) } /// The shape of this matrix wrapped into their representative types (`Const` or `Dyn`). #[inline] #[must_use] pub fn shape_generic(&self) -> (R, C) { self.data.shape() } /// The number of rows of this matrix. /// /// # Example /// ``` /// # use nalgebra::Matrix3x4; /// let mat = Matrix3x4::::zeros(); /// assert_eq!(mat.nrows(), 3); /// ``` #[inline] #[must_use] pub fn nrows(&self) -> usize { self.shape().0 } /// The number of columns of this matrix. /// /// # Example /// ``` /// # use nalgebra::Matrix3x4; /// let mat = Matrix3x4::::zeros(); /// assert_eq!(mat.ncols(), 4); /// ``` #[inline] #[must_use] pub fn ncols(&self) -> usize { self.shape().1 } /// The strides (row stride, column stride) of this matrix. /// /// # Example /// ``` /// # use nalgebra::DMatrix; /// let mat = DMatrix::::zeros(10, 10); /// let view = mat.view_with_steps((0, 0), (5, 3), (1, 2)); /// // The column strides is the number of steps (here 2) multiplied by the corresponding dimension. /// assert_eq!(mat.strides(), (1, 10)); /// ``` #[inline] #[must_use] pub fn strides(&self) -> (usize, usize) { let (srows, scols) = self.data.strides(); (srows.value(), scols.value()) } /// Computes the row and column coordinates of the i-th element of this matrix seen as a /// vector. /// /// # Example /// ``` /// # use nalgebra::Matrix2; /// let m = Matrix2::new(1, 2, /// 3, 4); /// let i = m.vector_to_matrix_index(3); /// assert_eq!(i, (1, 1)); /// assert_eq!(m[i], m[3]); /// ``` #[inline] #[must_use] pub fn vector_to_matrix_index(&self, i: usize) -> (usize, usize) { let (nrows, ncols) = self.shape(); // Two most common uses that should be optimized by the compiler for statically-sized // matrices. if nrows == 1 { (0, i) } else if ncols == 1 { (i, 0) } else { (i % nrows, i / nrows) } } /// Returns a pointer to the start of the matrix. /// /// If the matrix is not empty, this pointer is guaranteed to be aligned /// and non-null. /// /// # Example /// ``` /// # use nalgebra::Matrix2; /// let m = Matrix2::new(1, 2, /// 3, 4); /// let ptr = m.as_ptr(); /// assert_eq!(unsafe { *ptr }, m[0]); /// ``` #[inline] #[must_use] pub fn as_ptr(&self) -> *const T { self.data.ptr() } /// Tests whether `self` and `rhs` are equal up to a given epsilon. /// /// See `relative_eq` from the `RelativeEq` trait for more details. #[inline] #[must_use] pub fn relative_eq( &self, other: &Matrix, eps: T::Epsilon, max_relative: T::Epsilon, ) -> bool where T: RelativeEq + Scalar, R2: Dim, C2: Dim, SB: Storage, T::Epsilon: Clone, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { assert!(self.shape() == other.shape()); self.iter() .zip(other.iter()) .all(|(a, b)| a.relative_eq(b, eps.clone(), max_relative.clone())) } /// Tests whether `self` and `rhs` are exactly equal. #[inline] #[must_use] #[allow(clippy::should_implement_trait)] pub fn eq(&self, other: &Matrix) -> bool where T: PartialEq, R2: Dim, C2: Dim, SB: RawStorage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { assert!(self.shape() == other.shape()); self.iter().zip(other.iter()).all(|(a, b)| *a == *b) } /// Moves this matrix into one that owns its data. #[inline] pub fn into_owned(self) -> OMatrix where T: Scalar, S: Storage, DefaultAllocator: Allocator, { Matrix::from_data(self.data.into_owned()) } // TODO: this could probably benefit from specialization. // XXX: bad name. /// Moves this matrix into one that owns its data. The actual type of the result depends on /// matrix storage combination rules for addition. #[inline] pub fn into_owned_sum(self) -> MatrixSum where T: Scalar, S: Storage, R2: Dim, C2: Dim, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { if TypeId::of::>() == TypeId::of::>() { // We can just return `self.into_owned()`. unsafe { // TODO: check that those copies are optimized away by the compiler. let owned = self.into_owned(); let res = mem::transmute_copy(&owned); mem::forget(owned); res } } else { self.clone_owned_sum() } } /// Clones this matrix to one that owns its data. #[inline] #[must_use] pub fn clone_owned(&self) -> OMatrix where T: Scalar, S: Storage, DefaultAllocator: Allocator, { Matrix::from_data(self.data.clone_owned()) } /// Clones this matrix into one that owns its data. The actual type of the result depends on /// matrix storage combination rules for addition. #[inline] #[must_use] pub fn clone_owned_sum(&self) -> MatrixSum where T: Scalar, S: Storage, R2: Dim, C2: Dim, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { let (nrows, ncols) = self.shape(); let nrows: SameShapeR = Dim::from_usize(nrows); let ncols: SameShapeC = Dim::from_usize(ncols); let mut res = Matrix::uninit(nrows, ncols); unsafe { // TODO: use copy_from? for j in 0..res.ncols() { for i in 0..res.nrows() { *res.get_unchecked_mut((i, j)) = MaybeUninit::new(self.get_unchecked((i, j)).clone()); } } // SAFETY: the output has been initialized above. res.assume_init() } } /// Transposes `self` and store the result into `out`. #[inline] fn transpose_to_uninit( &self, _status: Status, out: &mut Matrix, ) where Status: InitStatus, T: Scalar, R2: Dim, C2: Dim, SB: RawStorageMut, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { let (nrows, ncols) = self.shape(); assert!( (ncols, nrows) == out.shape(), "Incompatible shape for transposition." ); // TODO: optimize that. for i in 0..nrows { for j in 0..ncols { // Safety: the indices are in range. unsafe { Status::init( out.get_unchecked_mut((j, i)), self.get_unchecked((i, j)).clone(), ); } } } } /// Transposes `self` and store the result into `out`. #[inline] pub fn transpose_to(&self, out: &mut Matrix) where T: Scalar, R2: Dim, C2: Dim, SB: RawStorageMut, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { self.transpose_to_uninit(Init, out) } /// Transposes `self`. #[inline] #[must_use = "Did you mean to use transpose_mut()?"] pub fn transpose(&self) -> OMatrix where T: Scalar, DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let mut res = Matrix::uninit(ncols, nrows); self.transpose_to_uninit(Uninit, &mut res); // Safety: res is now fully initialized. unsafe { res.assume_init() } } } /// # Elementwise mapping and folding impl> Matrix { /// Returns a matrix containing the result of `f` applied to each of its entries. #[inline] #[must_use] pub fn map T2>(&self, mut f: F) -> OMatrix where T: Scalar, DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let mut res = Matrix::uninit(nrows, ncols); for j in 0..ncols.value() { for i in 0..nrows.value() { // Safety: all indices are in range. unsafe { let a = self.data.get_unchecked(i, j).clone(); *res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a)); } } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let q = Vector3::new(1.0f64, 2.0, 3.0); /// let q2 = q.cast::(); /// assert_eq!(q2, Vector3::new(1.0f32, 2.0, 3.0)); /// ``` pub fn cast(self) -> OMatrix where T: Scalar, OMatrix: SupersetOf, DefaultAllocator: Allocator, { crate::convert(self) } /// Attempts to cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let q = Vector3::new(1.0f64, 2.0, 3.0); /// let q2 = q.try_cast::(); /// assert_eq!(q2, Some(Vector3::new(1, 2, 3))); /// ``` pub fn try_cast(self) -> Option> where T: Scalar, Self: SupersetOf>, DefaultAllocator: Allocator, { crate::try_convert(self) } /// Similar to `self.iter().fold(init, f)` except that `init` is replaced by a closure. /// /// The initialization closure is given the first component of this matrix: /// - If the matrix has no component (0 rows or 0 columns) then `init_f` is called with `None` /// and its return value is the value returned by this method. /// - If the matrix has has least one component, then `init_f` is called with the first component /// to compute the initial value. Folding then continues on all the remaining components of the matrix. #[inline] #[must_use] pub fn fold_with( &self, init_f: impl FnOnce(Option<&T>) -> T2, f: impl FnMut(T2, &T) -> T2, ) -> T2 where T: Scalar, { let mut it = self.iter(); let init = init_f(it.next()); it.fold(init, f) } /// Returns a matrix containing the result of `f` applied to each of its entries. Unlike `map`, /// `f` also gets passed the row and column index, i.e. `f(row, col, value)`. #[inline] #[must_use] pub fn map_with_location T2>( &self, mut f: F, ) -> OMatrix where T: Scalar, DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let mut res = Matrix::uninit(nrows, ncols); for j in 0..ncols.value() { for i in 0..nrows.value() { // Safety: all indices are in range. unsafe { let a = self.data.get_unchecked(i, j).clone(); *res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(i, j, a)); } } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Returns a matrix containing the result of `f` applied to each entries of `self` and /// `rhs`. #[inline] #[must_use] pub fn zip_map(&self, rhs: &Matrix, mut f: F) -> OMatrix where T: Scalar, T2: Scalar, N3: Scalar, S2: RawStorage, F: FnMut(T, T2) -> N3, DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let mut res = Matrix::uninit(nrows, ncols); assert_eq!( (nrows.value(), ncols.value()), rhs.shape(), "Matrix simultaneous traversal error: dimension mismatch." ); for j in 0..ncols.value() { for i in 0..nrows.value() { // Safety: all indices are in range. unsafe { let a = self.data.get_unchecked(i, j).clone(); let b = rhs.data.get_unchecked(i, j).clone(); *res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a, b)) } } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Returns a matrix containing the result of `f` applied to each entries of `self` and /// `b`, and `c`. #[inline] #[must_use] pub fn zip_zip_map( &self, b: &Matrix, c: &Matrix, mut f: F, ) -> OMatrix where T: Scalar, T2: Scalar, N3: Scalar, N4: Scalar, S2: RawStorage, S3: RawStorage, F: FnMut(T, T2, N3) -> N4, DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let mut res = Matrix::uninit(nrows, ncols); assert_eq!( (nrows.value(), ncols.value()), b.shape(), "Matrix simultaneous traversal error: dimension mismatch." ); assert_eq!( (nrows.value(), ncols.value()), c.shape(), "Matrix simultaneous traversal error: dimension mismatch." ); for j in 0..ncols.value() { for i in 0..nrows.value() { // Safety: all indices are in range. unsafe { let a = self.data.get_unchecked(i, j).clone(); let b = b.data.get_unchecked(i, j).clone(); let c = c.data.get_unchecked(i, j).clone(); *res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a, b, c)) } } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Folds a function `f` on each entry of `self`. #[inline] #[must_use] pub fn fold(&self, init: Acc, mut f: impl FnMut(Acc, T) -> Acc) -> Acc where T: Scalar, { let (nrows, ncols) = self.shape_generic(); let mut res = init; for j in 0..ncols.value() { for i in 0..nrows.value() { // Safety: all indices are in range. unsafe { let a = self.data.get_unchecked(i, j).clone(); res = f(res, a) } } } res } /// Folds a function `f` on each pairs of entries from `self` and `rhs`. #[inline] #[must_use] pub fn zip_fold( &self, rhs: &Matrix, init: Acc, mut f: impl FnMut(Acc, T, T2) -> Acc, ) -> Acc where T: Scalar, T2: Scalar, R2: Dim, C2: Dim, S2: RawStorage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { let (nrows, ncols) = self.shape_generic(); let mut res = init; assert_eq!( (nrows.value(), ncols.value()), rhs.shape(), "Matrix simultaneous traversal error: dimension mismatch." ); for j in 0..ncols.value() { for i in 0..nrows.value() { unsafe { let a = self.data.get_unchecked(i, j).clone(); let b = rhs.data.get_unchecked(i, j).clone(); res = f(res, a, b) } } } res } /// Applies a closure `f` to modify each component of `self`. #[inline] pub fn apply(&mut self, mut f: F) where S: RawStorageMut, { let (nrows, ncols) = self.shape(); for j in 0..ncols { for i in 0..nrows { unsafe { let e = self.data.get_unchecked_mut(i, j); f(e) } } } } /// Replaces each component of `self` by the result of a closure `f` applied on its components /// joined with the components from `rhs`. #[inline] pub fn zip_apply( &mut self, rhs: &Matrix, mut f: impl FnMut(&mut T, T2), ) where S: RawStorageMut, T2: Scalar, R2: Dim, C2: Dim, S2: RawStorage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { let (nrows, ncols) = self.shape(); assert_eq!( (nrows, ncols), rhs.shape(), "Matrix simultaneous traversal error: dimension mismatch." ); for j in 0..ncols { for i in 0..nrows { unsafe { let e = self.data.get_unchecked_mut(i, j); let rhs = rhs.get_unchecked((i, j)).clone(); f(e, rhs) } } } } /// Replaces each component of `self` by the result of a closure `f` applied on its components /// joined with the components from `b` and `c`. #[inline] pub fn zip_zip_apply( &mut self, b: &Matrix, c: &Matrix, mut f: impl FnMut(&mut T, T2, N3), ) where S: RawStorageMut, T2: Scalar, R2: Dim, C2: Dim, S2: RawStorage, N3: Scalar, R3: Dim, C3: Dim, S3: RawStorage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { let (nrows, ncols) = self.shape(); assert_eq!( (nrows, ncols), b.shape(), "Matrix simultaneous traversal error: dimension mismatch." ); assert_eq!( (nrows, ncols), c.shape(), "Matrix simultaneous traversal error: dimension mismatch." ); for j in 0..ncols { for i in 0..nrows { unsafe { let e = self.data.get_unchecked_mut(i, j); let b = b.get_unchecked((i, j)).clone(); let c = c.get_unchecked((i, j)).clone(); f(e, b, c) } } } } } /// # Iteration on components, rows, and columns impl> Matrix { /// Iterates through this matrix coordinates in column-major order. /// /// # Example /// ``` /// # use nalgebra::Matrix2x3; /// let mat = Matrix2x3::new(11, 12, 13, /// 21, 22, 23); /// let mut it = mat.iter(); /// assert_eq!(*it.next().unwrap(), 11); /// assert_eq!(*it.next().unwrap(), 21); /// assert_eq!(*it.next().unwrap(), 12); /// assert_eq!(*it.next().unwrap(), 22); /// assert_eq!(*it.next().unwrap(), 13); /// assert_eq!(*it.next().unwrap(), 23); /// assert!(it.next().is_none()); /// ``` #[inline] pub fn iter(&self) -> MatrixIter<'_, T, R, C, S> { MatrixIter::new(&self.data) } /// Iterate through the rows of this matrix. /// /// # Example /// ``` /// # use nalgebra::Matrix2x3; /// let mut a = Matrix2x3::new(1, 2, 3, /// 4, 5, 6); /// for (i, row) in a.row_iter().enumerate() { /// assert_eq!(row, a.row(i)) /// } /// ``` #[inline] pub fn row_iter(&self) -> RowIter<'_, T, R, C, S> { RowIter::new(self) } /// Iterate through the columns of this matrix. /// /// # Example /// ``` /// # use nalgebra::Matrix2x3; /// let mut a = Matrix2x3::new(1, 2, 3, /// 4, 5, 6); /// for (i, column) in a.column_iter().enumerate() { /// assert_eq!(column, a.column(i)) /// } /// ``` #[inline] pub fn column_iter(&self) -> ColumnIter<'_, T, R, C, S> { ColumnIter::new(self) } /// Mutably iterates through this matrix coordinates. #[inline] pub fn iter_mut(&mut self) -> MatrixIterMut<'_, T, R, C, S> where S: RawStorageMut, { MatrixIterMut::new(&mut self.data) } /// Mutably iterates through this matrix rows. /// /// # Example /// ``` /// # use nalgebra::Matrix2x3; /// let mut a = Matrix2x3::new(1, 2, 3, /// 4, 5, 6); /// for (i, mut row) in a.row_iter_mut().enumerate() { /// row *= (i + 1) * 10; /// } /// /// let expected = Matrix2x3::new(10, 20, 30, /// 80, 100, 120); /// assert_eq!(a, expected); /// ``` #[inline] pub fn row_iter_mut(&mut self) -> RowIterMut<'_, T, R, C, S> where S: RawStorageMut, { RowIterMut::new(self) } /// Mutably iterates through this matrix columns. /// /// # Example /// ``` /// # use nalgebra::Matrix2x3; /// let mut a = Matrix2x3::new(1, 2, 3, /// 4, 5, 6); /// for (i, mut col) in a.column_iter_mut().enumerate() { /// col *= (i + 1) * 10; /// } /// /// let expected = Matrix2x3::new(10, 40, 90, /// 40, 100, 180); /// assert_eq!(a, expected); /// ``` #[inline] pub fn column_iter_mut(&mut self) -> ColumnIterMut<'_, T, R, C, S> where S: RawStorageMut, { ColumnIterMut::new(self) } } impl> Matrix { /// Returns a mutable pointer to the start of the matrix. /// /// If the matrix is not empty, this pointer is guaranteed to be aligned /// and non-null. #[inline] pub fn as_mut_ptr(&mut self) -> *mut T { self.data.ptr_mut() } /// 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()); debug_assert!(row_cols2.0 < self.nrows() && row_cols2.1 < self.ncols()); self.data.swap_unchecked(row_cols1, row_cols2) } /// Swaps two entries. #[inline] pub fn swap(&mut self, row_cols1: (usize, usize), row_cols2: (usize, usize)) { let (nrows, ncols) = self.shape(); assert!( row_cols1.0 < nrows && row_cols1.1 < ncols, "Matrix elements swap index out of bounds." ); assert!( row_cols2.0 < nrows && row_cols2.1 < ncols, "Matrix elements swap index out of bounds." ); unsafe { self.swap_unchecked(row_cols1, row_cols2) } } /// Fills this matrix with the content of a slice. Both must hold the same number of elements. /// /// The components of the slice are assumed to be ordered in column-major order. #[inline] pub fn copy_from_slice(&mut self, slice: &[T]) where T: Scalar, { let (nrows, ncols) = self.shape(); assert!( nrows * ncols == slice.len(), "The slice must contain the same number of elements as the matrix." ); for j in 0..ncols { for i in 0..nrows { unsafe { *self.get_unchecked_mut((i, j)) = slice.get_unchecked(i + j * nrows).clone(); } } } } /// Fills this matrix with the content of another one. Both must have the same shape. #[inline] pub fn copy_from(&mut self, other: &Matrix) where T: Scalar, R2: Dim, C2: Dim, SB: RawStorage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { assert!( self.shape() == other.shape(), "Unable to copy from a matrix with a different shape." ); for j in 0..self.ncols() { for i in 0..self.nrows() { unsafe { *self.get_unchecked_mut((i, j)) = other.get_unchecked((i, j)).clone(); } } } } /// Fills this matrix with the content of the transpose another one. #[inline] pub fn tr_copy_from(&mut self, other: &Matrix) where T: Scalar, R2: Dim, C2: Dim, SB: RawStorage, ShapeConstraint: DimEq + SameNumberOfColumns, { let (nrows, ncols) = self.shape(); assert!( (ncols, nrows) == other.shape(), "Unable to copy from a matrix with incompatible shape." ); for j in 0..ncols { for i in 0..nrows { unsafe { *self.get_unchecked_mut((i, j)) = other.get_unchecked((j, i)).clone(); } } } } // TODO: rename `apply` to `apply_mut` and `apply_into` to `apply`? /// Returns `self` with each of its components replaced by the result of a closure `f` applied on it. #[inline] pub fn apply_into(mut self, f: F) -> Self { self.apply(f); self } } impl> Vector { /// 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 { debug_assert!(i < self.nrows(), "Vector index out of bounds."); let i = i * self.strides().0; self.data.get_unchecked_linear(i) } } impl> Vector { /// 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 { debug_assert!(i < self.nrows(), "Vector index out of bounds."); let i = i * self.strides().0; self.data.get_unchecked_linear_mut(i) } } impl + IsContiguous> Matrix { /// Extracts a slice containing the entire matrix entries ordered column-by-columns. #[inline] #[must_use] pub fn as_slice(&self) -> &[T] { // Safety: this is OK thanks to the IsContiguous trait. unsafe { self.data.as_slice_unchecked() } } } impl + IsContiguous> Matrix { /// Extracts a mutable slice containing the entire matrix entries ordered column-by-columns. #[inline] #[must_use] pub fn as_mut_slice(&mut self) -> &mut [T] { // Safety: this is OK thanks to the IsContiguous trait. unsafe { self.data.as_mut_slice_unchecked() } } } impl> Matrix { /// Transposes the square matrix `self` in-place. pub fn transpose_mut(&mut self) { assert!( self.is_square(), "Unable to transpose a non-square matrix in-place." ); let dim = self.shape().0; for i in 1..dim { for j in 0..i { unsafe { self.swap_unchecked((i, j), (j, i)) } } } } } impl> Matrix { /// Takes the adjoint (aka. conjugate-transpose) of `self` and store the result into `out`. #[inline] fn adjoint_to_uninit( &self, _status: Status, out: &mut Matrix, ) where Status: InitStatus, R2: Dim, C2: Dim, SB: RawStorageMut, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { let (nrows, ncols) = self.shape(); assert!( (ncols, nrows) == out.shape(), "Incompatible shape for transpose-copy." ); // TODO: optimize that. for i in 0..nrows { for j in 0..ncols { // Safety: all indices are in range. unsafe { Status::init( out.get_unchecked_mut((j, i)), self.get_unchecked((i, j)).clone().simd_conjugate(), ); } } } } /// Takes the adjoint (aka. conjugate-transpose) of `self` and store the result into `out`. #[inline] pub fn adjoint_to(&self, out: &mut Matrix) where R2: Dim, C2: Dim, SB: RawStorageMut, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { self.adjoint_to_uninit(Init, out) } /// The adjoint (aka. conjugate-transpose) of `self`. #[inline] #[must_use = "Did you mean to use adjoint_mut()?"] pub fn adjoint(&self) -> OMatrix where DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let mut res = Matrix::uninit(ncols, nrows); self.adjoint_to_uninit(Uninit, &mut res); // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Takes the conjugate and transposes `self` and store the result into `out`. #[deprecated(note = "Renamed `self.adjoint_to(out)`.")] #[inline] pub fn conjugate_transpose_to(&self, out: &mut Matrix) where R2: Dim, C2: Dim, SB: RawStorageMut, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { self.adjoint_to(out) } /// The conjugate transposition of `self`. #[deprecated(note = "Renamed `self.adjoint()`.")] #[inline] pub fn conjugate_transpose(&self) -> OMatrix where DefaultAllocator: Allocator, { self.adjoint() } /// The conjugate of `self`. #[inline] #[must_use = "Did you mean to use conjugate_mut()?"] pub fn conjugate(&self) -> OMatrix where DefaultAllocator: Allocator, { self.map(|e| e.simd_conjugate()) } /// Divides each component of the complex matrix `self` by the given real. #[inline] #[must_use = "Did you mean to use unscale_mut()?"] pub fn unscale(&self, real: T::SimdRealField) -> OMatrix where DefaultAllocator: Allocator, { self.map(|e| e.simd_unscale(real.clone())) } /// Multiplies each component of the complex matrix `self` by the given real. #[inline] #[must_use = "Did you mean to use scale_mut()?"] pub fn scale(&self, real: T::SimdRealField) -> OMatrix where DefaultAllocator: Allocator, { self.map(|e| e.simd_scale(real.clone())) } } impl> Matrix { /// The conjugate of the complex matrix `self` computed in-place. #[inline] pub fn conjugate_mut(&mut self) { self.apply(|e| *e = e.clone().simd_conjugate()) } /// Divides each component of the complex matrix `self` by the given real. #[inline] pub fn unscale_mut(&mut self, real: T::SimdRealField) { self.apply(|e| *e = e.clone().simd_unscale(real.clone())) } /// Multiplies each component of the complex matrix `self` by the given real. #[inline] pub fn scale_mut(&mut self, real: T::SimdRealField) { self.apply(|e| *e = e.clone().simd_scale(real.clone())) } } impl> Matrix { /// Sets `self` to its adjoint. #[deprecated(note = "Renamed to `self.adjoint_mut()`.")] pub fn conjugate_transform_mut(&mut self) { self.adjoint_mut() } /// Sets `self` to its adjoint (aka. conjugate-transpose). pub fn adjoint_mut(&mut self) { assert!( self.is_square(), "Unable to transpose a non-square matrix in-place." ); let dim = self.shape().0; for i in 0..dim { for j in 0..i { unsafe { let ref_ij = self.get_unchecked((i, j)).clone(); let ref_ji = self.get_unchecked((j, i)).clone(); let conj_ij = ref_ij.simd_conjugate(); let conj_ji = ref_ji.simd_conjugate(); *self.get_unchecked_mut((i, j)) = conj_ji; *self.get_unchecked_mut((j, i)) = conj_ij; } } { let diag = unsafe { self.get_unchecked_mut((i, i)) }; *diag = diag.clone().simd_conjugate(); } } } } impl> SquareMatrix { /// The diagonal of this matrix. #[inline] #[must_use] pub fn diagonal(&self) -> OVector where DefaultAllocator: Allocator, { self.map_diagonal(|e| e) } /// Apply the given function to this matrix's diagonal and returns it. /// /// This is a more efficient version of `self.diagonal().map(f)` since this /// allocates only once. #[must_use] pub fn map_diagonal(&self, mut f: impl FnMut(T) -> T2) -> OVector where DefaultAllocator: Allocator, { assert!( self.is_square(), "Unable to get the diagonal of a non-square matrix." ); let dim = self.shape_generic().0; let mut res = Matrix::uninit(dim, Const::<1>); for i in 0..dim.value() { // Safety: all indices are in range. unsafe { *res.vget_unchecked_mut(i) = MaybeUninit::new(f(self.get_unchecked((i, i)).clone())); } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Computes a trace of a square matrix, i.e., the sum of its diagonal elements. #[inline] #[must_use] pub fn trace(&self) -> T where T: Scalar + Zero + ClosedAddAssign, { assert!( self.is_square(), "Cannot compute the trace of non-square matrix." ); let dim = self.shape_generic().0; let mut res = T::zero(); for i in 0..dim.value() { res += unsafe { self.get_unchecked((i, i)).clone() }; } res } } impl> SquareMatrix { /// The symmetric part of `self`, i.e., `0.5 * (self + self.transpose())`. #[inline] #[must_use] pub fn symmetric_part(&self) -> OMatrix where DefaultAllocator: Allocator, { assert!( self.is_square(), "Cannot compute the symmetric part of a non-square matrix." ); let mut tr = self.transpose(); tr += self; tr *= crate::convert::<_, T>(0.5); tr } /// The hermitian part of `self`, i.e., `0.5 * (self + self.adjoint())`. #[inline] #[must_use] pub fn hermitian_part(&self) -> OMatrix where DefaultAllocator: Allocator, { assert!( self.is_square(), "Cannot compute the hermitian part of a non-square matrix." ); let mut tr = self.adjoint(); tr += self; tr *= crate::convert::<_, T>(0.5); tr } } impl + IsNotStaticOne, S: RawStorage> Matrix { /// Yields the homogeneous matrix for this matrix, i.e., appending an additional dimension and /// and setting the diagonal element to `1`. #[inline] #[must_use] pub fn to_homogeneous(&self) -> OMatrix, DimSum> where DefaultAllocator: Allocator, DimSum>, { assert!( self.is_square(), "Only square matrices can currently be transformed to homogeneous coordinates." ); let dim = DimSum::::from_usize(self.nrows() + 1); let mut res = OMatrix::identity_generic(dim, dim); res.generic_view_mut::((0, 0), self.shape_generic()) .copy_from(self); res } } impl, S: RawStorage> Vector { /// Computes the coordinates in projective space of this vector, i.e., appends a `0` to its /// coordinates. #[inline] #[must_use] pub fn to_homogeneous(&self) -> OVector> where DefaultAllocator: Allocator>, { self.push(T::zero()) } /// Constructs a vector from coordinates in projective space, i.e., removes a `0` at the end of /// `self`. Returns `None` if this last component is not zero. #[inline] pub fn from_homogeneous(v: Vector, SB>) -> Option> where SB: RawStorage>, DefaultAllocator: Allocator, { if v[v.len() - 1].is_zero() { let nrows = D::from_usize(v.len() - 1); Some(v.generic_view((0, 0), (nrows, Const::<1>)).into_owned()) } else { None } } } impl, S: RawStorage> Vector { /// Constructs a new vector of higher dimension by appending `element` to the end of `self`. #[inline] #[must_use] pub fn push(&self, element: T) -> OVector> where DefaultAllocator: Allocator>, { let len = self.len(); let hnrows = DimSum::::from_usize(len + 1); let mut res = Matrix::uninit(hnrows, Const::<1>); // This is basically a copy_from except that we warp the copied // values into MaybeUninit. res.generic_view_mut((0, 0), self.shape_generic()) .zip_apply(self, |out, e| *out = MaybeUninit::new(e)); res[(len, 0)] = MaybeUninit::new(element); // Safety: res has been fully initialized. unsafe { res.assume_init() } } } impl AbsDiffEq for Matrix where T: Scalar + AbsDiffEq, S: RawStorage, T::Epsilon: Clone, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.iter() .zip(other.iter()) .all(|(a, b)| a.abs_diff_eq(b, epsilon.clone())) } } impl RelativeEq for Matrix where T: Scalar + RelativeEq, S: Storage, T::Epsilon: Clone, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.relative_eq(other, epsilon, max_relative) } } impl UlpsEq for Matrix where T: Scalar + UlpsEq, S: RawStorage, T::Epsilon: Clone, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { assert!(self.shape() == other.shape()); self.iter() .zip(other.iter()) .all(|(a, b)| a.ulps_eq(b, epsilon.clone(), max_ulps)) } } impl PartialOrd for Matrix where T: Scalar + PartialOrd, S: RawStorage, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { if self.shape() != other.shape() { return None; } if self.nrows() == 0 || self.ncols() == 0 { return Some(Ordering::Equal); } let mut first_ord = unsafe { self.data .get_unchecked_linear(0) .partial_cmp(other.data.get_unchecked_linear(0)) }; if let Some(first_ord) = first_ord.as_mut() { let mut it = self.iter().zip(other.iter()); let _ = it.next(); // Drop the first elements (we already tested it). for (left, right) in it { if let Some(ord) = left.partial_cmp(right) { match ord { Ordering::Equal => { /* Does not change anything. */ } Ordering::Less => { if *first_ord == Ordering::Greater { return None; } *first_ord = ord } Ordering::Greater => { if *first_ord == Ordering::Less { return None; } *first_ord = ord } } } else { return None; } } } first_ord } #[inline] fn lt(&self, right: &Self) -> bool { assert_eq!( self.shape(), right.shape(), "Matrix comparison error: dimensions mismatch." ); self.iter().zip(right.iter()).all(|(a, b)| a.lt(b)) } #[inline] fn le(&self, right: &Self) -> bool { assert_eq!( self.shape(), right.shape(), "Matrix comparison error: dimensions mismatch." ); self.iter().zip(right.iter()).all(|(a, b)| a.le(b)) } #[inline] fn gt(&self, right: &Self) -> bool { assert_eq!( self.shape(), right.shape(), "Matrix comparison error: dimensions mismatch." ); self.iter().zip(right.iter()).all(|(a, b)| a.gt(b)) } #[inline] fn ge(&self, right: &Self) -> bool { assert_eq!( self.shape(), right.shape(), "Matrix comparison error: dimensions mismatch." ); self.iter().zip(right.iter()).all(|(a, b)| a.ge(b)) } } impl Eq for Matrix where T: Eq, S: RawStorage, { } impl PartialEq> for Matrix where T: PartialEq, C: Dim, C2: Dim, R: Dim, R2: Dim, S: RawStorage, S2: RawStorage, { #[inline] fn eq(&self, right: &Matrix) -> bool { self.shape() == right.shape() && self.iter().zip(right.iter()).all(|(l, r)| l == r) } } macro_rules! impl_fmt { ($trait: path, $fmt_str_without_precision: expr, $fmt_str_with_precision: expr) => { impl $trait for Matrix where T: Scalar + $trait, S: RawStorage, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "std")] fn val_width(val: &T, f: &mut fmt::Formatter<'_>) -> usize { match f.precision() { Some(precision) => format!($fmt_str_with_precision, val, precision) .chars() .count(), None => format!($fmt_str_without_precision, val).chars().count(), } } #[cfg(not(feature = "std"))] fn val_width(_: &T, _: &mut fmt::Formatter<'_>) -> usize { 4 } let (nrows, ncols) = self.shape(); if nrows == 0 || ncols == 0 { return write!(f, "[ ]"); } let mut max_length = 0; for i in 0..nrows { for j in 0..ncols { max_length = crate::max(max_length, val_width(&self[(i, j)], f)); } } let max_length_with_space = max_length + 1; writeln!(f)?; writeln!( f, " ┌ {:>width$} ┐", "", width = max_length_with_space * ncols - 1 )?; for i in 0..nrows { write!(f, " │")?; for j in 0..ncols { let number_length = val_width(&self[(i, j)], f) + 1; let pad = max_length_with_space - number_length; write!(f, " {:>thepad$}", "", thepad = pad)?; match f.precision() { Some(precision) => { write!(f, $fmt_str_with_precision, (*self)[(i, j)], precision)? } None => write!(f, $fmt_str_without_precision, (*self)[(i, j)])?, } } writeln!(f, " │")?; } writeln!( f, " └ {:>width$} ┘", "", width = max_length_with_space * ncols - 1 )?; writeln!(f) } } }; } impl_fmt!(fmt::Display, "{}", "{:.1$}"); impl_fmt!(fmt::LowerExp, "{:e}", "{:.1$e}"); impl_fmt!(fmt::UpperExp, "{:E}", "{:.1$E}"); impl_fmt!(fmt::Octal, "{:o}", "{:1$o}"); impl_fmt!(fmt::LowerHex, "{:x}", "{:1$x}"); impl_fmt!(fmt::UpperHex, "{:X}", "{:1$X}"); impl_fmt!(fmt::Binary, "{:b}", "{:.1$b}"); impl_fmt!(fmt::Pointer, "{:p}", "{:.1$p}"); #[cfg(test)] mod tests { #[test] fn empty_display() { let vec: Vec = Vec::new(); let dvector = crate::DVector::from_vec(vec); assert_eq!(format!("{}", dvector), "[ ]") } #[test] fn lower_exp() { let test = crate::Matrix2::new(1e6, 2e5, 2e-5, 1.); assert_eq!( format!("{:e}", test), r" ┌ ┐ │ 1e6 2e5 │ │ 2e-5 1e0 │ └ ┘ " ) } } /// # Cross product impl< T: Scalar + ClosedAddAssign + ClosedSubAssign + ClosedMulAssign, R: Dim, C: Dim, S: RawStorage, > Matrix { /// The perpendicular product between two 2D column vectors, i.e. `a.x * b.y - a.y * b.x`. #[inline] #[must_use] pub fn perp(&self, b: &Matrix) -> T where R2: Dim, C2: Dim, SB: RawStorage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + SameNumberOfRows + SameNumberOfColumns, { let shape = self.shape(); assert_eq!( shape, b.shape(), "2D vector perpendicular product dimension mismatch." ); assert_eq!( shape, (2, 1), "2D perpendicular product requires (2, 1) vectors {:?}", shape ); // SAFETY: assertion above ensures correct shape let ax = unsafe { self.get_unchecked((0, 0)).clone() }; let ay = unsafe { self.get_unchecked((1, 0)).clone() }; let bx = unsafe { b.get_unchecked((0, 0)).clone() }; let by = unsafe { b.get_unchecked((1, 0)).clone() }; ax * by - ay * bx } // TODO: use specialization instead of an assertion. /// The 3D cross product between two vectors. /// /// Panics if the shape is not 3D vector. In the future, this will be implemented only for /// dynamically-sized matrices and statically-sized 3D matrices. #[inline] #[must_use] pub fn cross(&self, b: &Matrix) -> MatrixCross where R2: Dim, C2: Dim, SB: RawStorage, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { let shape = self.shape(); assert_eq!(shape, b.shape(), "Vector cross product dimension mismatch."); assert!( shape == (3, 1) || shape == (1, 3), "Vector cross product dimension mismatch: must be (3, 1) or (1, 3) but found {:?}.", shape ); if shape.0 == 3 { unsafe { let mut res = Matrix::uninit(Dim::from_usize(3), Dim::from_usize(1)); let ax = self.get_unchecked((0, 0)); let ay = self.get_unchecked((1, 0)); let az = self.get_unchecked((2, 0)); let bx = b.get_unchecked((0, 0)); let by = b.get_unchecked((1, 0)); let bz = b.get_unchecked((2, 0)); *res.get_unchecked_mut((0, 0)) = MaybeUninit::new(ay.clone() * bz.clone() - az.clone() * by.clone()); *res.get_unchecked_mut((1, 0)) = MaybeUninit::new(az.clone() * bx.clone() - ax.clone() * bz.clone()); *res.get_unchecked_mut((2, 0)) = MaybeUninit::new(ax.clone() * by.clone() - ay.clone() * bx.clone()); // Safety: res is now fully initialized. res.assume_init() } } else { unsafe { let mut res = Matrix::uninit(Dim::from_usize(1), Dim::from_usize(3)); let ax = self.get_unchecked((0, 0)); let ay = self.get_unchecked((0, 1)); let az = self.get_unchecked((0, 2)); let bx = b.get_unchecked((0, 0)); let by = b.get_unchecked((0, 1)); let bz = b.get_unchecked((0, 2)); *res.get_unchecked_mut((0, 0)) = MaybeUninit::new(ay.clone() * bz.clone() - az.clone() * by.clone()); *res.get_unchecked_mut((0, 1)) = MaybeUninit::new(az.clone() * bx.clone() - ax.clone() * bz.clone()); *res.get_unchecked_mut((0, 2)) = MaybeUninit::new(ax.clone() * by.clone() - ay.clone() * bx.clone()); // Safety: res is now fully initialized. res.assume_init() } } } } impl> Vector { /// Computes the matrix `M` such that for all vector `v` we have `M * v == self.cross(&v)`. #[inline] #[must_use] pub fn cross_matrix(&self) -> OMatrix { OMatrix::::new( T::zero(), -self[2].clone(), self[1].clone(), self[2].clone(), T::zero(), -self[0].clone(), -self[1].clone(), self[0].clone(), T::zero(), ) } } impl> Matrix { /// The smallest angle between two vectors. #[inline] #[must_use] pub fn angle(&self, other: &Matrix) -> T::SimdRealField where SB: Storage, ShapeConstraint: DimEq + DimEq, { let prod = self.dotc(other); let n1 = self.norm(); let n2 = other.norm(); if n1.is_zero() || n2.is_zero() { T::SimdRealField::zero() } else { let cang = prod.simd_real() / (n1 * n2); cang.simd_clamp(-T::SimdRealField::one(), T::SimdRealField::one()) .simd_acos() } } } impl AbsDiffEq for Unit> where T: Scalar + AbsDiffEq, S: RawStorage, T::Epsilon: Clone, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.as_ref().abs_diff_eq(other.as_ref(), epsilon) } } impl RelativeEq for Unit> where T: Scalar + RelativeEq, S: Storage, T::Epsilon: Clone, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.as_ref() .relative_eq(other.as_ref(), epsilon, max_relative) } } impl UlpsEq for Unit> where T: Scalar + UlpsEq, S: RawStorage, T::Epsilon: Clone, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) } } impl Hash for Matrix where T: Scalar + Hash, R: Dim, C: Dim, S: RawStorage, { fn hash(&self, state: &mut H) { let (nrows, ncols) = self.shape(); (nrows, ncols).hash(state); for j in 0..ncols { for i in 0..nrows { unsafe { self.get_unchecked((i, j)).hash(state); } } } } } impl Unit> where T: Scalar, D: Dim, S: RawStorage, { /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let v = Vector3::::y_axis(); /// let v2 = v.cast::(); /// assert_eq!(v2, Vector3::::y_axis()); /// ``` pub fn cast(self) -> Unit> where T: Scalar, OVector: SupersetOf>, DefaultAllocator: Allocator, { Unit::new_unchecked(crate::convert_ref(self.as_ref())) } } impl Matrix where S: RawStorage, { /// Returns a reference to the single element in this matrix. /// /// As opposed to indexing, using this provides type-safety /// when flattening dimensions. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let v = Vector3::new(0., 0., 1.); /// let inner_product: f32 = *(v.transpose() * v).as_scalar(); /// ``` /// ///```compile_fail /// # use nalgebra::Vector3; /// let v = Vector3::new(0., 0., 1.); /// let inner_product = (v * v.transpose()).item(); // Typo, does not compile. ///``` pub fn as_scalar(&self) -> &T { &self[(0, 0)] } /// Get a mutable reference to the single element in this matrix /// /// As opposed to indexing, using this provides type-safety /// when flattening dimensions. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let v = Vector3::new(0., 0., 1.); /// let mut inner_product = (v.transpose() * v); /// *inner_product.as_scalar_mut() = 3.; /// ``` /// ///```compile_fail /// # use nalgebra::Vector3; /// let v = Vector3::new(0., 0., 1.); /// let mut inner_product = (v * v.transpose()); /// *inner_product.as_scalar_mut() = 3.; ///``` pub fn as_scalar_mut(&mut self) -> &mut T where S: RawStorageMut, { &mut self[(0, 0)] } /// Convert this 1x1 matrix by reference into a scalar. /// /// As opposed to indexing, using this provides type-safety /// when flattening dimensions. /// /// # Example /// ``` /// # use nalgebra::Vector3; /// let v = Vector3::new(0., 0., 1.); /// let mut inner_product: f32 = (v.transpose() * v).to_scalar(); /// ``` /// ///```compile_fail /// # use nalgebra::Vector3; /// let v = Vector3::new(0., 0., 1.); /// let mut inner_product: f32 = (v * v.transpose()).to_scalar(); ///``` pub fn to_scalar(&self) -> T where T: Clone, { self.as_scalar().clone() } } impl super::alias::Matrix1 { /// Convert this 1x1 matrix into a scalar. /// /// As opposed to indexing, using this provides type-safety /// when flattening dimensions. /// /// # Example /// ``` /// # use nalgebra::{Vector3, Matrix2, U1}; /// let v = Vector3::new(0., 0., 1.); /// let inner_product: f32 = (v.transpose() * v).into_scalar(); /// assert_eq!(inner_product, 1.); /// ``` /// ///```compile_fail /// # use nalgebra::Vector3; /// let v = Vector3::new(0., 0., 1.); /// let mut inner_product: f32 = (v * v.transpose()).into_scalar(); ///``` pub fn into_scalar(self) -> T { let [[scalar]] = self.data.0; scalar } } nalgebra-0.33.0/src/base/matrix_simba.rs000064400000000000000000000024570072674642500162500ustar 00000000000000use simba::simd::SimdValue; use crate::base::allocator::Allocator; use crate::base::dimension::Dim; use crate::base::{DefaultAllocator, OMatrix, Scalar}; /* * * Simd structures. * */ impl SimdValue for OMatrix where T: Scalar + SimdValue, R: Dim, C: Dim, T::Element: Scalar, DefaultAllocator: Allocator, { const LANES: usize = T::LANES; type Element = OMatrix; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { val.map(T::splat) } #[inline] fn extract(&self, i: usize) -> Self::Element { self.map(|e| e.extract(i)) } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { self.map(|e| e.extract_unchecked(i)) } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.zip_apply(&val, |a, b| { a.replace(i, b); }) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.zip_apply(&val, |a, b| { a.replace_unchecked(i, b); }) } fn select(self, cond: Self::SimdBool, other: Self) -> Self { self.zip_map(&other, |a, b| a.select(cond, b)) } } nalgebra-0.33.0/src/base/matrix_view.rs000064400000000000000000001316360072674642500161310ustar 00000000000000use std::marker::PhantomData; use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo}; use std::slice; use crate::base::allocator::Allocator; use crate::base::default_allocator::DefaultAllocator; use crate::base::dimension::{Const, Dim, DimName, Dyn, IsNotStaticOne, U1}; use crate::base::iter::MatrixIter; use crate::base::storage::{IsContiguous, Owned, RawStorage, RawStorageMut, Storage}; use crate::base::{Matrix, Scalar}; use crate::constraint::{DimEq, ShapeConstraint}; use crate::ReshapableStorage; macro_rules! view_storage_impl ( ($doc: expr; $Storage: ident as $SRef: ty; $legacy_name:ident => $T: ident.$get_addr: ident ($Ptr: ty as $Ref: ty)) => { #[doc = $doc] #[derive(Debug)] pub struct $T<'a, T, R: Dim, C: Dim, RStride: Dim, CStride: Dim> { ptr: $Ptr, shape: (R, C), strides: (RStride, CStride), _phantoms: PhantomData<$Ref>, } #[doc = $doc] /// /// This type alias exists only for legacy purposes and is deprecated. It will be removed /// in a future release. Please use /// [` #[doc = stringify!($T)] /// `] instead. See [issue #1076](https://github.com/dimforge/nalgebra/issues/1076) /// for the rationale. #[deprecated = "Use ViewStorage(Mut) instead."] pub type $legacy_name<'a, T, R, C, RStride, CStride> = $T<'a, T, R, C, RStride, CStride>; unsafe impl<'a, T: Send, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Send for $T<'a, T, R, C, RStride, CStride> {} unsafe impl<'a, T: Sync, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Sync for $T<'a, T, R, C, RStride, CStride> {} 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), strides: (RStride, CStride)) -> Self where RStride: Dim, CStride: Dim { $T { ptr, shape, strides, _phantoms: PhantomData } } } // 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(storage: $SRef, start: (usize, usize), shape: (R, C)) -> $T<'a, T, R, C, S::RStride, S::CStride> where RStor: Dim, CStor: Dim, S: $Storage { let strides = storage.strides(); $T::new_with_strides_unchecked(storage, start, shape, strides) } /// Create a new matrix view without bounds checking. /// /// # Safety /// /// `strides` must be a valid stride indexing. #[inline] pub unsafe fn new_with_strides_unchecked(storage: $SRef, start: (usize, usize), shape: (R, C), strides: (RStride, CStride)) -> $T<'a, T, R, C, RStride, CStride> where RStor: Dim, CStor: Dim, S: $Storage, RStride: Dim, CStride: Dim { $T::from_raw_parts(storage.$get_addr(start.0, start.1), shape, strides) } } impl <'a, T, R: Dim, C: Dim, RStride: Dim, CStride: Dim> $T<'a, T, R, C, RStride, CStride> where Self: RawStorage + IsContiguous { /// Extracts the original slice from this storage. pub fn into_slice(self) -> &'a [T] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); unsafe { slice::from_raw_parts(self.ptr, sz + 1) } } else { unsafe { slice::from_raw_parts(self.ptr, 0) } } } } } ); view_storage_impl!("A matrix data storage for a matrix view. Only contains an internal reference \ to another matrix data storage."; RawStorage as &'a S; SliceStorage => ViewStorage.get_address_unchecked(*const T as &'a T)); view_storage_impl!("A mutable matrix data storage for mutable matrix view. Only contains an \ internal mutable reference to another matrix data storage."; RawStorageMut as &'a mut S; SliceStorageMut => ViewStorageMut.get_address_unchecked_mut(*mut T as &'a mut T) ); impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Copy for ViewStorage<'a, T, R, C, RStride, CStride> { } impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Clone for ViewStorage<'a, T, R, C, RStride, CStride> { #[inline] fn clone(&self) -> Self { *self } } impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> ViewStorageMut<'a, T, R, C, RStride, CStride> where Self: RawStorageMut + IsContiguous, { /// Extracts the original slice from this storage pub fn into_slice_mut(self) -> &'a mut [T] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) } } else { unsafe { slice::from_raw_parts_mut(self.ptr, 0) } } } } macro_rules! storage_impl( ($($T: ident),* $(,)*) => {$( unsafe impl<'a, T, R: Dim, C: Dim, RStride: Dim, CStride: Dim> RawStorage for $T<'a, T, R, C, RStride, CStride> { type RStride = RStride; type CStride = CStride; #[inline] fn ptr(&self) -> *const T { self.ptr } #[inline] fn shape(&self) -> (R, C) { self.shape } #[inline] fn strides(&self) -> (Self::RStride, Self::CStride) { self.strides } #[inline] fn is_contiguous(&self) -> bool { // Common cases that can be deduced at compile-time even if one of the dimensions // is Dyn. if (RStride::is::() && C::is::()) || // Column vector. (CStride::is::() && R::is::()) { // Row vector. true } else { let (nrows, _) = self.shape(); let (srows, scols) = self.strides(); srows.value() == 1 && scols.value() == nrows.value() } } #[inline] unsafe fn as_slice_unchecked(&self) -> &[T] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); slice::from_raw_parts(self.ptr, sz + 1) } else { slice::from_raw_parts(self.ptr, 0) } } } unsafe impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> Storage for $T<'a, T, R, C, RStride, CStride> { #[inline] fn into_owned(self) -> Owned where DefaultAllocator: Allocator { self.clone_owned() } #[inline] fn clone_owned(&self) -> Owned where DefaultAllocator: Allocator { let (nrows, ncols) = self.shape(); let it = MatrixIter::new(self).cloned(); DefaultAllocator::allocate_from_iterator(nrows, ncols, it) } #[inline] fn forget_elements(self) { // No cleanup required. } } )*} ); storage_impl!(ViewStorage, ViewStorageMut); unsafe impl<'a, T, R: Dim, C: Dim, RStride: Dim, CStride: Dim> RawStorageMut for ViewStorageMut<'a, T, R, C, RStride, CStride> { #[inline] fn ptr_mut(&mut self) -> *mut T { self.ptr } #[inline] unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { let (nrows, ncols) = self.shape(); if nrows.value() != 0 && ncols.value() != 0 { let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); slice::from_raw_parts_mut(self.ptr, sz + 1) } else { slice::from_raw_parts_mut(self.ptr, 0) } } } unsafe impl<'a, T, R: Dim, CStride: Dim> IsContiguous for ViewStorage<'a, T, R, U1, U1, CStride> {} unsafe impl<'a, T, R: Dim, CStride: Dim> IsContiguous for ViewStorageMut<'a, T, R, U1, U1, CStride> { } unsafe impl<'a, T, R: DimName, C: Dim + IsNotStaticOne> IsContiguous for ViewStorage<'a, T, R, C, U1, R> { } unsafe impl<'a, T, R: DimName, C: Dim + IsNotStaticOne> IsContiguous for ViewStorageMut<'a, T, R, C, U1, R> { } impl> Matrix { #[inline] fn assert_view_index( &self, start: (usize, usize), shape: (usize, usize), steps: (usize, usize), ) { let my_shape = self.shape(); // NOTE: we don't do any subtraction to avoid underflow for zero-sized matrices. // // Terms that would have been negative are moved to the other side of the inequality // instead. assert!( start.0 + (steps.0 + 1) * shape.0 <= my_shape.0 + steps.0, "Matrix slicing out of bounds." ); assert!( start.1 + (steps.1 + 1) * shape.1 <= my_shape.1 + steps.1, "Matrix slicing out of bounds." ); } } macro_rules! matrix_view_impl ( ($me: ident: $Me: ty, $MatrixView: ident, $ViewStorage: ident, $Storage: ident.$get_addr: ident (), $data: expr; $row: ident, $row_part: ident, $rows: ident, $rows_with_step: ident, $fixed_rows: ident, $fixed_rows_with_step: ident, $rows_generic: ident, $rows_generic_with_step: ident, $column: ident, $column_part: ident, $columns: ident, $columns_with_step: ident, $fixed_columns: ident, $fixed_columns_with_step: ident, $columns_generic: ident, $columns_generic_with_step: ident, $slice: ident => $view:ident, $slice_with_steps: ident => $view_with_steps:ident, $fixed_slice: ident => $fixed_view:ident, $fixed_slice_with_steps: ident => $fixed_view_with_steps:ident, $generic_slice: ident => $generic_view:ident, $generic_slice_with_steps: ident => $generic_view_with_steps:ident, $rows_range_pair: ident, $columns_range_pair: ident) => { /* * * Row slicing. * */ /// Returns a view containing the i-th row of this matrix. #[inline] pub fn $row($me: $Me, i: usize) -> $MatrixView<'_, T, U1, C, S::RStride, S::CStride> { $me.$fixed_rows::<1>(i) } /// Returns a view containing the `n` first elements of the i-th row of this matrix. #[inline] pub fn $row_part($me: $Me, i: usize, n: usize) -> $MatrixView<'_, T, U1, Dyn, S::RStride, S::CStride> { $me.$generic_view((i, 0), (Const::<1>, Dyn(n))) } /// Extracts from this matrix a set of consecutive rows. #[inline] pub fn $rows($me: $Me, first_row: usize, nrows: usize) -> $MatrixView<'_, T, Dyn, C, S::RStride, S::CStride> { $me.$rows_generic(first_row, Dyn(nrows)) } /// Extracts from this matrix a set of consecutive rows regularly skipping `step` rows. #[inline] pub fn $rows_with_step($me: $Me, first_row: usize, nrows: usize, step: usize) -> $MatrixView<'_, T, Dyn, C, Dyn, S::CStride> { $me.$rows_generic_with_step(first_row, Dyn(nrows), step) } /// Extracts a compile-time number of consecutive rows from this matrix. #[inline] pub fn $fixed_rows($me: $Me, first_row: usize) -> $MatrixView<'_, T, Const, C, S::RStride, S::CStride> { $me.$rows_generic(first_row, Const::) } /// Extracts from this matrix a compile-time number of rows regularly skipping `step` /// rows. #[inline] pub fn $fixed_rows_with_step($me: $Me, first_row: usize, step: usize) -> $MatrixView<'_, T, Const, C, Dyn, S::CStride> { $me.$rows_generic_with_step(first_row, Const::, step) } /// Extracts from this matrix `nrows` rows regularly skipping `step` rows. Both /// argument may or may not be values known at compile-time. #[inline] pub fn $rows_generic($me: $Me, row_start: usize, nrows: RView) -> $MatrixView<'_, T, RView, C, S::RStride, S::CStride> { let my_shape = $me.shape_generic(); $me.assert_view_index((row_start, 0), (nrows.value(), my_shape.1.value()), (0, 0)); let shape = (nrows, my_shape.1); unsafe { let data = $ViewStorage::new_unchecked($data, (row_start, 0), shape); Matrix::from_data_statically_unchecked(data) } } /// Extracts from this matrix `nrows` rows regularly skipping `step` rows. Both /// argument may or may not be values known at compile-time. #[inline] pub fn $rows_generic_with_step($me: $Me, row_start: usize, nrows: RView, step: usize) -> $MatrixView<'_, T, RView, C, Dyn, S::CStride> where RView: Dim { let my_shape = $me.shape_generic(); let my_strides = $me.data.strides(); $me.assert_view_index((row_start, 0), (nrows.value(), my_shape.1.value()), (step, 0)); let strides = (Dyn((step + 1) * my_strides.0.value()), my_strides.1); let shape = (nrows, my_shape.1); unsafe { let data = $ViewStorage::new_with_strides_unchecked($data, (row_start, 0), shape, strides); Matrix::from_data_statically_unchecked(data) } } /* * * Column slicing. * */ /// Returns a view containing the i-th column of this matrix. #[inline] pub fn $column($me: $Me, i: usize) -> $MatrixView<'_, T, R, U1, S::RStride, S::CStride> { $me.$fixed_columns::<1>(i) } /// Returns a view containing the `n` first elements of the i-th column of this matrix. #[inline] pub fn $column_part($me: $Me, i: usize, n: usize) -> $MatrixView<'_, T, Dyn, U1, S::RStride, S::CStride> { $me.$generic_view((0, i), (Dyn(n), Const::<1>)) } /// Extracts from this matrix a set of consecutive columns. #[inline] pub fn $columns($me: $Me, first_col: usize, ncols: usize) -> $MatrixView<'_, T, R, Dyn, S::RStride, S::CStride> { $me.$columns_generic(first_col, Dyn(ncols)) } /// Extracts from this matrix a set of consecutive columns regularly skipping `step` /// columns. #[inline] pub fn $columns_with_step($me: $Me, first_col: usize, ncols: usize, step: usize) -> $MatrixView<'_, T, R, Dyn, S::RStride, Dyn> { $me.$columns_generic_with_step(first_col, Dyn(ncols), step) } /// Extracts a compile-time number of consecutive columns from this matrix. #[inline] pub fn $fixed_columns($me: $Me, first_col: usize) -> $MatrixView<'_, T, R, Const, S::RStride, S::CStride> { $me.$columns_generic(first_col, Const::) } /// Extracts from this matrix a compile-time number of columns regularly skipping /// `step` columns. #[inline] pub fn $fixed_columns_with_step($me: $Me, first_col: usize, step: usize) -> $MatrixView<'_, T, R, Const, S::RStride, Dyn> { $me.$columns_generic_with_step(first_col, Const::, step) } /// Extracts from this matrix `ncols` columns. The number of columns may or may not be /// known at compile-time. #[inline] pub fn $columns_generic($me: $Me, first_col: usize, ncols: CView) -> $MatrixView<'_, T, R, CView, S::RStride, S::CStride> { let my_shape = $me.shape_generic(); $me.assert_view_index((0, first_col), (my_shape.0.value(), ncols.value()), (0, 0)); let shape = (my_shape.0, ncols); unsafe { let data = $ViewStorage::new_unchecked($data, (0, first_col), shape); Matrix::from_data_statically_unchecked(data) } } /// Extracts from this matrix `ncols` columns skipping `step` columns. Both argument may /// or may not be values known at compile-time. #[inline] pub fn $columns_generic_with_step($me: $Me, first_col: usize, ncols: CView, step: usize) -> $MatrixView<'_, T, R, CView, S::RStride, Dyn> { let my_shape = $me.shape_generic(); let my_strides = $me.data.strides(); $me.assert_view_index((0, first_col), (my_shape.0.value(), ncols.value()), (0, step)); let strides = (my_strides.0, Dyn((step + 1) * my_strides.1.value())); let shape = (my_shape.0, ncols); unsafe { let data = $ViewStorage::new_with_strides_unchecked($data, (0, first_col), shape, strides); Matrix::from_data_statically_unchecked(data) } } /* * * General slicing. * */ /// Slices this matrix starting at its component `(irow, icol)` and with `(nrows, ncols)` /// consecutive elements. #[inline] #[deprecated = slice_deprecation_note!($view)] pub fn $slice($me: $Me, start: (usize, usize), shape: (usize, usize)) -> $MatrixView<'_, T, Dyn, Dyn, S::RStride, S::CStride> { $me.$view(start, shape) } /// Return a view of this matrix starting at its component `(irow, icol)` and with `(nrows, ncols)` /// consecutive elements. #[inline] pub fn $view($me: $Me, start: (usize, usize), shape: (usize, usize)) -> $MatrixView<'_, T, Dyn, Dyn, S::RStride, S::CStride> { $me.assert_view_index(start, shape, (0, 0)); let shape = (Dyn(shape.0), Dyn(shape.1)); unsafe { let data = $ViewStorage::new_unchecked($data, start, shape); Matrix::from_data_statically_unchecked(data) } } /// Slices this matrix starting at its component `(start.0, start.1)` and with /// `(shape.0, shape.1)` components. Each row (resp. column) of the sliced matrix is /// separated by `steps.0` (resp. `steps.1`) ignored rows (resp. columns) of the /// original matrix. #[inline] #[deprecated = slice_deprecation_note!($view_with_steps)] pub fn $slice_with_steps($me: $Me, start: (usize, usize), shape: (usize, usize), steps: (usize, usize)) -> $MatrixView<'_, T, Dyn, Dyn, Dyn, Dyn> { $me.$view_with_steps(start, shape, steps) } /// Return a view of this matrix starting at its component `(start.0, start.1)` and with /// `(shape.0, shape.1)` components. Each row (resp. column) of the matrix view is /// separated by `steps.0` (resp. `steps.1`) ignored rows (resp. columns) of the /// original matrix. #[inline] pub fn $view_with_steps($me: $Me, start: (usize, usize), shape: (usize, usize), steps: (usize, usize)) -> $MatrixView<'_, T, Dyn, Dyn, Dyn, Dyn> { let shape = (Dyn(shape.0), Dyn(shape.1)); $me.$generic_view_with_steps(start, shape, steps) } /// 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($me: $Me, irow: usize, icol: usize) -> $MatrixView<'_, T, Const, Const, S::RStride, S::CStride> { $me.$fixed_view(irow, icol) } /// Return a view of this matrix starting at its component `(irow, icol)` and with /// `(RVIEW, CVIEW)` consecutive components. #[inline] pub fn $fixed_view($me: $Me, irow: usize, icol: usize) -> $MatrixView<'_, T, Const, Const, S::RStride, S::CStride> { $me.assert_view_index((irow, icol), (RVIEW, CVIEW), (0, 0)); let shape = (Const::, Const::); unsafe { let data = $ViewStorage::new_unchecked($data, (irow, icol), shape); Matrix::from_data_statically_unchecked(data) } } /// Slices this matrix starting at its component `(start.0, start.1)` and with /// `(RVIEW, CVIEW)` components. Each row (resp. column) of the sliced /// matrix is separated by `steps.0` (resp. `steps.1`) ignored rows (resp. columns) of /// the original matrix. #[inline] #[deprecated = slice_deprecation_note!($fixed_view_with_steps)] pub fn $fixed_slice_with_steps($me: $Me, start: (usize, usize), steps: (usize, usize)) -> $MatrixView<'_, T, Const, Const, Dyn, Dyn> { $me.$fixed_view_with_steps(start, steps) } /// Returns a view of this matrix starting at its component `(start.0, start.1)` and with /// `(RVIEW, CVIEW)` components. Each row (resp. column) of the matrix view /// is separated by `steps.0` (resp. `steps.1`) ignored rows (resp. columns) of /// the original matrix. #[inline] pub fn $fixed_view_with_steps($me: $Me, start: (usize, usize), steps: (usize, usize)) -> $MatrixView<'_, T, Const, Const, Dyn, Dyn> { let shape = (Const::, Const::); $me.$generic_view_with_steps(start, shape, steps) } /// Creates a slice that may or may not have a fixed size and stride. #[inline] #[deprecated = slice_deprecation_note!($generic_view)] pub fn $generic_slice($me: $Me, start: (usize, usize), shape: (RView, CView)) -> $MatrixView<'_, T, RView, CView, S::RStride, S::CStride> where RView: Dim, CView: Dim { $me.$generic_view(start, shape) } /// Creates a matrix view that may or may not have a fixed size and stride. #[inline] pub fn $generic_view($me: $Me, start: (usize, usize), shape: (RView, CView)) -> $MatrixView<'_, T, RView, CView, S::RStride, S::CStride> where RView: Dim, CView: Dim { $me.assert_view_index(start, (shape.0.value(), shape.1.value()), (0, 0)); unsafe { let data = $ViewStorage::new_unchecked($data, start, shape); Matrix::from_data_statically_unchecked(data) } } /// Creates a slice that may or may not have a fixed size and stride. #[inline] #[deprecated = slice_deprecation_note!($generic_view_with_steps)] pub fn $generic_slice_with_steps($me: $Me, start: (usize, usize), shape: (RView, CView), steps: (usize, usize)) -> $MatrixView<'_, T, RView, CView, Dyn, Dyn> where RView: Dim, CView: Dim { $me.$generic_view_with_steps(start, shape, steps) } /// Creates a matrix view that may or may not have a fixed size and stride. #[inline] pub fn $generic_view_with_steps($me: $Me, start: (usize, usize), shape: (RView, CView), steps: (usize, usize)) -> $MatrixView<'_, T, RView, CView, Dyn, Dyn> where RView: Dim, CView: Dim { $me.assert_view_index(start, (shape.0.value(), shape.1.value()), steps); let my_strides = $me.data.strides(); let strides = (Dyn((steps.0 + 1) * my_strides.0.value()), Dyn((steps.1 + 1) * my_strides.1.value())); unsafe { let data = $ViewStorage::new_with_strides_unchecked($data, start, shape, strides); Matrix::from_data_statically_unchecked(data) } } /* * * Splitting. * */ /// Splits this `NxM` matrix into two parts delimited by two ranges. /// /// Panics if the ranges overlap or if the first range is empty. #[inline] pub fn $rows_range_pair, Range2: DimRange>($me: $Me, r1: Range1, r2: Range2) -> ($MatrixView<'_, T, Range1::Size, C, S::RStride, S::CStride>, $MatrixView<'_, T, Range2::Size, C, S::RStride, S::CStride>) { let (nrows, ncols) = $me.shape_generic(); let strides = $me.data.strides(); let start1 = r1.begin(nrows); let start2 = r2.begin(nrows); let end1 = r1.end(nrows); let end2 = r2.end(nrows); let nrows1 = r1.size(nrows); let nrows2 = r2.size(nrows); assert!(start2 >= end1 || start1 >= end2, "Rows range pair: the ranges must not overlap."); assert!(end2 <= nrows.value(), "Rows range pair: index out of range."); unsafe { let ptr1 = $data.$get_addr(start1, 0); let ptr2 = $data.$get_addr(start2, 0); let data1 = $ViewStorage::from_raw_parts(ptr1, (nrows1, ncols), strides); let data2 = $ViewStorage::from_raw_parts(ptr2, (nrows2, ncols), strides); let view1 = Matrix::from_data_statically_unchecked(data1); let view2 = Matrix::from_data_statically_unchecked(data2); (view1, view2) } } /// Splits this `NxM` matrix into two parts delimited by two ranges. /// /// Panics if the ranges overlap or if the first range is empty. #[inline] pub fn $columns_range_pair, Range2: DimRange>($me: $Me, r1: Range1, r2: Range2) -> ($MatrixView<'_, T, R, Range1::Size, S::RStride, S::CStride>, $MatrixView<'_, T, R, Range2::Size, S::RStride, S::CStride>) { let (nrows, ncols) = $me.shape_generic(); let strides = $me.data.strides(); let start1 = r1.begin(ncols); let start2 = r2.begin(ncols); let end1 = r1.end(ncols); let end2 = r2.end(ncols); let ncols1 = r1.size(ncols); let ncols2 = r2.size(ncols); assert!(start2 >= end1 || start1 >= end2, "Columns range pair: the ranges must not overlap."); assert!(end2 <= ncols.value(), "Columns range pair: index out of range."); unsafe { let ptr1 = $data.$get_addr(0, start1); let ptr2 = $data.$get_addr(0, start2); let data1 = $ViewStorage::from_raw_parts(ptr1, (nrows, ncols1), strides); let data2 = $ViewStorage::from_raw_parts(ptr2, (nrows, ncols2), strides); let view1 = Matrix::from_data_statically_unchecked(data1); let view2 = Matrix::from_data_statically_unchecked(data2); (view1, view2) } } } ); /// A matrix slice. /// /// This type alias exists only for legacy purposes and is deprecated. It will be removed /// in a future release. Please use [`MatrixView`] instead. /// See [issue #1076](https://github.com/dimforge/nalgebra/issues/1076) /// for the rationale. #[deprecated = "Use MatrixView instead."] pub type MatrixSlice<'a, T, R, C, RStride = U1, CStride = R> = MatrixView<'a, T, R, C, RStride, CStride>; /// A matrix view. pub type MatrixView<'a, T, R, C, RStride = U1, CStride = R> = Matrix>; /// A mutable matrix slice. /// /// This type alias exists only for legacy purposes and is deprecated. It will be removed /// in a future release. Please use [`MatrixViewMut`] instead. /// See [issue #1076](https://github.com/dimforge/nalgebra/issues/1076) /// for the rationale. #[deprecated = "Use MatrixViewMut instead."] pub type MatrixSliceMut<'a, T, R, C, RStride = U1, CStride = R> = MatrixViewMut<'a, T, R, C, RStride, CStride>; /// A mutable matrix view. pub type MatrixViewMut<'a, T, R, C, RStride = U1, CStride = R> = Matrix>; /// # Views based on index and length impl> Matrix { matrix_view_impl!( self: &Self, MatrixView, ViewStorage, RawStorage.get_address_unchecked(), &self.data; row, row_part, rows, rows_with_step, fixed_rows, fixed_rows_with_step, rows_generic, rows_generic_with_step, column, column_part, columns, columns_with_step, fixed_columns, fixed_columns_with_step, columns_generic, columns_generic_with_step, slice => view, slice_with_steps => view_with_steps, fixed_slice => fixed_view, fixed_slice_with_steps => fixed_view_with_steps, generic_slice => generic_view, generic_slice_with_steps => generic_view_with_steps, rows_range_pair, columns_range_pair); } /// # Mutable views based on index and length impl> Matrix { matrix_view_impl!( self: &mut Self, MatrixViewMut, ViewStorageMut, RawStorageMut.get_address_unchecked_mut(), &mut self.data; row_mut, row_part_mut, rows_mut, rows_with_step_mut, fixed_rows_mut, fixed_rows_with_step_mut, rows_generic_mut, rows_generic_with_step_mut, column_mut, column_part_mut, columns_mut, columns_with_step_mut, fixed_columns_mut, fixed_columns_with_step_mut, columns_generic_mut, columns_generic_with_step_mut, slice_mut => view_mut, slice_with_steps_mut => view_with_steps_mut, fixed_slice_mut => fixed_view_mut, fixed_slice_with_steps_mut => fixed_view_with_steps_mut, generic_slice_mut => generic_view_mut, generic_slice_with_steps_mut => generic_view_with_steps_mut, rows_range_pair_mut, columns_range_pair_mut); } /// A range with a size that may be known at compile-time. /// /// This may be: /// * A single `usize` index, e.g., `4` /// * A left-open range `std::ops::RangeTo`, e.g., `.. 4` /// * A right-open range `std::ops::RangeFrom`, e.g., `4 ..` /// * A full range `std::ops::RangeFull`, e.g., `..` pub trait DimRange { /// Type of the range size. May be a type-level integer. type Size: Dim; /// The start index of the range. fn begin(&self, shape: D) -> usize; // NOTE: this is the index immediately after the last index. /// The index immediately after the last index inside of the range. fn end(&self, shape: D) -> usize; /// The number of elements of the range, i.e., `self.end - self.begin`. fn size(&self, shape: D) -> Self::Size; } /// A range with a size that may be known at compile-time. /// /// This is merely a legacy trait alias to minimize breakage. Use the [`DimRange`] trait instead. #[deprecated = slice_deprecation_note!(DimRange)] pub trait SliceRange: DimRange {} #[allow(deprecated)] impl, D: Dim> SliceRange for R {} impl DimRange for usize { type Size = U1; #[inline(always)] fn begin(&self, _: D) -> usize { *self } #[inline(always)] fn end(&self, _: D) -> usize { *self + 1 } #[inline(always)] fn size(&self, _: D) -> Self::Size { Const::<1> } } impl DimRange for Range { type Size = Dyn; #[inline(always)] fn begin(&self, _: D) -> usize { self.start } #[inline(always)] fn end(&self, _: D) -> usize { self.end } #[inline(always)] fn size(&self, _: D) -> Self::Size { Dyn(self.end - self.start) } } impl DimRange for RangeFrom { type Size = Dyn; #[inline(always)] fn begin(&self, _: D) -> usize { self.start } #[inline(always)] fn end(&self, dim: D) -> usize { dim.value() } #[inline(always)] fn size(&self, dim: D) -> Self::Size { Dyn(dim.value() - self.start) } } impl DimRange for RangeTo { type Size = Dyn; #[inline(always)] fn begin(&self, _: D) -> usize { 0 } #[inline(always)] fn end(&self, _: D) -> usize { self.end } #[inline(always)] fn size(&self, _: D) -> Self::Size { Dyn(self.end) } } impl DimRange for RangeFull { type Size = D; #[inline(always)] fn begin(&self, _: D) -> usize { 0 } #[inline(always)] fn end(&self, dim: D) -> usize { dim.value() } #[inline(always)] fn size(&self, dim: D) -> Self::Size { dim } } impl DimRange for RangeInclusive { type Size = Dyn; #[inline(always)] fn begin(&self, _: D) -> usize { *self.start() } #[inline(always)] fn end(&self, _: D) -> usize { *self.end() + 1 } #[inline(always)] fn size(&self, _: D) -> Self::Size { Dyn(*self.end() + 1 - *self.start()) } } // TODO: see how much of this overlaps with the general indexing // methods from indexing.rs. impl> Matrix { /// Slices a sub-matrix containing the rows indexed by the range `rows` and the columns indexed /// by the range `cols`. #[inline] #[must_use] #[deprecated = slice_deprecation_note!(view_range)] pub fn slice_range( &self, rows: RowRange, cols: ColRange, ) -> MatrixView<'_, T, RowRange::Size, ColRange::Size, S::RStride, S::CStride> where RowRange: DimRange, ColRange: DimRange, { let (nrows, ncols) = self.shape_generic(); self.generic_view( (rows.begin(nrows), cols.begin(ncols)), (rows.size(nrows), cols.size(ncols)), ) } /// Returns a view containing the rows indexed by the range `rows` and the columns indexed /// by the range `cols`. #[inline] #[must_use] pub fn view_range( &self, rows: RowRange, cols: ColRange, ) -> MatrixView<'_, T, RowRange::Size, ColRange::Size, S::RStride, S::CStride> where RowRange: DimRange, ColRange: DimRange, { let (nrows, ncols) = self.shape_generic(); self.generic_view( (rows.begin(nrows), cols.begin(ncols)), (rows.size(nrows), cols.size(ncols)), ) } /// View containing all the rows indexed by the range `rows`. #[inline] #[must_use] pub fn rows_range>( &self, rows: RowRange, ) -> MatrixView<'_, T, RowRange::Size, C, S::RStride, S::CStride> { self.view_range(rows, ..) } /// View containing all the columns indexed by the range `rows`. #[inline] #[must_use] pub fn columns_range>( &self, cols: ColRange, ) -> MatrixView<'_, T, R, ColRange::Size, S::RStride, S::CStride> { self.view_range(.., cols) } } // TODO: see how much of this overlaps with the general indexing // methods from indexing.rs. impl> Matrix { /// Slices a mutable sub-matrix containing the rows indexed by the range `rows` and the columns /// indexed by the range `cols`. #[deprecated = slice_deprecation_note!(view_range_mut)] pub fn slice_range_mut( &mut self, rows: RowRange, cols: ColRange, ) -> MatrixViewMut<'_, T, RowRange::Size, ColRange::Size, S::RStride, S::CStride> where RowRange: DimRange, ColRange: DimRange, { self.view_range_mut(rows, cols) } /// Return a mutable view containing the rows indexed by the range `rows` and the columns /// indexed by the range `cols`. pub fn view_range_mut( &mut self, rows: RowRange, cols: ColRange, ) -> MatrixViewMut<'_, T, RowRange::Size, ColRange::Size, S::RStride, S::CStride> where RowRange: DimRange, ColRange: DimRange, { let (nrows, ncols) = self.shape_generic(); self.generic_view_mut( (rows.begin(nrows), cols.begin(ncols)), (rows.size(nrows), cols.size(ncols)), ) } /// Mutable view containing all the rows indexed by the range `rows`. #[inline] pub fn rows_range_mut>( &mut self, rows: RowRange, ) -> MatrixViewMut<'_, T, RowRange::Size, C, S::RStride, S::CStride> { self.view_range_mut(rows, ..) } /// Mutable view containing all the columns indexed by the range `cols`. #[inline] pub fn columns_range_mut>( &mut self, cols: ColRange, ) -> MatrixViewMut<'_, T, R, ColRange::Size, S::RStride, S::CStride> { self.view_range_mut(.., cols) } } impl<'a, T, R, C, RStride, CStride> From> for MatrixView<'a, T, R, C, RStride, CStride> where R: Dim, C: Dim, RStride: Dim, CStride: Dim, { fn from(view_mut: MatrixViewMut<'a, T, R, C, RStride, CStride>) -> Self { let data = ViewStorage { ptr: view_mut.data.ptr, shape: view_mut.data.shape, strides: view_mut.data.strides, _phantoms: PhantomData, }; unsafe { Matrix::from_data_statically_unchecked(data) } } } impl Matrix where R: Dim, C: Dim, S: RawStorage, { /// Returns this matrix as a view. /// /// The returned view type is generally ambiguous unless specified. /// This is particularly useful when working with functions or methods that take /// matrix views as input. /// /// # Panics /// Panics if the dimensions of the view and the matrix are not compatible and this cannot /// be proven at compile-time. This might happen, for example, when constructing a static /// view of size 3x3 from a dynamically sized matrix of dimension 5x5. /// /// # Examples /// ``` /// use nalgebra::{DMatrixSlice, SMatrixView}; /// /// fn consume_view(_: DMatrixSlice) {} /// /// let matrix = nalgebra::Matrix3::zeros(); /// consume_view(matrix.as_view()); /// /// let dynamic_view: DMatrixSlice = matrix.as_view(); /// let static_view_from_dyn: SMatrixView = dynamic_view.as_view(); /// ``` pub fn as_view( &self, ) -> MatrixView<'_, T, RView, CView, RViewStride, CViewStride> where RView: Dim, CView: Dim, RViewStride: Dim, CViewStride: Dim, ShapeConstraint: DimEq + DimEq + DimEq + DimEq, { // Defer to (&matrix).into() self.into() } } impl Matrix where R: Dim, C: Dim, S: RawStorageMut, { /// Returns this matrix as a mutable view. /// /// The returned view type is generally ambiguous unless specified. /// This is particularly useful when working with functions or methods that take /// matrix views as input. /// /// # Panics /// Panics if the dimensions of the view and the matrix are not compatible and this cannot /// be proven at compile-time. This might happen, for example, when constructing a static /// view of size 3x3 from a dynamically sized matrix of dimension 5x5. /// /// # Examples /// ``` /// use nalgebra::{DMatrixViewMut, SMatrixViewMut}; /// /// fn consume_view(_: DMatrixViewMut) {} /// /// let mut matrix = nalgebra::Matrix3::zeros(); /// consume_view(matrix.as_view_mut()); /// /// let mut dynamic_view: DMatrixViewMut = matrix.as_view_mut(); /// let static_view_from_dyn: SMatrixViewMut = dynamic_view.as_view_mut(); /// ``` pub fn as_view_mut( &mut self, ) -> MatrixViewMut<'_, T, RView, CView, RViewStride, CViewStride> where RView: Dim, CView: Dim, RViewStride: Dim, CViewStride: Dim, ShapeConstraint: DimEq + DimEq + DimEq + DimEq, { // Defer to (&mut matrix).into() self.into() } } // TODO: Arbitrary strides? impl<'a, T, R1, C1, R2, C2> ReshapableStorage for ViewStorage<'a, T, R1, C1, U1, R1> where T: Scalar, R1: Dim, C1: Dim, R2: Dim, C2: Dim, { type Output = ViewStorage<'a, T, R2, C2, U1, R2>; fn reshape_generic(self, nrows: R2, ncols: C2) -> Self::Output { let (r1, c1) = self.shape(); assert_eq!(nrows.value() * ncols.value(), r1.value() * c1.value()); let ptr = self.ptr(); let new_shape = (nrows, ncols); let strides = (U1::name(), nrows); unsafe { ViewStorage::from_raw_parts(ptr, new_shape, strides) } } } // TODO: Arbitrary strides? impl<'a, T, R1, C1, R2, C2> ReshapableStorage for ViewStorageMut<'a, T, R1, C1, U1, R1> where T: Scalar, R1: Dim, C1: Dim, R2: Dim, C2: Dim, { type Output = ViewStorageMut<'a, T, R2, C2, U1, R2>; fn reshape_generic(mut self, nrows: R2, ncols: C2) -> Self::Output { let (r1, c1) = self.shape(); assert_eq!(nrows.value() * ncols.value(), r1.value() * c1.value()); let ptr = self.ptr_mut(); let new_shape = (nrows, ncols); let strides = (U1::name(), nrows); unsafe { ViewStorageMut::from_raw_parts(ptr, new_shape, strides) } } } nalgebra-0.33.0/src/base/min_max.rs000064400000000000000000000262500072674642500152160ustar 00000000000000use crate::storage::RawStorage; use crate::{ComplexField, Dim, Matrix, Scalar, SimdComplexField, SimdPartialOrd, Vector}; use num::{Signed, Zero}; use simba::simd::SimdSigned; /// # Find the min and max components impl> Matrix { /// Returns the absolute value of the component with the largest absolute value. /// # Example /// ``` /// # use nalgebra::Vector3; /// assert_eq!(Vector3::new(-1.0, 2.0, 3.0).amax(), 3.0); /// assert_eq!(Vector3::new(-1.0, -2.0, -3.0).amax(), 3.0); /// ``` #[inline] #[must_use] pub fn amax(&self) -> T where T: Zero + SimdSigned + SimdPartialOrd, { self.fold_with( |e| e.unwrap_or(&T::zero()).simd_abs(), |a, b| a.simd_max(b.simd_abs()), ) } /// Returns the the 1-norm of the complex component with the largest 1-norm. /// # Example /// ``` /// # use nalgebra::{Vector3, Complex}; /// assert_eq!(Vector3::new( /// Complex::new(-3.0, -2.0), /// Complex::new(1.0, 2.0), /// Complex::new(1.0, 3.0)).camax(), 5.0); /// ``` #[inline] #[must_use] pub fn camax(&self) -> T::SimdRealField where T: SimdComplexField, { self.fold_with( |e| e.unwrap_or(&T::zero()).clone().simd_norm1(), |a, b| a.simd_max(b.clone().simd_norm1()), ) } /// Returns the component with the largest value. /// # Example /// ``` /// # use nalgebra::Vector3; /// assert_eq!(Vector3::new(-1.0, 2.0, 3.0).max(), 3.0); /// assert_eq!(Vector3::new(-1.0, -2.0, -3.0).max(), -1.0); /// assert_eq!(Vector3::new(5u32, 2, 3).max(), 5); /// ``` #[inline] #[must_use] pub fn max(&self) -> T where T: SimdPartialOrd + Zero, { self.fold_with( |e| e.cloned().unwrap_or_else(T::zero), |a, b| a.simd_max(b.clone()), ) } /// Returns the absolute value of the component with the smallest absolute value. /// # Example /// ``` /// # use nalgebra::Vector3; /// assert_eq!(Vector3::new(-1.0, 2.0, -3.0).amin(), 1.0); /// assert_eq!(Vector3::new(10.0, 2.0, 30.0).amin(), 2.0); /// ``` #[inline] #[must_use] pub fn amin(&self) -> T where T: Zero + SimdPartialOrd + SimdSigned, { self.fold_with( |e| e.map(|e| e.simd_abs()).unwrap_or_else(T::zero), |a, b| a.simd_min(b.simd_abs()), ) } /// Returns the the 1-norm of the complex component with the smallest 1-norm. /// # Example /// ``` /// # use nalgebra::{Vector3, Complex}; /// assert_eq!(Vector3::new( /// Complex::new(-3.0, -2.0), /// Complex::new(1.0, 2.0), /// Complex::new(1.0, 3.0)).camin(), 3.0); /// ``` #[inline] #[must_use] pub fn camin(&self) -> T::SimdRealField where T: SimdComplexField, { self.fold_with( |e| { e.map(|e| e.clone().simd_norm1()) .unwrap_or_else(T::SimdRealField::zero) }, |a, b| a.simd_min(b.clone().simd_norm1()), ) } /// Returns the component with the smallest value. /// # Example /// ``` /// # use nalgebra::Vector3; /// assert_eq!(Vector3::new(-1.0, 2.0, 3.0).min(), -1.0); /// assert_eq!(Vector3::new(1.0, 2.0, 3.0).min(), 1.0); /// assert_eq!(Vector3::new(5u32, 2, 3).min(), 2); /// ``` #[inline] #[must_use] pub fn min(&self) -> T where T: SimdPartialOrd + Zero, { self.fold_with( |e| e.cloned().unwrap_or_else(T::zero), |a, b| a.simd_min(b.clone()), ) } /// Computes the index of the matrix component with the largest absolute value. /// /// # Examples: /// /// ``` /// # extern crate num_complex; /// # extern crate nalgebra; /// # use num_complex::Complex; /// # use nalgebra::Matrix2x3; /// let mat = Matrix2x3::new(Complex::new(11.0, 1.0), Complex::new(-12.0, 2.0), Complex::new(13.0, 3.0), /// Complex::new(21.0, 43.0), Complex::new(22.0, 5.0), Complex::new(-23.0, 0.0)); /// assert_eq!(mat.icamax_full(), (1, 0)); /// ``` #[inline] #[must_use] pub fn icamax_full(&self) -> (usize, usize) where T: ComplexField, { assert!(!self.is_empty(), "The input matrix must not be empty."); let mut the_max = unsafe { self.get_unchecked((0, 0)).clone().norm1() }; let mut the_ij = (0, 0); for j in 0..self.ncols() { for i in 0..self.nrows() { let val = unsafe { self.get_unchecked((i, j)).clone().norm1() }; if val > the_max { the_max = val; the_ij = (i, j); } } } the_ij } } impl> Matrix { /// Computes the index of the matrix component with the largest absolute value. /// /// # Examples: /// /// ``` /// # use nalgebra::Matrix2x3; /// let mat = Matrix2x3::new(11, -12, 13, /// 21, 22, -23); /// assert_eq!(mat.iamax_full(), (1, 2)); /// ``` #[inline] #[must_use] pub fn iamax_full(&self) -> (usize, usize) { assert!(!self.is_empty(), "The input matrix must not be empty."); let mut the_max = unsafe { self.get_unchecked((0, 0)).abs() }; let mut the_ij = (0, 0); for j in 0..self.ncols() { for i in 0..self.nrows() { let val = unsafe { self.get_unchecked((i, j)).abs() }; if val > the_max { the_max = val; the_ij = (i, j); } } } the_ij } } // TODO: find a way to avoid code duplication just for complex number support. /// # Find the min and max components (vector-specific methods) impl> Vector { /// Computes the index of the vector component with the largest complex or real absolute value. /// /// # Examples: /// /// ``` /// # extern crate num_complex; /// # extern crate nalgebra; /// # use num_complex::Complex; /// # use nalgebra::Vector3; /// let vec = Vector3::new(Complex::new(11.0, 3.0), Complex::new(-15.0, 0.0), Complex::new(13.0, 5.0)); /// assert_eq!(vec.icamax(), 2); /// ``` #[inline] #[must_use] pub fn icamax(&self) -> usize where T: ComplexField, { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0).clone().norm1() }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i).clone().norm1() }; if val > the_max { the_max = val; the_i = i; } } the_i } /// Computes the index and value of the vector component with the largest value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); /// assert_eq!(vec.argmax(), (2, 13)); /// ``` #[inline] #[must_use] pub fn argmax(&self) -> (usize, T) where T: PartialOrd, { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0) }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i) }; if val > the_max { the_max = val; the_i = i; } } (the_i, the_max.clone()) } /// Computes the index of the vector component with the largest value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); /// assert_eq!(vec.imax(), 2); /// ``` #[inline] #[must_use] pub fn imax(&self) -> usize where T: PartialOrd, { self.argmax().0 } /// Computes the index of the vector component with the largest absolute value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); /// assert_eq!(vec.iamax(), 1); /// ``` #[inline] #[must_use] pub fn iamax(&self) -> usize where T: PartialOrd + Signed, { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_max = unsafe { self.vget_unchecked(0).abs() }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i).abs() }; if val > the_max { the_max = val; the_i = i; } } the_i } /// Computes the index and value of the vector component with the smallest value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); /// assert_eq!(vec.argmin(), (1, -15)); /// ``` #[inline] #[must_use] pub fn argmin(&self) -> (usize, T) where T: PartialOrd, { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_min = unsafe { self.vget_unchecked(0) }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i) }; if val < the_min { the_min = val; the_i = i; } } (the_i, the_min.clone()) } /// Computes the index of the vector component with the smallest value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); /// assert_eq!(vec.imin(), 1); /// ``` #[inline] #[must_use] pub fn imin(&self) -> usize where T: PartialOrd, { self.argmin().0 } /// Computes the index of the vector component with the smallest absolute value. /// /// # Examples: /// /// ``` /// # use nalgebra::Vector3; /// let vec = Vector3::new(11, -15, 13); /// assert_eq!(vec.iamin(), 0); /// ``` #[inline] #[must_use] pub fn iamin(&self) -> usize where T: PartialOrd + Signed, { assert!(!self.is_empty(), "The input vector must not be empty."); let mut the_min = unsafe { self.vget_unchecked(0).abs() }; let mut the_i = 0; for i in 1..self.nrows() { let val = unsafe { self.vget_unchecked(i).abs() }; if val < the_min { the_min = val; the_i = i; } } the_i } } nalgebra-0.33.0/src/base/mod.rs000064400000000000000000000025500072674642500143420ustar 00000000000000//! [Reexported at the root of this crate.] Data structures for vector and matrix computations. pub mod allocator; mod blas; pub mod constraint; pub mod coordinates; pub mod default_allocator; pub mod dimension; pub mod iter; mod ops; pub mod storage; mod alias; mod alias_slice; mod alias_view; mod array_storage; mod cg; mod componentwise; #[macro_use] mod construction; mod construction_view; mod conversion; mod edition; pub mod indexing; mod matrix; mod matrix_simba; mod matrix_view; mod norm; mod properties; mod scalar; mod statistics; mod swizzle; mod unit; #[cfg(any(feature = "std", feature = "alloc"))] mod vec_storage; mod blas_uninit; #[doc(hidden)] pub mod helper; mod interpolation; mod min_max; /// Mechanisms for working with values that may not be initialized. pub mod uninit; #[cfg(feature = "rayon")] pub mod par_iter; #[cfg(feature = "rkyv-serialize-no-std")] mod rkyv_wrappers; pub use self::matrix::*; pub use self::norm::*; pub use self::scalar::*; pub use self::unit::*; pub use self::default_allocator::*; pub use self::dimension::*; pub use self::alias::*; pub use self::alias_slice::*; pub use self::alias_view::*; pub use self::array_storage::*; pub use self::matrix_view::*; pub use self::storage::*; #[cfg(any(feature = "std", feature = "alloc"))] pub use self::vec_storage::*; nalgebra-0.33.0/src/base/norm.rs000064400000000000000000000464070072674642500145470ustar 00000000000000#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; use num::Zero; use std::ops::Neg; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, Dim, DimName, Matrix, Normed, OMatrix, OVector}; use crate::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use crate::storage::{Storage, StorageMut}; use crate::{ComplexField, Scalar, SimdComplexField, Unit}; use simba::scalar::ClosedNeg; use simba::simd::{SimdOption, SimdPartialOrd, SimdValue}; // TODO: this should be be a trait on alga? /// A trait for abstract matrix norms. /// /// This may be moved to the alga crate in the future. pub trait Norm { /// Apply this norm to the given matrix. fn norm(&self, m: &Matrix) -> T::SimdRealField where R: Dim, C: Dim, S: Storage; /// Use the metric induced by this norm to compute the metric distance between the two given matrices. fn metric_distance( &self, m1: &Matrix, m2: &Matrix, ) -> T::SimdRealField where R1: Dim, C1: Dim, S1: Storage, R2: Dim, C2: Dim, S2: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns; } /// Euclidean norm. #[derive(Copy, Clone, Debug)] pub struct EuclideanNorm; /// Lp norm. #[derive(Copy, Clone, Debug)] pub struct LpNorm(pub i32); /// L-infinite norm aka. Chebytchev norm aka. uniform norm aka. suppremum norm. #[derive(Copy, Clone, Debug)] pub struct UniformNorm; impl Norm for EuclideanNorm { #[inline] fn norm(&self, m: &Matrix) -> T::SimdRealField where R: Dim, C: Dim, S: Storage, { m.norm_squared().simd_sqrt() } #[inline] fn metric_distance( &self, m1: &Matrix, m2: &Matrix, ) -> T::SimdRealField where R1: Dim, C1: Dim, S1: Storage, R2: Dim, C2: Dim, S2: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { m1.zip_fold(m2, T::SimdRealField::zero(), |acc, a, b| { let diff = a - b; acc + diff.simd_modulus_squared() }) .simd_sqrt() } } impl Norm for LpNorm { #[inline] fn norm(&self, m: &Matrix) -> T::SimdRealField where R: Dim, C: Dim, S: Storage, { m.fold(T::SimdRealField::zero(), |a, b| { a + b.simd_modulus().simd_powi(self.0) }) .simd_powf(crate::convert(1.0 / (self.0 as f64))) } #[inline] fn metric_distance( &self, m1: &Matrix, m2: &Matrix, ) -> T::SimdRealField where R1: Dim, C1: Dim, S1: Storage, R2: Dim, C2: Dim, S2: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { m1.zip_fold(m2, T::SimdRealField::zero(), |acc, a, b| { let diff = a - b; acc + diff.simd_modulus().simd_powi(self.0) }) .simd_powf(crate::convert(1.0 / (self.0 as f64))) } } impl Norm for UniformNorm { #[inline] fn norm(&self, m: &Matrix) -> T::SimdRealField where R: Dim, C: Dim, S: Storage, { // NOTE: we don't use `m.amax()` here because for the complex // numbers this will return the max norm1 instead of the modulus. m.fold(T::SimdRealField::zero(), |acc, a| { acc.simd_max(a.simd_modulus()) }) } #[inline] fn metric_distance( &self, m1: &Matrix, m2: &Matrix, ) -> T::SimdRealField where R1: Dim, C1: Dim, S1: Storage, R2: Dim, C2: Dim, S2: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { m1.zip_fold(m2, T::SimdRealField::zero(), |acc, a, b| { let val = (a - b).simd_modulus(); acc.simd_max(val) }) } } /// # Magnitude and norms impl> Matrix { /// The squared L2 norm of this vector. #[inline] #[must_use] pub fn norm_squared(&self) -> T::SimdRealField where T: SimdComplexField, { let mut res = T::SimdRealField::zero(); for i in 0..self.ncols() { let col = self.column(i); res += col.dotc(&col).simd_real() } res } /// The L2 norm of this matrix. /// /// Use `.apply_norm` to apply a custom norm. #[inline] #[must_use] pub fn norm(&self) -> T::SimdRealField where T: SimdComplexField, { self.norm_squared().simd_sqrt() } /// Compute the distance between `self` and `rhs` using the metric induced by the euclidean norm. /// /// Use `.apply_metric_distance` to apply a custom norm. #[inline] #[must_use] pub fn metric_distance(&self, rhs: &Matrix) -> T::SimdRealField where T: SimdComplexField, R2: Dim, C2: Dim, S2: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { self.apply_metric_distance(rhs, &EuclideanNorm) } /// Uses the given `norm` to compute the norm of `self`. /// /// # Example /// /// ``` /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; /// /// let v = Vector3::new(1.0, 2.0, 3.0); /// assert_eq!(v.apply_norm(&UniformNorm), 3.0); /// assert_eq!(v.apply_norm(&LpNorm(1)), 6.0); /// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm()); /// ``` #[inline] #[must_use] pub fn apply_norm(&self, norm: &impl Norm) -> T::SimdRealField where T: SimdComplexField, { norm.norm(self) } /// Uses the metric induced by the given `norm` to compute the metric distance between `self` and `rhs`. /// /// # Example /// /// ``` /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm}; /// /// let v1 = Vector3::new(1.0, 2.0, 3.0); /// let v2 = Vector3::new(10.0, 20.0, 30.0); /// /// assert_eq!(v1.apply_metric_distance(&v2, &UniformNorm), 27.0); /// assert_eq!(v1.apply_metric_distance(&v2, &LpNorm(1)), 27.0 + 18.0 + 9.0); /// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm()); /// ``` #[inline] #[must_use] pub fn apply_metric_distance( &self, rhs: &Matrix, norm: &impl Norm, ) -> T::SimdRealField where T: SimdComplexField, R2: Dim, C2: Dim, S2: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns, { norm.metric_distance(self, rhs) } /// A synonym for the norm of this matrix. /// /// Aka the length. /// /// This function is simply implemented as a call to `norm()` #[inline] #[must_use] pub fn magnitude(&self) -> T::SimdRealField where T: SimdComplexField, { self.norm() } /// A synonym for the squared norm of this matrix. /// /// Aka the squared length. /// /// This function is simply implemented as a call to `norm_squared()` #[inline] #[must_use] pub fn magnitude_squared(&self) -> T::SimdRealField where T: SimdComplexField, { self.norm_squared() } /// Sets the magnitude of this vector. #[inline] pub fn set_magnitude(&mut self, magnitude: T::SimdRealField) where T: SimdComplexField, S: StorageMut, { let n = self.norm(); self.scale_mut(magnitude / n) } /// Returns a normalized version of this matrix. #[inline] #[must_use = "Did you mean to use normalize_mut()?"] pub fn normalize(&self) -> OMatrix where T: SimdComplexField, DefaultAllocator: Allocator, { self.unscale(self.norm()) } /// The Lp norm of this matrix. #[inline] #[must_use] pub fn lp_norm(&self, p: i32) -> T::SimdRealField where T: SimdComplexField, { self.apply_norm(&LpNorm(p)) } /// Attempts to normalize `self`. /// /// The components of this matrix can be SIMD types. #[inline] #[must_use = "Did you mean to use simd_try_normalize_mut()?"] pub fn simd_try_normalize(&self, min_norm: T::SimdRealField) -> SimdOption> where T: SimdComplexField, T::Element: Scalar, DefaultAllocator: Allocator, { let n = self.norm(); let le = n.clone().simd_le(min_norm); let val = self.unscale(n); SimdOption::new(val, le) } /// 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`. #[inline] pub fn try_set_magnitude(&mut self, magnitude: T::RealField, min_magnitude: T::RealField) where T: ComplexField, S: StorageMut, { let n = self.norm(); if n > min_magnitude { self.scale_mut(magnitude / n) } } /// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`. #[inline] #[must_use] pub fn cap_magnitude(&self, max: T::RealField) -> OMatrix where T: ComplexField, DefaultAllocator: Allocator, { let n = self.norm(); if n > max { self.scale(max / n) } else { self.clone_owned() } } /// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`. #[inline] #[must_use] pub fn simd_cap_magnitude(&self, max: T::SimdRealField) -> OMatrix where T: SimdComplexField, T::Element: Scalar, DefaultAllocator: Allocator, { let n = self.norm(); let scaled = self.scale(max.clone() / n.clone()); let use_scaled = n.simd_gt(max); scaled.select(use_scaled, self.clone_owned()) } /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`. /// /// The components of this matrix cannot be SIMD types (see `simd_try_normalize`) instead. #[inline] #[must_use = "Did you mean to use try_normalize_mut()?"] pub fn try_normalize(&self, min_norm: T::RealField) -> Option> where T: ComplexField, DefaultAllocator: Allocator, { let n = self.norm(); if n <= min_norm { None } else { Some(self.unscale(n)) } } } /// # In-place normalization impl> Matrix { /// Normalizes this matrix in-place and returns its norm. /// /// The components of the matrix cannot be SIMD types (see `simd_try_normalize_mut` instead). #[inline] pub fn normalize_mut(&mut self) -> T::SimdRealField where T: SimdComplexField, { let n = self.norm(); self.unscale_mut(n.clone()); n } /// Normalizes this matrix in-place and return its norm. /// /// The components of the matrix can be SIMD types. #[inline] #[must_use = "Did you mean to use simd_try_normalize_mut()?"] pub fn simd_try_normalize_mut( &mut self, min_norm: T::SimdRealField, ) -> SimdOption where T: SimdComplexField, T::Element: Scalar, DefaultAllocator: Allocator, { let n = self.norm(); let le = n.clone().simd_le(min_norm); self.apply(|e| *e = e.clone().simd_unscale(n.clone()).select(le, e.clone())); SimdOption::new(n, le) } /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`. /// /// If the normalization succeeded, returns the old norm of this matrix. #[inline] pub fn try_normalize_mut(&mut self, min_norm: T::RealField) -> Option where T: ComplexField, { let n = self.norm(); if n <= min_norm { None } else { self.unscale_mut(n.clone()); Some(n) } } } impl Normed for OMatrix where DefaultAllocator: Allocator, { type Norm = T::SimdRealField; #[inline] fn norm(&self) -> T::SimdRealField { self.norm() } #[inline] fn norm_squared(&self) -> T::SimdRealField { self.norm_squared() } #[inline] fn scale_mut(&mut self, n: Self::Norm) { self.scale_mut(n) } #[inline] fn unscale_mut(&mut self, n: Self::Norm) { self.unscale_mut(n) } } impl Neg for Unit> where DefaultAllocator: Allocator, { type Output = Unit>; #[inline] fn neg(self) -> Self::Output { Unit::new_unchecked(-self.value) } } // TODO: specialization will greatly simplify this implementation in the future. // In particular: // − use `x()` instead of `::canonical_basis_element` // − use `::new(x, y, z)` instead of `::from_slice` /// # Basis and orthogonalization impl OVector where DefaultAllocator: Allocator, { /// The i-the canonical basis element. #[inline] fn canonical_basis_element(i: usize) -> Self { let mut res = Self::zero(); res[i] = T::one(); res } /// Orthonormalizes the given family of vectors. The largest free family of vectors is moved at /// the beginning of the array and its size is returned. Vectors at an indices larger or equal to /// this length can be modified to an arbitrary value. #[inline] pub fn orthonormalize(vs: &mut [Self]) -> usize { let mut nbasis_elements = 0; for i in 0..vs.len() { { let (elt, basis) = vs[..i + 1].split_last_mut().unwrap(); for basis_element in &basis[..nbasis_elements] { *elt -= basis_element * elt.dot(basis_element) } } if vs[i].try_normalize_mut(T::RealField::zero()).is_some() { // TODO: this will be efficient on dynamically-allocated vectors but for // statically-allocated ones, `.clone_from` would be better. vs.swap(nbasis_elements, i); nbasis_elements += 1; // All the other vectors will be dependent. if nbasis_elements == D::dim() { break; } } } nbasis_elements } /// Applies the given closure to each element of the orthonormal basis of the subspace /// orthogonal to free family of vectors `vs`. If `vs` is not a free family, the result is /// unspecified. // TODO: return an iterator instead when `-> impl Iterator` will be supported by Rust. #[inline] pub fn orthonormal_subspace_basis(vs: &[Self], mut f: F) where F: FnMut(&Self) -> bool, { // TODO: is this necessary? assert!( vs.len() <= D::dim(), "The given set of vectors has no chance of being a free family." ); match D::dim() { 1 => { if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)); } } 2 => { if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)) && f(&Self::canonical_basis_element(1)); } else if vs.len() == 1 { let v = &vs[0]; let res = Self::from_column_slice(&[-v[1].clone(), v[0].clone()]); let _ = f(&res.normalize()); } // Otherwise, nothing. } 3 => { if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)) && f(&Self::canonical_basis_element(1)) && f(&Self::canonical_basis_element(2)); } else if vs.len() == 1 { let v = &vs[0]; let mut a; if v[0].clone().norm1() > v[1].clone().norm1() { a = Self::from_column_slice(&[v[2].clone(), T::zero(), -v[0].clone()]); } else { a = Self::from_column_slice(&[T::zero(), -v[2].clone(), v[1].clone()]); }; let _ = a.normalize_mut(); if f(&a.cross(v)) { let _ = f(&a); } } else if vs.len() == 2 { let _ = f(&vs[0].cross(&vs[1]).normalize()); } } _ => { #[cfg(any(feature = "std", feature = "alloc"))] { // XXX: use a GenericArray instead. let mut known_basis = Vec::new(); for v in vs.iter() { known_basis.push(v.normalize()) } for i in 0..D::dim() - vs.len() { let mut elt = Self::canonical_basis_element(i); for v in &known_basis { elt -= v * elt.dot(v) } if let Some(subsp_elt) = elt.try_normalize(T::RealField::zero()) { if !f(&subsp_elt) { return; }; known_basis.push(subsp_elt); } } } #[cfg(all(not(feature = "std"), not(feature = "alloc")))] { panic!("Cannot compute the orthogonal subspace basis of a vector with a dimension greater than 3 \ if #![no_std] is enabled and the 'alloc' feature is not enabled.") } } } } } nalgebra-0.33.0/src/base/ops.rs000064400000000000000000000761200072674642500143700ustar 00000000000000use num::{One, Zero}; use std::iter; use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; use simba::scalar::{ ClosedAddAssign, ClosedDivAssign, ClosedMulAssign, ClosedNeg, ClosedSubAssign, }; use crate::base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR}; use crate::base::blas_uninit::gemm_uninit; use crate::base::constraint::{ AreMultipliable, DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint, }; use crate::base::dimension::{Dim, DimMul, DimName, DimProd, Dyn}; use crate::base::storage::{Storage, StorageMut}; use crate::base::uninit::Uninit; use crate::base::{DefaultAllocator, Matrix, MatrixSum, OMatrix, Scalar, VectorView}; use crate::storage::IsContiguous; use crate::uninit::{Init, InitStatus}; use crate::{RawStorage, RawStorageMut, SimdComplexField}; use std::mem::MaybeUninit; /* * * Indexing. * */ impl> Index for Matrix { type Output = T; #[inline] fn index(&self, i: usize) -> &Self::Output { let ij = self.vector_to_matrix_index(i); &self[ij] } } impl> Index<(usize, usize)> for Matrix { type Output = T; #[inline] fn index(&self, ij: (usize, usize)) -> &Self::Output { let shape = self.shape(); assert!( ij.0 < shape.0 && ij.1 < shape.1, "Matrix index out of bounds." ); unsafe { self.get_unchecked((ij.0, ij.1)) } } } // Mutable versions. impl> IndexMut for Matrix { #[inline] fn index_mut(&mut self, i: usize) -> &mut T { let ij = self.vector_to_matrix_index(i); &mut self[ij] } } impl> IndexMut<(usize, usize)> for Matrix { #[inline] fn index_mut(&mut self, ij: (usize, usize)) -> &mut T { let shape = self.shape(); assert!( ij.0 < shape.0 && ij.1 < shape.1, "Matrix index out of bounds." ); unsafe { self.get_unchecked_mut((ij.0, ij.1)) } } } /* * * Neg * */ impl Neg for Matrix where T: Scalar + ClosedNeg, S: Storage, DefaultAllocator: Allocator, { type Output = OMatrix; #[inline] fn neg(self) -> Self::Output { let mut res = self.into_owned(); res.neg_mut(); res } } impl<'a, T, R: Dim, C: Dim, S> Neg for &'a Matrix where T: Scalar + ClosedNeg, S: Storage, DefaultAllocator: Allocator, { type Output = OMatrix; #[inline] fn neg(self) -> Self::Output { -self.clone_owned() } } impl Matrix where T: Scalar + ClosedNeg, S: StorageMut, { /// Negates `self` in-place. #[inline] pub fn neg_mut(&mut self) { for e in self.iter_mut() { *e = -e.clone() } } } /* * * Addition & Subtraction * */ macro_rules! componentwise_binop_impl( ($Trait: ident, $method: ident, $bound: ident; $TraitAssign: ident, $method_assign: ident, $method_assign_statically_unchecked: ident, $method_assign_statically_unchecked_rhs: ident; $method_to: ident, $method_to_statically_unchecked_uninit: ident) => { impl> Matrix where T: Scalar + $bound { /* * * Methods without dimension checking at compile-time. * This is useful for code reuse because the sum representative system does not plays * easily with static checks. * */ #[inline] fn $method_to_statically_unchecked_uninit(&self, _status: Status, rhs: &Matrix, out: &mut Matrix) where Status: InitStatus, SB: RawStorage, SC: RawStorageMut { assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch."); assert_eq!(self.shape(), out.shape(), "Matrix addition/subtraction output dimensions mismatch."); // This is the most common case and should be deduced at compile-time. // TODO: use specialization instead? unsafe { if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() { let arr1 = self.data.as_slice_unchecked(); let arr2 = rhs.data.as_slice_unchecked(); let out = out.data.as_mut_slice_unchecked(); for i in 0 .. arr1.len() { Status::init(out.get_unchecked_mut(i), arr1.get_unchecked(i).clone().$method(arr2.get_unchecked(i).clone())); } } else { for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { let val = self.get_unchecked((i, j)).clone().$method(rhs.get_unchecked((i, j)).clone()); Status::init(out.get_unchecked_mut((i, j)), val); } } } } } #[inline] fn $method_assign_statically_unchecked(&mut self, rhs: &Matrix) where R2: Dim, C2: Dim, SA: StorageMut, SB: Storage { assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch."); // This is the most common case and should be deduced at compile-time. // TODO: use specialization instead? unsafe { if self.data.is_contiguous() && rhs.data.is_contiguous() { let arr1 = self.data.as_mut_slice_unchecked(); let arr2 = rhs.data.as_slice_unchecked(); for i in 0 .. arr2.len() { arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).clone()); } } else { for j in 0 .. rhs.ncols() { for i in 0 .. rhs.nrows() { self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).clone()) } } } } } #[inline] fn $method_assign_statically_unchecked_rhs(&self, rhs: &mut Matrix) where R2: Dim, C2: Dim, SB: StorageMut { assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch."); // This is the most common case and should be deduced at compile-time. // TODO: use specialization instead? unsafe { if self.data.is_contiguous() && rhs.data.is_contiguous() { let arr1 = self.data.as_slice_unchecked(); let arr2 = rhs.data.as_mut_slice_unchecked(); for i in 0 .. arr1.len() { let res = arr1.get_unchecked(i).clone().$method(arr2.get_unchecked(i).clone()); *arr2.get_unchecked_mut(i) = res; } } else { for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { let r = rhs.get_unchecked_mut((i, j)); *r = self.get_unchecked((i, j)).clone().$method(r.clone()) } } } } } /* * * Methods without dimension checking at compile-time. * This is useful for code reuse because the sum representative system does not plays * easily with static checks. * */ /// Equivalent to `self + rhs` but stores the result into `out` to avoid allocations. #[inline] pub fn $method_to(&self, rhs: &Matrix, out: &mut Matrix) where SB: Storage, SC: StorageMut, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + SameNumberOfRows + SameNumberOfColumns { self.$method_to_statically_unchecked_uninit(Init, rhs, out) } } impl<'b, T, R1, C1, R2, C2, SA, SB> $Trait<&'b Matrix> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, T: Scalar + $bound, SA: Storage, SB: Storage, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, rhs: &'b Matrix) -> Self::Output { assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch."); let mut res = self.into_owned_sum::(); res.$method_assign_statically_unchecked(rhs); res } } impl<'a, T, R1, C1, R2, C2, SA, SB> $Trait> for &'a Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, T: Scalar + $bound, SA: Storage, SB: Storage, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, rhs: Matrix) -> Self::Output { let mut rhs = rhs.into_owned_sum::(); assert_eq!(self.shape(), rhs.shape(), "Matrix addition/subtraction dimensions mismatch."); self.$method_assign_statically_unchecked_rhs(&mut rhs); rhs } } impl $Trait> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, T: Scalar + $bound, SA: Storage, SB: Storage, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, rhs: Matrix) -> Self::Output { self.$method(&rhs) } } impl<'a, 'b, T, R1, C1, R2, C2, SA, SB> $Trait<&'b Matrix> for &'a Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, T: Scalar + $bound, SA: Storage, SB: Storage, DefaultAllocator: SameShapeAllocator, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { type Output = MatrixSum; #[inline] fn $method(self, rhs: &'b Matrix) -> Self::Output { let (nrows, ncols) = self.shape(); let nrows: SameShapeR = Dim::from_usize(nrows); let ncols: SameShapeC = Dim::from_usize(ncols); let mut res = Matrix::uninit(nrows, ncols); self.$method_to_statically_unchecked_uninit(Uninit, rhs, &mut res); // SAFETY: the output has been initialized above. unsafe { res.assume_init() } } } impl<'b, T, R1, C1, R2, C2, SA, SB> $TraitAssign<&'b Matrix> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, T: Scalar + $bound, SA: StorageMut, SB: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { #[inline] fn $method_assign(&mut self, rhs: &'b Matrix) { self.$method_assign_statically_unchecked(rhs) } } impl $TraitAssign> for Matrix where R1: Dim, C1: Dim, R2: Dim, C2: Dim, T: Scalar + $bound, SA: StorageMut, SB: Storage, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns { #[inline] fn $method_assign(&mut self, rhs: Matrix) { self.$method_assign(&rhs) } } } ); componentwise_binop_impl!(Add, add, ClosedAddAssign; AddAssign, add_assign, add_assign_statically_unchecked, add_assign_statically_unchecked_mut; add_to, add_to_statically_unchecked_uninit); componentwise_binop_impl!(Sub, sub, ClosedSubAssign; SubAssign, sub_assign, sub_assign_statically_unchecked, sub_assign_statically_unchecked_mut; sub_to, sub_to_statically_unchecked_uninit); impl iter::Sum for OMatrix where T: Scalar + ClosedAddAssign + Zero, DefaultAllocator: Allocator, { fn sum>>(iter: I) -> OMatrix { iter.fold(Matrix::zero(), |acc, x| acc + x) } } impl iter::Sum for OMatrix where T: Scalar + ClosedAddAssign + Zero, DefaultAllocator: Allocator, { /// # Example /// ``` /// # use nalgebra::DVector; /// assert_eq!(vec![DVector::repeat(3, 1.0f64), /// DVector::repeat(3, 1.0f64), /// DVector::repeat(3, 1.0f64)].into_iter().sum::>(), /// DVector::repeat(3, 1.0f64) + DVector::repeat(3, 1.0f64) + DVector::repeat(3, 1.0f64)); /// ``` /// /// # Panics /// Panics if the iterator is empty: /// ```should_panic /// # use std::iter; /// # use nalgebra::DMatrix; /// iter::empty::>().sum::>(); // panics! /// ``` fn sum>>(mut iter: I) -> OMatrix { if let Some(first) = iter.next() { iter.fold(first, |acc, x| acc + x) } else { panic!("Cannot compute `sum` of empty iterator.") } } } impl<'a, T, R: DimName, C: DimName> iter::Sum<&'a OMatrix> for OMatrix where T: Scalar + ClosedAddAssign + Zero, DefaultAllocator: Allocator, { fn sum>>(iter: I) -> OMatrix { iter.fold(Matrix::zero(), |acc, x| acc + x) } } impl<'a, T, C: Dim> iter::Sum<&'a OMatrix> for OMatrix where T: Scalar + ClosedAddAssign + Zero, DefaultAllocator: Allocator, { /// # Example /// ``` /// # use nalgebra::DVector; /// let v = &DVector::repeat(3, 1.0f64); /// /// assert_eq!(vec![v, v, v].into_iter().sum::>(), /// v + v + v); /// ``` /// /// # Panics /// Panics if the iterator is empty: /// ```should_panic /// # use std::iter; /// # use nalgebra::DMatrix; /// iter::empty::<&DMatrix>().sum::>(); // panics! /// ``` fn sum>>(mut iter: I) -> OMatrix { if let Some(first) = iter.next() { iter.fold(first.clone(), |acc, x| acc + x) } else { panic!("Cannot compute `sum` of empty iterator.") } } } /* * * Multiplication * */ // Matrix × Scalar // Matrix / Scalar macro_rules! componentwise_scalarop_impl( ($Trait: ident, $method: ident, $bound: ident; $TraitAssign: ident, $method_assign: ident) => { impl $Trait for Matrix where T: Scalar + $bound, S: Storage, DefaultAllocator: Allocator { type Output = OMatrix; #[inline] fn $method(self, rhs: T) -> Self::Output { let mut res = self.into_owned(); // XXX: optimize our iterator! // // Using our own iterator prevents loop unrolling, which breaks some optimization // (like SIMD). On the other hand, using the slice iterator is 4x faster. // for left in res.iter_mut() { for left in res.as_mut_slice().iter_mut() { *left = left.clone().$method(rhs.clone()) } res } } impl<'a, T, R: Dim, C: Dim, S> $Trait for &'a Matrix where T: Scalar + $bound, S: Storage, DefaultAllocator: Allocator { type Output = OMatrix; #[inline] fn $method(self, rhs: T) -> Self::Output { self.clone_owned().$method(rhs) } } impl $TraitAssign for Matrix where T: Scalar + $bound, S: StorageMut { #[inline] fn $method_assign(&mut self, rhs: T) { for j in 0 .. self.ncols() { for i in 0 .. self.nrows() { unsafe { self.get_unchecked_mut((i, j)).$method_assign(rhs.clone()) }; } } } } } ); componentwise_scalarop_impl!(Mul, mul, ClosedMulAssign; MulAssign, mul_assign); componentwise_scalarop_impl!(Div, div, ClosedDivAssign; DivAssign, div_assign); macro_rules! left_scalar_mul_impl( ($($T: ty),* $(,)*) => {$( impl> Mul> for $T where DefaultAllocator: Allocator { type Output = OMatrix<$T, R, C>; #[inline] fn mul(self, rhs: Matrix<$T, R, C, S>) -> Self::Output { let mut res = rhs.into_owned(); // XXX: optimize our iterator! // // Using our own iterator prevents loop unrolling, which breaks some optimization // (like SIMD). On the other hand, using the slice iterator is 4x faster. // for rhs in res.iter_mut() { for rhs in res.as_mut_slice().iter_mut() { *rhs *= self } res } } impl<'b, R: Dim, C: Dim, S: Storage<$T, R, C>> Mul<&'b Matrix<$T, R, C, S>> for $T where DefaultAllocator: Allocator { type Output = OMatrix<$T, R, C>; #[inline] fn mul(self, rhs: &'b Matrix<$T, R, C, S>) -> Self::Output { self * rhs.clone_owned() } } )*} ); left_scalar_mul_impl!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); // Matrix × Matrix impl<'a, 'b, T, R1: Dim, C1: Dim, R2: Dim, C2: Dim, SA, SB> Mul<&'b Matrix> for &'a Matrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SA: Storage, SB: Storage, DefaultAllocator: Allocator, ShapeConstraint: AreMultipliable, { type Output = OMatrix; #[inline] fn mul(self, rhs: &'b Matrix) -> Self::Output { let mut res = Matrix::uninit(self.shape_generic().0, rhs.shape_generic().1); unsafe { // SAFETY: this is OK because status = Uninit && bevy == 0 gemm_uninit(Uninit, &mut res, T::one(), self, rhs, T::zero()); res.assume_init() } } } impl<'a, T, R1: Dim, C1: Dim, R2: Dim, C2: Dim, SA, SB> Mul> for &'a Matrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SB: Storage, SA: Storage, DefaultAllocator: Allocator, ShapeConstraint: AreMultipliable, { type Output = OMatrix; #[inline] fn mul(self, rhs: Matrix) -> Self::Output { self * &rhs } } impl<'b, T, R1: Dim, C1: Dim, R2: Dim, C2: Dim, SA, SB> Mul<&'b Matrix> for Matrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SB: Storage, SA: Storage, DefaultAllocator: Allocator, ShapeConstraint: AreMultipliable, { type Output = OMatrix; #[inline] fn mul(self, rhs: &'b Matrix) -> Self::Output { &self * rhs } } impl Mul> for Matrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SB: Storage, SA: Storage, DefaultAllocator: Allocator, ShapeConstraint: AreMultipliable, { type Output = OMatrix; #[inline] fn mul(self, rhs: Matrix) -> Self::Output { &self * &rhs } } // TODO: this is too restrictive: // − we can't use `a *= b` when `a` is a mutable slice. // − we can't use `a *= b` when C2 is not equal to C1. impl MulAssign> for Matrix where R1: Dim, C1: Dim, R2: Dim, T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SB: Storage, SA: StorageMut + IsContiguous + Clone, // TODO: get rid of the IsContiguous ShapeConstraint: AreMultipliable, DefaultAllocator: Allocator = SA>, { #[inline] fn mul_assign(&mut self, rhs: Matrix) { *self = &*self * rhs } } impl<'b, T, R1, C1, R2, SA, SB> MulAssign<&'b Matrix> for Matrix where R1: Dim, C1: Dim, R2: Dim, T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SB: Storage, SA: StorageMut + IsContiguous + Clone, // TODO: get rid of the IsContiguous ShapeConstraint: AreMultipliable, // TODO: this is too restrictive. See comments for the non-ref version. DefaultAllocator: Allocator = SA>, { #[inline] fn mul_assign(&mut self, rhs: &'b Matrix) { *self = &*self * rhs } } /// # Special multiplications. impl Matrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, SA: Storage, { /// Equivalent to `self.transpose() * rhs`. #[inline] #[must_use] pub fn tr_mul(&self, rhs: &Matrix) -> OMatrix where SB: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = Matrix::uninit(self.shape_generic().1, rhs.shape_generic().1); self.xx_mul_to_uninit(Uninit, rhs, &mut res, |a, b| a.dot(b)); // SAFETY: this is OK because the result is now initialized. unsafe { res.assume_init() } } /// Equivalent to `self.adjoint() * rhs`. #[inline] #[must_use] pub fn ad_mul(&self, rhs: &Matrix) -> OMatrix where T: SimdComplexField, SB: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = Matrix::uninit(self.shape_generic().1, rhs.shape_generic().1); self.xx_mul_to_uninit(Uninit, rhs, &mut res, |a, b| a.dotc(b)); // SAFETY: this is OK because the result is now initialized. unsafe { res.assume_init() } } #[inline(always)] fn xx_mul_to_uninit( &self, _status: Status, rhs: &Matrix, out: &mut Matrix, dot: impl Fn( &VectorView<'_, T, R1, SA::RStride, SA::CStride>, &VectorView<'_, T, R2, SB::RStride, SB::CStride>, ) -> T, ) where Status: InitStatus, SB: RawStorage, SC: RawStorageMut, ShapeConstraint: SameNumberOfRows + DimEq + DimEq, { let (nrows1, ncols1) = self.shape(); let (nrows2, ncols2) = rhs.shape(); let (nrows3, ncols3) = out.shape(); assert!( nrows1 == nrows2, "Matrix multiplication dimensions mismatch {:?} and {:?}: left rows != right rows.", self.shape(), rhs.shape() ); assert!( ncols1 == nrows3, "Matrix multiplication output dimensions mismatch {:?} and {:?}: left cols != right rows.", self.shape(), out.shape() ); assert!( ncols2 == ncols3, "Matrix multiplication output dimensions mismatch {:?} and {:?}: left cols != right cols", rhs.shape(), out.shape() ); for i in 0..ncols1 { for j in 0..ncols2 { let dot = dot(&self.column(i), &rhs.column(j)); let elt = unsafe { out.get_unchecked_mut((i, j)) }; Status::init(elt, dot); } } } /// Equivalent to `self.transpose() * rhs` but stores the result into `out` to avoid /// allocations. #[inline] pub fn tr_mul_to( &self, rhs: &Matrix, out: &mut Matrix, ) where SB: Storage, SC: StorageMut, ShapeConstraint: SameNumberOfRows + DimEq + DimEq, { self.xx_mul_to_uninit(Init, rhs, out, |a, b| a.dot(b)) } /// Equivalent to `self.adjoint() * rhs` but stores the result into `out` to avoid /// allocations. #[inline] pub fn ad_mul_to( &self, rhs: &Matrix, out: &mut Matrix, ) where T: SimdComplexField, SB: Storage, SC: StorageMut, ShapeConstraint: SameNumberOfRows + DimEq + DimEq, { self.xx_mul_to_uninit(Init, rhs, out, |a, b| a.dotc(b)) } /// Equivalent to `self * rhs` but stores the result into `out` to avoid allocations. #[inline] pub fn mul_to( &self, rhs: &Matrix, out: &mut Matrix, ) where SB: Storage, SC: StorageMut, ShapeConstraint: SameNumberOfRows + SameNumberOfColumns + AreMultipliable, { out.gemm(T::one(), self, rhs, T::zero()); } /// The kronecker product of two matrices (aka. tensor product of the corresponding linear /// maps). #[must_use] pub fn kronecker( &self, rhs: &Matrix, ) -> OMatrix, DimProd> where T: ClosedMulAssign, R1: DimMul, C1: DimMul, SB: Storage, DefaultAllocator: Allocator, DimProd>, { let (nrows1, ncols1) = self.shape_generic(); let (nrows2, ncols2) = rhs.shape_generic(); let mut res = Matrix::uninit(nrows1.mul(nrows2), ncols1.mul(ncols2)); let mut data_res = res.data.ptr_mut(); unsafe { for j1 in 0..ncols1.value() { for j2 in 0..ncols2.value() { for i1 in 0..nrows1.value() { let coeff = self.get_unchecked((i1, j1)).clone(); for i2 in 0..nrows2.value() { *data_res = MaybeUninit::new( coeff.clone() * rhs.get_unchecked((i2, j2)).clone(), ); data_res = data_res.offset(1); } } } } // SAFETY: the result matrix has been initialized by the loop above. res.assume_init() } } } impl iter::Product for OMatrix where T: Scalar + Zero + One + ClosedMulAssign + ClosedAddAssign, DefaultAllocator: Allocator, { fn product>>(iter: I) -> OMatrix { iter.fold(Matrix::one(), |acc, x| acc * x) } } impl<'a, T, D: DimName> iter::Product<&'a OMatrix> for OMatrix where T: Scalar + Zero + One + ClosedMulAssign + ClosedAddAssign, DefaultAllocator: Allocator, { fn product>>(iter: I) -> OMatrix { iter.fold(Matrix::one(), |acc, x| acc * x) } } nalgebra-0.33.0/src/base/par_iter.rs000064400000000000000000000223410072674642500153700ustar 00000000000000//! Parallel iterators for matrices compatible with rayon. // only enables the `doc_cfg` feature when // the `docsrs` configuration attribute is defined #![cfg_attr(docsrs, feature(doc_cfg))] use crate::{ iter::{ColumnIter, ColumnIterMut}, Dim, Matrix, MatrixView, MatrixViewMut, RawStorage, RawStorageMut, Scalar, U1, }; use rayon::iter::plumbing::Producer; use rayon::{iter::plumbing::bridge, prelude::*}; /// A rayon parallel iterator over the columns of a matrix. It is created /// using the [`par_column_iter`] method of [`Matrix`]. /// /// *Only available if compiled with the feature `rayon`.* /// [`par_column_iter`]: crate::Matrix::par_column_iter /// [`Matrix`]: crate::Matrix #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] pub struct ParColumnIter<'a, T, R: Dim, Cols: Dim, S: RawStorage> { mat: &'a Matrix, } impl<'a, T, R: Dim, Cols: Dim, S: RawStorage> ParColumnIter<'a, T, R, Cols, S> { /// Create a new parallel iterator for the given matrix. fn new(matrix: &'a Matrix) -> Self { Self { mat: matrix } } } #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] impl<'a, T, R: Dim, Cols: Dim, S: RawStorage> ParallelIterator for ParColumnIter<'a, T, R, Cols, S> where T: Sync + Send + Scalar, S: Sync, { type Item = MatrixView<'a, T, R, U1, S::RStride, S::CStride>; fn drive_unindexed(self, consumer: Consumer) -> Consumer::Result where Consumer: rayon::iter::plumbing::UnindexedConsumer, { bridge(self, consumer) } fn opt_len(&self) -> Option { Some(self.mat.ncols()) } } #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] /// *Only available if compiled with the feature `rayon`.* impl<'a, T, R: Dim, Cols: Dim, S: RawStorage> IndexedParallelIterator for ParColumnIter<'a, T, R, Cols, S> where T: Send + Sync + Scalar, S: Sync, { fn len(&self) -> usize { self.mat.ncols() } fn drive>(self, consumer: C) -> C::Result { bridge(self, consumer) } fn with_producer>( self, callback: CB, ) -> CB::Output { let producer = ColumnProducer(ColumnIter::new(self.mat)); callback.callback(producer) } } #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] /// A rayon parallel iterator through the mutable columns of a matrix. /// *Only available if compiled with the feature `rayon`.* pub struct ParColumnIterMut< 'a, T, R: Dim, Cols: Dim, S: RawStorage + RawStorageMut, > { mat: &'a mut Matrix, } #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] /// *only available if compiled with the feature `rayon`* impl<'a, T, R, Cols, S> ParColumnIterMut<'a, T, R, Cols, S> where R: Dim, Cols: Dim, S: RawStorage + RawStorageMut, { /// create a new parallel iterator for the given matrix. fn new(mat: &'a mut Matrix) -> Self { Self { mat } } } #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] /// *Only available if compiled with the feature `rayon`* impl<'a, T, R, Cols, S> ParallelIterator for ParColumnIterMut<'a, T, R, Cols, S> where R: Dim, Cols: Dim, S: RawStorage + RawStorageMut, T: Send + Sync + Scalar, S: Send + Sync, { type Item = MatrixViewMut<'a, T, R, U1, S::RStride, S::CStride>; fn drive_unindexed(self, consumer: C) -> C::Result where C: rayon::iter::plumbing::UnindexedConsumer, { bridge(self, consumer) } fn opt_len(&self) -> Option { Some(self.mat.ncols()) } } #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] /// *Only available if compiled with the feature `rayon`* impl<'a, T, R, Cols, S> IndexedParallelIterator for ParColumnIterMut<'a, T, R, Cols, S> where R: Dim, Cols: Dim, S: RawStorage + RawStorageMut, T: Send + Sync + Scalar, S: Send + Sync, { fn drive>(self, consumer: C) -> C::Result { bridge(self, consumer) } fn len(&self) -> usize { self.mat.ncols() } fn with_producer>( self, callback: CB, ) -> CB::Output { let producer = ColumnProducerMut(ColumnIterMut::new(self.mat)); callback.callback(producer) } } #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] /// # Parallel iterators using `rayon` /// *Only available if compiled with the feature `rayon`* impl> Matrix where T: Send + Sync + Scalar, S: Sync, { /// Iterate through the columns of the matrix in parallel using rayon. /// This iterates over *immutable* references to the columns of the matrix, /// if *mutable* access to the columns is required, use [`par_column_iter_mut`] /// instead. /// /// # Example /// Using parallel column iterators to calculate the sum of the maximum /// elements in each column: /// ``` /// use nalgebra::{dmatrix, DMatrix}; /// use rayon::prelude::*; /// /// let matrix : DMatrix = dmatrix![1.0, 0.0, 5.0; /// 2.0, 4.0, 1.0; /// 3.0, 2.0, 2.0; /// ]; /// let sum_of_max :f64 = matrix /// .par_column_iter() /// .map(|col| col.max()) /// .sum(); /// /// assert_eq!(sum_of_max,3.0 + 4.0 + 5.0); /// /// ``` /// /// [`par_column_iter_mut`]: crate::Matrix::par_column_iter_mut pub fn par_column_iter(&self) -> ParColumnIter<'_, T, R, Cols, S> { ParColumnIter::new(self) } /// Mutably iterate through the columns of this matrix in parallel using rayon. /// Allows mutable access to the columns in parallel using mutable references. /// If mutable access to the columns is not required rather use [`par_column_iter`] /// instead. /// /// # Example /// Normalize each column of a matrix with respect to its own maximum value. /// /// ``` /// use nalgebra::{dmatrix, DMatrix}; /// use rayon::prelude::*; /// /// let mut matrix : DMatrix = dmatrix![ /// 2.0, 4.0, 6.0; /// 1.0, 2.0, 3.0; /// ]; /// matrix.par_column_iter_mut().for_each(|mut col| col /= col.max()); /// /// assert_eq!(matrix, dmatrix![1.0, 1.0, 1.0; 0.5, 0.5, 0.5]); /// ``` /// /// [`par_column_iter`]: crate::Matrix::par_column_iter pub fn par_column_iter_mut(&mut self) -> ParColumnIterMut<'_, T, R, Cols, S> where S: RawStorageMut, { ParColumnIterMut::new(self) } } /// A private helper newtype that wraps the `ColumnIter` and implements /// the rayon `Producer` trait. It's just here so we don't have to make the /// rayon trait part of the public interface of the `ColumnIter`. struct ColumnProducer<'a, T, R: Dim, C: Dim, S: RawStorage>(ColumnIter<'a, T, R, C, S>); #[cfg_attr(doc_cfg, doc(cfg(feature = "rayon")))] /// *only available if compiled with the feature `rayon`* impl<'a, T, R: Dim, Cols: Dim, S: RawStorage> Producer for ColumnProducer<'a, T, R, Cols, S> where T: Send + Sync + Scalar, S: Sync, { type Item = MatrixView<'a, T, R, U1, S::RStride, S::CStride>; type IntoIter = ColumnIter<'a, T, R, Cols, S>; #[inline] fn into_iter(self) -> Self::IntoIter { self.0 } #[inline] fn split_at(self, index: usize) -> (Self, Self) { // The index is relative to the size of this current iterator. // It will always start at zero so it serves as an offset. let (left_iter, right_iter) = self.0.split_at(index); (Self(left_iter), Self(right_iter)) } } /// See `ColumnProducer`. A private wrapper newtype that keeps the Producer /// implementation private struct ColumnProducerMut<'a, T, R: Dim, C: Dim, S: RawStorageMut>( ColumnIterMut<'a, T, R, C, S>, ); impl<'a, T, R: Dim, C: Dim, S: 'a + RawStorageMut> Producer for ColumnProducerMut<'a, T, R, C, S> where T: Send + Sync + Scalar, S: Send + Sync, { type Item = MatrixViewMut<'a, T, R, U1, S::RStride, S::CStride>; type IntoIter = ColumnIterMut<'a, T, R, C, S>; fn into_iter(self) -> Self::IntoIter { self.0 } fn split_at(self, index: usize) -> (Self, Self) { // The index is relative to the size of this current iterator // it will always start at zero so it serves as an offset. let (left_iter, right_iter) = self.0.split_at(index); (Self(left_iter), Self(right_iter)) } } /// this implementation is safe because we are enforcing exclusive access /// to the columns through the active range of the iterator unsafe impl<'a, T: Scalar, R: Dim, C: Dim, S: 'a + RawStorageMut> Send for ColumnIterMut<'a, T, R, C, S> { } nalgebra-0.33.0/src/base/properties.rs000064400000000000000000000071530072674642500157630ustar 00000000000000// Matrix properties checks. use approx::RelativeEq; use num::{One, Zero}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, ComplexField, RealField}; use crate::base::allocator::Allocator; use crate::base::dimension::{Dim, DimMin}; use crate::base::storage::Storage; use crate::base::{DefaultAllocator, Matrix, SquareMatrix}; use crate::RawStorage; impl> Matrix { /// The total number of elements of this matrix. /// /// # Examples: /// /// ``` /// # use nalgebra::Matrix3x4; /// let mat = Matrix3x4::::zeros(); /// assert_eq!(mat.len(), 12); /// ``` #[inline] #[must_use] pub fn len(&self) -> usize { let (nrows, ncols) = self.shape(); nrows * ncols } /// Returns true if the matrix contains no elements. /// /// # Examples: /// /// ``` /// # use nalgebra::Matrix3x4; /// let mat = Matrix3x4::::zeros(); /// assert!(!mat.is_empty()); /// ``` #[inline] #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Indicates if this is a square matrix. #[inline] #[must_use] pub fn is_square(&self) -> bool { let (nrows, ncols) = self.shape(); nrows == ncols } // TODO: RelativeEq prevents us from using those methods on integer matrices… /// Indicated if this is the identity matrix within a relative error of `eps`. /// /// If the matrix is diagonal, this checks that diagonal elements (i.e. at coordinates `(i, i)` /// for i from `0` to `min(R, C)`) are equal one; and that all other elements are zero. #[inline] #[must_use] pub fn is_identity(&self, eps: T::Epsilon) -> bool where T: Zero + One + RelativeEq, T::Epsilon: Clone, { let (nrows, ncols) = self.shape(); for j in 0..ncols { for i in 0..nrows { let el = unsafe { self.get_unchecked((i, j)) }; if (i == j && !relative_eq!(*el, T::one(), epsilon = eps.clone())) || (i != j && !relative_eq!(*el, T::zero(), epsilon = eps.clone())) { return false; } } } true } } impl> Matrix { /// Checks that `Mᵀ × M = Id`. /// /// In this definition `Id` is approximately equal to the identity matrix with a relative error /// equal to `eps`. #[inline] #[must_use] pub fn is_orthogonal(&self, eps: T::Epsilon) -> bool where T: Zero + One + ClosedAddAssign + ClosedMulAssign + RelativeEq, S: Storage, T::Epsilon: Clone, DefaultAllocator: Allocator + Allocator, { (self.ad_mul(self)).is_identity(eps) } } impl> SquareMatrix where DefaultAllocator: Allocator, { /// Checks that this matrix is orthogonal and has a determinant equal to 1. #[inline] #[must_use] pub fn is_special_orthogonal(&self, eps: T) -> bool where D: DimMin, DefaultAllocator: Allocator, { self.is_square() && self.is_orthogonal(eps) && self.determinant() > T::zero() } /// Returns `true` if this matrix is invertible. #[inline] #[must_use] pub fn is_invertible(&self) -> bool { // TODO: improve this? self.clone_owned().try_inverse().is_some() } } nalgebra-0.33.0/src/base/rkyv_wrappers.rs000064400000000000000000000024560072674642500165060ustar 00000000000000//! Wrapper that allows changing the generic type of a PhantomData //! //! Copied from (MIT-Apache2 licences) which isn’t published yet. use rkyv::{ with::{ArchiveWith, DeserializeWith, SerializeWith}, Fallible, }; use std::marker::PhantomData; /// A wrapper that allows for changing the generic type of a `PhantomData`. pub struct CustomPhantom { _data: PhantomData<*const NT>, } impl ArchiveWith> for CustomPhantom { type Archived = PhantomData; type Resolver = (); #[inline] unsafe fn resolve_with( _: &PhantomData, _: usize, _: Self::Resolver, _: *mut Self::Archived, ) { } } impl SerializeWith, S> for CustomPhantom { #[inline] fn serialize_with(_: &PhantomData, _: &mut S) -> Result { Ok(()) } } impl DeserializeWith, PhantomData, D> for CustomPhantom { #[inline] fn deserialize_with(_: &PhantomData, _: &mut D) -> Result, D::Error> { Ok(PhantomData) } } nalgebra-0.33.0/src/base/scalar.rs000064400000000000000000000004420072674642500150260ustar 00000000000000use std::fmt::Debug; /// The basic scalar type for all structures of `nalgebra`. /// /// This does not make any assumption on the algebraic properties of `Self`. pub trait Scalar: 'static + Clone + PartialEq + Debug {} impl Scalar for T {} nalgebra-0.33.0/src/base/statistics.rs000064400000000000000000000362220072674642500157600ustar 00000000000000use crate::allocator::Allocator; use crate::storage::RawStorage; use crate::{Const, DefaultAllocator, Dim, Matrix, OVector, RowOVector, Scalar, VectorView, U1}; use num::{One, Zero}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, Field, SupersetOf}; use std::mem::MaybeUninit; /// # Folding on columns and rows impl> Matrix { /// Returns a row vector where each element is the result of the application of `f` on the /// corresponding column of the original matrix. #[inline] #[must_use] pub fn compress_rows( &self, f: impl Fn(VectorView<'_, T, R, S::RStride, S::CStride>) -> T, ) -> RowOVector where DefaultAllocator: Allocator, { let ncols = self.shape_generic().1; let mut res = Matrix::uninit(Const::<1>, ncols); for i in 0..ncols.value() { // TODO: avoid bound checking of column. // Safety: all indices are in range. unsafe { *res.get_unchecked_mut((0, i)) = MaybeUninit::new(f(self.column(i))); } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Returns a column vector where each element is the result of the application of `f` on the /// corresponding column of the original matrix. /// /// This is the same as `self.compress_rows(f).transpose()`. #[inline] #[must_use] pub fn compress_rows_tr( &self, f: impl Fn(VectorView<'_, T, R, S::RStride, S::CStride>) -> T, ) -> OVector where DefaultAllocator: Allocator, { let ncols = self.shape_generic().1; let mut res = Matrix::uninit(ncols, Const::<1>); for i in 0..ncols.value() { // TODO: avoid bound checking of column. // Safety: all indices are in range. unsafe { *res.vget_unchecked_mut(i) = MaybeUninit::new(f(self.column(i))); } } // Safety: res is now fully initialized. unsafe { res.assume_init() } } /// Returns a column vector resulting from the folding of `f` on each column of this matrix. #[inline] #[must_use] pub fn compress_columns( &self, init: OVector, f: impl Fn(&mut OVector, VectorView<'_, T, R, S::RStride, S::CStride>), ) -> OVector where DefaultAllocator: Allocator, { let mut res = init; for i in 0..self.ncols() { f(&mut res, self.column(i)) } res } } /// # Common statistics operations impl> Matrix { /* * * Sum computation. * */ /// The sum of all the elements of this matrix. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2x3; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.sum(), 21.0); /// ``` #[inline] #[must_use] pub fn sum(&self) -> T where T: ClosedAddAssign + Zero, { self.iter().cloned().fold(T::zero(), |a, b| a + b) } /// The sum of all the rows of this matrix. /// /// Use `.row_sum_tr` if you need the result in a column vector instead. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Matrix3x2}; /// # use nalgebra::{RowVector2, RowVector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_sum(), RowVector3::new(5.0, 7.0, 9.0)); /// /// let mint = Matrix3x2::new(1, 2, /// 3, 4, /// 5, 6); /// assert_eq!(mint.row_sum(), RowVector2::new(9,12)); /// ``` #[inline] #[must_use] pub fn row_sum(&self) -> RowOVector where T: ClosedAddAssign + Zero, DefaultAllocator: Allocator, { self.compress_rows(|col| col.sum()) } /// The sum of all the rows of this matrix. The result is transposed and returned as a column vector. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Matrix3x2}; /// # use nalgebra::{Vector2, Vector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_sum_tr(), Vector3::new(5.0, 7.0, 9.0)); /// /// let mint = Matrix3x2::new(1, 2, /// 3, 4, /// 5, 6); /// assert_eq!(mint.row_sum_tr(), Vector2::new(9, 12)); /// ``` #[inline] #[must_use] pub fn row_sum_tr(&self) -> OVector where T: ClosedAddAssign + Zero, DefaultAllocator: Allocator, { self.compress_rows_tr(|col| col.sum()) } /// The sum of all the columns of this matrix. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Matrix3x2}; /// # use nalgebra::{Vector2, Vector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.column_sum(), Vector2::new(6.0, 15.0)); /// /// let mint = Matrix3x2::new(1, 2, /// 3, 4, /// 5, 6); /// assert_eq!(mint.column_sum(), Vector3::new(3, 7, 11)); /// ``` #[inline] #[must_use] pub fn column_sum(&self) -> OVector where T: ClosedAddAssign + Zero, DefaultAllocator: Allocator, { let nrows = self.shape_generic().0; self.compress_columns(OVector::zeros_generic(nrows, Const::<1>), |out, col| { *out += col; }) } /* * * Product computation. * */ /// The product of all the elements of this matrix. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2x3; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.product(), 720.0); /// ``` #[inline] #[must_use] pub fn product(&self) -> T where T: ClosedMulAssign + One, { self.iter().cloned().fold(T::one(), |a, b| a * b) } /// The product of all the rows of this matrix. /// /// Use `.row_sum_tr` if you need the result in a column vector instead. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Matrix3x2}; /// # use nalgebra::{RowVector2, RowVector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_product(), RowVector3::new(4.0, 10.0, 18.0)); /// /// let mint = Matrix3x2::new(1, 2, /// 3, 4, /// 5, 6); /// assert_eq!(mint.row_product(), RowVector2::new(15, 48)); /// ``` #[inline] #[must_use] pub fn row_product(&self) -> RowOVector where T: ClosedMulAssign + One, DefaultAllocator: Allocator, { self.compress_rows(|col| col.product()) } /// The product of all the rows of this matrix. The result is transposed and returned as a column vector. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Matrix3x2}; /// # use nalgebra::{Vector2, Vector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_product_tr(), Vector3::new(4.0, 10.0, 18.0)); /// /// let mint = Matrix3x2::new(1, 2, /// 3, 4, /// 5, 6); /// assert_eq!(mint.row_product_tr(), Vector2::new(15, 48)); /// ``` #[inline] #[must_use] pub fn row_product_tr(&self) -> OVector where T: ClosedMulAssign + One, DefaultAllocator: Allocator, { self.compress_rows_tr(|col| col.product()) } /// The product of all the columns of this matrix. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Matrix3x2}; /// # use nalgebra::{Vector2, Vector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.column_product(), Vector2::new(6.0, 120.0)); /// /// let mint = Matrix3x2::new(1, 2, /// 3, 4, /// 5, 6); /// assert_eq!(mint.column_product(), Vector3::new(2, 12, 30)); /// ``` #[inline] #[must_use] pub fn column_product(&self) -> OVector where T: ClosedMulAssign + One, DefaultAllocator: Allocator, { let nrows = self.shape_generic().0; self.compress_columns( OVector::repeat_generic(nrows, Const::<1>, T::one()), |out, col| { out.component_mul_assign(&col); }, ) } /* * * Variance computation. * */ /// The variance of all the elements of this matrix. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Matrix2x3; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_relative_eq!(m.variance(), 35.0 / 12.0, epsilon = 1.0e-8); /// ``` #[inline] #[must_use] pub fn variance(&self) -> T where T: Field + SupersetOf, { if self.is_empty() { T::zero() } else { let n_elements: T = crate::convert(self.len() as f64); let mean = self.mean(); self.iter().cloned().fold(T::zero(), |acc, x| { acc + (x.clone() - mean.clone()) * (x - mean.clone()) }) / n_elements } } /// The variance of all the rows of this matrix. /// /// Use `.row_variance_tr` if you need the result in a column vector instead. /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, RowVector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_variance(), RowVector3::new(2.25, 2.25, 2.25)); /// ``` #[inline] #[must_use] pub fn row_variance(&self) -> RowOVector where T: Field + SupersetOf, DefaultAllocator: Allocator, { self.compress_rows(|col| col.variance()) } /// The variance of all the rows of this matrix. The result is transposed and returned as a column vector. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Vector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_variance_tr(), Vector3::new(2.25, 2.25, 2.25)); /// ``` #[inline] #[must_use] pub fn row_variance_tr(&self) -> OVector where T: Field + SupersetOf, DefaultAllocator: Allocator, { self.compress_rows_tr(|col| col.variance()) } /// The variance of all the columns of this matrix. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix2x3, Vector2}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_relative_eq!(m.column_variance(), Vector2::new(2.0 / 3.0, 2.0 / 3.0), epsilon = 1.0e-8); /// ``` #[inline] #[must_use] pub fn column_variance(&self) -> OVector where T: Field + SupersetOf, DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let mut mean = self.column_mean(); mean.apply(|e| *e = -(e.clone() * e.clone())); let denom = T::one() / crate::convert::<_, T>(ncols.value() as f64); self.compress_columns(mean, |out, col| { for i in 0..nrows.value() { unsafe { let val = col.vget_unchecked(i); *out.vget_unchecked_mut(i) += denom.clone() * val.clone() * val.clone() } } }) } /* * * Mean computation. * */ /// The mean of all the elements of this matrix. /// /// # Example /// /// ``` /// # use nalgebra::Matrix2x3; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.mean(), 3.5); /// ``` #[inline] #[must_use] pub fn mean(&self) -> T where T: Field + SupersetOf, { if self.is_empty() { T::zero() } else { self.sum() / crate::convert(self.len() as f64) } } /// The mean of all the rows of this matrix. /// /// Use `.row_mean_tr` if you need the result in a column vector instead. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, RowVector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_mean(), RowVector3::new(2.5, 3.5, 4.5)); /// ``` #[inline] #[must_use] pub fn row_mean(&self) -> RowOVector where T: Field + SupersetOf, DefaultAllocator: Allocator, { self.compress_rows(|col| col.mean()) } /// The mean of all the rows of this matrix. The result is transposed and returned as a column vector. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Vector3}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.row_mean_tr(), Vector3::new(2.5, 3.5, 4.5)); /// ``` #[inline] #[must_use] pub fn row_mean_tr(&self) -> OVector where T: Field + SupersetOf, DefaultAllocator: Allocator, { self.compress_rows_tr(|col| col.mean()) } /// The mean of all the columns of this matrix. /// /// # Example /// /// ``` /// # use nalgebra::{Matrix2x3, Vector2}; /// /// let m = Matrix2x3::new(1.0, 2.0, 3.0, /// 4.0, 5.0, 6.0); /// assert_eq!(m.column_mean(), Vector2::new(2.0, 5.0)); /// ``` #[inline] #[must_use] pub fn column_mean(&self) -> OVector where T: Field + SupersetOf, DefaultAllocator: Allocator, { let (nrows, ncols) = self.shape_generic(); let denom = T::one() / crate::convert::<_, T>(ncols.value() as f64); self.compress_columns(OVector::zeros_generic(nrows, Const::<1>), |out, col| { out.axpy(denom.clone(), &col, T::one()) }) } } nalgebra-0.33.0/src/base/storage.rs000064400000000000000000000311450072674642500152310ustar 00000000000000//! Abstract definition of a matrix data storage. use std::ptr; use crate::base::allocator::{Allocator, SameShapeC, SameShapeR}; use crate::base::default_allocator::DefaultAllocator; use crate::base::dimension::{Dim, U1}; use crate::base::Scalar; /* * Aliases for allocation results. */ /// The data storage for the sum of two matrices with dimensions `(R1, C1)` and `(R2, C2)`. pub type SameShapeStorage = , SameShapeC>>::Buffer; // TODO: better name than Owned ? /// The owned data storage that can be allocated from `S`. pub type Owned = >::Buffer; /// The owned data storage that can be allocated from `S`. pub type OwnedUninit = >::BufferUninit; /// The row-stride of the owned data storage for a buffer of dimension `(R, C)`. pub type RStride = <>::Buffer as RawStorage>::RStride; /// The column-stride of the owned data storage for a buffer of dimension `(R, C)`. pub type CStride = <>::Buffer as RawStorage>::CStride; /// 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` elements. /// /// 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 RawStorage: Sized { /// The static stride of this storage's rows. type RStride: Dim; /// The static stride of this storage's columns. type CStride: Dim; /// The matrix data pointer. fn ptr(&self) -> *const T; /// The dimension of the matrix at run-time. Arr length of zero indicates the additive identity /// element of any dimension. Must be equal to `Self::dimension()` if it is not `None`. fn shape(&self) -> (R, C); /// The spacing between consecutive row elements and consecutive column elements. /// /// 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 /// index must be such that the following holds: /// /// ```ignore /// let lindex = self.linear_index(irow, icol); /// assert!(*self.get_unchecked(irow, icol) == *self.get_unchecked_linear(lindex)) /// ``` #[inline] fn linear_index(&self, irow: usize, icol: usize) -> usize { let (rstride, cstride) = self.strides(); irow * rstride.value() + icol * cstride.value() } /// Gets the address of the i-th matrix component without performing bound-checking. /// /// # Safety /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] fn get_address_unchecked_linear(&self, i: usize) -> *const T { self.ptr().wrapping_add(i) } /// Gets the address of the i-th matrix component without performing bound-checking. /// /// # Safety /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] fn get_address_unchecked(&self, irow: usize, icol: usize) -> *const T { self.get_address_unchecked_linear(self.linear_index(irow, icol)) } /// Retrieves a reference to the i-th element without bound-checking. /// /// # Safety /// If the index is out of bounds, the method will cause undefined behavior. #[inline] unsafe fn get_unchecked_linear(&self, i: usize) -> &T { &*self.get_address_unchecked_linear(i) } /// Retrieves a reference to the i-th element without bound-checking. /// /// # Safety /// If the index is out of bounds, the method will cause undefined behavior. #[inline] unsafe fn get_unchecked(&self, irow: usize, icol: usize) -> &T { self.get_unchecked_linear(self.linear_index(irow, icol)) } /// Indicates whether this data buffer stores its elements contiguously. /// /// # Safety /// This function must not return `true` if the underlying storage is not contiguous, /// or undefined behaviour will occur. fn is_contiguous(&self) -> bool; /// Retrieves the data buffer as a contiguous slice. /// /// # Safety /// The matrix components may not be stored in a contiguous way, depending on the strides. /// This method is unsafe because this can yield to invalid aliasing when called on some pairs /// of matrix views originating from the same matrix with strides. /// /// Call the safe alternative `matrix.as_slice()` instead. unsafe fn as_slice_unchecked(&self) -> &[T]; } /// Trait shared by all matrix data storage that don’t 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: RawStorage { /// Builds a matrix data storage that does not contain any reference. fn into_owned(self) -> Owned where DefaultAllocator: Allocator; /// Clones this data storage to one that does not contain any reference. fn clone_owned(&self) -> Owned where DefaultAllocator: Allocator; /// Drops the storage without calling the destructors on the contained elements. fn forget_elements(self); } /// 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` elements. /// /// Note that a mutable access does not mean that the matrix owns its data. For example, a mutable /// matrix view can provide mutable access to its elements even if it does not own its data (it /// contains only an internal reference to them). pub unsafe trait RawStorageMut: RawStorage { /// The matrix mutable data pointer. fn ptr_mut(&mut self) -> *mut T; /// Gets the mutable address of the i-th matrix component without performing bound-checking. /// /// # Safety /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] fn get_address_unchecked_linear_mut(&mut self, i: usize) -> *mut T { self.ptr_mut().wrapping_add(i) } /// Gets the mutable address of the i-th matrix component without performing bound-checking. /// /// # Safety /// If the index is out of bounds, dereferencing the result will cause undefined behavior. #[inline] fn get_address_unchecked_mut(&mut self, irow: usize, icol: usize) -> *mut T { let lid = self.linear_index(irow, icol); self.get_address_unchecked_linear_mut(lid) } /// Retrieves a mutable reference to the i-th element without bound-checking. /// /// # Safety /// If the index is out of bounds, the method will cause undefined behavior. unsafe fn get_unchecked_linear_mut(&mut self, i: usize) -> &mut T { &mut *self.get_address_unchecked_linear_mut(i) } /// Retrieves a mutable reference to the element at `(irow, icol)` without bound-checking. /// /// # Safety /// If the index is out of bounds, the method will cause undefined behavior. #[inline] unsafe fn get_unchecked_mut(&mut self, irow: usize, icol: usize) -> &mut T { &mut *self.get_address_unchecked_mut(irow, icol) } /// Swaps two elements using their linear index without bound-checking. /// /// # 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) { // 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); } /// Swaps two elements without bound-checking. /// /// # Safety /// If the indices are out of bounds, the method will cause undefined behavior. #[inline] unsafe fn swap_unchecked(&mut self, row_col1: (usize, usize), row_col2: (usize, usize)) { let lid1 = self.linear_index(row_col1.0, row_col1.1); let lid2 = self.linear_index(row_col2.0, row_col2.1); self.swap_unchecked_linear(lid1, lid2) } /// Retrieves the mutable data buffer as a contiguous slice. /// /// Matrix components may not be contiguous, depending on its strides. /// /// # Safety /// The matrix components may not be stored in a contiguous way, depending on the strides. /// This method is unsafe because this can yield to invalid aliasing when called on some pairs /// of matrix slices originating from the same matrix with strides. unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T]; } /// Trait shared by all mutable matrix data storage that don’t contain any uninitialized elements. /// /// # Safety /// /// See safety note for `Storage`, `RawStorageMut`. pub unsafe trait StorageMut: Storage + RawStorageMut { } unsafe impl StorageMut for S where R: Dim, C: Dim, S: Storage + RawStorageMut, { } /// 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. pub unsafe trait IsContiguous {} /// A matrix storage that can be reshaped in-place. pub trait ReshapableStorage: RawStorage where T: Scalar, R1: Dim, C1: Dim, R2: Dim, C2: Dim, { /// The reshaped storage type. type Output: RawStorage; /// Reshapes the storage into the output storage type. fn reshape_generic(self, nrows: R2, ncols: C2) -> Self::Output; } nalgebra-0.33.0/src/base/swizzle.rs000064400000000000000000000046140072674642500152750ustar 00000000000000use crate::base::{DimName, Scalar, ToTypenum, Vector, Vector2, Vector3}; use crate::storage::RawStorage; use typenum::{self, Cmp, Greater}; macro_rules! impl_swizzle { ($( where $BaseDim: ident: $( $name: ident() -> $Result: ident[$($i: expr),+] ),+ ;)* ) => { $( $( /// Builds a new vector from components of `self`. #[inline] #[must_use] pub fn $name(&self) -> $Result where D::Typenum: Cmp { $Result::new($(self[$i].clone()),*) } )* )* } } /// # Swizzling impl> Vector where D: DimName + ToTypenum, { impl_swizzle!( where U0: xx() -> Vector2[0, 0], xxx() -> Vector3[0, 0, 0]; where U1: xy() -> Vector2[0, 1], yx() -> Vector2[1, 0], yy() -> Vector2[1, 1], xxy() -> Vector3[0, 0, 1], xyx() -> Vector3[0, 1, 0], xyy() -> Vector3[0, 1, 1], yxx() -> Vector3[1, 0, 0], yxy() -> Vector3[1, 0, 1], yyx() -> Vector3[1, 1, 0], yyy() -> Vector3[1, 1, 1]; where U2: xz() -> Vector2[0, 2], yz() -> Vector2[1, 2], zx() -> Vector2[2, 0], zy() -> Vector2[2, 1], zz() -> Vector2[2, 2], xxz() -> Vector3[0, 0, 2], xyz() -> Vector3[0, 1, 2], xzx() -> Vector3[0, 2, 0], xzy() -> Vector3[0, 2, 1], xzz() -> Vector3[0, 2, 2], yxz() -> Vector3[1, 0, 2], yyz() -> Vector3[1, 1, 2], yzx() -> Vector3[1, 2, 0], yzy() -> Vector3[1, 2, 1], yzz() -> Vector3[1, 2, 2], zxx() -> Vector3[2, 0, 0], zxy() -> Vector3[2, 0, 1], zxz() -> Vector3[2, 0, 2], zyx() -> Vector3[2, 1, 0], zyy() -> Vector3[2, 1, 1], zyz() -> Vector3[2, 1, 2], zzx() -> Vector3[2, 2, 0], zzy() -> Vector3[2, 2, 1], zzz() -> Vector3[2, 2, 2]; ); } nalgebra-0.33.0/src/base/uninit.rs000064400000000000000000000046020072674642500150710ustar 00000000000000use std::mem::MaybeUninit; /// This trait is used to write code that may work on matrices that may or may not /// be initialized. /// /// This trait is used to describe how a value must be accessed to initialize it or /// to retrieve a reference or mutable reference. Typically, a function accepting /// both initialized and uninitialized inputs should have a `Status: InitStatus` /// type parameter. Then the methods of the `Status` can be used to access the element. /// /// # Safety /// This trait must not be implemented outside of this crate. pub unsafe trait InitStatus: Copy { /// The type of the values with the initialization status described by `Self`. type Value; /// Initialize the given element. fn init(out: &mut Self::Value, t: T); /// Retrieve a reference to the element, assuming that it is initialized. /// /// # Safety /// This is unsound if the referenced value isn’t initialized. unsafe fn assume_init_ref(t: &Self::Value) -> &T; /// Retrieve a mutable reference to the element, assuming that it is initialized. /// /// # Safety /// This is unsound if the referenced value isn’t initialized. unsafe fn assume_init_mut(t: &mut Self::Value) -> &mut T; } #[derive(Copy, Clone, Debug, PartialEq, Eq)] /// A type implementing `InitStatus` indicating that the value is completely initialized. pub struct Init; #[derive(Copy, Clone, Debug, PartialEq, Eq)] /// A type implementing `InitStatus` indicating that the value is completely uninitialized. pub struct Uninit; unsafe impl InitStatus for Init { type Value = T; #[inline(always)] fn init(out: &mut T, t: T) { *out = t; } #[inline(always)] unsafe fn assume_init_ref(t: &T) -> &T { t } #[inline(always)] unsafe fn assume_init_mut(t: &mut T) -> &mut T { t } } unsafe impl InitStatus for Uninit { type Value = MaybeUninit; #[inline(always)] fn init(out: &mut MaybeUninit, t: T) { *out = MaybeUninit::new(t); } #[inline(always)] unsafe fn assume_init_ref(t: &MaybeUninit) -> &T { &*t.as_ptr() // TODO: use t.assume_init_ref() } #[inline(always)] unsafe fn assume_init_mut(t: &mut MaybeUninit) -> &mut T { &mut *t.as_mut_ptr() // TODO: use t.assume_init_mut() } } nalgebra-0.33.0/src/base/unit.rs000064400000000000000000000276270072674642500145560ustar 00000000000000use std::fmt; use std::ops::Deref; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::allocator::Allocator; use crate::base::DefaultAllocator; use crate::storage::RawStorage; use crate::{Dim, Matrix, OMatrix, RealField, Scalar, SimdComplexField, SimdRealField}; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A wrapper that ensures the underlying algebraic entity has a unit norm. /// /// **It is likely that the only piece of documentation that you need in this page are:** /// - **[The construction with normalization](#construction-with-normalization)** /// - **[Data extraction and construction without normalization](#data-extraction-and-construction-without-normalization)** /// - **[Interpolation between two unit vectors](#interpolation-between-two-unit-vectors)** /// /// All the other impl blocks you will see in this page are about [`UnitComplex`](crate::UnitComplex) /// and [`UnitQuaternion`](crate::UnitQuaternion); both built on top of `Unit`. If you are interested /// in their documentation, read their dedicated pages directly. #[repr(transparent)] #[derive(Clone, Hash, Copy)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Unit", bound(archive = " T: rkyv::Archive, ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] pub struct Unit { pub(crate) value: T, } impl fmt::Debug for Unit { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.value.fmt(formatter) } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Unit where T: bytemuck::Zeroable {} #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Unit where T: bytemuck::Pod {} #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Unit { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.value.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'de, T: Deserialize<'de>> Deserialize<'de> for Unit { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { T::deserialize(deserializer).map(|x| Unit { value: x }) } } impl PartialEq for Unit> where T: Scalar + PartialEq, R: Dim, C: Dim, S: RawStorage, { #[inline] fn eq(&self, rhs: &Self) -> bool { self.value.eq(&rhs.value) } } impl Eq for Unit> where T: Scalar + Eq, R: Dim, C: Dim, S: RawStorage, { } /// Trait implemented by entities scan be be normalized and put in an `Unit` struct. pub trait Normed { /// The type of the norm. type Norm: SimdRealField; /// Computes the norm. fn norm(&self) -> Self::Norm; /// Computes the squared norm. fn norm_squared(&self) -> Self::Norm; /// Multiply `self` by n. fn scale_mut(&mut self, n: Self::Norm); /// Divides `self` by n. fn unscale_mut(&mut self, n: Self::Norm); } /// # Construction with normalization impl Unit { /// Normalize the given vector and return it wrapped on a `Unit` structure. #[inline] pub fn new_normalize(value: T) -> Self { Self::new_and_get(value).0 } /// Attempts to normalize the given vector and return it wrapped on a `Unit` structure. /// /// Returns `None` if the norm was smaller or equal to `min_norm`. #[inline] pub fn try_new(value: T, min_norm: T::Norm) -> Option where T::Norm: RealField, { Self::try_new_and_get(value, min_norm).map(|res| res.0) } /// Normalize the given vector and return it wrapped on a `Unit` structure and its norm. #[inline] pub fn new_and_get(mut value: T) -> (Self, T::Norm) { let n = value.norm(); value.unscale_mut(n.clone()); (Unit { value }, n) } /// Normalize the given vector and return it wrapped on a `Unit` structure and its norm. /// /// Returns `None` if the norm was smaller or equal to `min_norm`. #[inline] pub fn try_new_and_get(mut value: T, min_norm: T::Norm) -> Option<(Self, T::Norm)> where T::Norm: RealField, { let sq_norm = value.norm_squared(); if sq_norm > min_norm.clone() * min_norm { let n = sq_norm.simd_sqrt(); value.unscale_mut(n.clone()); Some((Unit { value }, n)) } else { None } } /// Normalizes this vector again. This is useful when repeated computations /// might cause a drift in the norm because of float inaccuracies. /// /// Returns the norm before re-normalization. See `.renormalize_fast` for a faster alternative /// that may be slightly less accurate if `self` drifted significantly from having a unit length. #[inline] pub fn renormalize(&mut self) -> T::Norm { let n = self.norm(); self.value.unscale_mut(n.clone()); n } /// Normalizes this vector again using a first-order Taylor approximation. /// This is useful when repeated computations might cause a drift in the norm /// because of float inaccuracies. #[inline] pub fn renormalize_fast(&mut self) { let sq_norm = self.value.norm_squared(); let three: T::Norm = crate::convert(3.0); let half: T::Norm = crate::convert(0.5); self.value.scale_mut(half * (three - sq_norm)); } } /// # Data extraction and construction without normalization impl Unit { /// Wraps the given value, assuming it is already normalized. #[inline] pub const fn new_unchecked(value: T) -> Self { Unit { value } } /// Wraps the given reference, assuming it is already normalized. #[inline] pub fn from_ref_unchecked(value: &T) -> &Self { unsafe { &*(value as *const T as *const Self) } } /// Retrieves the underlying value. #[inline] pub fn into_inner(self) -> T { self.value } /// Retrieves the underlying value. /// Deprecated: use [`Unit::into_inner`] instead. #[deprecated(note = "use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> T { self.value } /// Returns a mutable reference to the underlying value. This is `_unchecked` because modifying /// the underlying value in such a way that it no longer has unit length may lead to unexpected /// results. #[inline] pub fn as_mut_unchecked(&mut self) -> &mut T { &mut self.value } } impl AsRef for Unit { #[inline] fn as_ref(&self) -> &T { &self.value } } /* /* * * Conversions. * */ impl SubsetOf for Unit where T::RealField: RelativeEq { #[inline] fn to_superset(&self) -> T { self.clone().into_inner() } #[inline] fn is_in_subset(value: &T) -> bool { relative_eq!(value.norm_squared(), crate::one()) } #[inline] fn from_superset_unchecked(value: &T) -> Self { Unit::new_normalize(value.clone()) // We still need to re-normalize because the condition is inexact. } } // impl RelativeEq for Unit { // type Epsilon = T::Epsilon; // // #[inline] // fn default_epsilon() -> Self::Epsilon { // T::default_epsilon() // } // // #[inline] // fn default_max_relative() -> Self::Epsilon { // T::default_max_relative() // } // // #[inline] // fn default_max_ulps() -> u32 { // T::default_max_ulps() // } // // #[inline] // fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool { // self.value.relative_eq(&other.value, epsilon, max_relative) // } // // #[inline] // fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { // self.value.ulps_eq(&other.value, epsilon, max_ulps) // } // } */ // TODO:re-enable this impl when specialization is possible. // Currently, it is disabled so that we can have a nice output for the `UnitQuaternion` display. /* impl fmt::Display for Unit { // XXX: will not always work correctly due to rounding errors. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value.fmt(f) } } */ impl Deref for Unit { type Target = T; #[inline] fn deref(&self) -> &T { unsafe { &*(self as *const Self as *const T) } } } // NOTE: we can't use a generic implementation for `Unit` because // num_complex::Complex does not implement `From[Complex<...>...]` (and can't // because of the orphan rules). impl From<[Unit>; 2]> for Unit> where T: From<[::Element; 2]>, T::Element: Scalar, DefaultAllocator: Allocator, { #[inline] fn from(arr: [Unit>; 2]) -> Self { Self::new_unchecked(OMatrix::from([ arr[0].clone().into_inner(), arr[1].clone().into_inner(), ])) } } impl From<[Unit>; 4]> for Unit> where T: From<[::Element; 4]>, T::Element: Scalar, DefaultAllocator: Allocator, { #[inline] fn from(arr: [Unit>; 4]) -> Self { Self::new_unchecked(OMatrix::from([ arr[0].clone().into_inner(), arr[1].clone().into_inner(), arr[2].clone().into_inner(), arr[3].clone().into_inner(), ])) } } impl From<[Unit>; 8]> for Unit> where T: From<[::Element; 8]>, T::Element: Scalar, DefaultAllocator: Allocator, { #[inline] fn from(arr: [Unit>; 8]) -> Self { Self::new_unchecked(OMatrix::from([ arr[0].clone().into_inner(), arr[1].clone().into_inner(), arr[2].clone().into_inner(), arr[3].clone().into_inner(), arr[4].clone().into_inner(), arr[5].clone().into_inner(), arr[6].clone().into_inner(), arr[7].clone().into_inner(), ])) } } impl From<[Unit>; 16]> for Unit> where T: From<[::Element; 16]>, T::Element: Scalar, DefaultAllocator: Allocator, { #[inline] fn from(arr: [Unit>; 16]) -> Self { Self::new_unchecked(OMatrix::from([ arr[0].clone().into_inner(), arr[1].clone().into_inner(), arr[2].clone().into_inner(), arr[3].clone().into_inner(), arr[4].clone().into_inner(), arr[5].clone().into_inner(), arr[6].clone().into_inner(), arr[7].clone().into_inner(), arr[8].clone().into_inner(), arr[9].clone().into_inner(), arr[10].clone().into_inner(), arr[11].clone().into_inner(), arr[12].clone().into_inner(), arr[13].clone().into_inner(), arr[14].clone().into_inner(), arr[15].clone().into_inner(), ])) } } nalgebra-0.33.0/src/base/vec_storage.rs000064400000000000000000000353440072674642500160730ustar 00000000000000#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; use crate::base::allocator::Allocator; use crate::base::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::base::default_allocator::DefaultAllocator; use crate::base::dimension::{Dim, DimName, Dyn, U1}; use crate::base::storage::{IsContiguous, Owned, RawStorage, RawStorageMut, ReshapableStorage}; use crate::base::{Scalar, Vector}; #[cfg(feature = "serde-serialize-no-std")] use serde::{ de::{Deserialize, Deserializer, Error}, ser::{Serialize, Serializer}, }; use crate::Storage; use std::mem::MaybeUninit; /* * * RawStorage. * */ /// A Vec-based matrix data storage. It may be dynamically-sized. #[repr(C)] #[derive(Eq, Debug, Clone, PartialEq)] pub struct VecStorage { data: Vec, nrows: R, ncols: C, } impl Default for VecStorage { fn default() -> Self { Self { data: Vec::new(), nrows: Dyn::from_usize(0), ncols: Dyn::from_usize(0), } } } impl Default for VecStorage { fn default() -> Self { Self { data: Vec::new(), nrows: R::name(), ncols: Dyn::from_usize(0), } } } impl Default for VecStorage { fn default() -> Self { Self { data: Vec::new(), nrows: Dyn::from_usize(0), ncols: C::name(), } } } impl Default for VecStorage { fn default() -> Self { let nrows = R::name(); let ncols = C::name(); let mut data = Vec::new(); data.resize_with(nrows.value() * ncols.value(), Default::default); Self { data, nrows, ncols } } } #[cfg(feature = "serde-serialize")] impl Serialize for VecStorage where T: Serialize, R: Serialize, C: Serialize, { fn serialize(&self, serializer: Ser) -> Result where Ser: Serializer, { (&self.data, &self.nrows, &self.ncols).serialize(serializer) } } #[cfg(feature = "serde-serialize")] impl<'a, T, R: Dim, C: Dim> Deserialize<'a> for VecStorage where T: Deserialize<'a>, R: Deserialize<'a>, C: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let (data, nrows, ncols): (Vec, R, C) = Deserialize::deserialize(deserializer)?; // SAFETY: make sure the data we deserialize have the // correct number of elements. if nrows.value() * ncols.value() != data.len() { return Err(Des::Error::custom(format!( "Expected {} components, found {}", nrows.value() * ncols.value(), data.len() ))); } Ok(Self { data, nrows, ncols }) } } #[deprecated(note = "renamed to `VecStorage`")] /// Renamed to [`VecStorage`]. pub type MatrixVec = VecStorage; impl VecStorage { /// Creates a new dynamic matrix data storage from the given vector and shape. #[inline] pub fn new(nrows: R, ncols: C, data: Vec) -> Self { assert!( nrows.value() * ncols.value() == data.len(), "Data storage buffer dimension mismatch." ); Self { data, nrows, ncols } } /// The underlying data storage. #[inline] #[must_use] pub fn as_vec(&self) -> &Vec { &self.data } /// The underlying mutable data storage. /// /// # Safety /// This is unsafe because this may cause UB if the size of the vector is changed /// by the user. #[inline] pub unsafe fn as_vec_mut(&mut self) -> &mut Vec { &mut self.data } /// Resizes the underlying mutable data storage and unwraps it. /// /// # Safety /// - If `sz` is larger than the current size, additional elements are uninitialized. /// - If `sz` is smaller than the current size, additional elements are truncated but **not** dropped. /// It is the responsibility of the caller of this method to drop these elements. #[inline] pub unsafe fn resize(mut self, sz: usize) -> Vec> { let len = self.len(); let new_data = if sz < len { // Use `set_len` instead of `truncate` because we don’t want to // drop the removed elements (it’s the caller’s responsibility). self.data.set_len(sz); self.data.shrink_to_fit(); // Safety: // - MaybeUninit has the same alignment and layout as T. // - The length and capacity come from a valid vector. Vec::from_raw_parts( self.data.as_mut_ptr() as *mut MaybeUninit, self.data.len(), self.data.capacity(), ) } else { self.data.reserve_exact(sz - len); // Safety: // - MaybeUninit has the same alignment and layout as T. // - The length and capacity come from a valid vector. let mut new_data = Vec::from_raw_parts( self.data.as_mut_ptr() as *mut MaybeUninit, self.data.len(), self.data.capacity(), ); // Safety: we can set the length here because MaybeUninit is always assumed // to be initialized. new_data.set_len(sz); new_data }; // Avoid double-free by forgetting `self` because its data buffer has // been transferred to `new_data`. std::mem::forget(self); new_data } /// The number of elements on the underlying vector. #[inline] #[must_use] pub fn len(&self) -> usize { self.data.len() } /// Returns true if the underlying vector contains no elements. #[inline] #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// A slice containing all the components stored in this storage in column-major order. #[inline] pub fn as_slice(&self) -> &[T] { &self.data[..] } /// A mutable slice containing all the components stored in this storage in column-major order. #[inline] pub fn as_mut_slice(&mut self) -> &mut [T] { &mut self.data[..] } } impl From> for Vec { fn from(vec: VecStorage) -> Self { vec.data } } /* * * Dyn − Static * Dyn − Dyn * */ unsafe impl RawStorage for VecStorage { type RStride = U1; type CStride = Dyn; #[inline] fn ptr(&self) -> *const T { self.data.as_ptr() } #[inline] fn shape(&self) -> (Dyn, C) { (self.nrows, self.ncols) } #[inline] fn strides(&self) -> (Self::RStride, Self::CStride) { (Self::RStride::name(), self.nrows) } #[inline] fn is_contiguous(&self) -> bool { true } #[inline] unsafe fn as_slice_unchecked(&self) -> &[T] { &self.data } } unsafe impl Storage for VecStorage where DefaultAllocator: Allocator = Self>, { #[inline] fn into_owned(self) -> Owned where DefaultAllocator: Allocator, { self } #[inline] fn clone_owned(&self) -> Owned where DefaultAllocator: Allocator, { self.clone() } #[inline] fn forget_elements(mut self) { // SAFETY: setting the length to zero is always sound, as it does not // cause any memory to be deemed initialized. If the previous length was // non-zero, it is equivalent to using mem::forget to leak each element. // Then, when this function returns, self.data is dropped, freeing the // allocated memory, but the elements are not dropped because they are // now considered uninitialized. unsafe { self.data.set_len(0) }; } } unsafe impl RawStorage for VecStorage { type RStride = U1; type CStride = R; #[inline] fn ptr(&self) -> *const T { self.data.as_ptr() } #[inline] fn shape(&self) -> (R, Dyn) { (self.nrows, self.ncols) } #[inline] fn strides(&self) -> (Self::RStride, Self::CStride) { (Self::RStride::name(), self.nrows) } #[inline] fn is_contiguous(&self) -> bool { true } #[inline] unsafe fn as_slice_unchecked(&self) -> &[T] { &self.data } } unsafe impl Storage for VecStorage where DefaultAllocator: Allocator = Self>, { #[inline] fn into_owned(self) -> Owned where DefaultAllocator: Allocator, { self } #[inline] fn clone_owned(&self) -> Owned where DefaultAllocator: Allocator, { self.clone() } #[inline] fn forget_elements(mut self) { // SAFETY: setting the length to zero is always sound, as it does not // cause any memory to be deemed initialized. If the previous length was // non-zero, it is equivalent to using mem::forget to leak each element. // Then, when this function returns, self.data is dropped, freeing the // allocated memory, but the elements are not dropped because they are // now considered uninitialized. unsafe { self.data.set_len(0) }; } } /* * * RawStorageMut, ContiguousStorage. * */ unsafe impl RawStorageMut for VecStorage { #[inline] fn ptr_mut(&mut self) -> *mut T { self.data.as_mut_ptr() } #[inline] unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { &mut self.data[..] } } unsafe impl IsContiguous for VecStorage {} impl ReshapableStorage for VecStorage where T: Scalar, C1: Dim, C2: Dim, { type Output = VecStorage; fn reshape_generic(self, nrows: Dyn, ncols: C2) -> Self::Output { assert_eq!(nrows.value() * ncols.value(), self.data.len()); VecStorage { data: self.data, nrows, ncols, } } } impl ReshapableStorage for VecStorage where T: Scalar, C1: Dim, R2: DimName, { type Output = VecStorage; fn reshape_generic(self, nrows: R2, ncols: Dyn) -> Self::Output { assert_eq!(nrows.value() * ncols.value(), self.data.len()); VecStorage { data: self.data, nrows, ncols, } } } unsafe impl RawStorageMut for VecStorage { #[inline] fn ptr_mut(&mut self) -> *mut T { self.data.as_mut_ptr() } #[inline] unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] { &mut self.data[..] } } impl ReshapableStorage for VecStorage where T: Scalar, R1: DimName, C2: Dim, { type Output = VecStorage; fn reshape_generic(self, nrows: Dyn, ncols: C2) -> Self::Output { assert_eq!(nrows.value() * ncols.value(), self.data.len()); VecStorage { data: self.data, nrows, ncols, } } } impl ReshapableStorage for VecStorage where T: Scalar, R1: DimName, R2: DimName, { type Output = VecStorage; fn reshape_generic(self, nrows: R2, ncols: Dyn) -> Self::Output { assert_eq!(nrows.value() * ncols.value(), self.data.len()); VecStorage { data: self.data, nrows, ncols, } } } impl Extend for VecStorage { /// Extends the number of columns of the `VecStorage` with elements /// from the given iterator. /// /// # Panics /// This function panics if the number of elements yielded by the /// given iterator is not a multiple of the number of rows of the /// `VecStorage`. fn extend>(&mut self, iter: I) { self.data.extend(iter); self.ncols = Dyn(self.data.len() / self.nrows.value()); assert!(self.data.len() % self.nrows.value() == 0, "The number of elements produced by the given iterator was not a multiple of the number of rows."); } } impl<'a, T: 'a + Copy, R: Dim> Extend<&'a T> for VecStorage { /// Extends the number of columns of the `VecStorage` with elements /// from the given iterator. /// /// # Panics /// This function panics if the number of elements yielded by the /// given iterator is not a multiple of the number of rows of the /// `VecStorage`. fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().copied()) } } impl Extend> for VecStorage where T: Scalar, R: Dim, RV: Dim, SV: RawStorage, ShapeConstraint: SameNumberOfRows, { /// Extends the number of columns of the `VecStorage` with vectors /// from the given iterator. /// /// # Panics /// This function panics if the number of rows of each `Vector` /// yielded by the iterator is not equal to the number of rows /// of this `VecStorage`. fn extend>>(&mut self, iter: I) { let nrows = self.nrows.value(); let iter = iter.into_iter(); let (lower, _upper) = iter.size_hint(); self.data.reserve(nrows * lower); for vector in iter { assert_eq!(nrows, vector.shape().0); self.data.extend(vector.iter().cloned()); } self.ncols = Dyn(self.data.len() / nrows); } } impl Extend for VecStorage { /// Extends the number of rows of the `VecStorage` with elements /// from the given iterator. fn extend>(&mut self, iter: I) { self.data.extend(iter); self.nrows = Dyn(self.data.len()); } } nalgebra-0.33.0/src/debug/mod.rs000064400000000000000000000002560072674642500145170ustar 00000000000000//! Various tools useful for testing/debugging/benchmarking. mod random_orthogonal; mod random_sdp; pub use self::random_orthogonal::*; pub use self::random_sdp::*; nalgebra-0.33.0/src/debug/random_orthogonal.rs000064400000000000000000000032520072674642500174530ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use crate::base::allocator::Allocator; use crate::base::dimension::{Dim, Dyn}; use crate::base::Scalar; use crate::base::{DefaultAllocator, OMatrix}; use crate::linalg::givens::GivensRotation; use simba::scalar::ComplexField; /// A random orthogonal matrix. #[derive(Clone, Debug)] pub struct RandomOrthogonal where DefaultAllocator: Allocator, { m: OMatrix, } impl RandomOrthogonal where DefaultAllocator: Allocator, { /// Retrieve the generated matrix. pub fn unwrap(self) -> OMatrix { self.m } /// Creates a new random orthogonal matrix from its dimension and a random reals generators. pub fn new T>(dim: D, mut rand: Rand) -> Self { let mut res = OMatrix::identity_generic(dim, dim); // Create an orthogonal matrix by composing random Givens rotations rotations. for i in 0..dim.value() - 1 { let rot = GivensRotation::new(rand(), rand()).0; rot.rotate(&mut res.fixed_rows_mut::<2>(i)); } RandomOrthogonal { m: res } } } #[cfg(feature = "arbitrary")] impl Arbitrary for RandomOrthogonal where DefaultAllocator: Allocator, Owned: Clone + Send, { fn arbitrary(g: &mut Gen) -> Self { let dim = D::try_to_usize().unwrap_or(1 + usize::arbitrary(g) % 50); Self::new(D::from_usize(dim), || T::arbitrary(g)) } } nalgebra-0.33.0/src/debug/random_sdp.rs000064400000000000000000000032710072674642500160660ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use crate::base::allocator::Allocator; use crate::base::dimension::{Dim, Dyn}; use crate::base::Scalar; use crate::base::{DefaultAllocator, OMatrix}; use simba::scalar::ComplexField; use crate::debug::RandomOrthogonal; /// A random, well-conditioned, symmetric definite-positive matrix. #[derive(Clone, Debug)] pub struct RandomSDP where DefaultAllocator: Allocator, { m: OMatrix, } impl RandomSDP where DefaultAllocator: Allocator, { /// Retrieve the generated matrix. pub fn unwrap(self) -> OMatrix { self.m } /// Creates a new well conditioned symmetric definite-positive matrix from its dimension and a /// random reals generators. pub fn new T>(dim: D, mut rand: Rand) -> Self { let mut m = RandomOrthogonal::new(dim, || rand()).unwrap(); let mt = m.adjoint(); for i in 0..dim.value() { let mut col = m.column_mut(i); let eigenval = T::one() + T::from_real(rand().modulus()); col *= eigenval; } RandomSDP { m: m * mt } } } #[cfg(feature = "arbitrary")] impl Arbitrary for RandomSDP where DefaultAllocator: Allocator, Owned: Clone + Send, { fn arbitrary(g: &mut Gen) -> Self { let dim = D::try_to_usize().unwrap_or(1 + usize::arbitrary(g) % 50); Self::new(D::from_usize(dim), || T::arbitrary(g)) } } nalgebra-0.33.0/src/geometry/abstract_rotation.rs000064400000000000000000000077300072674642500202330ustar 00000000000000use crate::geometry::{Rotation, UnitComplex, UnitQuaternion}; use crate::{Const, OVector, Point, SVector, Scalar, SimdRealField, Unit}; use simba::scalar::ClosedMulAssign; /// Trait implemented by rotations that can be used inside of an `Isometry` or `Similarity`. pub trait AbstractRotation: PartialEq + ClosedMulAssign + Clone { /// The rotation identity. fn identity() -> Self; /// The rotation inverse. fn inverse(&self) -> Self; /// Change `self` to its inverse. fn inverse_mut(&mut self); /// Apply the rotation to the given vector. fn transform_vector(&self, v: &SVector) -> SVector; /// Apply the rotation to the given point. fn transform_point(&self, p: &Point) -> Point; /// Apply the inverse rotation to the given vector. fn inverse_transform_vector(&self, v: &OVector>) -> OVector>; /// Apply the inverse rotation to the given unit vector. fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { Unit::new_unchecked(self.inverse_transform_vector(&**v)) } /// Apply the inverse rotation to the given point. fn inverse_transform_point(&self, p: &Point) -> Point; } impl AbstractRotation for Rotation where T::Element: SimdRealField, { #[inline] fn identity() -> Self { Self::identity() } #[inline] fn inverse(&self) -> Self { self.inverse() } #[inline] fn inverse_mut(&mut self) { self.inverse_mut() } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { self * v } #[inline] fn transform_point(&self, p: &Point) -> Point { self * p } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.inverse_transform_vector(v) } #[inline] fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.inverse_transform_unit_vector(v) } #[inline] fn inverse_transform_point(&self, p: &Point) -> Point { self.inverse_transform_point(p) } } impl AbstractRotation for UnitQuaternion where T::Element: SimdRealField, { #[inline] fn identity() -> Self { Self::identity() } #[inline] fn inverse(&self) -> Self { self.inverse() } #[inline] fn inverse_mut(&mut self) { self.inverse_mut() } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { self * v } #[inline] fn transform_point(&self, p: &Point) -> Point { self * p } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.inverse_transform_vector(v) } #[inline] fn inverse_transform_point(&self, p: &Point) -> Point { self.inverse_transform_point(p) } } impl AbstractRotation for UnitComplex where T::Element: SimdRealField, { #[inline] fn identity() -> Self { Self::identity() } #[inline] fn inverse(&self) -> Self { self.inverse() } #[inline] fn inverse_mut(&mut self) { self.inverse_mut() } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { self * v } #[inline] fn transform_point(&self, p: &Point) -> Point { self * p } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.inverse_transform_vector(v) } #[inline] fn inverse_transform_point(&self, p: &Point) -> Point { self.inverse_transform_point(p) } } nalgebra-0.33.0/src/geometry/dual_quaternion.rs000064400000000000000000001050130072674642500176740ustar 00000000000000// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; use crate::{ Isometry3, Matrix4, Normed, OVector, Point3, Quaternion, Scalar, SimdRealField, Translation3, Unit, UnitQuaternion, Vector3, Zero, U8, }; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use simba::scalar::{ClosedNeg, RealField}; /// A dual quaternion. /// /// # Indexing /// /// `DualQuaternions` are stored as \[..real, ..dual\]. /// Both of the quaternion components are laid out in `i, j, k, w` order. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, Quaternion}; /// /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// /// let dq = DualQuaternion::from_real_and_dual(real, dual); /// assert_eq!(dq[0], 2.0); /// assert_eq!(dq[1], 3.0); /// /// assert_eq!(dq[4], 6.0); /// assert_eq!(dq[7], 5.0); /// ``` /// /// NOTE: /// As of December 2020, dual quaternion support is a work in progress. /// If a feature that you need is missing, feel free to open an issue or a PR. /// See #[repr(C)] #[derive(Debug, Copy, Clone)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "DualQuaternion", bound(archive = " T: rkyv::Archive, Quaternion: rkyv::Archive> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] pub struct DualQuaternion { /// The real component of the quaternion pub real: Quaternion, /// The dual component of the quaternion pub dual: Quaternion, } impl Eq for DualQuaternion {} impl PartialEq for DualQuaternion { #[inline] fn eq(&self, right: &Self) -> bool { self.real == right.real && self.dual == right.dual } } impl Default for DualQuaternion { fn default() -> Self { Self { real: Quaternion::default(), dual: Quaternion::default(), } } } impl DualQuaternion where T::Element: SimdRealField, { /// Normalizes this quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{DualQuaternion, Quaternion}; /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let dq = DualQuaternion::from_real_and_dual(real, dual); /// /// let dq_normalized = dq.normalize(); /// /// assert_relative_eq!(dq_normalized.real.norm(), 1.0); /// ``` #[inline] #[must_use = "Did you mean to use normalize_mut()?"] pub fn normalize(&self) -> Self { let real_norm = self.real.norm(); Self::from_real_and_dual( self.real.clone() / real_norm.clone(), self.dual.clone() / real_norm, ) } /// Normalizes this quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{DualQuaternion, Quaternion}; /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let mut dq = DualQuaternion::from_real_and_dual(real, dual); /// /// dq.normalize_mut(); /// /// assert_relative_eq!(dq.real.norm(), 1.0); /// ``` #[inline] pub fn normalize_mut(&mut self) -> T { let real_norm = self.real.norm(); self.real /= real_norm.clone(); self.dual /= real_norm.clone(); real_norm } /// The conjugate of this dual quaternion, containing the conjugate of /// the real and imaginary parts.. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, Quaternion}; /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let dq = DualQuaternion::from_real_and_dual(real, dual); /// /// let conj = dq.conjugate(); /// assert!(conj.real.i == -2.0 && conj.real.j == -3.0 && conj.real.k == -4.0); /// assert!(conj.real.w == 1.0); /// assert!(conj.dual.i == -6.0 && conj.dual.j == -7.0 && conj.dual.k == -8.0); /// assert!(conj.dual.w == 5.0); /// ``` #[inline] #[must_use = "Did you mean to use conjugate_mut()?"] pub fn conjugate(&self) -> Self { Self::from_real_and_dual(self.real.conjugate(), self.dual.conjugate()) } /// Replaces this quaternion by its conjugate. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, Quaternion}; /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let mut dq = DualQuaternion::from_real_and_dual(real, dual); /// /// dq.conjugate_mut(); /// assert!(dq.real.i == -2.0 && dq.real.j == -3.0 && dq.real.k == -4.0); /// assert!(dq.real.w == 1.0); /// assert!(dq.dual.i == -6.0 && dq.dual.j == -7.0 && dq.dual.k == -8.0); /// assert!(dq.dual.w == 5.0); /// ``` #[inline] pub fn conjugate_mut(&mut self) { self.real.conjugate_mut(); self.dual.conjugate_mut(); } /// Inverts this dual quaternion if it is not zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{DualQuaternion, Quaternion}; /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let dq = DualQuaternion::from_real_and_dual(real, dual); /// let inverse = dq.try_inverse(); /// /// assert!(inverse.is_some()); /// assert_relative_eq!(inverse.unwrap() * dq, DualQuaternion::identity()); /// /// //Non-invertible case /// let zero = Quaternion::new(0.0, 0.0, 0.0, 0.0); /// let dq = DualQuaternion::from_real_and_dual(zero, zero); /// let inverse = dq.try_inverse(); /// /// assert!(inverse.is_none()); /// ``` #[inline] #[must_use = "Did you mean to use try_inverse_mut()?"] pub fn try_inverse(&self) -> Option where T: RealField, { let mut res = self.clone(); if res.try_inverse_mut() { Some(res) } else { None } } /// Inverts this dual quaternion in-place if it is not zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{DualQuaternion, Quaternion}; /// let real = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let dual = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let dq = DualQuaternion::from_real_and_dual(real, dual); /// let mut dq_inverse = dq; /// dq_inverse.try_inverse_mut(); /// /// assert_relative_eq!(dq_inverse * dq, DualQuaternion::identity()); /// /// //Non-invertible case /// let zero = Quaternion::new(0.0, 0.0, 0.0, 0.0); /// let mut dq = DualQuaternion::from_real_and_dual(zero, zero); /// assert!(!dq.try_inverse_mut()); /// ``` #[inline] pub fn try_inverse_mut(&mut self) -> bool where T: RealField, { let inverted = self.real.try_inverse_mut(); if inverted { self.dual = -self.real.clone() * self.dual.clone() * self.real.clone(); true } else { false } } /// Linear interpolation between two dual quaternions. /// /// Computes `self * (1 - t) + other * t`. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, Quaternion}; /// let dq1 = DualQuaternion::from_real_and_dual( /// Quaternion::new(1.0, 0.0, 0.0, 4.0), /// Quaternion::new(0.0, 2.0, 0.0, 0.0) /// ); /// let dq2 = DualQuaternion::from_real_and_dual( /// Quaternion::new(2.0, 0.0, 1.0, 0.0), /// Quaternion::new(0.0, 2.0, 0.0, 0.0) /// ); /// assert_eq!(dq1.lerp(&dq2, 0.25), DualQuaternion::from_real_and_dual( /// Quaternion::new(1.25, 0.0, 0.25, 3.0), /// Quaternion::new(0.0, 2.0, 0.0, 0.0) /// )); /// ``` #[inline] #[must_use] pub fn lerp(&self, other: &Self, t: T) -> Self { self * (T::one() - t.clone()) + other * t } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for DualQuaternion where T: Scalar + bytemuck::Zeroable, Quaternion: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for DualQuaternion where T: Scalar + bytemuck::Pod, Quaternion: bytemuck::Pod, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for DualQuaternion where T: Serialize, { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: Serializer, { self.as_ref().serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: SimdRealField> Deserialize<'a> for DualQuaternion where T: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { type Dq = [T; 8]; let dq: Dq = Dq::::deserialize(deserializer)?; Ok(Self { real: Quaternion::new(dq[3].clone(), dq[0].clone(), dq[1].clone(), dq[2].clone()), dual: Quaternion::new(dq[7].clone(), dq[4].clone(), dq[5].clone(), dq[6].clone()), }) } } impl DualQuaternion { #[allow(clippy::wrong_self_convention)] fn to_vector(&self) -> OVector { self.as_ref().clone().into() } } impl> AbsDiffEq for DualQuaternion { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.to_vector().abs_diff_eq(&other.to_vector(), epsilon.clone()) || // Account for the double-covering of S², i.e. q = -q self.to_vector().iter().zip(other.to_vector().iter()).all(|(a, b)| a.abs_diff_eq(&-b.clone(), epsilon.clone())) } } impl> RelativeEq for DualQuaternion { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.to_vector().relative_eq(&other.to_vector(), epsilon.clone(), max_relative.clone()) || // Account for the double-covering of S², i.e. q = -q self.to_vector().iter().zip(other.to_vector().iter()).all(|(a, b)| a.relative_eq(&-b.clone(), epsilon.clone(), max_relative.clone())) } } impl> UlpsEq for DualQuaternion { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.to_vector().ulps_eq(&other.to_vector(), epsilon.clone(), max_ulps) || // Account for the double-covering of S², i.e. q = -q. self.to_vector().iter().zip(other.to_vector().iter()).all(|(a, b)| a.ulps_eq(&-b.clone(), epsilon.clone(), max_ulps)) } } /// A unit dual quaternion. May be used to represent a rotation followed by a /// translation. pub type UnitDualQuaternion = Unit>; impl PartialEq for UnitDualQuaternion { #[inline] fn eq(&self, rhs: &Self) -> bool { self.as_ref().eq(rhs.as_ref()) } } impl Eq for UnitDualQuaternion {} impl Normed for DualQuaternion { type Norm = T::SimdRealField; #[inline] fn norm(&self) -> T::SimdRealField { self.real.norm() } #[inline] fn norm_squared(&self) -> T::SimdRealField { self.real.norm_squared() } #[inline] fn scale_mut(&mut self, n: Self::Norm) { self.real.scale_mut(n.clone()); self.dual.scale_mut(n); } #[inline] fn unscale_mut(&mut self, n: Self::Norm) { self.real.unscale_mut(n.clone()); self.dual.unscale_mut(n); } } impl UnitDualQuaternion where T::Element: SimdRealField, { /// The underlying dual quaternion. /// /// Same as `self.as_ref()`. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, UnitDualQuaternion, Quaternion}; /// let id = UnitDualQuaternion::identity(); /// assert_eq!(*id.dual_quaternion(), DualQuaternion::from_real_and_dual( /// Quaternion::new(1.0, 0.0, 0.0, 0.0), /// Quaternion::new(0.0, 0.0, 0.0, 0.0) /// )); /// ``` #[inline] #[must_use] pub fn dual_quaternion(&self) -> &DualQuaternion { self.as_ref() } /// Compute the conjugate of this unit quaternion. /// /// # Example /// ``` /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion}; /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let unit = UnitDualQuaternion::new_normalize( /// DualQuaternion::from_real_and_dual(qr, qd) /// ); /// let conj = unit.conjugate(); /// assert_eq!(conj.real, unit.real.conjugate()); /// assert_eq!(conj.dual, unit.dual.conjugate()); /// ``` #[inline] #[must_use = "Did you mean to use conjugate_mut()?"] pub fn conjugate(&self) -> Self { Self::new_unchecked(self.as_ref().conjugate()) } /// Compute the conjugate of this unit quaternion in-place. /// /// # Example /// ``` /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion}; /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let unit = UnitDualQuaternion::new_normalize( /// DualQuaternion::from_real_and_dual(qr, qd) /// ); /// let mut conj = unit.clone(); /// conj.conjugate_mut(); /// assert_eq!(conj.as_ref().real, unit.as_ref().real.conjugate()); /// assert_eq!(conj.as_ref().dual, unit.as_ref().dual.conjugate()); /// ``` #[inline] pub fn conjugate_mut(&mut self) { self.as_mut_unchecked().conjugate_mut() } /// Inverts this dual quaternion if it is not zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, Quaternion, DualQuaternion}; /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let unit = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qr, qd)); /// let inv = unit.inverse(); /// assert_relative_eq!(unit * inv, UnitDualQuaternion::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(inv * unit, UnitDualQuaternion::identity(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(&self) -> Self { let real = Unit::new_unchecked(self.as_ref().real.clone()) .inverse() .into_inner(); let dual = -real.clone() * self.as_ref().dual.clone() * real.clone(); UnitDualQuaternion::new_unchecked(DualQuaternion { real, dual }) } /// Inverts this dual quaternion in place if it is not zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, Quaternion, DualQuaternion}; /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let unit = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qr, qd)); /// let mut inv = unit.clone(); /// inv.inverse_mut(); /// assert_relative_eq!(unit * inv, UnitDualQuaternion::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(inv * unit, UnitDualQuaternion::identity(), epsilon = 1.0e-6); /// ``` #[inline] pub fn inverse_mut(&mut self) { let quat = self.as_mut_unchecked(); quat.real = Unit::new_unchecked(quat.real.clone()) .inverse() .into_inner(); quat.dual = -quat.real.clone() * quat.dual.clone() * quat.real.clone(); } /// The unit dual quaternion needed to make `self` and `other` coincide. /// /// The result is such that: `self.isometry_to(other) * self == other`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion}; /// let qr = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let qd = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// let dq1 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qr, qd)); /// let dq2 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual(qd, qr)); /// let dq_to = dq1.isometry_to(&dq2); /// assert_relative_eq!(dq_to * dq1, dq2, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn isometry_to(&self, other: &Self) -> Self { other / self } /// Linear interpolation between two unit dual quaternions. /// /// The result is not normalized. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion}; /// let dq1 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual( /// Quaternion::new(0.5, 0.0, 0.5, 0.0), /// Quaternion::new(0.0, 0.5, 0.0, 0.5) /// )); /// let dq2 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual( /// Quaternion::new(0.5, 0.0, 0.0, 0.5), /// Quaternion::new(0.5, 0.0, 0.5, 0.0) /// )); /// assert_relative_eq!( /// UnitDualQuaternion::new_normalize(dq1.lerp(&dq2, 0.5)), /// UnitDualQuaternion::new_normalize( /// DualQuaternion::from_real_and_dual( /// Quaternion::new(0.5, 0.0, 0.25, 0.25), /// Quaternion::new(0.25, 0.25, 0.25, 0.25) /// ) /// ), /// epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn lerp(&self, other: &Self, t: T) -> DualQuaternion { self.as_ref().lerp(other.as_ref(), t) } /// Normalized linear interpolation between two unit quaternions. /// /// This is the same as `self.lerp` except that the result is normalized. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, Quaternion}; /// let dq1 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual( /// Quaternion::new(0.5, 0.0, 0.5, 0.0), /// Quaternion::new(0.0, 0.5, 0.0, 0.5) /// )); /// let dq2 = UnitDualQuaternion::new_normalize(DualQuaternion::from_real_and_dual( /// Quaternion::new(0.5, 0.0, 0.0, 0.5), /// Quaternion::new(0.5, 0.0, 0.5, 0.0) /// )); /// assert_relative_eq!(dq1.nlerp(&dq2, 0.2), UnitDualQuaternion::new_normalize( /// DualQuaternion::from_real_and_dual( /// Quaternion::new(0.5, 0.0, 0.4, 0.1), /// Quaternion::new(0.1, 0.4, 0.1, 0.4) /// ) /// ), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn nlerp(&self, other: &Self, t: T) -> Self { let mut res = self.lerp(other, t); let _ = res.normalize_mut(); Self::new_unchecked(res) } /// Screw linear interpolation between two unit quaternions. This creates a /// smooth arc from one dual-quaternion to another. /// /// Panics if the angle between both quaternion is 180 degrees (in which /// case the interpolation is not well-defined). Use `.try_sclerp` /// instead to avoid the panic. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, DualQuaternion, UnitQuaternion, Vector3}; /// /// let dq1 = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0), /// ); /// /// let dq2 = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 0.0, 3.0).into(), /// UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0), /// ); /// /// let dq = dq1.sclerp(&dq2, 1.0 / 3.0); /// /// assert_relative_eq!( /// dq.rotation().euler_angles().0, std::f32::consts::FRAC_PI_2, epsilon = 1.0e-6 /// ); /// assert_relative_eq!(dq.translation().vector.y, 3.0, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn sclerp(&self, other: &Self, t: T) -> Self where T: RealField, { self.try_sclerp(other, t, T::default_epsilon()) .expect("DualQuaternion sclerp: ambiguous configuration.") } /// Computes the screw-linear interpolation between two unit quaternions or /// returns `None` if both quaternions are approximately 180 degrees /// apart (in which case the interpolation is not well-defined). /// /// # Arguments /// * `self`: the first quaternion to interpolate from. /// * `other`: the second quaternion to interpolate toward. /// * `t`: the interpolation parameter. Should be between 0 and 1. /// * `epsilon`: the value below which the sinus of the angle separating /// both quaternion /// must be to return `None`. #[inline] #[must_use] pub fn try_sclerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, { let two = T::one() + T::one(); let half = T::one() / two.clone(); // Invert one of the quaternions if we've got a longest-path // interpolation. let other = { let dot_product = self.as_ref().real.coords.dot(&other.as_ref().real.coords); if relative_eq!(dot_product, T::zero(), epsilon = epsilon.clone()) { return None; } if dot_product < T::zero() { -other.clone() } else { other.clone() } }; let difference = self.as_ref().conjugate() * other.as_ref(); let norm_squared = difference.real.vector().norm_squared(); if relative_eq!(norm_squared, T::zero(), epsilon = epsilon) { return Some(Self::from_parts( self.translation() .vector .lerp(&other.translation().vector, t) .into(), self.rotation(), )); } let scalar: T = difference.real.scalar(); let mut angle = two.clone() * scalar.acos(); let inverse_norm_squared: T = T::one() / norm_squared; let inverse_norm = inverse_norm_squared.sqrt(); let mut pitch = -two * difference.dual.scalar() * inverse_norm.clone(); let direction = difference.real.vector() * inverse_norm.clone(); let moment = (difference.dual.vector() - direction.clone() * (pitch.clone() * difference.real.scalar() * half.clone())) * inverse_norm; angle *= t.clone(); pitch *= t; let sin = (half.clone() * angle.clone()).sin(); let cos = (half.clone() * angle).cos(); let real = Quaternion::from_parts(cos.clone(), direction.clone() * sin.clone()); let dual = Quaternion::from_parts( -pitch.clone() * half.clone() * sin.clone(), moment * sin + direction * (pitch * half * cos), ); Some( self * UnitDualQuaternion::new_unchecked(DualQuaternion::from_real_and_dual( real, dual, )), ) } /// Return the rotation part of this unit dual quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0) /// ); /// /// assert_relative_eq!( /// dq.rotation().angle(), std::f32::consts::FRAC_PI_4, epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn rotation(&self) -> UnitQuaternion { Unit::new_unchecked(self.as_ref().real.clone()) } /// Return the translation part of this unit dual quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0) /// ); /// /// assert_relative_eq!( /// dq.translation().vector, Vector3::new(0.0, 3.0, 0.0), epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn translation(&self) -> Translation3 { let two = T::one() + T::one(); Translation3::from( ((self.as_ref().dual.clone() * self.as_ref().real.clone().conjugate()) * two) .vector() .into_owned(), ) } /// Builds an isometry from this unit dual quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3}; /// let rotation = UnitQuaternion::from_euler_angles(std::f32::consts::PI, 0.0, 0.0); /// let translation = Vector3::new(1.0, 3.0, 2.5); /// let dq = UnitDualQuaternion::from_parts( /// translation.into(), /// rotation /// ); /// let iso = dq.to_isometry(); /// /// assert_relative_eq!(iso.rotation.angle(), std::f32::consts::PI, epsilon = 1.0e-6); /// assert_relative_eq!(iso.translation.vector, translation, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn to_isometry(self) -> Isometry3 { Isometry3::from_parts(self.translation(), self.rotation()) } /// Rotate and translate a point by this unit dual quaternion interpreted /// as an isometry. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3, Point3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0) /// ); /// let point = Point3::new(1.0, 2.0, 3.0); /// /// assert_relative_eq!( /// dq.transform_point(&point), Point3::new(1.0, 0.0, 2.0), epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point3) -> Point3 { self * pt } /// Rotate a vector by this unit dual quaternion, ignoring the translational /// component. /// /// This is the same as the multiplication `self * v`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0) /// ); /// let vector = Vector3::new(1.0, 2.0, 3.0); /// /// assert_relative_eq!( /// dq.transform_vector(&vector), Vector3::new(1.0, -3.0, 2.0), epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn transform_vector(&self, v: &Vector3) -> Vector3 { self * v } /// Rotate and translate a point by the inverse of this unit quaternion. /// /// This may be cheaper than inverting the unit dual quaternion and /// transforming the point. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3, Point3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0) /// ); /// let point = Point3::new(1.0, 2.0, 3.0); /// /// assert_relative_eq!( /// dq.inverse_transform_point(&point), Point3::new(1.0, 3.0, 1.0), epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point3) -> Point3 { self.inverse() * pt } /// Rotate a vector by the inverse of this unit quaternion, ignoring the /// translational component. /// /// This may be cheaper than inverting the unit dual quaternion and /// transforming the vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0) /// ); /// let vector = Vector3::new(1.0, 2.0, 3.0); /// /// assert_relative_eq!( /// dq.inverse_transform_vector(&vector), Vector3::new(1.0, 3.0, -2.0), epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 { self.inverse() * v } /// Rotate a unit vector by the inverse of this unit quaternion, ignoring /// the translational component. This may be /// cheaper than inverting the unit dual quaternion and transforming the /// vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Unit, Vector3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0) /// ); /// let vector = Unit::new_unchecked(Vector3::new(0.0, 1.0, 0.0)); /// /// assert_relative_eq!( /// dq.inverse_transform_unit_vector(&vector), /// Unit::new_unchecked(Vector3::new(0.0, 0.0, -1.0)), /// epsilon = 1.0e-6 /// ); /// ``` #[inline] #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.inverse() * v } } impl UnitDualQuaternion where T::Element: SimdRealField, { /// Converts this unit dual quaternion interpreted as an isometry /// into its equivalent homogeneous transformation matrix. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix4, UnitDualQuaternion, UnitQuaternion, Vector3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(1.0, 3.0, 2.0).into(), /// UnitQuaternion::from_axis_angle(&Vector3::z_axis(), std::f32::consts::FRAC_PI_6) /// ); /// let expected = Matrix4::new(0.8660254, -0.5, 0.0, 1.0, /// 0.5, 0.8660254, 0.0, 3.0, /// 0.0, 0.0, 1.0, 2.0, /// 0.0, 0.0, 0.0, 1.0); /// /// assert_relative_eq!(dq.to_homogeneous(), expected, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn to_homogeneous(self) -> Matrix4 { self.to_isometry().to_homogeneous() } } impl Default for UnitDualQuaternion { fn default() -> Self { Self::identity() } } impl fmt::Display for UnitDualQuaternion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(axis) = self.rotation().axis() { let axis = axis.into_inner(); write!( f, "UnitDualQuaternion translation: {} − angle: {} − axis: ({}, {}, {})", self.translation().vector, self.rotation().angle(), axis[0], axis[1], axis[2] ) } else { write!( f, "UnitDualQuaternion translation: {} − angle: {} − axis: (undefined)", self.translation().vector, self.rotation().angle() ) } } } impl> AbsDiffEq for UnitDualQuaternion { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.as_ref().abs_diff_eq(other.as_ref(), epsilon) } } impl> RelativeEq for UnitDualQuaternion { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.as_ref() .relative_eq(other.as_ref(), epsilon, max_relative) } } impl> UlpsEq for UnitDualQuaternion { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) } } nalgebra-0.33.0/src/geometry/dual_quaternion_construction.rs000064400000000000000000000174740072674642500225230ustar 00000000000000use crate::{ DualQuaternion, Isometry3, Quaternion, Scalar, SimdRealField, Translation3, UnitDualQuaternion, UnitQuaternion, }; use num::{One, Zero}; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use simba::scalar::SupersetOf; impl DualQuaternion { /// Creates a dual quaternion from its rotation and translation components. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, Quaternion}; /// let rot = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let trans = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// /// let dq = DualQuaternion::from_real_and_dual(rot, trans); /// assert_eq!(dq.real.w, 1.0); /// ``` #[inline] pub fn from_real_and_dual(real: Quaternion, dual: Quaternion) -> Self { Self { real, dual } } /// The dual quaternion multiplicative identity. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, Quaternion}; /// /// let dq1 = DualQuaternion::identity(); /// let dq2 = DualQuaternion::from_real_and_dual( /// Quaternion::new(1.,2.,3.,4.), /// Quaternion::new(5.,6.,7.,8.) /// ); /// /// assert_eq!(dq1 * dq2, dq2); /// assert_eq!(dq2 * dq1, dq2); /// ``` #[inline] pub fn identity() -> Self where T: SimdRealField, { Self::from_real_and_dual( Quaternion::from_real(T::one()), Quaternion::from_real(T::zero()), ) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::{Quaternion, DualQuaternion}; /// let q = DualQuaternion::from_real(Quaternion::new(1.0f64, 2.0, 3.0, 4.0)); /// let q2 = q.cast::(); /// assert_eq!(q2, DualQuaternion::from_real(Quaternion::new(1.0f32, 2.0, 3.0, 4.0))); /// ``` pub fn cast(self) -> DualQuaternion where DualQuaternion: SupersetOf, { crate::convert(self) } } impl DualQuaternion where T::Element: SimdRealField, { /// Creates a dual quaternion from only its real part, with no translation /// component. /// /// # Example /// ``` /// # use nalgebra::{DualQuaternion, Quaternion}; /// let rot = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// /// let dq = DualQuaternion::from_real(rot); /// assert_eq!(dq.real.w, 1.0); /// assert_eq!(dq.dual.w, 0.0); /// ``` #[inline] pub fn from_real(real: Quaternion) -> Self { Self { real, dual: Quaternion::zero(), } } } impl One for DualQuaternion where T::Element: SimdRealField, { #[inline] fn one() -> Self { Self::identity() } } impl Zero for DualQuaternion where T::Element: SimdRealField, { #[inline] fn zero() -> Self { DualQuaternion::from_real_and_dual(Quaternion::zero(), Quaternion::zero()) } #[inline] fn is_zero(&self) -> bool { self.real.is_zero() && self.dual.is_zero() } } #[cfg(feature = "arbitrary")] impl Arbitrary for DualQuaternion where T: SimdRealField + Arbitrary + Send, T::Element: SimdRealField, { #[inline] fn arbitrary(rng: &mut Gen) -> Self { Self::from_real_and_dual(Arbitrary::arbitrary(rng), Arbitrary::arbitrary(rng)) } } impl UnitDualQuaternion { /// The unit dual quaternion multiplicative identity, which also represents /// the identity transformation as an isometry. /// /// # Example /// ``` /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3, Point3}; /// let ident = UnitDualQuaternion::identity(); /// let point = Point3::new(1.0, -4.3, 3.33); /// /// assert_eq!(ident * point, point); /// assert_eq!(ident, ident.inverse()); /// ``` #[inline] pub fn identity() -> Self { Self::new_unchecked(DualQuaternion::identity()) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::UnitDualQuaternion; /// let q = UnitDualQuaternion::::identity(); /// let q2 = q.cast::(); /// assert_eq!(q2, UnitDualQuaternion::::identity()); /// ``` pub fn cast(self) -> UnitDualQuaternion where UnitDualQuaternion: SupersetOf, { crate::convert(self) } } impl UnitDualQuaternion where T::Element: SimdRealField, { /// Return a dual quaternion representing the translation and orientation /// given by the provided rotation quaternion and translation vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitDualQuaternion, UnitQuaternion, Vector3, Point3}; /// let dq = UnitDualQuaternion::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0) /// ); /// let point = Point3::new(1.0, 2.0, 3.0); /// /// assert_relative_eq!(dq * point, Point3::new(1.0, 0.0, 2.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn from_parts(translation: Translation3, rotation: UnitQuaternion) -> Self { let half: T = crate::convert(0.5f64); UnitDualQuaternion::new_unchecked(DualQuaternion { real: rotation.clone().into_inner(), dual: Quaternion::from_parts(T::zero(), translation.vector) * rotation.into_inner() * half, }) } /// Return a unit dual quaternion representing the translation and orientation /// given by the provided isometry. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Isometry3, UnitDualQuaternion, UnitQuaternion, Vector3, Point3}; /// let iso = Isometry3::from_parts( /// Vector3::new(0.0, 3.0, 0.0).into(), /// UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_2, 0.0, 0.0) /// ); /// let dq = UnitDualQuaternion::from_isometry(&iso); /// let point = Point3::new(1.0, 2.0, 3.0); /// /// assert_relative_eq!(dq * point, iso * point, epsilon = 1.0e-6); /// ``` #[inline] pub fn from_isometry(isometry: &Isometry3) -> Self { // TODO: take the isometry by-move instead of cloning it. let isometry = isometry.clone(); UnitDualQuaternion::from_parts(isometry.translation, isometry.rotation) } /// Creates a dual quaternion from a unit quaternion rotation. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitQuaternion, UnitDualQuaternion, Quaternion}; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let rot = UnitQuaternion::new_normalize(q); /// /// let dq = UnitDualQuaternion::from_rotation(rot); /// assert_relative_eq!(dq.as_ref().real.norm(), 1.0, epsilon = 1.0e-6); /// assert_eq!(dq.as_ref().dual.norm(), 0.0); /// ``` #[inline] pub fn from_rotation(rotation: UnitQuaternion) -> Self { Self::new_unchecked(DualQuaternion::from_real(rotation.into_inner())) } } impl One for UnitDualQuaternion where T::Element: SimdRealField, { #[inline] fn one() -> Self { Self::identity() } } #[cfg(feature = "arbitrary")] impl Arbitrary for UnitDualQuaternion where T: SimdRealField + Arbitrary + Send, T::Element: SimdRealField, { #[inline] fn arbitrary(rng: &mut Gen) -> Self { Self::new_normalize(Arbitrary::arbitrary(rng)) } } nalgebra-0.33.0/src/geometry/dual_quaternion_conversion.rs000064400000000000000000000122170072674642500221440ustar 00000000000000use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::SimdRealField; use crate::base::{Matrix4, Vector4}; use crate::geometry::{ DualQuaternion, Isometry3, Similarity3, SuperTCategoryOf, TAffine, Transform, Translation3, UnitDualQuaternion, UnitQuaternion, }; /* * This file provides the following conversions: * ============================================= * * DualQuaternion -> DualQuaternion * UnitDualQuaternion -> UnitDualQuaternion * UnitDualQuaternion -> Isometry<3> * UnitDualQuaternion -> Similarity<3> * UnitDualQuaternion -> Transform<3> * UnitDualQuaternion -> Matrix (homogeneous) * * NOTE: * UnitDualQuaternion -> DualQuaternion is already provided by: Unit -> T */ impl SubsetOf> for DualQuaternion where T1: SimdRealField, T2: SimdRealField + SupersetOf, { #[inline] fn to_superset(&self) -> DualQuaternion { DualQuaternion::from_real_and_dual(self.real.to_superset(), self.dual.to_superset()) } #[inline] fn is_in_subset(dq: &DualQuaternion) -> bool { crate::is_convertible::<_, Vector4>(&dq.real.coords) && crate::is_convertible::<_, Vector4>(&dq.dual.coords) } #[inline] fn from_superset_unchecked(dq: &DualQuaternion) -> Self { DualQuaternion::from_real_and_dual( dq.real.to_subset_unchecked(), dq.dual.to_subset_unchecked(), ) } } impl SubsetOf> for UnitDualQuaternion where T1: SimdRealField, T2: SimdRealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitDualQuaternion { UnitDualQuaternion::new_unchecked(self.as_ref().to_superset()) } #[inline] fn is_in_subset(dq: &UnitDualQuaternion) -> bool { crate::is_convertible::<_, DualQuaternion>(dq.as_ref()) } #[inline] fn from_superset_unchecked(dq: &UnitDualQuaternion) -> Self { Self::new_unchecked(crate::convert_ref_unchecked(dq.as_ref())) } } impl SubsetOf> for UnitDualQuaternion where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> Isometry3 { let dq: UnitDualQuaternion = self.to_superset(); let iso = dq.to_isometry(); crate::convert_unchecked(iso) } #[inline] fn is_in_subset(iso: &Isometry3) -> bool { crate::is_convertible::<_, UnitQuaternion>(&iso.rotation) && crate::is_convertible::<_, Translation3>(&iso.translation) } #[inline] fn from_superset_unchecked(iso: &Isometry3) -> Self { let dq = UnitDualQuaternion::::from_isometry(iso); crate::convert_unchecked(dq) } } impl SubsetOf> for UnitDualQuaternion where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> Similarity3 { Similarity3::from_isometry(crate::convert_ref(self), T2::one()) } #[inline] fn is_in_subset(sim: &Similarity3) -> bool { sim.scaling() == T2::one() } #[inline] fn from_superset_unchecked(sim: &Similarity3) -> Self { crate::convert_ref_unchecked(&sim.isometry) } } impl SubsetOf> for UnitDualQuaternion where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.clone().to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl> SubsetOf> for UnitDualQuaternion { #[inline] fn to_superset(&self) -> Matrix4 { self.clone().to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &Matrix4) -> bool { crate::is_convertible::<_, Isometry3>(m) } #[inline] fn from_superset_unchecked(m: &Matrix4) -> Self { let iso: Isometry3 = crate::convert_ref_unchecked(m); Self::from_isometry(&iso) } } impl From> for Matrix4 where T::Element: SimdRealField, { #[inline] fn from(dq: UnitDualQuaternion) -> Self { dq.to_homogeneous() } } impl From> for Isometry3 where T::Element: SimdRealField, { #[inline] fn from(dq: UnitDualQuaternion) -> Self { dq.to_isometry() } } impl From> for UnitDualQuaternion where T::Element: SimdRealField, { #[inline] fn from(iso: Isometry3) -> Self { Self::from_isometry(&iso) } } nalgebra-0.33.0/src/geometry/dual_quaternion_ops.rs000064400000000000000000001151730072674642500205650ustar 00000000000000// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] /* * This file provides: * * NOTE: Work in progress https://github.com/dimforge/nalgebra/issues/487 * * (Dual Quaternion) * * Index * IndexMut * * (Assignment Operators) * * -DualQuaternion * DualQuaternion × Scalar * DualQuaternion × DualQuaternion * DualQuaternion + DualQuaternion * DualQuaternion - DualQuaternion * DualQuaternion × UnitDualQuaternion * DualQuaternion ÷ UnitDualQuaternion * -UnitDualQuaternion * UnitDualQuaternion × DualQuaternion * UnitDualQuaternion × UnitDualQuaternion * UnitDualQuaternion ÷ UnitDualQuaternion * UnitDualQuaternion × Translation3 * UnitDualQuaternion ÷ Translation3 * UnitDualQuaternion × UnitQuaternion * UnitDualQuaternion ÷ UnitQuaternion * Translation3 × UnitDualQuaternion * Translation3 ÷ UnitDualQuaternion * UnitQuaternion × UnitDualQuaternion * UnitQuaternion ÷ UnitDualQuaternion * UnitDualQuaternion × Isometry3 * UnitDualQuaternion ÷ Isometry3 * Isometry3 × UnitDualQuaternion * Isometry3 ÷ UnitDualQuaternion * UnitDualQuaternion × Point * UnitDualQuaternion × Vector * UnitDualQuaternion × Unit * * --- * * References: * Multiplication: * - https://cs.gmu.edu/~jmlien/teaching/cs451/uploads/Main/dual-quaternion.pdf */ use crate::base::storage::Storage; use crate::{ DualQuaternion, Isometry3, Point, Point3, Quaternion, SimdRealField, Translation3, Unit, UnitDualQuaternion, UnitQuaternion, Vector, Vector3, U3, }; use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; impl AsRef<[T; 8]> for DualQuaternion { #[inline] fn as_ref(&self) -> &[T; 8] { unsafe { &*(self as *const Self as *const [T; 8]) } } } impl AsMut<[T; 8]> for DualQuaternion { #[inline] fn as_mut(&mut self) -> &mut [T; 8] { unsafe { &mut *(self as *mut Self as *mut [T; 8]) } } } impl Index for DualQuaternion { type Output = T; #[inline] fn index(&self, i: usize) -> &Self::Output { &self.as_ref()[i] } } impl IndexMut for DualQuaternion { #[inline] fn index_mut(&mut self, i: usize) -> &mut T { &mut self.as_mut()[i] } } impl Neg for DualQuaternion where T::Element: SimdRealField, { type Output = DualQuaternion; #[inline] fn neg(self) -> Self::Output { DualQuaternion::from_real_and_dual(-self.real, -self.dual) } } impl<'a, T: SimdRealField> Neg for &'a DualQuaternion where T::Element: SimdRealField, { type Output = DualQuaternion; #[inline] fn neg(self) -> Self::Output { DualQuaternion::from_real_and_dual(-&self.real, -&self.dual) } } impl Neg for UnitDualQuaternion where T::Element: SimdRealField, { type Output = UnitDualQuaternion; #[inline] fn neg(self) -> Self::Output { UnitDualQuaternion::new_unchecked(-self.into_inner()) } } impl<'a, T: SimdRealField> Neg for &'a UnitDualQuaternion where T::Element: SimdRealField, { type Output = UnitDualQuaternion; #[inline] fn neg(self) -> Self::Output { UnitDualQuaternion::new_unchecked(-self.as_ref()) } } macro_rules! dual_quaternion_op_impl( ($Op: ident, $op: ident; ($LhsRDim: ident, $LhsCDim: ident), ($RhsRDim: ident, $RhsCDim: ident) $(for $Storage: ident: $StoragesBound: ident $(<$($BoundParam: ty),*>)*),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Result: ty $(=> $VDimA: ty, $VDimB: ty)*; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField $(, $Storage: $StoragesBound $(<$($BoundParam),*>)*)*> $Op<$Rhs> for $Lhs where T::Element: SimdRealField, { type Output = $Result; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); // DualQuaternion + DualQuaternion dual_quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( &self.real + &rhs.real, &self.dual + &rhs.dual, ); 'a, 'b); dual_quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( &self.real + rhs.real, &self.dual + rhs.dual, ); 'a); dual_quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( self.real + &rhs.real, self.dual + &rhs.dual, ); 'b); dual_quaternion_op_impl!( Add, add; (U4, U1), (U4, U1); self: DualQuaternion, rhs: DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( self.real + rhs.real, self.dual + rhs.dual, ); ); // DualQuaternion - DualQuaternion dual_quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( &self.real - &rhs.real, &self.dual - &rhs.dual, ); 'a, 'b); dual_quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( &self.real - rhs.real, &self.dual - rhs.dual, ); 'a); dual_quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( self.real - &rhs.real, self.dual - &rhs.dual, ); 'b); dual_quaternion_op_impl!( Sub, sub; (U4, U1), (U4, U1); self: DualQuaternion, rhs: DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( self.real - rhs.real, self.dual - rhs.dual, ); ); // DualQuaternion × DualQuaternion dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion; DualQuaternion::from_real_and_dual( &self.real * &rhs.real, &self.real * &rhs.dual + &self.dual * &rhs.real, ); 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: DualQuaternion, Output = DualQuaternion; self * &rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion; &self * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: DualQuaternion, rhs: DualQuaternion, Output = DualQuaternion; &self * &rhs; ); // DualQuaternion × UnitDualQuaternion dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: &'b UnitDualQuaternion, Output = DualQuaternion; self * rhs.dual_quaternion(); 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: UnitDualQuaternion, Output = DualQuaternion; self * rhs.dual_quaternion(); 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b UnitDualQuaternion, Output = DualQuaternion; self * rhs.dual_quaternion(); 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: DualQuaternion, rhs: UnitDualQuaternion, Output = DualQuaternion; self * rhs.dual_quaternion();); // DualQuaternion ÷ UnitDualQuaternion dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: &'b UnitDualQuaternion, Output = DualQuaternion; #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse().dual_quaternion() }; 'a, 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a DualQuaternion, rhs: UnitDualQuaternion, Output = DualQuaternion; #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse().dual_quaternion() }; 'a); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b UnitDualQuaternion, Output = DualQuaternion; #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse().dual_quaternion() }; 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: DualQuaternion, rhs: UnitDualQuaternion, Output = DualQuaternion; #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse().dual_quaternion() };); // UnitDualQuaternion × UnitDualQuaternion dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion; UnitDualQuaternion::new_unchecked(self.as_ref() * rhs.as_ref()); 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion; self * &rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion; &self * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion; &self * &rhs; ); // UnitDualQuaternion ÷ UnitDualQuaternion dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion; #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; 'a, 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion; self / &rhs; 'a); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion; &self / rhs; 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion; &self / &rhs; ); // UnitDualQuaternion × DualQuaternion dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion => U1, U4; self.dual_quaternion() * rhs; 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: DualQuaternion, Output = DualQuaternion => U3, U3; self.dual_quaternion() * rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b DualQuaternion, Output = DualQuaternion => U3, U3; self.dual_quaternion() * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: DualQuaternion, Output = DualQuaternion => U3, U3; self.dual_quaternion() * rhs;); // UnitDualQuaternion × UnitQuaternion dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: &'b UnitQuaternion, Output = UnitDualQuaternion => U1, U4; self * UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(rhs.clone().into_inner())); 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: UnitQuaternion, Output = UnitDualQuaternion => U3, U3; self * UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(rhs.into_inner())); 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitQuaternion, Output = UnitDualQuaternion => U3, U3; self * UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(rhs.clone().into_inner())); 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitQuaternion, Output = UnitDualQuaternion => U3, U3; self * UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(rhs.into_inner()));); // UnitQuaternion × UnitDualQuaternion dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U1, U4; UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(self.clone().into_inner())) * rhs; 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: &'a UnitQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U3; UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(self.clone().into_inner())) * rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U3; UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(self.into_inner())) * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U4, U1); self: UnitQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U3; UnitDualQuaternion::::new_unchecked(DualQuaternion::from_real(self.into_inner())) * rhs;); // UnitDualQuaternion ÷ UnitQuaternion dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: &'b UnitQuaternion, Output = UnitDualQuaternion => U1, U4; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_rotation(rhs.inverse()) }; 'a, 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a UnitDualQuaternion, rhs: UnitQuaternion, Output = UnitDualQuaternion => U3, U3; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_rotation(rhs.inverse()) }; 'a); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitQuaternion, Output = UnitDualQuaternion => U3, U3; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_rotation(rhs.inverse()) }; 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitQuaternion, Output = UnitDualQuaternion => U3, U3; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_rotation(rhs.inverse()) };); // UnitQuaternion ÷ UnitDualQuaternion dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a UnitQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U1, U4; #[allow(clippy::suspicious_arithmetic_impl)] { UnitDualQuaternion::::new_unchecked( DualQuaternion::from_real(self.clone().into_inner()) ) * rhs.inverse() }; 'a, 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: &'a UnitQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U3; #[allow(clippy::suspicious_arithmetic_impl)] { UnitDualQuaternion::::new_unchecked( DualQuaternion::from_real(self.clone().into_inner()) ) * rhs.inverse() }; 'a); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: UnitQuaternion, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U3; #[allow(clippy::suspicious_arithmetic_impl)] { UnitDualQuaternion::::new_unchecked( DualQuaternion::from_real(self.into_inner()) ) * rhs.inverse() }; 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U4, U1); self: UnitQuaternion, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U3; #[allow(clippy::suspicious_arithmetic_impl)] { UnitDualQuaternion::::new_unchecked( DualQuaternion::from_real(self.into_inner()) ) * rhs.inverse() };); // UnitDualQuaternion × Translation3 dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1); self: &'a UnitDualQuaternion, rhs: &'b Translation3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_parts(rhs.clone(), UnitQuaternion::identity()); 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U3); self: &'a UnitDualQuaternion, rhs: Translation3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_parts(rhs, UnitQuaternion::identity()); 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: &'b Translation3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_parts(rhs.clone(), UnitQuaternion::identity()); 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: Translation3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_parts(rhs, UnitQuaternion::identity()); ); // UnitDualQuaternion ÷ Translation3 dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U1); self: &'a UnitDualQuaternion, rhs: &'b Translation3, Output = UnitDualQuaternion => U3, U1; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_parts(rhs.inverse(), UnitQuaternion::identity()) }; 'a, 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U3); self: &'a UnitDualQuaternion, rhs: Translation3, Output = UnitDualQuaternion => U3, U1; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_parts(rhs.inverse(), UnitQuaternion::identity()) }; 'a); dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: &'b Translation3, Output = UnitDualQuaternion => U3, U1; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_parts(rhs.inverse(), UnitQuaternion::identity()) }; 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: Translation3, Output = UnitDualQuaternion => U3, U1; #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitDualQuaternion::::from_parts(rhs.inverse(), UnitQuaternion::identity()) };); // Translation3 × UnitDualQuaternion dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: &'b Translation3, rhs: &'a UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) * rhs; 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: &'a Translation3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) * rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: Translation3, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self, UnitQuaternion::identity()) * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: Translation3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self, UnitQuaternion::identity()) * rhs;); // Translation3 ÷ UnitDualQuaternion dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: &'b Translation3, rhs: &'a UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) / rhs; 'a, 'b); dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: &'a Translation3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()) / rhs; 'a); dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: Translation3, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self, UnitQuaternion::identity()) / rhs; 'b); dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: Translation3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_parts(self, UnitQuaternion::identity()) / rhs;); // UnitDualQuaternion × Isometry3 dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1); self: &'a UnitDualQuaternion, rhs: &'b Isometry3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_isometry(rhs); 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U3); self: &'a UnitDualQuaternion, rhs: Isometry3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_isometry(&rhs); 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: &'b Isometry3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_isometry(rhs); 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: Isometry3, Output = UnitDualQuaternion => U3, U1; self * UnitDualQuaternion::::from_isometry(&rhs); ); // UnitDualQuaternion ÷ Isometry3 dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U1); self: &'a UnitDualQuaternion, rhs: &'b Isometry3, Output = UnitDualQuaternion => U3, U1; // TODO: can we avoid the conversion to a rotation matrix? self / UnitDualQuaternion::::from_isometry(rhs); 'a, 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U3); self: &'a UnitDualQuaternion, rhs: Isometry3, Output = UnitDualQuaternion => U3, U1; self / UnitDualQuaternion::::from_isometry(&rhs); 'a); dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: &'b Isometry3, Output = UnitDualQuaternion => U3, U1; self / UnitDualQuaternion::::from_isometry(rhs); 'b); dual_quaternion_op_impl!( Div, div; (U4, U1), (U3, U3); self: UnitDualQuaternion, rhs: Isometry3, Output = UnitDualQuaternion => U3, U1; self / UnitDualQuaternion::::from_isometry(&rhs); ); // Isometry × UnitDualQuaternion dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: &'a Isometry3, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_isometry(self) * rhs; 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: &'a Isometry3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_isometry(self) * rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: Isometry3, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_isometry(&self) * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U3, U1), (U4, U1); self: Isometry3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_isometry(&self) * rhs; ); // Isometry ÷ UnitDualQuaternion dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: &'a Isometry3, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; // TODO: can we avoid the conversion from a rotation matrix? UnitDualQuaternion::::from_isometry(self) / rhs; 'a, 'b); dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: &'a Isometry3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_isometry(self) / rhs; 'a); dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: Isometry3, rhs: &'b UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_isometry(&self) / rhs; 'b); dual_quaternion_op_impl!( Div, div; (U3, U1), (U4, U1); self: Isometry3, rhs: UnitDualQuaternion, Output = UnitDualQuaternion => U3, U1; UnitDualQuaternion::::from_isometry(&self) / rhs; ); // UnitDualQuaternion × Vector dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: &'a UnitDualQuaternion, rhs: &'b Vector, Output = Vector3 => U3, U1; Unit::new_unchecked(self.as_ref().real.clone()) * rhs; 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: &'a UnitDualQuaternion, rhs: Vector, Output = Vector3 => U3, U1; self * &rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: UnitDualQuaternion, rhs: &'b Vector, Output = Vector3 => U3, U1; &self * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: UnitDualQuaternion, rhs: Vector, Output = Vector3 => U3, U1; &self * &rhs; ); // UnitDualQuaternion × Point dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1); self: &'a UnitDualQuaternion, rhs: &'b Point3, Output = Point3 => U3, U1; { let two: T = crate::convert(2.0f64); let q_point = Quaternion::from_parts(T::zero(), rhs.coords.clone()); Point::from( ((self.as_ref().real.clone() * q_point + self.as_ref().dual.clone() * two) * self.as_ref().real.clone().conjugate()) .vector() .into_owned(), ) }; 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1); self: &'a UnitDualQuaternion, rhs: Point3, Output = Point3 => U3, U1; self * &rhs; 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1); self: UnitDualQuaternion, rhs: &'b Point3, Output = Point3 => U3, U1; &self * rhs; 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1); self: UnitDualQuaternion, rhs: Point3, Output = Point3 => U3, U1; &self * &rhs; ); // UnitDualQuaternion × Unit dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: &'a UnitDualQuaternion, rhs: &'b Unit>, Output = Unit> => U3, U4; Unit::new_unchecked(self * rhs.as_ref()); 'a, 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: &'a UnitDualQuaternion, rhs: Unit>, Output = Unit> => U3, U4; Unit::new_unchecked(self * rhs.into_inner()); 'a); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: UnitDualQuaternion, rhs: &'b Unit>, Output = Unit> => U3, U4; Unit::new_unchecked(self * rhs.as_ref()); 'b); dual_quaternion_op_impl!( Mul, mul; (U4, U1), (U3, U1) for SB: Storage ; self: UnitDualQuaternion, rhs: Unit>, Output = Unit> => U3, U4; Unit::new_unchecked(self * rhs.into_inner()); ); macro_rules! left_scalar_mul_impl( ($($T: ty),* $(,)*) => {$( impl Mul> for $T { type Output = DualQuaternion<$T>; #[inline] fn mul(self, right: DualQuaternion<$T>) -> Self::Output { DualQuaternion::from_real_and_dual( self * right.real, self * right.dual ) } } impl<'b> Mul<&'b DualQuaternion<$T>> for $T { type Output = DualQuaternion<$T>; #[inline] fn mul(self, right: &'b DualQuaternion<$T>) -> Self::Output { DualQuaternion::from_real_and_dual( self * &right.real, self * &right.dual ) } } )*} ); left_scalar_mul_impl!(f32, f64); macro_rules! dual_quaternion_op_impl( ($OpAssign: ident, $op_assign: ident; ($LhsRDim: ident, $LhsCDim: ident), ($RhsRDim: ident, $RhsCDim: ident); $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty $(=> $VDimA: ty, $VDimB: ty)*; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField> $OpAssign<$Rhs> for $Lhs where T::Element: SimdRealField { #[inline] fn $op_assign(&mut $lhs, $rhs: $Rhs) { $action } } } ); // DualQuaternion += DualQuaternion dual_quaternion_op_impl!( AddAssign, add_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b DualQuaternion; { self.real += &rhs.real; self.dual += &rhs.dual; }; 'b); dual_quaternion_op_impl!( AddAssign, add_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: DualQuaternion; { self.real += rhs.real; self.dual += rhs.dual; };); // DualQuaternion -= DualQuaternion dual_quaternion_op_impl!( SubAssign, sub_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b DualQuaternion; { self.real -= &rhs.real; self.dual -= &rhs.dual; }; 'b); dual_quaternion_op_impl!( SubAssign, sub_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: DualQuaternion; { self.real -= rhs.real; self.dual -= rhs.dual; };); // DualQuaternion ×= DualQuaternion dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b DualQuaternion; { let res = &*self * rhs; self.real.coords.copy_from(&res.real.coords); self.dual.coords.copy_from(&res.dual.coords); }; 'b); dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: DualQuaternion; *self *= &rhs;); // DualQuaternion ×= UnitDualQuaternion dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b UnitDualQuaternion; { let res = &*self * rhs; self.real.coords.copy_from(&res.real.coords); self.dual.coords.copy_from(&res.dual.coords); }; 'b); dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: UnitDualQuaternion; *self *= &rhs; ); // DualQuaternion ÷= UnitDualQuaternion dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: &'b UnitDualQuaternion; { let res = &*self / rhs; self.real.coords.copy_from(&res.real.coords); self.dual.coords.copy_from(&res.dual.coords); }; 'b); dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: DualQuaternion, rhs: UnitDualQuaternion; *self /= &rhs; ); // UnitDualQuaternion ×= UnitDualQuaternion dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitDualQuaternion; { let res = &*self * rhs; self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); }; 'b); dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitDualQuaternion; *self *= &rhs; ); // UnitDualQuaternion ÷= UnitDualQuaternion dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitDualQuaternion; { let res = &*self / rhs; self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); }; 'b); dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitDualQuaternion; *self /= &rhs; ); // UnitDualQuaternion ×= UnitQuaternion dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitQuaternion; { let res = &*self * UnitDualQuaternion::from_rotation(rhs); self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); };); dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitQuaternion; *self *= rhs.clone(); 'b); // UnitDualQuaternion ÷= UnitQuaternion dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b UnitQuaternion; #[allow(clippy::suspicious_op_assign_impl)] { let res = &*self * UnitDualQuaternion::from_rotation(rhs.inverse()); self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); }; 'b); dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: UnitQuaternion; *self /= &rhs; ); // UnitDualQuaternion ×= Translation3 dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: Translation3; { let res = &*self * UnitDualQuaternion::from_parts(rhs, UnitQuaternion::identity()); self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); };); dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b Translation3; *self *= rhs.clone(); 'b); // UnitDualQuaternion ÷= Translation3 dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: &'b Translation3; #[allow(clippy::suspicious_op_assign_impl)] { let res = &*self * UnitDualQuaternion::from_parts(rhs.inverse(), UnitQuaternion::identity()); self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); }; 'b); dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U4, U1); self: UnitDualQuaternion, rhs: Translation3; *self /= &rhs; ); // UnitDualQuaternion ×= Isometry3 dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U3, U1); self: UnitDualQuaternion, rhs: &'b Isometry3 => U3, U1; { let res = &*self * rhs; self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); }; 'b); dual_quaternion_op_impl!( MulAssign, mul_assign; (U4, U1), (U3, U1); self: UnitDualQuaternion, rhs: Isometry3 => U3, U1; *self *= &rhs; ); // UnitDualQuaternion ÷= Isometry3 dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U3, U1); self: UnitDualQuaternion, rhs: &'b Isometry3 => U3, U1; { let res = &*self / rhs; self.as_mut_unchecked().real.coords.copy_from(&res.as_ref().real.coords); self.as_mut_unchecked().dual.coords.copy_from(&res.as_ref().dual.coords); }; 'b); dual_quaternion_op_impl!( DivAssign, div_assign; (U4, U1), (U3, U1); self: UnitDualQuaternion, rhs: Isometry3 => U3, U1; *self /= &rhs; ); macro_rules! scalar_op_impl( ($($Op: ident, $op: ident, $OpAssign: ident, $op_assign: ident);* $(;)*) => {$( impl $Op for DualQuaternion where T::Element: SimdRealField { type Output = DualQuaternion; #[inline] fn $op(self, n: T) -> Self::Output { DualQuaternion::from_real_and_dual( self.real.clone().$op(n.clone()), self.dual.clone().$op(n) ) } } impl<'a, T: SimdRealField> $Op for &'a DualQuaternion where T::Element: SimdRealField { type Output = DualQuaternion; #[inline] fn $op(self, n: T) -> Self::Output { DualQuaternion::from_real_and_dual( self.real.clone().$op(n.clone()), self.dual.clone().$op(n) ) } } impl $OpAssign for DualQuaternion where T::Element: SimdRealField { #[inline] fn $op_assign(&mut self, n: T) { self.real.$op_assign(n.clone()); self.dual.$op_assign(n); } } )*} ); scalar_op_impl!( Mul, mul, MulAssign, mul_assign; Div, div, DivAssign, div_assign; ); nalgebra-0.33.0/src/geometry/isometry.rs000064400000000000000000000522220072674642500163600ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::fmt; use std::hash; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use simba::scalar::{RealField, SubsetOf}; use simba::simd::SimdRealField; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::storage::Owned; use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar, Unit}; use crate::geometry::{AbstractRotation, Point, Translation}; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A direct isometry, i.e., a rotation followed by a translation (aka. a rigid-body motion). /// /// This is also known as an element of a Special Euclidean (SE) group. /// The `Isometry` type can either represent a 2D or 3D isometry. /// A 2D isometry is composed of: /// - A translation part of type [`Translation2`](crate::Translation2) /// - A rotation part which can either be a [`UnitComplex`](crate::UnitComplex) or a [`Rotation2`](crate::Rotation2). /// /// A 3D isometry is composed of: /// - A translation part of type [`Translation3`](crate::Translation3) /// - A rotation part which can either be a [`UnitQuaternion`](crate::UnitQuaternion) or a [`Rotation3`](crate::Rotation3). /// /// Note that instead of using the [`Isometry`](crate::Isometry) type in your code directly, you should use one /// of its aliases: [`Isometry2`](crate::Isometry2), [`Isometry3`](crate::Isometry3), /// [`IsometryMatrix2`](crate::IsometryMatrix2), [`IsometryMatrix3`](crate::IsometryMatrix3). Though /// keep in mind that all the documentation of all the methods of these aliases will also appears on /// this page. /// /// # Construction /// * [From a 2D vector and/or an angle `new`, `translation`, `rotation`…](#construction-from-a-2d-vector-andor-a-rotation-angle) /// * [From a 3D vector and/or an axis-angle `new`, `translation`, `rotation`…](#construction-from-a-3d-vector-andor-an-axis-angle) /// * [From a 3D eye position and target point `look_at`, `look_at_lh`, `face_towards`…](#construction-from-a-3d-eye-position-and-target-point) /// * [From the translation and rotation parts `from_parts`…](#from-the-translation-and-rotation-parts) /// /// # Transformation and composition /// Note that transforming vectors and points can be done by multiplication, e.g., `isometry * point`. /// Composing an isometry with another transformation can also be done by multiplication or division. /// /// * [Transformation of a vector or a point `transform_vector`, `inverse_transform_point`…](#transformation-of-a-vector-or-a-point) /// * [Inversion and in-place composition `inverse`, `append_rotation_wrt_point_mut`…](#inversion-and-in-place-composition) /// * [Interpolation `lerp_slerp`…](#interpolation) /// /// # Conversion to a matrix /// * [Conversion to a matrix `to_matrix`…](#conversion-to-a-matrix) /// #[repr(C)] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "R: Serialize, DefaultAllocator: Allocator>, Owned>: Serialize, T: Scalar")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "R: Deserialize<'de>, DefaultAllocator: Allocator>, Owned>: Deserialize<'de>, T: Scalar")) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Isometry", bound(archive = " T: rkyv::Archive, R: rkyv::Archive, Translation: rkyv::Archive> ") ) )] pub struct Isometry { /// The pure rotational part of this isometry. pub rotation: R, /// The pure translational part of this isometry. pub translation: Translation, } impl hash::Hash for Isometry where Owned>: hash::Hash, { fn hash(&self, state: &mut H) { self.translation.hash(state); self.rotation.hash(state); } } /// # From the translation and rotation parts impl, const D: usize> Isometry { /// Creates a new isometry from its rotational and translational parts. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::PI); /// let iso = Isometry3::from_parts(tra, rot); /// /// assert_relative_eq!(iso * Point3::new(1.0, 2.0, 3.0), Point3::new(-1.0, 2.0, 0.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn from_parts(translation: Translation, rotation: R) -> Self { Self { rotation, translation, } } } /// # Inversion and in-place composition impl, const D: usize> Isometry where T::Element: SimdRealField, { /// Inverts `self`. /// /// # Example /// /// ``` /// # use std::f32; /// # use nalgebra::{Isometry2, Point2, Vector2}; /// let iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// let inv = iso.inverse(); /// let pt = Point2::new(1.0, 2.0); /// /// assert_eq!(inv * (iso * pt), pt); /// ``` #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(&self) -> Self { let mut res = self.clone(); res.inverse_mut(); res } /// Inverts `self` in-place. /// /// # Example /// /// ``` /// # use std::f32; /// # use nalgebra::{Isometry2, Point2, Vector2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// let pt = Point2::new(1.0, 2.0); /// let transformed_pt = iso * pt; /// iso.inverse_mut(); /// /// assert_eq!(iso * transformed_pt, pt); /// ``` #[inline] pub fn inverse_mut(&mut self) { self.rotation.inverse_mut(); self.translation.inverse_mut(); self.translation.vector = self.rotation.transform_vector(&self.translation.vector); } /// Computes `self.inverse() * rhs` in a more efficient way. /// /// # Example /// /// ``` /// # use std::f32; /// # use nalgebra::{Isometry2, Point2, Vector2}; /// let mut iso1 = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// let mut iso2 = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_4); /// /// assert_eq!(iso1.inverse() * iso2, iso1.inv_mul(&iso2)); /// ``` #[inline] #[must_use] pub fn inv_mul(&self, rhs: &Isometry) -> Self { let inv_rot1 = self.rotation.inverse(); let tr_12 = &rhs.translation.vector - &self.translation.vector; Isometry::from_parts( inv_rot1.transform_vector(&tr_12).into(), inv_rot1 * rhs.rotation.clone(), ) } /// Appends to `self` the given translation in-place. /// /// # Example /// /// ``` /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, Vector2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// let tra = Translation2::new(3.0, 4.0); /// // Same as `iso = tra * iso`. /// iso.append_translation_mut(&tra); /// /// assert_eq!(iso.translation, Translation2::new(4.0, 6.0)); /// ``` #[inline] pub fn append_translation_mut(&mut self, t: &Translation) { self.translation.vector += &t.vector } /// Appends to `self` the given rotation in-place. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::PI / 6.0); /// let rot = UnitComplex::new(f32::consts::PI / 2.0); /// // Same as `iso = rot * iso`. /// iso.append_rotation_mut(&rot); /// /// assert_relative_eq!(iso, Isometry2::new(Vector2::new(-2.0, 1.0), f32::consts::PI * 2.0 / 3.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn append_rotation_mut(&mut self, r: &R) { self.rotation = r.clone() * self.rotation.clone(); self.translation.vector = r.transform_vector(&self.translation.vector); } /// Appends in-place to `self` a rotation centered at the point `p`, i.e., the rotation that /// lets `p` invariant. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// let pt = Point2::new(1.0, 0.0); /// iso.append_rotation_wrt_point_mut(&rot, &pt); /// /// assert_relative_eq!(iso * pt, Point2::new(-2.0, 0.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn append_rotation_wrt_point_mut(&mut self, r: &R, p: &Point) { self.translation.vector -= &p.coords; self.append_rotation_mut(r); self.translation.vector += &p.coords; } /// Appends in-place to `self` a rotation centered at the point with coordinates /// `self.translation`. /// /// # Example /// /// ``` /// # use std::f32; /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2}; /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// iso.append_rotation_wrt_center_mut(&rot); /// /// // The translation part should not have changed. /// assert_eq!(iso.translation.vector, Vector2::new(1.0, 2.0)); /// assert_eq!(iso.rotation, UnitComplex::new(f32::consts::PI)); /// ``` #[inline] pub fn append_rotation_wrt_center_mut(&mut self, r: &R) { self.rotation = r.clone() * self.rotation.clone(); } } /// # Transformation of a vector or a point impl, const D: usize> Isometry where T::Element: SimdRealField, { /// Transform the given point by this isometry. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2); /// let iso = Isometry3::from_parts(tra, rot); /// /// let transformed_point = iso.transform_point(&Point3::new(1.0, 2.0, 3.0)); /// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, 2.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } /// Transform the given vector by this isometry, ignoring the translation /// component of the isometry. /// /// This is the same as the multiplication `self * v`. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2); /// let iso = Isometry3::from_parts(tra, rot); /// /// let transformed_point = iso.transform_vector(&Vector3::new(1.0, 2.0, 3.0)); /// assert_relative_eq!(transformed_point, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } /// Transform the given point by the inverse of this isometry. This may be /// less expensive than computing the entire isometry inverse and then /// transforming the point. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2); /// let iso = Isometry3::from_parts(tra, rot); /// /// let transformed_point = iso.inverse_transform_point(&Point3::new(1.0, 2.0, 3.0)); /// assert_relative_eq!(transformed_point, Point3::new(0.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { self.rotation .inverse_transform_point(&(pt - &self.translation.vector)) } /// Transform the given vector by the inverse of this isometry, ignoring the /// translation component of the isometry. This may be /// less expensive than computing the entire isometry inverse and then /// transforming the point. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2); /// let iso = Isometry3::from_parts(tra, rot); /// /// let transformed_point = iso.inverse_transform_vector(&Vector3::new(1.0, 2.0, 3.0)); /// assert_relative_eq!(transformed_point, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.rotation.inverse_transform_vector(v) } /// Transform the given unit vector by the inverse of this isometry, ignoring the /// translation component of the isometry. This may be /// less expensive than computing the entire isometry inverse and then /// transforming the point. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3}; /// let tra = Translation3::new(0.0, 0.0, 3.0); /// let rot = UnitQuaternion::from_scaled_axis(Vector3::z() * f32::consts::FRAC_PI_2); /// let iso = Isometry3::from_parts(tra, rot); /// /// let transformed_point = iso.inverse_transform_unit_vector(&Vector3::x_axis()); /// assert_relative_eq!(transformed_point, -Vector3::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.rotation.inverse_transform_unit_vector(v) } } // NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation // and makes it hard to use it, e.g., for Transform × Isometry implementation. // This is OK since all constructors of the isometry enforce the Rotation bound already (and // explicit struct construction is prevented by the dummy ZST field). /// # Conversion to a matrix impl Isometry { /// Converts this isometry into its equivalent homogeneous transformation matrix. /// /// This is the same as `self.to_matrix()`. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry2, Vector2, Matrix3}; /// let iso = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_6); /// let expected = Matrix3::new(0.8660254, -0.5, 10.0, /// 0.5, 0.8660254, 20.0, /// 0.0, 0.0, 1.0); /// /// assert_relative_eq!(iso.to_homogeneous(), expected, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, R: SubsetOf, U1>, DimNameSum, U1>>>, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { let mut res: OMatrix = crate::convert_ref(&self.rotation); res.fixed_view_mut::(0, D) .copy_from(&self.translation.vector); res } /// Converts this isometry into its equivalent homogeneous transformation matrix. /// /// This is the same as `self.to_homogeneous()`. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry2, Vector2, Matrix3}; /// let iso = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_6); /// let expected = Matrix3::new(0.8660254, -0.5, 10.0, /// 0.5, 0.8660254, 20.0, /// 0.0, 0.0, 1.0); /// /// assert_relative_eq!(iso.to_matrix(), expected, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn to_matrix(&self) -> OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, R: SubsetOf, U1>, DimNameSum, U1>>>, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { self.to_homogeneous() } } impl Eq for Isometry where R: AbstractRotation + Eq { } impl PartialEq for Isometry where R: AbstractRotation + PartialEq, { #[inline] fn eq(&self, right: &Self) -> bool { self.translation == right.translation && self.rotation == right.rotation } } impl AbsDiffEq for Isometry where R: AbstractRotation + AbsDiffEq, T::Epsilon: Clone, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.translation .abs_diff_eq(&other.translation, epsilon.clone()) && self.rotation.abs_diff_eq(&other.rotation, epsilon) } } impl RelativeEq for Isometry where R: AbstractRotation + RelativeEq, T::Epsilon: Clone, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.translation .relative_eq(&other.translation, epsilon.clone(), max_relative.clone()) && self .rotation .relative_eq(&other.rotation, epsilon, max_relative) } } impl UlpsEq for Isometry where R: AbstractRotation + UlpsEq, T::Epsilon: Clone, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.translation .ulps_eq(&other.translation, epsilon.clone(), max_ulps) && self.rotation.ulps_eq(&other.rotation, epsilon, max_ulps) } } /* * * Display * */ impl fmt::Display for Isometry where R: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let precision = f.precision().unwrap_or(3); writeln!(f, "Isometry {{")?; write!(f, "{:.*}", precision, self.translation)?; write!(f, "{:.*}", precision, self.rotation)?; writeln!(f, "}}") } } nalgebra-0.33.0/src/geometry/isometry_alias.rs000064400000000000000000000034420072674642500175310ustar 00000000000000use crate::geometry::{Isometry, Rotation2, Rotation3, UnitComplex, UnitQuaternion}; /// A 2-dimensional direct isometry using a unit complex number for its rotational part. /// /// **Because this is an alias, not all its methods are listed here. See the [`Isometry`](crate::Isometry) type too.** /// /// Also known as a 2D rigid-body motion, or as an element of SE(2). pub type Isometry2 = Isometry, 2>; /// A 3-dimensional direct isometry using a unit quaternion for its rotational part. /// /// **Because this is an alias, not all its methods are listed here. See the [`Isometry`](crate::Isometry) type too.** /// /// Also known as a rigid-body motion, or as an element of SE(3). pub type Isometry3 = Isometry, 3>; /// A 2-dimensional direct isometry using a rotation matrix for its rotational part. /// /// **Because this is an alias, not all its methods are listed here. See the [`Isometry`](crate::Isometry) type too.** /// /// Also known as a rigid-body motion, or as an element of SE(2). pub type IsometryMatrix2 = Isometry, 2>; /// A 3-dimensional direct isometry using a rotation matrix for its rotational part. /// /// **Because this is an alias, not all its methods are listed here. See the [`Isometry`](crate::Isometry) type too.** /// /// Also known as a rigid-body motion, or as an element of SE(3). pub type IsometryMatrix3 = Isometry, 3>; // This tests that the types correctly implement `Copy`, without having to run tests // (when targeting no-std for example). #[allow(dead_code)] fn ensure_copy() { fn is_copy() {} is_copy::>(); is_copy::>(); is_copy::>(); is_copy::>(); } nalgebra-0.33.0/src/geometry/isometry_construction.rs000064400000000000000000000411550072674642500211750ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::One; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use simba::scalar::SupersetOf; use simba::simd::SimdRealField; use crate::base::{Vector2, Vector3}; use crate::{ AbstractRotation, Isometry, Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, Point, Point3, Rotation, Rotation3, Scalar, Translation, Translation2, Translation3, UnitComplex, UnitQuaternion, }; impl, const D: usize> Default for Isometry where T::Element: SimdRealField, { fn default() -> Self { Self::identity() } } impl, const D: usize> Isometry where T::Element: SimdRealField, { /// Creates a new identity isometry. /// /// # Example /// /// ``` /// # use nalgebra::{Isometry2, Point2, Isometry3, Point3}; /// /// let iso = Isometry2::identity(); /// let pt = Point2::new(1.0, 2.0); /// assert_eq!(iso * pt, pt); /// /// let iso = Isometry3::identity(); /// let pt = Point3::new(1.0, 2.0, 3.0); /// assert_eq!(iso * pt, pt); /// ``` #[inline] pub fn identity() -> Self { Self::from_parts(Translation::identity(), R::identity()) } /// The isometry that applies the rotation `r` with its axis passing through the point `p`. /// This effectively lets `p` invariant. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry2, Point2, UnitComplex}; /// let rot = UnitComplex::new(f32::consts::PI); /// let pt = Point2::new(1.0, 0.0); /// let iso = Isometry2::rotation_wrt_point(rot, pt); /// /// assert_eq!(iso * pt, pt); // The rotation center is not affected. /// assert_relative_eq!(iso * Point2::new(1.0, 2.0), Point2::new(1.0, -2.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn rotation_wrt_point(r: R, p: Point) -> Self { let shift = r.transform_vector(&-&p.coords); Self::from_parts(Translation::from(shift + p.coords), r) } } impl, const D: usize> One for Isometry where T::Element: SimdRealField, { /// Creates a new identity isometry. #[inline] fn one() -> Self { Self::identity() } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where R: AbstractRotation, Standard: Distribution + Distribution, { #[inline] fn sample(&self, rng: &mut G) -> Isometry { Isometry::from_parts(rng.gen(), rng.gen()) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Isometry where T: SimdRealField + Arbitrary + Send, T::Element: SimdRealField, R: AbstractRotation + Arbitrary + Send, Owned>: Send, { #[inline] fn arbitrary(rng: &mut Gen) -> Self { Self::from_parts(Arbitrary::arbitrary(rng), Arbitrary::arbitrary(rng)) } } /* * * Constructors for various static dimensions. * */ /// # Construction from a 2D vector and/or a rotation angle impl IsometryMatrix2 where T::Element: SimdRealField, { /// Creates a new 2D isometry from a translation and a rotation angle. /// /// Its rotational part is represented as a 2x2 rotation matrix. /// /// # Example /// /// ``` /// # use std::f32; /// # use nalgebra::{Isometry2, Vector2, Point2}; /// let iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// /// assert_eq!(iso * Point2::new(3.0, 4.0), Point2::new(-3.0, 5.0)); /// ``` #[inline] pub fn new(translation: Vector2, angle: T) -> Self { Self::from_parts(Translation::from(translation), Rotation::::new(angle)) } /// Creates a new isometry from the given translation coordinates. #[inline] pub fn translation(x: T, y: T) -> Self { Self::new(Vector2::new(x, y), T::zero()) } /// Creates a new isometry from the given rotation angle. #[inline] pub fn rotation(angle: T) -> Self { Self::new(Vector2::zeros(), angle) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::IsometryMatrix2; /// let iso = IsometryMatrix2::::identity(); /// let iso2 = iso.cast::(); /// assert_eq!(iso2, IsometryMatrix2::::identity()); /// ``` pub fn cast(self) -> IsometryMatrix2 where IsometryMatrix2: SupersetOf, { crate::convert(self) } } impl Isometry2 where T::Element: SimdRealField, { /// Creates a new 2D isometry from a translation and a rotation angle. /// /// Its rotational part is represented as an unit complex number. /// /// # Example /// /// ``` /// # use std::f32; /// # use nalgebra::{IsometryMatrix2, Point2, Vector2}; /// let iso = IsometryMatrix2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2); /// /// assert_eq!(iso * Point2::new(3.0, 4.0), Point2::new(-3.0, 5.0)); /// ``` #[inline] pub fn new(translation: Vector2, angle: T) -> Self { Self::from_parts( Translation::from(translation), UnitComplex::from_angle(angle), ) } /// Creates a new isometry from the given translation coordinates. #[inline] pub fn translation(x: T, y: T) -> Self { Self::from_parts(Translation2::new(x, y), UnitComplex::identity()) } /// Creates a new isometry from the given rotation angle. #[inline] pub fn rotation(angle: T) -> Self { Self::new(Vector2::zeros(), angle) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Isometry2; /// let iso = Isometry2::::identity(); /// let iso2 = iso.cast::(); /// assert_eq!(iso2, Isometry2::::identity()); /// ``` pub fn cast(self) -> Isometry2 where Isometry2: SupersetOf, { crate::convert(self) } } // 3D rotation. macro_rules! basic_isometry_construction_impl( ($RotId: ident < $($RotParams: ident),*>) => { /// Creates a new isometry from a translation and a rotation axis-angle. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// let translation = Vector3::new(1.0, 2.0, 3.0); /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// /// // Isometry with its rotation part represented as a UnitQuaternion /// let iso = Isometry3::new(translation, axisangle); /// assert_relative_eq!(iso * pt, Point3::new(7.0, 7.0, -1.0), epsilon = 1.0e-6); /// assert_relative_eq!(iso * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // Isometry with its rotation part represented as a Rotation3 (a 3x3 rotation matrix). /// let iso = IsometryMatrix3::new(translation, axisangle); /// assert_relative_eq!(iso * pt, Point3::new(7.0, 7.0, -1.0), epsilon = 1.0e-6); /// assert_relative_eq!(iso * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn new(translation: Vector3, axisangle: Vector3) -> Self { Self::from_parts( Translation::from(translation), $RotId::<$($RotParams),*>::from_scaled_axis(axisangle)) } /// Creates a new isometry from the given translation coordinates. #[inline] pub fn translation(x: T, y: T, z: T) -> Self { Self::from_parts(Translation3::new(x, y, z), $RotId::identity()) } /// Creates a new isometry from the given rotation angle. #[inline] pub fn rotation(axisangle: Vector3) -> Self { Self::new(Vector3::zeros(), axisangle) } } ); macro_rules! look_at_isometry_construction_impl( ($RotId: ident < $($RotParams: ident),*>) => { /// Creates an isometry that corresponds to the local frame of an observer standing at the /// point `eye` and looking toward `target`. /// /// It maps the `z` axis to the view direction `target - eye`and the origin to the `eye`. /// /// # Arguments /// * eye - The observer position. /// * target - The target position. /// * up - Vertical direction. The only requirement of this parameter is to not be collinear /// to `eye - at`. Non-collinearity is not checked. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); /// let target = Point3::new(2.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// // Isometry with its rotation part represented as a UnitQuaternion /// let iso = Isometry3::face_towards(&eye, &target, &up); /// assert_eq!(iso * Point3::origin(), eye); /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); /// /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). /// let iso = IsometryMatrix3::face_towards(&eye, &target, &up); /// assert_eq!(iso * Point3::origin(), eye); /// assert_relative_eq!(iso * Vector3::z(), Vector3::x()); /// ``` #[inline] pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3) -> Self { Self::from_parts( Translation::from(eye.coords.clone()), $RotId::face_towards(&(target - eye), up)) } /// Deprecated: Use [`Isometry::face_towards`] instead. #[deprecated(note="renamed to `face_towards`")] pub fn new_observer_frame(eye: &Point3, target: &Point3, up: &Vector3) -> Self { Self::face_towards(eye, target, up) } /// Builds a right-handed look-at view matrix. /// /// It maps the view direction `target - eye` to the **negative** `z` axis to and the `eye` to the origin. /// This conforms to the common notion of right handed camera look-at **view matrix** from /// the computer graphics community, i.e. the camera is assumed to look toward its local `-z` axis. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); /// let target = Point3::new(2.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// // Isometry with its rotation part represented as a UnitQuaternion /// let iso = Isometry3::look_at_rh(&eye, &target, &up); /// assert_eq!(iso * eye, Point3::origin()); /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z()); /// /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). /// let iso = IsometryMatrix3::look_at_rh(&eye, &target, &up); /// assert_eq!(iso * eye, Point3::origin()); /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z()); /// ``` #[inline] pub fn look_at_rh(eye: &Point3, target: &Point3, up: &Vector3) -> Self { let rotation = $RotId::look_at_rh(&(target - eye), up); let trans = &rotation * (-eye); Self::from_parts(Translation::from(trans.coords), rotation) } /// Builds a left-handed look-at view matrix. /// /// It maps the view direction `target - eye` to the **positive** `z` axis and the `eye` to the origin. /// This conforms to the common notion of left handed camera look-at **view matrix** from /// the computer graphics community, i.e. the camera is assumed to look toward its local `z` axis. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Isometry3, IsometryMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); /// let target = Point3::new(2.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// // Isometry with its rotation part represented as a UnitQuaternion /// let iso = Isometry3::look_at_lh(&eye, &target, &up); /// assert_eq!(iso * eye, Point3::origin()); /// assert_relative_eq!(iso * Vector3::x(), Vector3::z()); /// /// // Isometry with its rotation part represented as Rotation3 (a 3x3 rotation matrix). /// let iso = IsometryMatrix3::look_at_lh(&eye, &target, &up); /// assert_eq!(iso * eye, Point3::origin()); /// assert_relative_eq!(iso * Vector3::x(), Vector3::z()); /// ``` #[inline] pub fn look_at_lh(eye: &Point3, target: &Point3, up: &Vector3) -> Self { let rotation = $RotId::look_at_lh(&(target - eye), up); let trans = &rotation * (-eye); Self::from_parts(Translation::from(trans.coords), rotation) } } ); /// # Construction from a 3D vector and/or an axis-angle impl Isometry3 where T::Element: SimdRealField, { basic_isometry_construction_impl!(UnitQuaternion); /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Isometry3; /// let iso = Isometry3::::identity(); /// let iso2 = iso.cast::(); /// assert_eq!(iso2, Isometry3::::identity()); /// ``` pub fn cast(self) -> Isometry3 where Isometry3: SupersetOf, { crate::convert(self) } } impl IsometryMatrix3 where T::Element: SimdRealField, { basic_isometry_construction_impl!(Rotation3); /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::IsometryMatrix3; /// let iso = IsometryMatrix3::::identity(); /// let iso2 = iso.cast::(); /// assert_eq!(iso2, IsometryMatrix3::::identity()); /// ``` pub fn cast(self) -> IsometryMatrix3 where IsometryMatrix3: SupersetOf, { crate::convert(self) } } /// # Construction from a 3D eye position and target point impl Isometry3 where T::Element: SimdRealField, { look_at_isometry_construction_impl!(UnitQuaternion); } impl IsometryMatrix3 where T::Element: SimdRealField, { look_at_isometry_construction_impl!(Rotation3); } nalgebra-0.33.0/src/geometry/isometry_conversion.rs000064400000000000000000000267560072674642500206420ustar 00000000000000use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::{PrimitiveSimdValue, SimdRealField, SimdValue}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimMin, DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix, Scalar}; use crate::geometry::{ AbstractRotation, Isometry, Isometry3, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, UnitDualQuaternion, UnitQuaternion, }; use crate::{ArrayStorage, Point, SVector}; /* * This file provides the following conversions: * ============================================= * * Isometry -> Isometry * Isometry3 -> UnitDualQuaternion * Isometry -> Similarity * Isometry -> Transform * Isometry -> Matrix (homogeneous) */ impl SubsetOf> for Isometry where T1: RealField, T2: RealField + SupersetOf, R1: AbstractRotation + SubsetOf, R2: AbstractRotation, { #[inline] fn to_superset(&self) -> Isometry { Isometry::from_parts(self.translation.to_superset(), self.rotation.to_superset()) } #[inline] fn is_in_subset(iso: &Isometry) -> bool { crate::is_convertible::<_, Translation>(&iso.translation) && crate::is_convertible::<_, R1>(&iso.rotation) } #[inline] fn from_superset_unchecked(iso: &Isometry) -> Self { Isometry::from_parts( iso.translation.to_subset_unchecked(), iso.rotation.to_subset_unchecked(), ) } } impl SubsetOf> for Isometry3 where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitDualQuaternion { let dq = UnitDualQuaternion::::from_isometry(self); dq.to_superset() } #[inline] fn is_in_subset(dq: &UnitDualQuaternion) -> bool { crate::is_convertible::<_, UnitQuaternion>(&dq.rotation()) && crate::is_convertible::<_, Translation>(&dq.translation()) } #[inline] fn from_superset_unchecked(dq: &UnitDualQuaternion) -> Self { let dq: UnitDualQuaternion = crate::convert_ref_unchecked(dq); dq.to_isometry() } } impl SubsetOf> for Isometry where T1: RealField, T2: RealField + SupersetOf, R1: AbstractRotation + SubsetOf, R2: AbstractRotation, { #[inline] fn to_superset(&self) -> Similarity { Similarity::from_isometry(self.to_superset(), T2::one()) } #[inline] fn is_in_subset(sim: &Similarity) -> bool { crate::is_convertible::<_, Isometry>(&sim.isometry) && sim.scaling() == T2::one() } #[inline] fn from_superset_unchecked(sim: &Similarity) -> Self { crate::convert_ref_unchecked(&sim.isometry) } } impl SubsetOf> for Isometry where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, R: AbstractRotation + SubsetOf, U1>, DimNameSum, U1>>> + SubsetOf, U1>, DimNameSum, U1>>>, Const: DimNameAdd + DimMin, Output = Const>, // needed by .is_special_orthogonal() DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, // + Allocator // + Allocator // + Allocator // + Allocator { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl SubsetOf, U1>, DimNameSum, U1>>> for Isometry where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SubsetOf, U1>, DimNameSum, U1>>> + SubsetOf, U1>, DimNameSum, U1>>>, Const: DimNameAdd + DimMin, Output = Const>, // needed by .is_special_orthogonal() DefaultAllocator: Allocator, Const<1>, Buffer = ArrayStorage> + Allocator, U1>, DimNameSum, U1>>, { #[inline] fn to_superset(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &OMatrix, U1>, DimNameSum, U1>>) -> bool { let rot = m.fixed_view::(0, 0); let bottom = m.fixed_view::<1, D>(D, 0); // Scalar types agree. m.iter().all(|e| SupersetOf::::is_in_subset(e)) && // The block part is a rotation. rot.is_special_orthogonal(T2::default_epsilon() * crate::convert(100.0)) && // The bottom row is (0, 0, ..., 1) bottom.iter().all(|e| e.is_zero()) && m[(D, D)] == T2::one() } #[inline] fn from_superset_unchecked( m: &OMatrix, U1>, DimNameSum, U1>>, ) -> Self { let t = m.fixed_view::(0, D).into_owned(); let t = Translation { vector: crate::convert_unchecked(t), }; Self::from_parts(t, crate::convert_unchecked(m.clone_owned())) } } impl, const D: usize> From> for Isometry { #[inline] fn from(tra: Translation) -> Self { Self::from_parts(tra, R::identity()) } } impl From> for OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, R: SubsetOf, U1>, DimNameSum, U1>>>, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, // + Allocator, { #[inline] fn from(iso: Isometry) -> Self { iso.to_homogeneous() } } impl From<[T; D]> for Isometry where R: AbstractRotation, { #[inline] fn from(coords: [T; D]) -> Self { Self::from_parts(coords.into(), R::identity()) } } impl From> for Isometry where R: AbstractRotation, { #[inline] fn from(coords: SVector) -> Self { Self::from_parts(coords.into(), R::identity()) } } impl From> for Isometry where R: AbstractRotation, { #[inline] fn from(coords: Point) -> Self { Self::from_parts(coords.into(), R::identity()) } } impl From<[Isometry; 2]> for Isometry where T: From<[::Element; 2]>, R: SimdValue + AbstractRotation + From<[::Element; 2]>, R::Element: AbstractRotation, T::Element: Scalar + Copy, R::Element: Scalar + Copy, { #[inline] fn from(arr: [Isometry; 2]) -> Self { let tra = Translation::from([arr[0].translation, arr[1].translation]); let rot = R::from([arr[0].rotation, arr[0].rotation]); Self::from_parts(tra, rot) } } impl From<[Isometry; 4]> for Isometry where T: From<[::Element; 4]>, R: SimdValue + AbstractRotation + From<[::Element; 4]>, R::Element: AbstractRotation, T::Element: Scalar + Copy, R::Element: Scalar + Copy, { #[inline] fn from(arr: [Isometry; 4]) -> Self { let tra = Translation::from([ arr[0].translation, arr[1].translation, arr[2].translation, arr[3].translation, ]); let rot = R::from([ arr[0].rotation, arr[1].rotation, arr[2].rotation, arr[3].rotation, ]); Self::from_parts(tra, rot) } } impl From<[Isometry; 8]> for Isometry where T: From<[::Element; 8]>, R: SimdValue + AbstractRotation + From<[::Element; 8]>, R::Element: AbstractRotation, T::Element: Scalar + Copy, R::Element: Scalar + Copy, { #[inline] fn from(arr: [Isometry; 8]) -> Self { let tra = Translation::from([ arr[0].translation, arr[1].translation, arr[2].translation, arr[3].translation, arr[4].translation, arr[5].translation, arr[6].translation, arr[7].translation, ]); let rot = R::from([ arr[0].rotation, arr[1].rotation, arr[2].rotation, arr[3].rotation, arr[4].rotation, arr[5].rotation, arr[6].rotation, arr[7].rotation, ]); Self::from_parts(tra, rot) } } impl From<[Isometry; 16]> for Isometry where T: From<[::Element; 16]>, R: SimdValue + AbstractRotation + From<[::Element; 16]>, R::Element: AbstractRotation, T::Element: Scalar + Copy, R::Element: Scalar + Copy, { #[inline] fn from(arr: [Isometry; 16]) -> Self { let tra = Translation::from([ arr[0].translation, arr[1].translation, arr[2].translation, arr[3].translation, arr[4].translation, arr[5].translation, arr[6].translation, arr[7].translation, arr[8].translation, arr[9].translation, arr[10].translation, arr[11].translation, arr[12].translation, arr[13].translation, arr[14].translation, arr[15].translation, ]); let rot = R::from([ arr[0].rotation, arr[1].rotation, arr[2].rotation, arr[3].rotation, arr[4].rotation, arr[5].rotation, arr[6].rotation, arr[7].rotation, arr[8].rotation, arr[9].rotation, arr[10].rotation, arr[11].rotation, arr[12].rotation, arr[13].rotation, arr[14].rotation, arr[15].rotation, ]); Self::from_parts(tra, rot) } } nalgebra-0.33.0/src/geometry/isometry_interpolation.rs000064400000000000000000000220450072674642500213270ustar 00000000000000use crate::{Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, RealField, SimdRealField}; /// # Interpolation impl Isometry3 { /// Interpolates between two isometries using a linear interpolation for the translation part, /// and a spherical interpolation for the rotation part. /// /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic. /// /// # Examples: /// /// ``` /// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion}; /// /// let t1 = Translation3::new(1.0, 2.0, 3.0); /// let t2 = Translation3::new(4.0, 8.0, 12.0); /// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0); /// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0); /// let iso1 = Isometry3::from_parts(t1, q1); /// let iso2 = Isometry3::from_parts(t2, q2); /// /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0); /// /// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0)); /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, { let tr = self .translation .vector .lerp(&other.translation.vector, t.clone()); let rot = self.rotation.slerp(&other.rotation, t); Self::from_parts(tr.into(), rot) } /// Attempts to interpolate between two isometries using a linear interpolation for the translation part, /// and a spherical interpolation for the rotation part. /// /// Returns `None` if the angle between both rotations is 180 degrees (in which case the interpolation /// is not well-defined). /// /// # Examples: /// /// ``` /// # use nalgebra::{Vector3, Translation3, Isometry3, UnitQuaternion}; /// /// let t1 = Translation3::new(1.0, 2.0, 3.0); /// let t2 = Translation3::new(4.0, 8.0, 12.0); /// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0); /// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0); /// let iso1 = Isometry3::from_parts(t1, q1); /// let iso2 = Isometry3::from_parts(t2, q2); /// /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0); /// /// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0)); /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn try_lerp_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, { let tr = self .translation .vector .lerp(&other.translation.vector, t.clone()); let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?; Some(Self::from_parts(tr.into(), rot)) } } impl IsometryMatrix3 { /// Interpolates between two isometries using a linear interpolation for the translation part, /// and a spherical interpolation for the rotation part. /// /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic. /// /// # Examples: /// /// ``` /// # use nalgebra::{Vector3, Translation3, Rotation3, IsometryMatrix3}; /// /// let t1 = Translation3::new(1.0, 2.0, 3.0); /// let t2 = Translation3::new(4.0, 8.0, 12.0); /// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0); /// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0); /// let iso1 = IsometryMatrix3::from_parts(t1, q1); /// let iso2 = IsometryMatrix3::from_parts(t2, q2); /// /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0); /// /// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0)); /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, { let tr = self .translation .vector .lerp(&other.translation.vector, t.clone()); let rot = self.rotation.slerp(&other.rotation, t); Self::from_parts(tr.into(), rot) } /// Attempts to interpolate between two isometries using a linear interpolation for the translation part, /// and a spherical interpolation for the rotation part. /// /// Returns `None` if the angle between both rotations is 180 degrees (in which case the interpolation /// is not well-defined). /// /// # Examples: /// /// ``` /// # use nalgebra::{Vector3, Translation3, Rotation3, IsometryMatrix3}; /// /// let t1 = Translation3::new(1.0, 2.0, 3.0); /// let t2 = Translation3::new(4.0, 8.0, 12.0); /// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0); /// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0); /// let iso1 = IsometryMatrix3::from_parts(t1, q1); /// let iso2 = IsometryMatrix3::from_parts(t2, q2); /// /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0); /// /// assert_eq!(iso3.translation.vector, Vector3::new(2.0, 4.0, 6.0)); /// assert_eq!(iso3.rotation.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn try_lerp_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, { let tr = self .translation .vector .lerp(&other.translation.vector, t.clone()); let rot = self.rotation.try_slerp(&other.rotation, t, epsilon)?; Some(Self::from_parts(tr.into(), rot)) } } impl Isometry2 { /// Interpolates between two isometries using a linear interpolation for the translation part, /// and a spherical interpolation for the rotation part. /// /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic. /// /// # Examples: /// /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector2, Translation2, UnitComplex, Isometry2}; /// /// let t1 = Translation2::new(1.0, 2.0); /// let t2 = Translation2::new(4.0, 8.0); /// let q1 = UnitComplex::new(std::f32::consts::FRAC_PI_4); /// let q2 = UnitComplex::new(-std::f32::consts::PI); /// let iso1 = Isometry2::from_parts(t1, q1); /// let iso2 = Isometry2::from_parts(t2, q2); /// /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0); /// /// assert_eq!(iso3.translation.vector, Vector2::new(2.0, 4.0)); /// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, { let tr = self .translation .vector .lerp(&other.translation.vector, t.clone()); let rot = self.rotation.slerp(&other.rotation, t); Self::from_parts(tr.into(), rot) } } impl IsometryMatrix2 { /// Interpolates between two isometries using a linear interpolation for the translation part, /// and a spherical interpolation for the rotation part. /// /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation /// is not well-defined). Use `.try_lerp_slerp` instead to avoid the panic. /// /// # Examples: /// /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector2, Translation2, Rotation2, IsometryMatrix2}; /// /// let t1 = Translation2::new(1.0, 2.0); /// let t2 = Translation2::new(4.0, 8.0); /// let q1 = Rotation2::new(std::f32::consts::FRAC_PI_4); /// let q2 = Rotation2::new(-std::f32::consts::PI); /// let iso1 = IsometryMatrix2::from_parts(t1, q1); /// let iso2 = IsometryMatrix2::from_parts(t2, q2); /// /// let iso3 = iso1.lerp_slerp(&iso2, 1.0 / 3.0); /// /// assert_eq!(iso3.translation.vector, Vector2::new(2.0, 4.0)); /// assert_relative_eq!(iso3.rotation.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] #[must_use] pub fn lerp_slerp(&self, other: &Self, t: T) -> Self where T: RealField, { let tr = self .translation .vector .lerp(&other.translation.vector, t.clone()); let rot = self.rotation.slerp(&other.rotation, t); Self::from_parts(tr.into(), rot) } } nalgebra-0.33.0/src/geometry/isometry_ops.rs000064400000000000000000000474730072674642500172550ustar 00000000000000// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] use num::{One, Zero}; use std::ops::{Div, DivAssign, Mul, MulAssign}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; use simba::simd::SimdRealField; use crate::base::{SVector, Unit}; use crate::Scalar; use crate::geometry::{ AbstractRotation, Isometry, Point, Rotation, Translation, UnitComplex, UnitQuaternion, }; // TODO: there are several cloning of rotations that we could probably get rid of (but we didn't // yet because that would require to add a bound like `where for<'a, 'b> &'a R: Mul<&'b R, Output = R>` // which is quite ugly. /* * * In this file, we provide: * ========================= * * * (Operators) * * Isometry × Isometry * Isometry × R * * * Isometry ÷ Isometry * Isometry ÷ R * * Isometry × Point * Isometry × Vector * Isometry × Unit * * * Isometry × Translation * Translation × Isometry * Translation × R -> Isometry * * NOTE: The following are provided explicitly because we can't have R × Isometry. * Rotation × Isometry * UnitQuaternion × Isometry * * Rotation ÷ Isometry * UnitQuaternion ÷ Isometry * * Rotation × Translation -> Isometry * UnitQuaternion × Translation -> Isometry * * * (Assignment Operators) * * Isometry ×= Translation * * Isometry ×= Isometry * Isometry ×= R * * Isometry ÷= Isometry * Isometry ÷= R * */ macro_rules! isometry_binop_impl( ($Op: ident, $op: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField, R, const D: usize> $Op<$Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation, { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! isometry_binop_impl_all( ($Op: ident, $op: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { isometry_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); isometry_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); isometry_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); isometry_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); macro_rules! isometry_binop_assign_impl_all( ($OpAssign: ident, $op_assign: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty; [val] => $action_val: expr; [ref] => $action_ref: expr;) => { impl $OpAssign<$Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation { #[inline] fn $op_assign(&mut $lhs, $rhs: $Rhs) { $action_val } } impl<'b, T: SimdRealField, R, const D: usize> $OpAssign<&'b $Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation { #[inline] fn $op_assign(&mut $lhs, $rhs: &'b $Rhs) { $action_ref } } } ); // Isometry × Isometry // Isometry ÷ Isometry isometry_binop_impl_all!( Mul, mul; self: Isometry, rhs: Isometry, Output = Isometry; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let shift = self.rotation.transform_vector(&rhs.translation.vector); #[allow(clippy::suspicious_arithmetic_impl)] Isometry::from_parts(Translation::from(&self.translation.vector + shift), self.rotation.clone() * rhs.rotation.clone()) // TODO: too bad we have to clone. }; ); isometry_binop_impl_all!( Div, div; self: Isometry, rhs: Isometry, Output = Isometry; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // Isometry ×= Translation isometry_binop_assign_impl_all!( MulAssign, mul_assign; self: Isometry, rhs: Translation; [val] => *self *= &rhs; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { let shift = self.rotation.transform_vector(&rhs.vector); self.translation.vector += shift; }; ); // Isometry ×= Isometry // Isometry ÷= Isometry isometry_binop_assign_impl_all!( MulAssign, mul_assign; self: Isometry, rhs: Isometry; [val] => *self *= &rhs; [ref] => { let shift = self.rotation.transform_vector(&rhs.translation.vector); self.translation.vector += shift; self.rotation *= rhs.rotation.clone(); }; ); isometry_binop_assign_impl_all!( DivAssign, div_assign; self: Isometry, rhs: Isometry; [val] => *self /= &rhs; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Isometry ×= R // Isometry ÷= R md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (Const, U1), (Const, Const) const D; for; where; self: Isometry, D>, rhs: Rotation; [val] => self.rotation *= rhs; [ref] => self.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (Const, U1), (Const, Const) const D; for; where; self: Isometry, D>, rhs: Rotation; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (U3, U3), (U3, U3) const; for; where; self: Isometry, 3>, rhs: UnitQuaternion; [val] => self.rotation *= rhs; [ref] => self.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (U3, U3), (U3, U3) const; for; where; self: Isometry, 3>, rhs: UnitQuaternion; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (U2, U2), (U2, U2) const; for; where; self: Isometry, 2>, rhs: UnitComplex; [val] => self.rotation *= rhs; [ref] => self.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (U2, U2), (U2, U2) const; for; where; self: Isometry, 2>, rhs: UnitComplex; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Isometry × Point isometry_binop_impl_all!( Mul, mul; self: Isometry, right: Point, Output = Point; [val val] => self.translation * self.rotation.transform_point(&right); [ref val] => &self.translation * self.rotation.transform_point(&right); [val ref] => self.translation * self.rotation.transform_point(right); [ref ref] => &self.translation * self.rotation.transform_point(right); ); // Isometry × Vector isometry_binop_impl_all!( Mul, mul; // TODO: because of `transform_vector`, we cant use a generic storage type for the rhs vector, // i.e., right: Vector where S: Storage. self: Isometry, right: SVector, Output = SVector; [val val] => self.rotation.transform_vector(&right); [ref val] => self.rotation.transform_vector(&right); [val ref] => self.rotation.transform_vector(right); [ref ref] => self.rotation.transform_vector(right); ); // Isometry × Unit isometry_binop_impl_all!( Mul, mul; // TODO: because of `transform_vector`, we cant use a generic storage type for the rhs vector, // i.e., right: Vector where S: Storage. self: Isometry, right: Unit>, Output = Unit>; [val val] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); [ref val] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); [val ref] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); [ref ref] => Unit::new_unchecked(self.rotation.transform_vector(right.as_ref())); ); // Isometry × Translation isometry_binop_impl_all!( Mul, mul; self: Isometry, right: Translation, Output = Isometry; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { #[allow(clippy::suspicious_arithmetic_impl)] let new_tr = &self.translation.vector + self.rotation.transform_vector(&right.vector); Isometry::from_parts(Translation::from(new_tr), self.rotation.clone()) }; ); // Translation × Isometry isometry_binop_impl_all!( Mul, mul; self: Translation, right: Isometry, Output = Isometry; [val val] => Isometry::from_parts(self * right.translation, right.rotation); [ref val] => Isometry::from_parts(self * &right.translation, right.rotation); [val ref] => Isometry::from_parts(self * &right.translation, right.rotation.clone()); [ref ref] => Isometry::from_parts(self * &right.translation, right.rotation.clone()); ); macro_rules! isometry_from_composition_impl( ($Op: ident, $op: ident; $($Dims: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField $(, const $Dims: usize)*> $Op<$Rhs> for $Lhs where T::Element: SimdRealField { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! isometry_from_composition_impl_all( ($Op: ident, $op: ident; $($Dims: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); isometry_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); // Rotation × Translation isometry_from_composition_impl_all!( Mul, mul; D; self: Rotation, right: Translation, Output = Isometry, D>; [val val] => Isometry::from_parts(Translation::from(&self * right.vector), self); [ref val] => Isometry::from_parts(Translation::from(self * right.vector), self.clone()); [val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self); [ref ref] => Isometry::from_parts(Translation::from(self * &right.vector), self.clone()); ); // UnitQuaternion × Translation isometry_from_composition_impl_all!( Mul, mul; ; self: UnitQuaternion, right: Translation, Output = Isometry, 3>; [val val] => Isometry::from_parts(Translation::from(&self * right.vector), self); [ref val] => Isometry::from_parts(Translation::from( self * right.vector), self.clone()); [val ref] => Isometry::from_parts(Translation::from(&self * &right.vector), self); [ref ref] => Isometry::from_parts(Translation::from( self * &right.vector), self.clone()); ); // Isometry × Rotation isometry_from_composition_impl_all!( Mul, mul; D; self: Isometry, D>, rhs: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone()); ); // Rotation × Isometry isometry_from_composition_impl_all!( Mul, mul; D; self: Rotation, right: Isometry, D>, Output = Isometry, D>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { let shift = self * &right.translation.vector; Isometry::from_parts(Translation::from(shift), self * &right.rotation) }; ); // Isometry ÷ Rotation isometry_from_composition_impl_all!( Div, div; D; self: Isometry, D>, rhs: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone()); ); // Rotation ÷ Isometry isometry_from_composition_impl_all!( Div, div; D; self: Rotation, right: Isometry, D>, Output = Isometry, D>; // TODO: don't call inverse explicitly? [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Isometry × UnitQuaternion isometry_from_composition_impl_all!( Mul, mul; ; self: Isometry, 3>, rhs: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone()); ); // UnitQuaternion × Isometry isometry_from_composition_impl_all!( Mul, mul; ; self: UnitQuaternion, right: Isometry, 3>, Output = Isometry, 3>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { let shift = self * &right.translation.vector; Isometry::from_parts(Translation::from(shift), self * &right.rotation) }; ); // Isometry ÷ UnitQuaternion isometry_from_composition_impl_all!( Div, div; ; self: Isometry, 3>, rhs: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone()); ); // UnitQuaternion ÷ Isometry isometry_from_composition_impl_all!( Div, div; ; self: UnitQuaternion, right: Isometry, 3>, Output = Isometry, 3>; // TODO: don't call inverse explicitly? [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Translation × Rotation isometry_from_composition_impl_all!( Mul, mul; D; self: Translation, right: Rotation, Output = Isometry, D>; [val val] => Isometry::from_parts(self, right); [ref val] => Isometry::from_parts(self.clone(), right); [val ref] => Isometry::from_parts(self, right.clone()); [ref ref] => Isometry::from_parts(self.clone(), right.clone()); ); // Translation × UnitQuaternion isometry_from_composition_impl_all!( Mul, mul; ; self: Translation, right: UnitQuaternion, Output = Isometry, 3>; [val val] => Isometry::from_parts(self, right); [ref val] => Isometry::from_parts(self.clone(), right); [val ref] => Isometry::from_parts(self, right.clone()); [ref ref] => Isometry::from_parts(self.clone(), right.clone()); ); // Isometry × UnitComplex isometry_from_composition_impl_all!( Mul, mul; ; self: Isometry, 2>, rhs: UnitComplex, Output = Isometry, 2>; [val val] => Isometry::from_parts(self.translation, self.rotation * rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation * rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() * rhs.clone()); ); // Isometry ÷ UnitComplex isometry_from_composition_impl_all!( Div, div; ; self: Isometry, 2>, rhs: UnitComplex, Output = Isometry, 2>; [val val] => Isometry::from_parts(self.translation, self.rotation / rhs); [ref val] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs); [val ref] => Isometry::from_parts(self.translation, self.rotation / rhs.clone()); [ref ref] => Isometry::from_parts(self.translation.clone(), self.rotation.clone() / rhs.clone()); ); nalgebra-0.33.0/src/geometry/isometry_simba.rs000064400000000000000000000032570072674642500175370ustar 00000000000000use simba::simd::SimdValue; use crate::SimdRealField; use crate::geometry::{AbstractRotation, Isometry, Translation}; impl SimdValue for Isometry where T::Element: SimdRealField, R: SimdValue + AbstractRotation, R::Element: AbstractRotation, { const LANES: usize = T::LANES; type Element = Isometry; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { Isometry::from_parts(Translation::splat(val.translation), R::splat(val.rotation)) } #[inline] fn extract(&self, i: usize) -> Self::Element { Isometry::from_parts(self.translation.extract(i), self.rotation.extract(i)) } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { Isometry::from_parts( self.translation.extract_unchecked(i), self.rotation.extract_unchecked(i), ) } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.translation.replace(i, val.translation); self.rotation.replace(i, val.rotation); } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.translation.replace_unchecked(i, val.translation); self.rotation.replace_unchecked(i, val.rotation); } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { Isometry::from_parts( self.translation.select(cond, other.translation), self.rotation.select(cond, other.rotation), ) } } nalgebra-0.33.0/src/geometry/mod.rs000064400000000000000000000050010072674642500152550ustar 00000000000000//! [Reexported at the root of this crate.] Data structures for points and usual transformations //! (rotations, isometries, etc.) mod op_macros; mod abstract_rotation; mod point; mod point_alias; mod point_construction; mod point_conversion; mod point_coordinates; mod point_ops; mod point_simba; mod rotation; mod rotation_alias; mod rotation_construction; mod rotation_conversion; mod rotation_interpolation; mod rotation_ops; mod rotation_simba; // TODO: implement Rotation methods. mod rotation_specialization; mod quaternion; mod quaternion_construction; mod quaternion_conversion; mod quaternion_coordinates; mod quaternion_ops; mod quaternion_simba; mod dual_quaternion; mod dual_quaternion_construction; mod dual_quaternion_conversion; mod dual_quaternion_ops; mod unit_complex; mod unit_complex_construction; mod unit_complex_conversion; mod unit_complex_ops; mod unit_complex_simba; mod translation; mod translation_alias; mod translation_construction; mod translation_conversion; mod translation_coordinates; mod translation_ops; mod translation_simba; mod scale; mod scale_alias; mod scale_construction; mod scale_conversion; mod scale_coordinates; mod scale_ops; mod scale_simba; mod isometry; mod isometry_alias; mod isometry_construction; mod isometry_conversion; mod isometry_interpolation; mod isometry_ops; mod isometry_simba; mod similarity; mod similarity_alias; mod similarity_construction; mod similarity_conversion; mod similarity_ops; mod similarity_simba; mod swizzle; mod transform; mod transform_alias; mod transform_construction; mod transform_conversion; mod transform_ops; mod transform_simba; mod reflection; mod reflection_alias; mod orthographic; mod perspective; pub use self::abstract_rotation::AbstractRotation; pub use self::point::*; pub use self::point_alias::*; pub use self::rotation::*; pub use self::rotation_alias::*; pub use self::quaternion::*; pub use self::dual_quaternion::*; pub use self::unit_complex::*; pub use self::translation::*; pub use self::translation_alias::*; pub use self::scale::*; pub use self::scale_alias::*; pub use self::isometry::*; pub use self::isometry_alias::*; pub use self::similarity::*; pub use self::similarity_alias::*; pub use self::transform::*; pub use self::transform_alias::*; pub use self::reflection::*; pub use self::reflection_alias::*; pub use self::orthographic::Orthographic3; pub use self::perspective::Perspective3; nalgebra-0.33.0/src/geometry/op_macros.rs000064400000000000000000000211450072674642500164670ustar 00000000000000#![macro_use] // TODO: merge with `md_impl`. /// Macro for the implementation of multiplication and division. macro_rules! md_impl( ( // Operator, operator method, and scalar bounds. $Op: ident, $op: ident $(where T: $($ScalarBounds: ident),*)*; // Storage dimensions, and dimension bounds. ($R1: ty, $C1: ty),($R2: ty, $C2: ty) // Const type declaration const $($D: ident),*; // Other generic type declarations. for $($DimsDecl: ident),*; // Where clause. where $($ConstraintType: ty: $ConstraintBound: ident$(<$($ConstraintBoundParams: ty $( = $EqBound: ty )*),*>)*),*; // Argument identifiers and types + output. $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Result: ty; // Operator actual implementation. $action: expr; // Lifetime. $($lives: tt),*) => { impl<$($lives ,)* T $(, $DimsDecl)* $(, const $D: usize)*> $Op<$Rhs> for $Lhs where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign $($(+ $ScalarBounds)*)*, $( $ConstraintType: $ConstraintBound$(<$( $ConstraintBoundParams $( = $EqBound )*),*>)* ),* { type Output = $Result; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); /// Macro for the implementation of multiplication and division. /// Implements all the argument reference combinations. macro_rules! md_impl_all( ( // Operator, operator method, and scalar bounds. $Op: ident, $op: ident $(where T: $($ScalarBounds: ident),*)*; // Storage dimensions, and dimension bounds. ($R1: ty, $C1: ty),($R2: ty, $C2: ty) // Const type declaration const $($D: ident),*; // Other generic type declarations. for $($DimsDecl: ident),*; // Where clause. where $($ConstraintType: ty: $ConstraintBound: ident$(<$($ConstraintBoundParams: ty $( = $EqBound: ty )*),*>)*),*; // Argument identifiers and types + output. $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Result: ty; // Operators actual implementations. [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { md_impl!( $Op, $op $(where T: $($ScalarBounds),*)*; ($R1, $C1),($R2, $C2) const $($D),*; for $($DimsDecl),*; where $($ConstraintType: $ConstraintBound$(<$($ConstraintBoundParams $( = $EqBound )*),*>)*),*; $lhs: $Lhs, $rhs: $Rhs, Output = $Result; $action_val_val; ); md_impl!( $Op, $op $(where T: $($ScalarBounds),*)*; ($R1, $C1),($R2, $C2) const $($D),*; for $($DimsDecl),*; where $($ConstraintType: $ConstraintBound$(<$($ConstraintBoundParams $( = $EqBound )*),*>)*),*; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Result; $action_ref_val; 'a); md_impl!( $Op, $op $(where T: $($ScalarBounds),*)*; ($R1, $C1),($R2, $C2) const $($D),*; for $($DimsDecl),*; where $($ConstraintType: $ConstraintBound$(<$($ConstraintBoundParams $( = $EqBound )*),*>)*),*; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Result; $action_val_ref; 'b); md_impl!( $Op, $op $(where T: $($ScalarBounds),*)*; ($R1, $C1),($R2, $C2) const $($D),*; for $($DimsDecl),*; where $($ConstraintType: $ConstraintBound$(<$($ConstraintBoundParams $( = $EqBound )*),*>)*),*; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Result; $action_ref_ref; 'a, 'b); } ); /// Macro for the implementation of assignment-multiplication and assignment-division. macro_rules! md_assign_impl( ( // Operator, operator method, and scalar bounds. $Op: ident, $op: ident $(where T: $($ScalarBounds: ident),*)* $(for T::Element: $($ElementBounds: ident),*)*; // Storage dimensions, and dimension bounds. ($R1: ty, $C1: ty),($R2: ty, $C2: ty) // Const type declaration const $($D: ident),*; // Other generic type declarations. for $($DimsDecl: ident),*; // Where clause. where $($ConstraintType: ty: $ConstraintBound: ident$(<$($ConstraintBoundParams: ty $( = $EqBound: ty )*),*>)*),*; // Argument identifiers and types. $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty; // Actual implementation and lifetimes. $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T $(, $DimsDecl)* $(, const $D: usize)*> $Op<$Rhs> for $Lhs where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign $($(+ $ScalarBounds)*)*, $($(T::Element: $ElementBounds,)*)* $( $ConstraintType: $ConstraintBound $(<$( $ConstraintBoundParams $( = $EqBound )*),*>)* ),* { #[inline] fn $op(&mut $lhs, $rhs: $Rhs) { $action } } } ); /// Macro for the implementation of assignment-multiplication and assignment-division with and /// without reference to the right-hand-side. macro_rules! md_assign_impl_all( ( // Operator, operator method, and scalar bounds. $Op: ident, $op: ident $(where T: $($ScalarBounds: ident),*)* $(for T::Element: $($ElementBounds: ident),*)*; // Storage dimensions, and dimension bounds. ($R1: ty, $C1: ty),($R2: ty, $C2: ty) // Const type declaration const $($D: ident),*; // Other generic type declarations. for $($DimsDecl: ident),*; // Where clause. where $($ConstraintType: ty: $ConstraintBound: ident$(<$($ConstraintBoundParams: ty $( = $EqBound: ty )*),*>)*),*; // Argument identifiers and types. $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty; // Actual implementation and lifetimes. [val] => $action_val: expr; [ref] => $action_ref: expr;) => { md_assign_impl!( $Op, $op $(where T: $($ScalarBounds),*)* $(for T::Element: $($ElementBounds),*)*; ($R1, $C1),($R2, $C2) const $($D),*; for $($DimsDecl),*; where $($ConstraintType: $ConstraintBound$(<$($ConstraintBoundParams $( = $EqBound )*),*>)*),*; $lhs: $Lhs, $rhs: $Rhs; $action_val; ); md_assign_impl!( $Op, $op $(where T: $($ScalarBounds),*)* $(for T::Element: $($ElementBounds),*)*; ($R1, $C1),($R2, $C2) const $($D),*; for $($DimsDecl),*; where $($ConstraintType: $ConstraintBound$(<$($ConstraintBoundParams $( = $EqBound )*),*>)*),*; $lhs: $Lhs, $rhs: &'b $Rhs; $action_ref; 'b); } ); // TODO: merge with `as_impl`. /// Macro for the implementation of addition and subtraction. macro_rules! add_sub_impl( ($Op: ident, $op: ident, $bound: ident; ($R1: ty, $C1: ty),($R2: ty, $C2: ty) $(-> ($RRes: ty, $CRes: ty))* // Const type declaration const $($D: ident),*; // Other generic type declarations. for $($DimsDecl: ident),*; // Where clause. where $($ConstraintType: ty: $ConstraintBound: ident$(<$($ConstraintBoundParams: ty $( = $EqBound: ty )*),*>)*),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Result: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T $(, $DimsDecl)* $(, const $D: usize)*> $Op<$Rhs> for $Lhs where T: Scalar + $bound, ShapeConstraint: SameNumberOfRows<$R1, $R2 $(, Representative = $RRes)*> + SameNumberOfColumns<$C1, $C2 $(, Representative = $CRes)*>, $( $ConstraintType: $ConstraintBound$(<$( $ConstraintBoundParams $( = $EqBound )*),*>)* ),* { type Output = $Result; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); // TODO: merge with `md_assign_impl`. /// Macro for the implementation of assignment-addition and assignment-subtraction. macro_rules! add_sub_assign_impl( ($Op: ident, $op: ident, $bound: ident; $(const $D: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T $(, const $D: usize),*> $Op<$Rhs> for $Lhs where T: Scalar + $bound { #[inline] fn $op(&mut $lhs, $rhs: $Rhs) { $action } } } ); nalgebra-0.33.0/src/geometry/orthographic.rs000064400000000000000000000717220072674642500172040ustar 00000000000000#[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use simba::scalar::RealField; use crate::base::dimension::U3; use crate::base::storage::Storage; use crate::base::{Matrix4, Vector, Vector3}; use crate::geometry::{Point3, Projective3}; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A 3D orthographic projection stored as a homogeneous 4x4 matrix. #[repr(C)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Orthographic3", bound(archive = " T: rkyv::Archive, Matrix4: rkyv::Archive> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[derive(Copy, Clone)] pub struct Orthographic3 { matrix: Matrix4, } impl fmt::Debug for Orthographic3 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.matrix.fmt(f) } } impl PartialEq for Orthographic3 { #[inline] fn eq(&self, right: &Self) -> bool { self.matrix == right.matrix } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Orthographic3 where T: RealField + bytemuck::Zeroable, Matrix4: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Orthographic3 where T: RealField + bytemuck::Pod, Matrix4: bytemuck::Pod, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Orthographic3 { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.matrix.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: RealField + Deserialize<'a>> Deserialize<'a> for Orthographic3 { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let matrix = Matrix4::::deserialize(deserializer)?; Ok(Self::from_matrix_unchecked(matrix)) } } impl Orthographic3 { /// Wraps the given matrix to interpret it as a 3D orthographic matrix. /// /// It is not checked whether or not the given matrix actually represents an orthographic /// projection. /// /// # Example /// ``` /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let mat = Matrix4::new( /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, /// 0.0, 0.0, 0.0, 1.0 /// ); /// let proj = Orthographic3::from_matrix_unchecked(mat); /// assert_eq!(proj, Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0)); /// ``` #[inline] pub const fn from_matrix_unchecked(matrix: Matrix4) -> Self { Self { matrix } } } impl Orthographic3 { /// Creates a new orthographic projection matrix. /// /// This follows the OpenGL convention, so this will flip the `z` axis. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// // Check this projection actually transforms the view cuboid into the double-unit cube. /// // See https://www.nalgebra.org/docs/user_guide/projections#orthographic-projection for more details. /// let p1 = Point3::new(1.0, 2.0, -0.1); /// let p2 = Point3::new(1.0, 2.0, -1000.0); /// let p3 = Point3::new(1.0, 20.0, -0.1); /// let p4 = Point3::new(1.0, 20.0, -1000.0); /// let p5 = Point3::new(10.0, 2.0, -0.1); /// let p6 = Point3::new(10.0, 2.0, -1000.0); /// let p7 = Point3::new(10.0, 20.0, -0.1); /// let p8 = Point3::new(10.0, 20.0, -1000.0); /// /// assert_relative_eq!(proj.project_point(&p1), Point3::new(-1.0, -1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p2), Point3::new(-1.0, -1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p3), Point3::new(-1.0, 1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p4), Point3::new(-1.0, 1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p5), Point3::new( 1.0, -1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p6), Point3::new( 1.0, -1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p7), Point3::new( 1.0, 1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p8), Point3::new( 1.0, 1.0, 1.0)); /// /// // This also works with flipped axis. In other words, we allow that /// // `left > right`, `bottom > top`, and/or `znear > zfar`. /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// /// assert_relative_eq!(proj.project_point(&p1), Point3::new( 1.0, 1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p2), Point3::new( 1.0, 1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p3), Point3::new( 1.0, -1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p4), Point3::new( 1.0, -1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p5), Point3::new(-1.0, 1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p6), Point3::new(-1.0, 1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p7), Point3::new(-1.0, -1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p8), Point3::new(-1.0, -1.0, -1.0)); /// ``` #[inline] pub fn new(left: T, right: T, bottom: T, top: T, znear: T, zfar: T) -> Self { let matrix = Matrix4::::identity(); let mut res = Self::from_matrix_unchecked(matrix); res.set_left_and_right(left, right); res.set_bottom_and_top(bottom, top); res.set_znear_and_zfar(znear, zfar); res } /// Creates a new orthographic projection matrix from an aspect ratio and the vertical field of view. #[inline] pub fn from_fov(aspect: T, vfov: T, znear: T, zfar: T) -> Self { assert!( znear != zfar, "The far plane must not be equal to the near plane." ); assert!( !relative_eq!(aspect, T::zero()), "The aspect ratio must not be zero." ); let half: T = crate::convert(0.5); let width = zfar.clone() * (vfov * half.clone()).tan(); let height = width.clone() / aspect; Self::new( -width.clone() * half.clone(), width * half.clone(), -height.clone() * half.clone(), height * half, znear, zfar, ) } /// Retrieves the inverse of the underlying homogeneous matrix. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let inv = proj.inverse(); /// /// assert_relative_eq!(inv * proj.as_matrix(), Matrix4::identity()); /// assert_relative_eq!(proj.as_matrix() * inv, Matrix4::identity()); /// /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// let inv = proj.inverse(); /// assert_relative_eq!(inv * proj.as_matrix(), Matrix4::identity()); /// assert_relative_eq!(proj.as_matrix() * inv, Matrix4::identity()); /// ``` #[inline] #[must_use] pub fn inverse(&self) -> Matrix4 { let mut res = self.clone().to_homogeneous(); let inv_m11 = T::one() / self.matrix[(0, 0)].clone(); let inv_m22 = T::one() / self.matrix[(1, 1)].clone(); let inv_m33 = T::one() / self.matrix[(2, 2)].clone(); res[(0, 0)] = inv_m11.clone(); res[(1, 1)] = inv_m22.clone(); res[(2, 2)] = inv_m33.clone(); res[(0, 3)] = -self.matrix[(0, 3)].clone() * inv_m11; res[(1, 3)] = -self.matrix[(1, 3)].clone() * inv_m22; res[(2, 3)] = -self.matrix[(2, 3)].clone() * inv_m33; res } /// Computes the corresponding homogeneous matrix. /// /// # Example /// ``` /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let expected = Matrix4::new( /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, /// 0.0, 0.0, 0.0, 1.0 /// ); /// assert_eq!(proj.to_homogeneous(), expected); /// ``` #[inline] #[must_use] pub fn to_homogeneous(self) -> Matrix4 { self.matrix } /// A reference to the underlying homogeneous transformation matrix. /// /// # Example /// ``` /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let expected = Matrix4::new( /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, /// 0.0, 0.0, 0.0, 1.0 /// ); /// assert_eq!(*proj.as_matrix(), expected); /// ``` #[inline] #[must_use] pub fn as_matrix(&self) -> &Matrix4 { &self.matrix } /// A reference to this transformation seen as a `Projective3`. /// /// # Example /// ``` /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_eq!(proj.as_projective().to_homogeneous(), proj.to_homogeneous()); /// ``` #[inline] #[must_use] pub fn as_projective(&self) -> &Projective3 { unsafe { &*(self as *const Orthographic3 as *const Projective3) } } /// This transformation seen as a `Projective3`. /// /// # Example /// ``` /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_eq!(proj.to_projective().to_homogeneous(), proj.to_homogeneous()); /// ``` #[inline] #[must_use] pub fn to_projective(self) -> Projective3 { Projective3::from_matrix_unchecked(self.matrix) } /// Retrieves the underlying homogeneous matrix. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Orthographic3, Point3, Matrix4}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// let expected = Matrix4::new( /// 2.0 / 9.0, 0.0, 0.0, -11.0 / 9.0, /// 0.0, 2.0 / 18.0, 0.0, -22.0 / 18.0, /// 0.0, 0.0, -2.0 / 999.9, -1000.1 / 999.9, /// 0.0, 0.0, 0.0, 1.0 /// ); /// assert_eq!(proj.into_inner(), expected); /// ``` #[inline] pub fn into_inner(self) -> Matrix4 { self.matrix } /// Retrieves the underlying homogeneous matrix. /// Deprecated: Use [`Orthographic3::into_inner`] instead. #[deprecated(note = "use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> Matrix4 { self.matrix } /// The left offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.left(), 1.0, epsilon = 1.0e-6); /// /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// assert_relative_eq!(proj.left(), 10.0, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn left(&self) -> T { (-T::one() - self.matrix[(0, 3)].clone()) / self.matrix[(0, 0)].clone() } /// The right offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.right(), 10.0, epsilon = 1.0e-6); /// /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// assert_relative_eq!(proj.right(), 1.0, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn right(&self) -> T { (T::one() - self.matrix[(0, 3)].clone()) / self.matrix[(0, 0)].clone() } /// The bottom offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.bottom(), 2.0, epsilon = 1.0e-6); /// /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// assert_relative_eq!(proj.bottom(), 20.0, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn bottom(&self) -> T { (-T::one() - self.matrix[(1, 3)].clone()) / self.matrix[(1, 1)].clone() } /// The top offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.top(), 20.0, epsilon = 1.0e-6); /// /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// assert_relative_eq!(proj.top(), 2.0, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn top(&self) -> T { (T::one() - self.matrix[(1, 3)].clone()) / self.matrix[(1, 1)].clone() } /// The near plane offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.znear(), 0.1, epsilon = 1.0e-6); /// /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// assert_relative_eq!(proj.znear(), 1000.0, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn znear(&self) -> T { (T::one() + self.matrix[(2, 3)].clone()) / self.matrix[(2, 2)].clone() } /// The far plane offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// assert_relative_eq!(proj.zfar(), 1000.0, epsilon = 1.0e-6); /// /// let proj = Orthographic3::new(10.0, 1.0, 20.0, 2.0, 1000.0, 0.1); /// assert_relative_eq!(proj.zfar(), 0.1, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn zfar(&self) -> T { (-T::one() + self.matrix[(2, 3)].clone()) / self.matrix[(2, 2)].clone() } // TODO: when we get specialization, specialize the Mul impl instead. /// Projects a point. Faster than matrix multiplication. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// /// let p1 = Point3::new(1.0, 2.0, -0.1); /// let p2 = Point3::new(1.0, 2.0, -1000.0); /// let p3 = Point3::new(1.0, 20.0, -0.1); /// let p4 = Point3::new(1.0, 20.0, -1000.0); /// let p5 = Point3::new(10.0, 2.0, -0.1); /// let p6 = Point3::new(10.0, 2.0, -1000.0); /// let p7 = Point3::new(10.0, 20.0, -0.1); /// let p8 = Point3::new(10.0, 20.0, -1000.0); /// /// assert_relative_eq!(proj.project_point(&p1), Point3::new(-1.0, -1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p2), Point3::new(-1.0, -1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p3), Point3::new(-1.0, 1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p4), Point3::new(-1.0, 1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p5), Point3::new( 1.0, -1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p6), Point3::new( 1.0, -1.0, 1.0)); /// assert_relative_eq!(proj.project_point(&p7), Point3::new( 1.0, 1.0, -1.0)); /// assert_relative_eq!(proj.project_point(&p8), Point3::new( 1.0, 1.0, 1.0)); /// ``` #[inline] #[must_use] pub fn project_point(&self, p: &Point3) -> Point3 { Point3::new( self.matrix[(0, 0)].clone() * p[0].clone() + self.matrix[(0, 3)].clone(), self.matrix[(1, 1)].clone() * p[1].clone() + self.matrix[(1, 3)].clone(), self.matrix[(2, 2)].clone() * p[2].clone() + self.matrix[(2, 3)].clone(), ) } /// Un-projects a point. Faster than multiplication by the underlying matrix inverse. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Orthographic3, Point3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// /// let p1 = Point3::new(-1.0, -1.0, -1.0); /// let p2 = Point3::new(-1.0, -1.0, 1.0); /// let p3 = Point3::new(-1.0, 1.0, -1.0); /// let p4 = Point3::new(-1.0, 1.0, 1.0); /// let p5 = Point3::new( 1.0, -1.0, -1.0); /// let p6 = Point3::new( 1.0, -1.0, 1.0); /// let p7 = Point3::new( 1.0, 1.0, -1.0); /// let p8 = Point3::new( 1.0, 1.0, 1.0); /// /// assert_relative_eq!(proj.unproject_point(&p1), Point3::new(1.0, 2.0, -0.1), epsilon = 1.0e-6); /// assert_relative_eq!(proj.unproject_point(&p2), Point3::new(1.0, 2.0, -1000.0), epsilon = 1.0e-6); /// assert_relative_eq!(proj.unproject_point(&p3), Point3::new(1.0, 20.0, -0.1), epsilon = 1.0e-6); /// assert_relative_eq!(proj.unproject_point(&p4), Point3::new(1.0, 20.0, -1000.0), epsilon = 1.0e-6); /// assert_relative_eq!(proj.unproject_point(&p5), Point3::new(10.0, 2.0, -0.1), epsilon = 1.0e-6); /// assert_relative_eq!(proj.unproject_point(&p6), Point3::new(10.0, 2.0, -1000.0), epsilon = 1.0e-6); /// assert_relative_eq!(proj.unproject_point(&p7), Point3::new(10.0, 20.0, -0.1), epsilon = 1.0e-6); /// assert_relative_eq!(proj.unproject_point(&p8), Point3::new(10.0, 20.0, -1000.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn unproject_point(&self, p: &Point3) -> Point3 { Point3::new( (p[0].clone() - self.matrix[(0, 3)].clone()) / self.matrix[(0, 0)].clone(), (p[1].clone() - self.matrix[(1, 3)].clone()) / self.matrix[(1, 1)].clone(), (p[2].clone() - self.matrix[(2, 3)].clone()) / self.matrix[(2, 2)].clone(), ) } // TODO: when we get specialization, specialize the Mul impl instead. /// Projects a vector. Faster than matrix multiplication. /// /// Vectors are not affected by the translation part of the projection. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Orthographic3, Vector3}; /// let proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// /// let v1 = Vector3::x(); /// let v2 = Vector3::y(); /// let v3 = Vector3::z(); /// /// assert_relative_eq!(proj.project_vector(&v1), Vector3::x() * 2.0 / 9.0); /// assert_relative_eq!(proj.project_vector(&v2), Vector3::y() * 2.0 / 18.0); /// assert_relative_eq!(proj.project_vector(&v3), Vector3::z() * -2.0 / 999.9); /// ``` #[inline] #[must_use] pub fn project_vector(&self, p: &Vector) -> Vector3 where SB: Storage, { Vector3::new( self.matrix[(0, 0)].clone() * p[0].clone(), self.matrix[(1, 1)].clone() * p[1].clone(), self.matrix[(2, 2)].clone() * p[2].clone(), ) } /// Sets the left offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_left(2.0); /// assert_relative_eq!(proj.left(), 2.0, epsilon = 1.0e-6); /// /// // It is OK to set a left offset greater than the current right offset. /// proj.set_left(20.0); /// assert_relative_eq!(proj.left(), 20.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_left(&mut self, left: T) { let right = self.right(); self.set_left_and_right(left, right); } /// Sets the right offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_right(15.0); /// assert_relative_eq!(proj.right(), 15.0, epsilon = 1.0e-6); /// /// // It is OK to set a right offset smaller than the current left offset. /// proj.set_right(-3.0); /// assert_relative_eq!(proj.right(), -3.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_right(&mut self, right: T) { let left = self.left(); self.set_left_and_right(left, right); } /// Sets the bottom offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_bottom(8.0); /// assert_relative_eq!(proj.bottom(), 8.0, epsilon = 1.0e-6); /// /// // It is OK to set a bottom offset greater than the current top offset. /// proj.set_bottom(50.0); /// assert_relative_eq!(proj.bottom(), 50.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_bottom(&mut self, bottom: T) { let top = self.top(); self.set_bottom_and_top(bottom, top); } /// Sets the top offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_top(15.0); /// assert_relative_eq!(proj.top(), 15.0, epsilon = 1.0e-6); /// /// // It is OK to set a top offset smaller than the current bottom offset. /// proj.set_top(-3.0); /// assert_relative_eq!(proj.top(), -3.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_top(&mut self, top: T) { let bottom = self.bottom(); self.set_bottom_and_top(bottom, top); } /// Sets the near plane offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_znear(8.0); /// assert_relative_eq!(proj.znear(), 8.0, epsilon = 1.0e-6); /// /// // It is OK to set a znear greater than the current zfar. /// proj.set_znear(5000.0); /// assert_relative_eq!(proj.znear(), 5000.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_znear(&mut self, znear: T) { let zfar = self.zfar(); self.set_znear_and_zfar(znear, zfar); } /// Sets the far plane offset of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_zfar(15.0); /// assert_relative_eq!(proj.zfar(), 15.0, epsilon = 1.0e-6); /// /// // It is OK to set a zfar smaller than the current znear. /// proj.set_zfar(-3.0); /// assert_relative_eq!(proj.zfar(), -3.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_zfar(&mut self, zfar: T) { let znear = self.znear(); self.set_znear_and_zfar(znear, zfar); } /// Sets the view cuboid offsets along the `x` axis. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_left_and_right(7.0, 70.0); /// assert_relative_eq!(proj.left(), 7.0, epsilon = 1.0e-6); /// assert_relative_eq!(proj.right(), 70.0, epsilon = 1.0e-6); /// /// // It is also OK to have `left > right`. /// proj.set_left_and_right(70.0, 7.0); /// assert_relative_eq!(proj.left(), 70.0, epsilon = 1.0e-6); /// assert_relative_eq!(proj.right(), 7.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_left_and_right(&mut self, left: T, right: T) { assert!( left != right, "The left corner must not be equal to the right corner." ); self.matrix[(0, 0)] = crate::convert::<_, T>(2.0) / (right.clone() - left.clone()); self.matrix[(0, 3)] = -(right.clone() + left.clone()) / (right - left); } /// Sets the view cuboid offsets along the `y` axis. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_bottom_and_top(7.0, 70.0); /// assert_relative_eq!(proj.bottom(), 7.0, epsilon = 1.0e-6); /// assert_relative_eq!(proj.top(), 70.0, epsilon = 1.0e-6); /// /// // It is also OK to have `bottom > top`. /// proj.set_bottom_and_top(70.0, 7.0); /// assert_relative_eq!(proj.bottom(), 70.0, epsilon = 1.0e-6); /// assert_relative_eq!(proj.top(), 7.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_bottom_and_top(&mut self, bottom: T, top: T) { assert_ne!( bottom, top, "The top corner must not be equal to the bottom corner." ); self.matrix[(1, 1)] = crate::convert::<_, T>(2.0) / (top.clone() - bottom.clone()); self.matrix[(1, 3)] = -(top.clone() + bottom.clone()) / (top - bottom); } /// Sets the near and far plane offsets of the view cuboid. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Orthographic3; /// let mut proj = Orthographic3::new(1.0, 10.0, 2.0, 20.0, 0.1, 1000.0); /// proj.set_znear_and_zfar(50.0, 5000.0); /// assert_relative_eq!(proj.znear(), 50.0, epsilon = 1.0e-6); /// assert_relative_eq!(proj.zfar(), 5000.0, epsilon = 1.0e-6); /// /// // It is also OK to have `znear > zfar`. /// proj.set_znear_and_zfar(5000.0, 0.5); /// assert_relative_eq!(proj.znear(), 5000.0, epsilon = 1.0e-6); /// assert_relative_eq!(proj.zfar(), 0.5, epsilon = 1.0e-6); /// ``` #[inline] pub fn set_znear_and_zfar(&mut self, znear: T, zfar: T) { assert!( zfar != znear, "The near-plane and far-plane must not be superimposed." ); self.matrix[(2, 2)] = -crate::convert::<_, T>(2.0) / (zfar.clone() - znear.clone()); self.matrix[(2, 3)] = -(zfar.clone() + znear.clone()) / (zfar - znear); } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where Standard: Distribution, { /// Generate an arbitrary random variate for testing purposes. fn sample(&self, r: &mut R) -> Orthographic3 { use crate::base::helper; let left = r.gen(); let right = helper::reject_rand(r, |x: &T| *x > left); let bottom = r.gen(); let top = helper::reject_rand(r, |x: &T| *x > bottom); let znear = r.gen(); let zfar = helper::reject_rand(r, |x: &T| *x > znear); Orthographic3::new(left, right, bottom, top, znear, zfar) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Orthographic3 where Matrix4: Send, { fn arbitrary(g: &mut Gen) -> Self { use crate::base::helper; let left = Arbitrary::arbitrary(g); let right = helper::reject(g, |x: &T| *x > left); let bottom = Arbitrary::arbitrary(g); let top = helper::reject(g, |x: &T| *x > bottom); let znear = Arbitrary::arbitrary(g); let zfar = helper::reject(g, |x: &T| *x > znear); Self::new(left, right, bottom, top, znear, zfar) } } impl From> for Matrix4 { #[inline] fn from(orth: Orthographic3) -> Self { orth.into_inner() } } nalgebra-0.33.0/src/geometry/perspective.rs000064400000000000000000000256410072674642500170430ustar 00000000000000#[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use simba::scalar::RealField; use crate::base::dimension::U3; use crate::base::storage::Storage; use crate::base::{Matrix4, Vector, Vector3}; use crate::geometry::{Point3, Projective3}; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A 3D perspective projection stored as a homogeneous 4x4 matrix. #[repr(C)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Perspective3", bound(archive = " T: rkyv::Archive, Matrix4: rkyv::Archive> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[derive(Copy, Clone)] pub struct Perspective3 { matrix: Matrix4, } impl fmt::Debug for Perspective3 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.matrix.fmt(f) } } impl PartialEq for Perspective3 { #[inline] fn eq(&self, right: &Self) -> bool { self.matrix == right.matrix } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Perspective3 where T: RealField + bytemuck::Zeroable, Matrix4: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Perspective3 where T: RealField + bytemuck::Pod, Matrix4: bytemuck::Pod, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Perspective3 { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.matrix.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: RealField + Deserialize<'a>> Deserialize<'a> for Perspective3 { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let matrix = Matrix4::::deserialize(deserializer)?; Ok(Self::from_matrix_unchecked(matrix)) } } impl Perspective3 { /// Wraps the given matrix to interpret it as a 3D perspective matrix. /// /// It is not checked whether or not the given matrix actually represents a perspective /// projection. #[inline] pub const fn from_matrix_unchecked(matrix: Matrix4) -> Self { Self { matrix } } } impl Perspective3 { /// Creates a new perspective matrix from the aspect ratio, y field of view, and near/far planes. pub fn new(aspect: T, fovy: T, znear: T, zfar: T) -> Self { assert!( relative_ne!(zfar, znear), "The near-plane and far-plane must not be superimposed." ); assert!( !relative_eq!(aspect, T::zero()), "The aspect ratio must not be zero." ); let matrix = Matrix4::identity(); let mut res = Self::from_matrix_unchecked(matrix); res.set_fovy(fovy); res.set_aspect(aspect); res.set_znear_and_zfar(znear, zfar); res.matrix[(3, 3)] = T::zero(); res.matrix[(3, 2)] = -T::one(); res } /// Retrieves the inverse of the underlying homogeneous matrix. #[inline] #[must_use] pub fn inverse(&self) -> Matrix4 { let mut res = self.clone().to_homogeneous(); res[(0, 0)] = T::one() / self.matrix[(0, 0)].clone(); res[(1, 1)] = T::one() / self.matrix[(1, 1)].clone(); res[(2, 2)] = T::zero(); let m23 = self.matrix[(2, 3)].clone(); let m32 = self.matrix[(3, 2)].clone(); res[(2, 3)] = T::one() / m32.clone(); res[(3, 2)] = T::one() / m23.clone(); res[(3, 3)] = -self.matrix[(2, 2)].clone() / (m23 * m32); res } /// Computes the corresponding homogeneous matrix. #[inline] #[must_use] pub fn to_homogeneous(self) -> Matrix4 { self.matrix.clone_owned() } /// A reference to the underlying homogeneous transformation matrix. #[inline] #[must_use] pub fn as_matrix(&self) -> &Matrix4 { &self.matrix } /// A reference to this transformation seen as a `Projective3`. #[inline] #[must_use] pub fn as_projective(&self) -> &Projective3 { unsafe { &*(self as *const Perspective3 as *const Projective3) } } /// This transformation seen as a `Projective3`. #[inline] #[must_use] pub fn to_projective(self) -> Projective3 { Projective3::from_matrix_unchecked(self.matrix) } /// Retrieves the underlying homogeneous matrix. #[inline] pub fn into_inner(self) -> Matrix4 { self.matrix } /// Retrieves the underlying homogeneous matrix. /// Deprecated: Use [`Perspective3::into_inner`] instead. #[deprecated(note = "use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> Matrix4 { self.matrix } /// Gets the `width / height` aspect ratio of the view frustum. #[inline] #[must_use] pub fn aspect(&self) -> T { self.matrix[(1, 1)].clone() / self.matrix[(0, 0)].clone() } /// Gets the y field of view of the view frustum. #[inline] #[must_use] pub fn fovy(&self) -> T { (T::one() / self.matrix[(1, 1)].clone()).atan() * crate::convert(2.0) } /// Gets the near plane offset of the view frustum. #[inline] #[must_use] pub fn znear(&self) -> T { let ratio = (-self.matrix[(2, 2)].clone() + T::one()) / (-self.matrix[(2, 2)].clone() - T::one()); self.matrix[(2, 3)].clone() / (ratio * crate::convert(2.0)) - self.matrix[(2, 3)].clone() / crate::convert(2.0) } /// Gets the far plane offset of the view frustum. #[inline] #[must_use] pub fn zfar(&self) -> T { let ratio = (-self.matrix[(2, 2)].clone() + T::one()) / (-self.matrix[(2, 2)].clone() - T::one()); (self.matrix[(2, 3)].clone() - ratio * self.matrix[(2, 3)].clone()) / crate::convert(2.0) } // TODO: add a method to retrieve znear and zfar simultaneously? // TODO: when we get specialization, specialize the Mul impl instead. /// Projects a point. Faster than matrix multiplication. #[inline] #[must_use] pub fn project_point(&self, p: &Point3) -> Point3 { let inverse_denom = -T::one() / p[2].clone(); Point3::new( self.matrix[(0, 0)].clone() * p[0].clone() * inverse_denom.clone(), self.matrix[(1, 1)].clone() * p[1].clone() * inverse_denom.clone(), (self.matrix[(2, 2)].clone() * p[2].clone() + self.matrix[(2, 3)].clone()) * inverse_denom, ) } /// Un-projects a point. Faster than multiplication by the matrix inverse. #[inline] #[must_use] pub fn unproject_point(&self, p: &Point3) -> Point3 { let inverse_denom = self.matrix[(2, 3)].clone() / (p[2].clone() + self.matrix[(2, 2)].clone()); Point3::new( p[0].clone() * inverse_denom.clone() / self.matrix[(0, 0)].clone(), p[1].clone() * inverse_denom.clone() / self.matrix[(1, 1)].clone(), -inverse_denom, ) } // TODO: when we get specialization, specialize the Mul impl instead. /// Projects a vector. Faster than matrix multiplication. #[inline] #[must_use] pub fn project_vector(&self, p: &Vector) -> Vector3 where SB: Storage, { let inverse_denom = -T::one() / p[2].clone(); Vector3::new( self.matrix[(0, 0)].clone() * p[0].clone() * inverse_denom.clone(), self.matrix[(1, 1)].clone() * p[1].clone() * inverse_denom, self.matrix[(2, 2)].clone(), ) } /// Updates this perspective matrix with a new `width / height` aspect ratio of the view /// frustum. #[inline] pub fn set_aspect(&mut self, aspect: T) { assert!( !relative_eq!(aspect, T::zero()), "The aspect ratio must not be zero." ); self.matrix[(0, 0)] = self.matrix[(1, 1)].clone() / aspect; } /// Updates this perspective with a new y field of view of the view frustum. #[inline] pub fn set_fovy(&mut self, fovy: T) { let old_m22 = self.matrix[(1, 1)].clone(); let new_m22 = T::one() / (fovy / crate::convert(2.0)).tan(); self.matrix[(1, 1)] = new_m22.clone(); self.matrix[(0, 0)] *= new_m22 / old_m22; } /// Updates this perspective matrix with a new near plane offset of the view frustum. #[inline] pub fn set_znear(&mut self, znear: T) { let zfar = self.zfar(); self.set_znear_and_zfar(znear, zfar); } /// Updates this perspective matrix with a new far plane offset of the view frustum. #[inline] pub fn set_zfar(&mut self, zfar: T) { let znear = self.znear(); self.set_znear_and_zfar(znear, zfar); } /// Updates this perspective matrix with new near and far plane offsets of the view frustum. #[inline] pub fn set_znear_and_zfar(&mut self, znear: T, zfar: T) { self.matrix[(2, 2)] = (zfar.clone() + znear.clone()) / (znear.clone() - zfar.clone()); self.matrix[(2, 3)] = zfar.clone() * znear.clone() * crate::convert(2.0) / (znear - zfar); } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where Standard: Distribution, { /// Generate an arbitrary random variate for testing purposes. fn sample(&self, r: &mut R) -> Perspective3 { use crate::base::helper; let znear = r.gen(); let zfar = helper::reject_rand(r, |x: &T| !(x.clone() - znear.clone()).is_zero()); let aspect = helper::reject_rand(r, |x: &T| !x.is_zero()); Perspective3::new(aspect, r.gen(), znear, zfar) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Perspective3 { fn arbitrary(g: &mut Gen) -> Self { use crate::base::helper; let znear: T = Arbitrary::arbitrary(g); let zfar = helper::reject(g, |x: &T| !(x.clone() - znear.clone()).is_zero()); let aspect = helper::reject(g, |x: &T| !x.is_zero()); Self::new(aspect, Arbitrary::arbitrary(g), znear, zfar) } } impl From> for Matrix4 { #[inline] fn from(pers: Perspective3) -> Self { pers.into_inner() } } nalgebra-0.33.0/src/geometry/point.rs000064400000000000000000000357250072674642500156470ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use num::{One, Zero}; use std::cmp::Ordering; use std::fmt; use std::hash; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use simba::simd::SimdPartialOrd; use crate::base::allocator::Allocator; use crate::base::dimension::{DimName, DimNameAdd, DimNameSum, U1}; use crate::base::iter::{MatrixIter, MatrixIterMut}; use crate::base::{Const, DefaultAllocator, OVector, Scalar}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, ClosedSubAssign}; use std::mem::MaybeUninit; /// A point in an euclidean space. /// /// The difference between a point and a vector is only semantic. See [the user guide](https://www.nalgebra.org/docs/user_guide/points_and_transformations) /// for details on the distinction. The most notable difference that vectors ignore translations. /// In particular, an [`Isometry2`](crate::Isometry2) or [`Isometry3`](crate::Isometry3) will /// transform points by applying a rotation and a translation on them. However, these isometries /// will only apply rotations to vectors (when doing `isometry * vector`, the translation part of /// the isometry is ignored). /// /// # Construction /// * [From individual components `new`…](#construction-from-individual-components) /// * [Swizzling `xx`, `yxz`…](#swizzling) /// * [Other construction methods `origin`, `from_slice`, `from_homogeneous`…](#other-construction-methods) /// /// # Transformation /// Transforming a point by an [Isometry](crate::Isometry), [rotation](crate::Rotation), etc. can be /// achieved by multiplication, e.g., `isometry * point` or `rotation * point`. Some of these transformation /// may have some other methods, e.g., `isometry.inverse_transform_point(&point)`. See the documentation /// of said transformations for details. #[repr(C)] #[derive(Clone)] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "OPoint", bound(archive = " T: rkyv::Archive, T::Archived: Scalar, OVector: rkyv::Archive>, DefaultAllocator: Allocator, ") ) )] pub struct OPoint where DefaultAllocator: Allocator, { /// The coordinates of this point, i.e., the shift from the origin. pub coords: OVector, } impl fmt::Debug for OPoint where DefaultAllocator: Allocator, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.coords.as_slice().fmt(formatter) } } impl hash::Hash for OPoint where DefaultAllocator: Allocator, { fn hash(&self, state: &mut H) { self.coords.hash(state) } } impl Copy for OPoint where DefaultAllocator: Allocator, OVector: Copy, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for OPoint where OVector: bytemuck::Zeroable, DefaultAllocator: Allocator, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for OPoint where T: Copy, OVector: bytemuck::Pod, DefaultAllocator: Allocator, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for OPoint where DefaultAllocator: Allocator, >::Buffer: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.coords.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: Scalar, D: DimName> Deserialize<'a> for OPoint where DefaultAllocator: Allocator, >::Buffer: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let coords = OVector::::deserialize(deserializer)?; Ok(Self::from(coords)) } } impl OPoint where DefaultAllocator: Allocator, { /// Returns a point containing the result of `f` applied to each of its entries. /// /// # Example /// ``` /// # use nalgebra::{Point2, Point3}; /// let p = Point2::new(1.0, 2.0); /// assert_eq!(p.map(|e| e * 10.0), Point2::new(10.0, 20.0)); /// /// // This works in any dimension. /// let p = Point3::new(1.1, 2.1, 3.1); /// assert_eq!(p.map(|e| e as u32), Point3::new(1, 2, 3)); /// ``` #[inline] #[must_use] pub fn map T2>(&self, f: F) -> OPoint where DefaultAllocator: Allocator, { self.coords.map(f).into() } /// Replaces each component of `self` by the result of a closure `f` applied on it. /// /// # Example /// ``` /// # use nalgebra::{Point2, Point3}; /// let mut p = Point2::new(1.0, 2.0); /// p.apply(|e| *e = *e * 10.0); /// assert_eq!(p, Point2::new(10.0, 20.0)); /// /// // This works in any dimension. /// let mut p = Point3::new(1.0, 2.0, 3.0); /// p.apply(|e| *e = *e * 10.0); /// assert_eq!(p, Point3::new(10.0, 20.0, 30.0)); /// ``` #[inline] pub fn apply(&mut self, f: F) { self.coords.apply(f) } /// Converts this point into a vector in homogeneous coordinates, i.e., appends a `1` at the /// end of it. /// /// This is the same as `.into()`. /// /// # Example /// ``` /// # use nalgebra::{Point2, Point3, Vector3, Vector4}; /// let p = Point2::new(10.0, 20.0); /// assert_eq!(p.to_homogeneous(), Vector3::new(10.0, 20.0, 1.0)); /// /// // This works in any dimension. /// let p = Point3::new(10.0, 20.0, 30.0); /// assert_eq!(p.to_homogeneous(), Vector4::new(10.0, 20.0, 30.0, 1.0)); /// ``` #[inline] #[must_use] pub fn to_homogeneous(&self) -> OVector> where T: One, D: DimNameAdd, DefaultAllocator: Allocator>, { // TODO: this is mostly a copy-past from Vector::push. // But we can’t use Vector::push because of the DimAdd bound // (which we don’t use because we use DimNameAdd). // We should find a way to re-use Vector::push. let len = self.len(); let mut res = crate::Matrix::uninit(DimNameSum::::name(), Const::<1>); // This is basically a copy_from except that we warp the copied // values into MaybeUninit. res.generic_view_mut((0, 0), self.coords.shape_generic()) .zip_apply(&self.coords, |out, e| *out = MaybeUninit::new(e)); res[(len, 0)] = MaybeUninit::new(T::one()); // Safety: res has been fully initialized. unsafe { res.assume_init() } } /// Linear interpolation between two points. /// /// Returns `self * (1.0 - t) + rhs.coords * t`, i.e., the linear blend of the points /// `self` and `rhs` using the scalar value `t`. /// /// The value for a is not restricted to the range `[0, 1]`. /// /// # Examples: /// /// ``` /// # use nalgebra::Point3; /// let a = Point3::new(1.0, 2.0, 3.0); /// let b = Point3::new(10.0, 20.0, 30.0); /// assert_eq!(a.lerp(&b, 0.1), Point3::new(1.9, 3.8, 5.7)); /// ``` #[must_use] pub fn lerp(&self, rhs: &OPoint, t: T) -> OPoint where T: Scalar + Zero + One + ClosedAddAssign + ClosedSubAssign + ClosedMulAssign, { OPoint { coords: self.coords.lerp(&rhs.coords, t), } } /// Creates a new point with the given coordinates. #[deprecated(note = "Use Point::from(vector) instead.")] #[inline] pub fn from_coordinates(coords: OVector) -> Self { Self { coords } } /// The dimension of this point. /// /// # Example /// ``` /// # use nalgebra::{Point2, Point3}; /// let p = Point2::new(1.0, 2.0); /// assert_eq!(p.len(), 2); /// /// // This works in any dimension. /// let p = Point3::new(10.0, 20.0, 30.0); /// assert_eq!(p.len(), 3); /// ``` #[inline] #[must_use] pub fn len(&self) -> usize { self.coords.len() } /// Returns true if the point contains no elements. /// /// # Example /// ``` /// # use nalgebra::{Point2, Point3}; /// let p = Point2::new(1.0, 2.0); /// assert!(!p.is_empty()); /// ``` #[inline] #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// The stride of this point. This is the number of buffer element separating each component of /// this point. #[inline] #[deprecated(note = "This methods is no longer significant and will always return 1.")] pub fn stride(&self) -> usize { self.coords.strides().0 } /// Iterates through this point coordinates. /// /// # Example /// ``` /// # use nalgebra::Point3; /// let p = Point3::new(1.0, 2.0, 3.0); /// let mut it = p.iter().cloned(); /// /// assert_eq!(it.next(), Some(1.0)); /// assert_eq!(it.next(), Some(2.0)); /// assert_eq!(it.next(), Some(3.0)); /// assert_eq!(it.next(), None); /// ``` #[inline] pub fn iter( &self, ) -> MatrixIter<'_, T, D, Const<1>, >::Buffer> { self.coords.iter() } /// 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 { self.coords.vget_unchecked(i) } /// Mutably iterates through this point coordinates. /// /// # Example /// ``` /// # use nalgebra::Point3; /// let mut p = Point3::new(1.0, 2.0, 3.0); /// /// for e in p.iter_mut() { /// *e *= 10.0; /// } /// /// assert_eq!(p, Point3::new(10.0, 20.0, 30.0)); /// ``` #[inline] pub fn iter_mut( &mut self, ) -> MatrixIterMut<'_, T, D, Const<1>, >::Buffer> { self.coords.iter_mut() } /// 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 { self.coords.vget_unchecked_mut(i) } /// 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)) } } impl AbsDiffEq for OPoint where T::Epsilon: Clone, DefaultAllocator: Allocator, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.coords.abs_diff_eq(&other.coords, epsilon) } } impl RelativeEq for OPoint where T::Epsilon: Clone, DefaultAllocator: Allocator, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.coords .relative_eq(&other.coords, epsilon, max_relative) } } impl UlpsEq for OPoint where T::Epsilon: Clone, DefaultAllocator: Allocator, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.coords.ulps_eq(&other.coords, epsilon, max_ulps) } } impl Eq for OPoint where DefaultAllocator: Allocator {} impl PartialEq for OPoint where DefaultAllocator: Allocator, { #[inline] fn eq(&self, right: &Self) -> bool { self.coords == right.coords } } impl PartialOrd for OPoint where DefaultAllocator: Allocator, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.coords.partial_cmp(&other.coords) } #[inline] fn lt(&self, right: &Self) -> bool { self.coords.lt(&right.coords) } #[inline] fn le(&self, right: &Self) -> bool { self.coords.le(&right.coords) } #[inline] fn gt(&self, right: &Self) -> bool { self.coords.gt(&right.coords) } #[inline] fn ge(&self, right: &Self) -> bool { self.coords.ge(&right.coords) } } /* * inf/sup */ impl OPoint where DefaultAllocator: Allocator, { /// Computes the infimum (aka. componentwise min) of two points. #[inline] #[must_use] pub fn inf(&self, other: &Self) -> OPoint { self.coords.inf(&other.coords).into() } /// Computes the supremum (aka. componentwise max) of two points. #[inline] #[must_use] pub fn sup(&self, other: &Self) -> OPoint { self.coords.sup(&other.coords).into() } /// Computes the (infimum, supremum) of two points. #[inline] #[must_use] pub fn inf_sup(&self, other: &Self) -> (OPoint, OPoint) { let (inf, sup) = self.coords.inf_sup(&other.coords); (inf.into(), sup.into()) } } /* * * Display * */ impl fmt::Display for OPoint where DefaultAllocator: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{{")?; let mut it = self.coords.iter(); ::fmt(it.next().unwrap(), f)?; for comp in it { write!(f, ", ")?; ::fmt(comp, f)?; } write!(f, "}}") } } nalgebra-0.33.0/src/geometry/point_alias.rs000064400000000000000000000025430072674642500170100ustar 00000000000000use crate::geometry::OPoint; use crate::Const; /// A point with `D` elements. pub type Point = OPoint>; /// A statically sized 1-dimensional column point. /// /// **Because this is an alias, not all its methods are listed here. See the [`Point`](crate::Point) type too.** pub type Point1 = Point; /// A statically sized 2-dimensional column point. /// /// **Because this is an alias, not all its methods are listed here. See the [`Point`](crate::Point) type too.** pub type Point2 = Point; /// A statically sized 3-dimensional column point. /// /// **Because this is an alias, not all its methods are listed here. See the [`Point`](crate::Point) type too.** pub type Point3 = Point; /// A statically sized 4-dimensional column point. /// /// **Because this is an alias, not all its methods are listed here. See the [`Point`](crate::Point) type too.** pub type Point4 = Point; /// A statically sized 5-dimensional column point. /// /// **Because this is an alias, not all its methods are listed here. See the [`Point`](crate::Point) type too.** pub type Point5 = Point; /// A statically sized 6-dimensional column point. /// /// **Because this is an alias, not all its methods are listed here. See the [`Point`](crate::Point) type too.** pub type Point6 = Point; nalgebra-0.33.0/src/geometry/point_construction.rs000064400000000000000000000165110072674642500204510ustar 00000000000000#[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::{Bounded, One, Zero}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{DefaultAllocator, Scalar}; use crate::{ Const, DimName, OPoint, OVector, Point1, Point2, Point3, Point4, Point5, Point6, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6, }; use simba::scalar::{ClosedDivAssign, SupersetOf}; use crate::geometry::Point; impl Default for OPoint where DefaultAllocator: Allocator, { fn default() -> Self { Self::origin() } } /// # Other construction methods impl OPoint where DefaultAllocator: Allocator, { /// Creates a new point with all coordinates equal to zero. /// /// # Example /// /// ``` /// # use nalgebra::{Point2, Point3}; /// // This works in any dimension. /// // The explicit crate:: type annotation may not always be needed, /// // depending on the context of type inference. /// let pt = Point2::::origin(); /// assert!(pt.x == 0.0 && pt.y == 0.0); /// /// let pt = Point3::::origin(); /// assert!(pt.x == 0.0 && pt.y == 0.0 && pt.z == 0.0); /// ``` #[inline] pub fn origin() -> Self where T: Zero, { Self::from(OVector::from_element(T::zero())) } /// Creates a new point from a slice. /// /// # Example /// /// ``` /// # use nalgebra::{Point2, Point3}; /// let data = [ 1.0, 2.0, 3.0 ]; /// /// let pt = Point2::from_slice(&data[..2]); /// assert_eq!(pt, Point2::new(1.0, 2.0)); /// /// let pt = Point3::from_slice(&data); /// assert_eq!(pt, Point3::new(1.0, 2.0, 3.0)); /// ``` #[inline] pub fn from_slice(components: &[T]) -> Self { Self::from(OVector::from_row_slice(components)) } /// Creates a new point from its homogeneous vector representation. /// /// In practice, this builds a D-dimensional points with the same first D component as `v` /// divided by the last component of `v`. Returns `None` if this divisor is zero. /// /// # Example /// /// ``` /// # use nalgebra::{Point2, Point3, Vector3, Vector4}; /// /// let coords = Vector4::new(1.0, 2.0, 3.0, 1.0); /// let pt = Point3::from_homogeneous(coords); /// assert_eq!(pt, Some(Point3::new(1.0, 2.0, 3.0))); /// /// // All component of the result will be divided by the /// // last component of the vector, here 2.0. /// let coords = Vector4::new(1.0, 2.0, 3.0, 2.0); /// let pt = Point3::from_homogeneous(coords); /// assert_eq!(pt, Some(Point3::new(0.5, 1.0, 1.5))); /// /// // Fails because the last component is zero. /// let coords = Vector4::new(1.0, 2.0, 3.0, 0.0); /// let pt = Point3::from_homogeneous(coords); /// assert!(pt.is_none()); /// /// // Works also in other dimensions. /// let coords = Vector3::new(1.0, 2.0, 1.0); /// let pt = Point2::from_homogeneous(coords); /// assert_eq!(pt, Some(Point2::new(1.0, 2.0))); /// ``` #[inline] pub fn from_homogeneous(v: OVector>) -> Option where T: Scalar + Zero + One + ClosedDivAssign, D: DimNameAdd, DefaultAllocator: Allocator>, { if !v[D::dim()].is_zero() { let coords = v.generic_view((0, 0), (D::name(), Const::<1>)) / v[D::dim()].clone(); Some(Self::from(coords)) } else { None } } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Point2; /// let pt = Point2::new(1.0f64, 2.0); /// let pt2 = pt.cast::(); /// assert_eq!(pt2, Point2::new(1.0f32, 2.0)); /// ``` pub fn cast(self) -> OPoint where OPoint: SupersetOf, DefaultAllocator: Allocator, { crate::convert(self) } } /* * * Traits that build points. * */ impl Bounded for OPoint where DefaultAllocator: Allocator, { #[inline] fn max_value() -> Self { Self::from(OVector::max_value()) } #[inline] fn min_value() -> Self { Self::from(OVector::min_value()) } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where Standard: Distribution, DefaultAllocator: Allocator, { /// Generate a `Point` where each coordinate is an independent variate from `[0, 1)`. #[inline] fn sample<'a, G: Rng + ?Sized>(&self, rng: &mut G) -> OPoint { OPoint::from(rng.gen::>()) } } #[cfg(feature = "arbitrary")] impl Arbitrary for OPoint where >::Buffer: Send, DefaultAllocator: Allocator, { #[inline] fn arbitrary(g: &mut Gen) -> Self { Self::from(OVector::arbitrary(g)) } } /* * * Small points construction from components. * */ // NOTE: the impl for Point1 is not with the others so that we // can add a section with the impl block comment. /// # Construction from individual components impl Point1 { /// Initializes this point from its components. /// /// # Example /// /// ``` /// # use nalgebra::Point1; /// let p = Point1::new(1.0); /// assert_eq!(p.x, 1.0); /// ``` #[inline] pub const 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),*);* $(;)*) => {$( impl $Point { #[doc = "Initializes this point from its components."] #[doc = "# Example\n```"] #[doc = $doc] #[doc = "```"] #[inline] pub const fn new($($args: T),*) -> Self { Point { coords: $Vector::new($($args),*) } } } )*} ); componentwise_constructors_impl!( "# use nalgebra::Point2;\nlet p = Point2::new(1.0, 2.0);\nassert!(p.x == 1.0 && p.y == 2.0);"; Point2, Vector2, x:0, y:1; "# use nalgebra::Point3;\nlet p = Point3::new(1.0, 2.0, 3.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0);"; Point3, Vector3, x:0, y:1, z:2; "# use nalgebra::Point4;\nlet p = Point4::new(1.0, 2.0, 3.0, 4.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0 && p.w == 4.0);"; Point4, Vector4, x:0, y:1, z:2, w:3; "# use nalgebra::Point5;\nlet p = Point5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0 && p.w == 4.0 && p.a == 5.0);"; Point5, Vector5, x:0, y:1, z:2, w:3, a:4; "# use nalgebra::Point6;\nlet p = Point6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(p.x == 1.0 && p.y == 2.0 && p.z == 3.0 && p.w == 4.0 && p.a == 5.0 && p.b == 6.0);"; Point6, Vector6, x:0, y:1, z:2, w:3, a:4, b:5; ); nalgebra-0.33.0/src/geometry/point_conversion.rs000064400000000000000000000125570072674642500201120ustar 00000000000000use num::{One, Zero}; use simba::scalar::{ClosedDivAssign, SubsetOf, SupersetOf}; use simba::simd::PrimitiveSimdValue; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, Matrix, OVector, Scalar}; use crate::geometry::Point; use crate::{DimName, OPoint}; /* * This file provides the following conversions: * ============================================= * * Point -> Point * Point -> Vector (homogeneous) */ impl SubsetOf> for OPoint where T1: Scalar, T2: Scalar + SupersetOf, DefaultAllocator: Allocator, { #[inline] fn to_superset(&self) -> OPoint { OPoint::from(self.coords.to_superset()) } #[inline] fn is_in_subset(m: &OPoint) -> bool { // TODO: is there a way to reuse the `.is_in_subset` from the matrix implementation of // SubsetOf? m.iter().all(|e| e.is_in_subset()) } #[inline] fn from_superset_unchecked(m: &OPoint) -> Self { Self::from(Matrix::from_superset_unchecked(&m.coords)) } } impl SubsetOf>> for OPoint where D: DimNameAdd, T1: Scalar, T2: Scalar + Zero + One + ClosedDivAssign + SupersetOf, DefaultAllocator: Allocator + Allocator>, // + Allocator // + Allocator, { #[inline] fn to_superset(&self) -> OVector> { let p: OPoint = self.to_superset(); p.to_homogeneous() } #[inline] fn is_in_subset(v: &OVector>) -> bool { crate::is_convertible::<_, OVector>>(v) && !v[D::dim()].is_zero() } #[inline] fn from_superset_unchecked(v: &OVector>) -> Self { let coords = v.generic_view((0, 0), (D::name(), Const::<1>)) / v[D::dim()].clone(); Self { coords: crate::convert_unchecked(coords), } } } impl From> for OVector> where D: DimNameAdd, DefaultAllocator: Allocator> + Allocator, { #[inline] fn from(t: OPoint) -> Self { t.to_homogeneous() } } impl From<[T; D]> for Point { #[inline] fn from(coords: [T; D]) -> Self { Point { coords: coords.into(), } } } impl From> for [T; D] { #[inline] fn from(p: Point) -> Self { p.coords.into() } } impl From> for OPoint where DefaultAllocator: Allocator, { #[inline] fn from(coords: OVector) -> Self { OPoint { coords } } } impl From<[Point; 2]> for Point where T: From<[::Element; 2]>, T::Element: Scalar + Copy, >>::Buffer: Copy, { #[inline] fn from(arr: [Point; 2]) -> Self { Self::from(OVector::from([arr[0].coords, arr[1].coords])) } } impl From<[Point; 4]> for Point where T: From<[::Element; 4]>, T::Element: Scalar + Copy, >>::Buffer: Copy, { #[inline] fn from(arr: [Point; 4]) -> Self { Self::from(OVector::from([ arr[0].coords, arr[1].coords, arr[2].coords, arr[3].coords, ])) } } impl From<[Point; 8]> for Point where T: From<[::Element; 8]>, T::Element: Scalar + Copy, >>::Buffer: Copy, { #[inline] fn from(arr: [Point; 8]) -> Self { Self::from(OVector::from([ arr[0].coords, arr[1].coords, arr[2].coords, arr[3].coords, arr[4].coords, arr[5].coords, arr[6].coords, arr[7].coords, ])) } } impl From<[Point; 16]> for Point where T: From<[::Element; 16]>, T::Element: Scalar + Copy, >>::Buffer: Copy, { #[inline] fn from(arr: [Point; 16]) -> Self { Self::from(OVector::from([ arr[0].coords, arr[1].coords, arr[2].coords, arr[3].coords, arr[4].coords, arr[5].coords, arr[6].coords, arr[7].coords, arr[8].coords, arr[9].coords, arr[10].coords, arr[11].coords, arr[12].coords, arr[13].coords, arr[14].coords, arr[15].coords, ])) } } nalgebra-0.33.0/src/geometry/point_coordinates.rs000064400000000000000000000017250072674642500202320ustar 00000000000000use std::ops::{Deref, DerefMut}; use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; use crate::base::{Scalar, U1, U2, U3, U4, U5, U6}; use crate::geometry::OPoint; /* * * Give coordinates to Point{1 .. 6} * */ macro_rules! deref_impl( ($D: ty, $Target: ident $(, $comps: ident)*) => { impl Deref for OPoint { type Target = $Target; #[inline] fn deref(&self) -> &Self::Target { &*self.coords } } impl DerefMut for OPoint { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.coords } } } ); deref_impl!(U1, X, x); deref_impl!(U2, XY, x, y); deref_impl!(U3, XYZ, x, y, z); deref_impl!(U4, XYZW, x, y, z, w); deref_impl!(U5, XYZWA, x, y, z, w, a); deref_impl!(U6, XYZWAB, x, y, z, w, a, b); nalgebra-0.33.0/src/geometry/point_ops.rs000064400000000000000000000224300072674642500165150ustar 00000000000000use num::{One, Zero}; use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; use simba::scalar::{ ClosedAddAssign, ClosedDivAssign, ClosedMulAssign, ClosedNeg, ClosedSubAssign, }; use crate::base::constraint::{ AreMultipliable, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint, }; use crate::base::dimension::{Dim, DimName, U1}; use crate::base::storage::Storage; use crate::base::{Const, Matrix, OVector, Scalar, Vector}; use crate::allocator::Allocator; use crate::geometry::{OPoint, Point}; use crate::DefaultAllocator; /* * * Indexing. * */ impl Index for OPoint where DefaultAllocator: Allocator, { type Output = T; #[inline] fn index(&self, i: usize) -> &Self::Output { &self.coords[i] } } impl IndexMut for OPoint where DefaultAllocator: Allocator, { #[inline] fn index_mut(&mut self, i: usize) -> &mut Self::Output { &mut self.coords[i] } } /* * * Neg. * */ impl Neg for OPoint where DefaultAllocator: Allocator, { type Output = Self; #[inline] fn neg(self) -> Self::Output { Self::Output::from(-self.coords) } } impl<'a, T: Scalar + ClosedNeg, D: DimName> Neg for &'a OPoint where DefaultAllocator: Allocator, { type Output = OPoint; #[inline] fn neg(self) -> Self::Output { Self::Output::from(-&self.coords) } } /* * * Subtraction & Addition. * */ // Point - Point add_sub_impl!(Sub, sub, ClosedSubAssign; (D, U1), (D, U1) -> (D, U1) const; for D; where D: DimName, DefaultAllocator: Allocator; self: &'a OPoint, right: &'b OPoint, Output = OVector; &self.coords - &right.coords; 'a, 'b); add_sub_impl!(Sub, sub, ClosedSubAssign; (D, U1), (D, U1) -> (D, U1) const; for D; where D: DimName, DefaultAllocator: Allocator; self: &'a OPoint, right: OPoint, Output = OVector; &self.coords - right.coords; 'a); add_sub_impl!(Sub, sub, ClosedSubAssign; (D, U1), (D, U1) -> (D, U1) const; for D; where D: DimName, DefaultAllocator: Allocator; self: OPoint, right: &'b OPoint, Output = OVector; self.coords - &right.coords; 'b); add_sub_impl!(Sub, sub, ClosedSubAssign; (D, U1), (D, U1) -> (D, U1) const; for D; where D: DimName, DefaultAllocator: Allocator; self: OPoint, right: OPoint, Output = OVector; self.coords - right.coords; ); // Point - Vector add_sub_impl!(Sub, sub, ClosedSubAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: &'a OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(&self.coords - right); 'a, 'b); add_sub_impl!(Sub, sub, ClosedSubAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: &'a OPoint, right: Vector, Output = OPoint; Self::Output::from(&self.coords - &right); 'a); // TODO: should not be a ref to `right`. add_sub_impl!(Sub, sub, ClosedSubAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(self.coords - right); 'b); add_sub_impl!(Sub, sub, ClosedSubAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: OPoint, right: Vector, Output = OPoint; Self::Output::from(self.coords - right); ); // Point + Vector add_sub_impl!(Add, add, ClosedAddAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: &'a OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(&self.coords + right); 'a, 'b); add_sub_impl!(Add, add, ClosedAddAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: &'a OPoint, right: Vector, Output = OPoint; Self::Output::from(&self.coords + &right); 'a); // TODO: should not be a ref to `right`. add_sub_impl!(Add, add, ClosedAddAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: OPoint, right: &'b Vector, Output = OPoint; Self::Output::from(self.coords + right); 'b); add_sub_impl!(Add, add, ClosedAddAssign; (D1, U1), (D2, U1) -> (D1, U1) const; for D1, D2, SB; where D1: DimName, D2: Dim, SB: Storage, DefaultAllocator: Allocator; self: OPoint, right: Vector, Output = OPoint; Self::Output::from(self.coords + right); ); // TODO: replace by the shared macro: add_sub_assign_impl? macro_rules! op_assign_impl( ($($TraitAssign: ident, $method_assign: ident, $bound: ident);* $(;)*) => {$( impl<'b, T, D1: DimName, D2: Dim, SB> $TraitAssign<&'b Vector> for OPoint where T: Scalar + $bound, SB: Storage, ShapeConstraint: SameNumberOfRows, DefaultAllocator: Allocator { #[inline] fn $method_assign(&mut self, right: &'b Vector) { self.coords.$method_assign(right) } } impl $TraitAssign> for OPoint where T: Scalar + $bound, SB: Storage, ShapeConstraint: SameNumberOfRows, DefaultAllocator: Allocator { #[inline] fn $method_assign(&mut self, right: Vector) { self.coords.$method_assign(right) } } )*} ); op_assign_impl!( AddAssign, add_assign, ClosedAddAssign; SubAssign, sub_assign, ClosedSubAssign; ); /* * * Matrix × Point * */ md_impl_all!( Mul, mul; (Const, Const), (Const, U1) const D2, R1, C1; for SA; where SA: Storage, Const>, ShapeConstraint: AreMultipliable, Const, Const, U1>; self: Matrix, Const, SA>, right: Point, Output = Point; [val val] => Point::from(self * right.coords); [ref val] => Point::from(self * right.coords); [val ref] => Point::from(self * &right.coords); [ref ref] => Point::from(self * &right.coords); ); /* * * Point ×/÷ Scalar * */ macro_rules! componentwise_scalarop_impl( ($Trait: ident, $method: ident, $bound: ident; $TraitAssign: ident, $method_assign: ident) => { impl $Trait for OPoint where DefaultAllocator: Allocator { type Output = OPoint; #[inline] fn $method(self, right: T) -> Self::Output { OPoint::from(self.coords.$method(right)) } } impl<'a, T: Scalar + $bound, D: DimName> $Trait for &'a OPoint where DefaultAllocator: Allocator { type Output = OPoint; #[inline] fn $method(self, right: T) -> Self::Output { OPoint::from((&self.coords).$method(right)) } } impl $TraitAssign for OPoint where DefaultAllocator: Allocator { #[inline] fn $method_assign(&mut self, right: T) { self.coords.$method_assign(right) } } } ); componentwise_scalarop_impl!(Mul, mul, ClosedMulAssign; MulAssign, mul_assign); componentwise_scalarop_impl!(Div, div, ClosedDivAssign; DivAssign, div_assign); macro_rules! left_scalar_mul_impl( ($($T: ty),* $(,)*) => {$( impl Mul> for $T where DefaultAllocator: Allocator { type Output = OPoint<$T, D>; #[inline] fn mul(self, right: OPoint<$T, D>) -> Self::Output { OPoint::from(self * right.coords) } } impl<'b, D: DimName> Mul<&'b OPoint<$T, D>> for $T where DefaultAllocator: Allocator { type Output = OPoint<$T, D>; #[inline] fn mul(self, right: &'b OPoint<$T, D>) -> Self::Output { OPoint::from(self * &right.coords) } } )*} ); left_scalar_mul_impl!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, f32, f64); nalgebra-0.33.0/src/geometry/point_simba.rs000064400000000000000000000021340072674642500170060ustar 00000000000000use simba::simd::SimdValue; use crate::base::{OVector, Scalar}; use crate::geometry::Point; impl SimdValue for Point where T::Element: Scalar, { const LANES: usize = T::LANES; type Element = Point; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { OVector::splat(val.coords).into() } #[inline] fn extract(&self, i: usize) -> Self::Element { self.coords.extract(i).into() } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { self.coords.extract_unchecked(i).into() } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.coords.replace(i, val.coords) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.coords.replace_unchecked(i, val.coords) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { self.coords.select(cond, other.coords).into() } } nalgebra-0.33.0/src/geometry/quaternion.rs000064400000000000000000001552100072674642500166730ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use num::Zero; use std::fmt; use std::hash::{Hash, Hasher}; #[cfg(feature = "serde-serialize-no-std")] use crate::base::storage::Owned; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use simba::scalar::{ClosedNeg, RealField}; use simba::simd::{SimdBool, SimdOption, SimdRealField}; use crate::base::dimension::{U1, U3, U4}; use crate::base::storage::{CStride, RStride}; use crate::base::{ Matrix3, Matrix4, MatrixView, MatrixViewMut, Normed, Scalar, Unit, Vector3, Vector4, }; use crate::geometry::{Point3, Rotation}; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A quaternion. See the type alias `UnitQuaternion = Unit` for a quaternion /// that may be used as a rotation. #[repr(C)] #[derive(Copy, Clone)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Quaternion", bound(archive = " T: rkyv::Archive, Vector4: rkyv::Archive> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] pub struct Quaternion { /// This quaternion as a 4D vector of coordinates in the `[ x, y, z, w ]` storage order. pub coords: Vector4, } impl fmt::Debug for Quaternion { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.coords.as_slice().fmt(formatter) } } impl Hash for Quaternion { fn hash(&self, state: &mut H) { self.coords.hash(state) } } impl Eq for Quaternion {} impl PartialEq for Quaternion { #[inline] fn eq(&self, right: &Self) -> bool { self.coords == right.coords } } impl Default for Quaternion { fn default() -> Self { Quaternion { coords: Vector4::zeros(), } } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Quaternion where Vector4: bytemuck::Zeroable {} #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Quaternion where Vector4: bytemuck::Pod, T: Copy, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Quaternion where Owned: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.coords.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: Scalar> Deserialize<'a> for Quaternion where Owned: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let coords = Vector4::::deserialize(deserializer)?; Ok(Self::from(coords)) } } impl Quaternion where T::Element: SimdRealField, { /// Moves this unit quaternion into one that owns its data. #[inline] #[deprecated(note = "This method is a no-op and will be removed in a future release.")] pub fn into_owned(self) -> Self { self } /// Clones this unit quaternion into one that owns its data. #[inline] #[deprecated(note = "This method is a no-op and will be removed in a future release.")] pub fn clone_owned(&self) -> Self { Self::from(self.coords.clone_owned()) } /// Normalizes this quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let q_normalized = q.normalize(); /// assert_relative_eq!(q_normalized.norm(), 1.0); /// ``` #[inline] #[must_use = "Did you mean to use normalize_mut()?"] pub fn normalize(&self) -> Self { Self::from(self.coords.normalize()) } /// The imaginary part of this quaternion. #[inline] #[must_use] pub fn imag(&self) -> Vector3 { self.coords.xyz() } /// The conjugate of this quaternion. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let conj = q.conjugate(); /// assert!(conj.i == -2.0 && conj.j == -3.0 && conj.k == -4.0 && conj.w == 1.0); /// ``` #[inline] #[must_use = "Did you mean to use conjugate_mut()?"] pub fn conjugate(&self) -> Self { Self::from_parts(self.w.clone(), -self.imag()) } /// Linear interpolation between two quaternion. /// /// Computes `self * (1 - t) + other * t`. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q1 = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let q2 = Quaternion::new(10.0, 20.0, 30.0, 40.0); /// /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(1.9, 3.8, 5.7, 7.6)); /// ``` #[inline] #[must_use] pub fn lerp(&self, other: &Self, t: T) -> Self { self * (T::one() - t.clone()) + other * t } /// The vector part `(i, j, k)` of this quaternion. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_eq!(q.vector()[0], 2.0); /// assert_eq!(q.vector()[1], 3.0); /// assert_eq!(q.vector()[2], 4.0); /// ``` #[inline] #[must_use] pub fn vector(&self) -> MatrixView<'_, T, U3, U1, RStride, CStride> { self.coords.fixed_rows::<3>(0) } /// The scalar part `w` of this quaternion. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_eq!(q.scalar(), 1.0); /// ``` #[inline] #[must_use] pub fn scalar(&self) -> T { self.coords[3].clone() } /// Reinterprets this quaternion as a 4D vector. /// /// # Example /// ``` /// # use nalgebra::{Vector4, Quaternion}; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// // Recall that the quaternion is stored internally as (i, j, k, w) /// // while the crate::new constructor takes the arguments as (w, i, j, k). /// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0)); /// ``` #[inline] #[must_use] pub fn as_vector(&self) -> &Vector4 { &self.coords } /// The norm of this quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.norm(), 5.47722557, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn norm(&self) -> T { self.coords.norm() } /// A synonym for the norm of this quaternion. /// /// Aka the length. /// This is the same as `.norm()` /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.magnitude(), 5.47722557, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn magnitude(&self) -> T { self.norm() } /// The squared norm of this quaternion. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_eq!(q.norm_squared(), 30.0); /// ``` #[inline] #[must_use] pub fn norm_squared(&self) -> T { self.coords.norm_squared() } /// A synonym for the squared norm of this quaternion. /// /// Aka the squared length. /// This is the same as `.norm_squared()` /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_eq!(q.magnitude_squared(), 30.0); /// ``` #[inline] #[must_use] pub fn magnitude_squared(&self) -> T { self.norm_squared() } /// The dot product of two quaternions. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q1 = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let q2 = Quaternion::new(5.0, 6.0, 7.0, 8.0); /// assert_eq!(q1.dot(&q2), 70.0); /// ``` #[inline] #[must_use] pub fn dot(&self, rhs: &Self) -> T { self.coords.dot(&rhs.coords) } } impl Quaternion where T::Element: SimdRealField, { /// Inverts this quaternion if it is not zero. /// /// This method also does not works with SIMD components (see `simd_try_inverse` instead). /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let inv_q = q.try_inverse(); /// /// assert!(inv_q.is_some()); /// assert_relative_eq!(inv_q.unwrap() * q, Quaternion::identity()); /// /// //Non-invertible case /// let q = Quaternion::new(0.0, 0.0, 0.0, 0.0); /// let inv_q = q.try_inverse(); /// /// assert!(inv_q.is_none()); /// ``` #[inline] #[must_use = "Did you mean to use try_inverse_mut()?"] pub fn try_inverse(&self) -> Option where T: RealField, { let mut res = self.clone(); if res.try_inverse_mut() { Some(res) } else { None } } /// Attempt to inverse this quaternion. /// /// This method also works with SIMD components. #[inline] #[must_use = "Did you mean to use try_inverse_mut()?"] pub fn simd_try_inverse(&self) -> SimdOption { let norm_squared = self.norm_squared(); let ge = norm_squared.clone().simd_ge(T::simd_default_epsilon()); SimdOption::new(self.conjugate() / norm_squared, ge) } /// Calculates the inner product (also known as the dot product). /// See "Foundations of Game Engine Development, Volume 1: Mathematics" by Lengyel /// Formula 4.89. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let a = Quaternion::new(0.0, 2.0, 3.0, 4.0); /// let b = Quaternion::new(0.0, 5.0, 2.0, 1.0); /// let expected = Quaternion::new(-20.0, 0.0, 0.0, 0.0); /// let result = a.inner(&b); /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn inner(&self, other: &Self) -> Self { (self * other + other * self).half() } /// Calculates the outer product (also known as the wedge product). /// See "Foundations of Game Engine Development, Volume 1: Mathematics" by Lengyel /// Formula 4.89. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let a = Quaternion::new(0.0, 2.0, 3.0, 4.0); /// let b = Quaternion::new(0.0, 5.0, 2.0, 1.0); /// let expected = Quaternion::new(0.0, -5.0, 18.0, -11.0); /// let result = a.outer(&b); /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn outer(&self, other: &Self) -> Self { #[allow(clippy::eq_op)] (self * other - other * self).half() } /// Calculates the projection of `self` onto `other` (also known as the parallel). /// See "Foundations of Game Engine Development, Volume 1: Mathematics" by Lengyel /// Formula 4.94. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let a = Quaternion::new(0.0, 2.0, 3.0, 4.0); /// let b = Quaternion::new(0.0, 5.0, 2.0, 1.0); /// let expected = Quaternion::new(0.0, 3.333333333333333, 1.3333333333333333, 0.6666666666666666); /// let result = a.project(&b).unwrap(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn project(&self, other: &Self) -> Option where T: RealField, { self.inner(other).right_div(other) } /// Calculates the rejection of `self` from `other` (also known as the perpendicular). /// See "Foundations of Game Engine Development, Volume 1: Mathematics" by Lengyel /// Formula 4.94. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let a = Quaternion::new(0.0, 2.0, 3.0, 4.0); /// let b = Quaternion::new(0.0, 5.0, 2.0, 1.0); /// let expected = Quaternion::new(0.0, -1.3333333333333333, 1.6666666666666665, 3.3333333333333335); /// let result = a.reject(&b).unwrap(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn reject(&self, other: &Self) -> Option where T: RealField, { self.outer(other).right_div(other) } /// The polar decomposition of this quaternion. /// /// Returns, from left to right: the quaternion norm, the half rotation angle, the rotation /// axis. If the rotation angle is zero, the rotation axis is set to `None`. /// /// # Example /// ``` /// # use std::f32; /// # use nalgebra::{Vector3, Quaternion}; /// let q = Quaternion::new(0.0, 5.0, 0.0, 0.0); /// let (norm, half_ang, axis) = q.polar_decomposition(); /// assert_eq!(norm, 5.0); /// assert_eq!(half_ang, f32::consts::FRAC_PI_2); /// assert_eq!(axis, Some(Vector3::x_axis())); /// ``` #[must_use] pub fn polar_decomposition(&self) -> (T, T, Option>>) where T: RealField, { if let Some((q, n)) = Unit::try_new_and_get(self.clone(), T::zero()) { if let Some(axis) = Unit::try_new(self.vector().clone_owned(), T::zero()) { let angle = q.angle() / crate::convert(2.0f64); (n, angle, Some(axis)) } else { (n, T::zero(), None) } } else { (T::zero(), T::zero(), None) } } /// Compute the natural logarithm of a quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(2.0, 5.0, 0.0, 0.0); /// assert_relative_eq!(q.ln(), Quaternion::new(1.683647, 1.190289, 0.0, 0.0), epsilon = 1.0e-6) /// ``` #[inline] #[must_use] pub fn ln(&self) -> Self { let n = self.norm(); let v = self.vector(); let s = self.scalar(); Self::from_parts(n.clone().simd_ln(), v.normalize() * (s / n).simd_acos()) } /// Compute the exponential of a quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); /// assert_relative_eq!(q.exp(), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5) /// ``` #[inline] #[must_use] pub fn exp(&self) -> Self { self.exp_eps(T::simd_default_epsilon()) } /// Compute the exponential of a quaternion. Returns the identity if the vector part of this quaternion /// has a norm smaller than `eps`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.683647, 1.190289, 0.0, 0.0); /// assert_relative_eq!(q.exp_eps(1.0e-6), Quaternion::new(2.0, 5.0, 0.0, 0.0), epsilon = 1.0e-5); /// /// // Singular case. /// let q = Quaternion::new(0.0000001, 0.0, 0.0, 0.0); /// assert_eq!(q.exp_eps(1.0e-6), Quaternion::identity()); /// ``` #[inline] #[must_use] pub fn exp_eps(&self, eps: T) -> Self { let v = self.vector(); let nn = v.norm_squared(); let le = nn.clone().simd_le(eps.clone() * eps); le.if_else(Self::identity, || { let w_exp = self.scalar().simd_exp(); let n = nn.simd_sqrt(); let nv = v * (w_exp.clone() * n.clone().simd_sin() / n.clone()); Self::from_parts(w_exp * n.simd_cos(), nv) }) } /// Raise the quaternion to a given floating power. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert_relative_eq!(q.powf(1.5), Quaternion::new( -6.2576659, 4.1549037, 6.2323556, 8.3098075), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn powf(&self, n: T) -> Self { (self.ln() * n).exp() } /// Transforms this quaternion into its 4D vector form (Vector part, Scalar part). /// /// # Example /// ``` /// # use nalgebra::{Quaternion, Vector4}; /// let mut q = Quaternion::identity(); /// *q.as_vector_mut() = Vector4::new(1.0, 2.0, 3.0, 4.0); /// assert!(q.i == 1.0 && q.j == 2.0 && q.k == 3.0 && q.w == 4.0); /// ``` #[inline] pub fn as_vector_mut(&mut self) -> &mut Vector4 { &mut self.coords } /// The mutable vector part `(i, j, k)` of this quaternion. /// /// # Example /// ``` /// # use nalgebra::{Quaternion, Vector4}; /// let mut q = Quaternion::identity(); /// { /// let mut v = q.vector_mut(); /// v[0] = 2.0; /// v[1] = 3.0; /// v[2] = 4.0; /// } /// assert!(q.i == 2.0 && q.j == 3.0 && q.k == 4.0 && q.w == 1.0); /// ``` #[inline] pub fn vector_mut( &mut self, ) -> MatrixViewMut<'_, T, U3, U1, RStride, CStride> { self.coords.fixed_rows_mut::<3>(0) } /// Replaces this quaternion by its conjugate. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// q.conjugate_mut(); /// assert!(q.i == -2.0 && q.j == -3.0 && q.k == -4.0 && q.w == 1.0); /// ``` #[inline] pub fn conjugate_mut(&mut self) { self.coords[0] = -self.coords[0].clone(); self.coords[1] = -self.coords[1].clone(); self.coords[2] = -self.coords[2].clone(); } /// Inverts this quaternion in-place if it is not zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let mut q = Quaternion::new(1.0f32, 2.0, 3.0, 4.0); /// /// assert!(q.try_inverse_mut()); /// assert_relative_eq!(q * Quaternion::new(1.0, 2.0, 3.0, 4.0), Quaternion::identity()); /// /// //Non-invertible case /// let mut q = Quaternion::new(0.0f32, 0.0, 0.0, 0.0); /// assert!(!q.try_inverse_mut()); /// ``` #[inline] pub fn try_inverse_mut(&mut self) -> T::SimdBool { let norm_squared = self.norm_squared(); let ge = norm_squared.clone().simd_ge(T::simd_default_epsilon()); *self = ge.if_else(|| self.conjugate() / norm_squared, || self.clone()); ge } /// Normalizes this quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let mut q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// q.normalize_mut(); /// assert_relative_eq!(q.norm(), 1.0); /// ``` #[inline] pub fn normalize_mut(&mut self) -> T { self.coords.normalize_mut() } /// Calculates square of a quaternion. #[inline] #[must_use] pub fn squared(&self) -> Self { self * self } /// Divides quaternion into two. #[inline] #[must_use] pub fn half(&self) -> Self { self / crate::convert(2.0f64) } /// Calculates square root. #[inline] #[must_use] pub fn sqrt(&self) -> Self { self.powf(crate::convert(0.5)) } /// Check if the quaternion is pure. /// /// A quaternion is pure if it has no real part (`self.w == 0.0`). #[inline] #[must_use] pub fn is_pure(&self) -> bool { self.w.is_zero() } /// Convert quaternion to pure quaternion. #[inline] #[must_use] pub fn pure(&self) -> Self { Self::from_imag(self.imag()) } /// Left quaternionic division. /// /// Calculates B-1 * A where A = self, B = other. #[inline] #[must_use] pub fn left_div(&self, other: &Self) -> Option where T: RealField, { other.try_inverse().map(|inv| inv * self) } /// Right quaternionic division. /// /// Calculates A * B-1 where A = self, B = other. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let a = Quaternion::new(0.0, 1.0, 2.0, 3.0); /// let b = Quaternion::new(0.0, 5.0, 2.0, 1.0); /// let result = a.right_div(&b).unwrap(); /// let expected = Quaternion::new(0.4, 0.13333333333333336, -0.4666666666666667, 0.26666666666666666); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn right_div(&self, other: &Self) -> Option where T: RealField, { other.try_inverse().map(|inv| self * inv) } /// Calculates the quaternionic cosinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(58.93364616794395, -34.086183690465596, -51.1292755356984, -68.17236738093119); /// let result = input.cos(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn cos(&self) -> Self { let z = self.imag().magnitude(); let w = -self.w.clone().simd_sin() * z.clone().simd_sinhc(); Self::from_parts(self.w.clone().simd_cos() * z.simd_cosh(), self.imag() * w) } /// Calculates the quaternionic arccosinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let result = input.cos().acos(); /// assert_relative_eq!(input, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn acos(&self) -> Self { let u = Self::from_imag(self.imag().normalize()); let identity = Self::identity(); let z = (self + (self.squared() - identity).sqrt()).ln(); -(u * z) } /// Calculates the quaternionic sinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(91.78371578403467, 21.886486853029176, 32.82973027954377, 43.77297370605835); /// let result = input.sin(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn sin(&self) -> Self { let z = self.imag().magnitude(); let w = self.w.clone().simd_cos() * z.clone().simd_sinhc(); Self::from_parts(self.w.clone().simd_sin() * z.simd_cosh(), self.imag() * w) } /// Calculates the quaternionic arcsinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let result = input.sin().asin(); /// assert_relative_eq!(input, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn asin(&self) -> Self { let u = Self::from_imag(self.imag().normalize()); let identity = Self::identity(); let z = ((u.clone() * self) + (identity - self.squared()).sqrt()).ln(); -(u * z) } /// Calculates the quaternionic tangent. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(0.00003821631725009489, 0.3713971716439371, 0.5570957574659058, 0.7427943432878743); /// let result = input.tan(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn tan(&self) -> Self where T: RealField, { self.sin().right_div(&self.cos()).unwrap() } /// Calculates the quaternionic arctangent. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let result = input.tan().atan(); /// assert_relative_eq!(input, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn atan(&self) -> Self where T: RealField, { let u = Self::from_imag(self.imag().normalize()); let num = u.clone() + self; let den = u.clone() - self; let fr = num.right_div(&den).unwrap(); let ln = fr.ln(); (u.half()) * ln } /// Calculates the hyperbolic quaternionic sinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(0.7323376060463428, -0.4482074499805421, -0.6723111749708133, -0.8964148999610843); /// let result = input.sinh(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn sinh(&self) -> Self { (self.exp() - (-self).exp()).half() } /// Calculates the hyperbolic quaternionic arcsinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(2.385889902585242, 0.514052600662788, 0.7710789009941821, 1.028105201325576); /// let result = input.asinh(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn asinh(&self) -> Self { let identity = Self::identity(); (self + (identity + self.squared()).sqrt()).ln() } /// Calculates the hyperbolic quaternionic cosinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(0.9615851176369566, -0.3413521745610167, -0.5120282618415251, -0.6827043491220334); /// let result = input.cosh(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn cosh(&self) -> Self { (self.exp() + (-self).exp()).half() } /// Calculates the hyperbolic quaternionic arccosinus. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(2.4014472020074007, 0.5162761016176176, 0.7744141524264264, 1.0325522032352352); /// let result = input.acosh(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn acosh(&self) -> Self { let identity = Self::identity(); (self + (self + identity.clone()).sqrt() * (self - identity).sqrt()).ln() } /// Calculates the hyperbolic quaternionic tangent. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(1.0248695360556623, -0.10229568178876419, -0.1534435226831464, -0.20459136357752844); /// let result = input.tanh(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn tanh(&self) -> Self where T: RealField, { self.sinh().right_div(&self.cosh()).unwrap() } /// Calculates the hyperbolic quaternionic arctangent. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Quaternion; /// let input = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// let expected = Quaternion::new(0.03230293287000163, 0.5173453683196951, 0.7760180524795426, 1.0346907366393903); /// let result = input.atanh(); /// assert_relative_eq!(expected, result, epsilon = 1.0e-7); /// ``` #[inline] #[must_use] pub fn atanh(&self) -> Self { let identity = Self::identity(); ((identity.clone() + self).ln() - (identity - self).ln()).half() } } impl> AbsDiffEq for Quaternion { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.as_vector().abs_diff_eq(other.as_vector(), epsilon.clone()) || // Account for the double-covering of S², i.e. q = -q self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.abs_diff_eq(&-b.clone(), epsilon.clone())) } } impl> RelativeEq for Quaternion { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.as_vector().relative_eq(other.as_vector(), epsilon.clone(), max_relative.clone()) || // Account for the double-covering of S², i.e. q = -q self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.relative_eq(&-b.clone(), epsilon.clone(), max_relative.clone())) } } impl> UlpsEq for Quaternion { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.as_vector().ulps_eq(other.as_vector(), epsilon.clone(), max_ulps) || // Account for the double-covering of S², i.e. q = -q. self.as_vector().iter().zip(other.as_vector().iter()).all(|(a, b)| a.ulps_eq(&-b.clone(), epsilon.clone(), max_ulps)) } } impl fmt::Display for Quaternion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Quaternion {} − ({}, {}, {})", self[3], self[0], self[1], self[2] ) } } /// A unit quaternions. May be used to represent a rotation. pub type UnitQuaternion = Unit>; impl PartialEq for UnitQuaternion { #[inline] fn eq(&self, rhs: &Self) -> bool { self.coords == rhs.coords || // Account for the double-covering of S², i.e. q = -q self.coords.iter().zip(rhs.coords.iter()).all(|(a, b)| *a == -b.clone()) } } impl Eq for UnitQuaternion {} impl Normed for Quaternion { type Norm = T::SimdRealField; #[inline] fn norm(&self) -> T::SimdRealField { self.coords.norm() } #[inline] fn norm_squared(&self) -> T::SimdRealField { self.coords.norm_squared() } #[inline] fn scale_mut(&mut self, n: Self::Norm) { self.coords.scale_mut(n) } #[inline] fn unscale_mut(&mut self, n: Self::Norm) { self.coords.unscale_mut(n) } } impl UnitQuaternion where T::Element: SimdRealField, { /// The rotation angle in [0; pi] of this unit quaternion. /// /// # Example /// ``` /// # use nalgebra::{Unit, UnitQuaternion, Vector3}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let rot = UnitQuaternion::from_axis_angle(&axis, 1.78); /// assert_eq!(rot.angle(), 1.78); /// ``` #[inline] #[must_use] pub fn angle(&self) -> T { let w = self.quaternion().scalar().simd_abs(); self.quaternion().imag().norm().simd_atan2(w) * crate::convert(2.0f64) } /// The underlying quaternion. /// /// Same as `self.as_ref()`. /// /// # Example /// ``` /// # use nalgebra::{UnitQuaternion, Quaternion}; /// let axis = UnitQuaternion::identity(); /// assert_eq!(*axis.quaternion(), Quaternion::new(1.0, 0.0, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn quaternion(&self) -> &Quaternion { self.as_ref() } /// Compute the conjugate of this unit quaternion. /// /// # Example /// ``` /// # use nalgebra::{Unit, UnitQuaternion, Vector3}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let rot = UnitQuaternion::from_axis_angle(&axis, 1.78); /// let conj = rot.conjugate(); /// assert_eq!(conj, UnitQuaternion::from_axis_angle(&-axis, 1.78)); /// ``` #[inline] #[must_use = "Did you mean to use conjugate_mut()?"] pub fn conjugate(&self) -> Self { Self::new_unchecked(self.as_ref().conjugate()) } /// Inverts this quaternion if it is not zero. /// /// # Example /// ``` /// # use nalgebra::{Unit, UnitQuaternion, Vector3}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let rot = UnitQuaternion::from_axis_angle(&axis, 1.78); /// let inv = rot.inverse(); /// assert_eq!(rot * inv, UnitQuaternion::identity()); /// assert_eq!(inv * rot, UnitQuaternion::identity()); /// ``` #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(&self) -> Self { self.conjugate() } /// The rotation angle needed to make `self` and `other` coincide. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn angle_to(&self, other: &Self) -> T { let delta = self.rotation_to(other); delta.angle() } /// The unit quaternion needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot1 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.1); /// let rot_to = rot1.rotation_to(&rot2); /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other / self } /// Linear interpolation between two unit quaternions. /// /// The result is not normalized. /// /// # Example /// ``` /// # use nalgebra::{UnitQuaternion, Quaternion}; /// let q1 = UnitQuaternion::new_normalize(Quaternion::new(1.0, 0.0, 0.0, 0.0)); /// let q2 = UnitQuaternion::new_normalize(Quaternion::new(0.0, 1.0, 0.0, 0.0)); /// assert_eq!(q1.lerp(&q2, 0.1), Quaternion::new(0.9, 0.1, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn lerp(&self, other: &Self, t: T) -> Quaternion { self.as_ref().lerp(other.as_ref(), t) } /// Normalized linear interpolation between two unit quaternions. /// /// This is the same as `self.lerp` except that the result is normalized. /// /// # Example /// ``` /// # use nalgebra::{UnitQuaternion, Quaternion}; /// let q1 = UnitQuaternion::new_normalize(Quaternion::new(1.0, 0.0, 0.0, 0.0)); /// let q2 = UnitQuaternion::new_normalize(Quaternion::new(0.0, 1.0, 0.0, 0.0)); /// assert_eq!(q1.nlerp(&q2, 0.1), UnitQuaternion::new_normalize(Quaternion::new(0.9, 0.1, 0.0, 0.0))); /// ``` #[inline] #[must_use] pub fn nlerp(&self, other: &Self, t: T) -> Self { let mut res = self.lerp(other, t); let _ = res.normalize_mut(); Self::new_unchecked(res) } /// Spherical linear interpolation between two unit quaternions. /// /// Panics if the angle between both quaternion is 180 degrees (in which case the interpolation /// is not well-defined). Use `.try_slerp` instead to avoid the panic. /// /// # Example /// ``` /// # use nalgebra::geometry::UnitQuaternion; /// /// let q1 = UnitQuaternion::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0); /// let q2 = UnitQuaternion::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0); /// /// let q = q1.slerp(&q2, 1.0 / 3.0); /// /// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self where T: RealField, { self.try_slerp(other, t, T::default_epsilon()) .expect("Quaternion slerp: ambiguous configuration.") } /// Computes the spherical linear interpolation between two unit quaternions or returns `None` /// if both quaternions are approximately 180 degrees apart (in which case the interpolation is /// not well-defined). /// /// # Arguments /// * `self`: the first quaternion to interpolate from. /// * `other`: the second quaternion to interpolate toward. /// * `t`: the interpolation parameter. Should be between 0 and 1. /// * `epsilon`: the value below which the sinus of the angle separating both quaternion /// must be to return `None`. #[inline] #[must_use] pub fn try_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, { let coords = if self.coords.dot(&other.coords) < T::zero() { Unit::new_unchecked(self.coords.clone()).try_slerp( &Unit::new_unchecked(-other.coords.clone()), t, epsilon, ) } else { Unit::new_unchecked(self.coords.clone()).try_slerp( &Unit::new_unchecked(other.coords.clone()), t, epsilon, ) }; coords.map(|q| Unit::new_unchecked(Quaternion::from(q.into_inner()))) } /// Compute the conjugate of this unit quaternion in-place. #[inline] pub fn conjugate_mut(&mut self) { self.as_mut_unchecked().conjugate_mut() } /// Inverts this quaternion if it is not zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let mut rot = UnitQuaternion::new(axisangle); /// rot.inverse_mut(); /// assert_relative_eq!(rot * UnitQuaternion::new(axisangle), UnitQuaternion::identity()); /// assert_relative_eq!(UnitQuaternion::new(axisangle) * rot, UnitQuaternion::identity()); /// ``` #[inline] pub fn inverse_mut(&mut self) { self.as_mut_unchecked().conjugate_mut() } /// The rotation axis of this unit quaternion or `None` if the rotation is zero. /// /// # Example /// ``` /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; /// let rot = UnitQuaternion::from_axis_angle(&axis, angle); /// assert_eq!(rot.axis(), Some(axis)); /// /// // Case with a zero angle. /// let rot = UnitQuaternion::from_axis_angle(&axis, 0.0); /// assert!(rot.axis().is_none()); /// ``` #[inline] #[must_use] pub fn axis(&self) -> Option>> where T: RealField, { let v = if self.quaternion().scalar() >= T::zero() { self.as_ref().vector().clone_owned() } else { -self.as_ref().vector() }; Unit::try_new(v, T::zero()) } /// The rotation axis of this unit quaternion multiplied by the rotation angle. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let rot = UnitQuaternion::new(axisangle); /// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn scaled_axis(&self) -> Vector3 where T: RealField, { if let Some(axis) = self.axis() { axis.into_inner() * self.angle() } else { Vector3::zero() } } /// The rotation axis and angle in (0, pi] of this unit quaternion. /// /// Returns `None` if the angle is zero. /// /// # Example /// ``` /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; /// let rot = UnitQuaternion::from_axis_angle(&axis, angle); /// assert_eq!(rot.axis_angle(), Some((axis, angle))); /// /// // Case with a zero angle. /// let rot = UnitQuaternion::from_axis_angle(&axis, 0.0); /// assert!(rot.axis_angle().is_none()); /// ``` #[inline] #[must_use] pub fn axis_angle(&self) -> Option<(Unit>, T)> where T: RealField, { self.axis().map(|axis| (axis, self.angle())) } /// Compute the exponential of a quaternion. /// /// Note that this function yields a `Quaternion` because it loses the unit property. #[inline] #[must_use] pub fn exp(&self) -> Quaternion { self.as_ref().exp() } /// Compute the natural logarithm of a quaternion. /// /// Note that this function yields a `Quaternion` because it loses the unit property. /// The vector part of the return value corresponds to the axis-angle representation (divided /// by 2.0) of this unit quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let q = UnitQuaternion::new(axisangle); /// assert_relative_eq!(q.ln().vector().into_owned(), axisangle, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn ln(&self) -> Quaternion where T: RealField, { if let Some(v) = self.axis() { Quaternion::from_imag(v.into_inner() * self.angle()) } else { Quaternion::zero() } } /// Raise the quaternion to a given floating power. /// /// This returns the unit quaternion that identifies a rotation with axis `self.axis()` and /// angle `self.angle() × n`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitQuaternion, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; /// let rot = UnitQuaternion::from_axis_angle(&axis, angle); /// let pow = rot.powf(2.0); /// assert_relative_eq!(pow.axis().unwrap(), axis, epsilon = 1.0e-6); /// assert_eq!(pow.angle(), 2.4); /// ``` #[inline] #[must_use] pub fn powf(&self, n: T) -> Self where T: RealField, { if let Some(v) = self.axis() { Self::from_axis_angle(&v, self.angle() * n) } else { Self::identity() } } /// Builds a rotation matrix from this unit quaternion. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Matrix3}; /// let q = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); /// let rot = q.to_rotation_matrix(); /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, /// 0.5, 0.8660254, 0.0, /// 0.0, 0.0, 1.0); /// /// assert_relative_eq!(*rot.matrix(), expected, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn to_rotation_matrix(self) -> Rotation { let i = self.as_ref()[0].clone(); let j = self.as_ref()[1].clone(); let k = self.as_ref()[2].clone(); let w = self.as_ref()[3].clone(); let ww = w.clone() * w.clone(); let ii = i.clone() * i.clone(); let jj = j.clone() * j.clone(); let kk = k.clone() * k.clone(); let ij = i.clone() * j.clone() * crate::convert(2.0f64); let wk = w.clone() * k.clone() * crate::convert(2.0f64); let wj = w.clone() * j.clone() * crate::convert(2.0f64); let ik = i.clone() * k.clone() * crate::convert(2.0f64); let jk = j * k * crate::convert(2.0f64); let wi = w * i * crate::convert(2.0f64); Rotation::from_matrix_unchecked(Matrix3::new( ww.clone() + ii.clone() - jj.clone() - kk.clone(), ij.clone() - wk.clone(), wj.clone() + ik.clone(), wk + ij, ww.clone() - ii.clone() + jj.clone() - kk.clone(), jk.clone() - wi.clone(), ik - wj, wi + jk, ww - ii - jj + kk, )) } /// Converts this unit quaternion into its equivalent Euler angles. /// /// The angles are produced in the form (roll, pitch, yaw). #[inline] #[deprecated(note = "This is renamed to use `.euler_angles()`.")] pub fn to_euler_angles(self) -> (T, T, T) where T: RealField, { self.euler_angles() } /// Retrieves the euler angles corresponding to this unit quaternion. /// /// The angles are produced in the form (roll, pitch, yaw). /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitQuaternion; /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn euler_angles(&self) -> (T, T, T) where T: RealField, { self.clone().to_rotation_matrix().euler_angles() } /// Converts this unit quaternion into its equivalent homogeneous transformation matrix. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Matrix4}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); /// let expected = Matrix4::new(0.8660254, -0.5, 0.0, 0.0, /// 0.5, 0.8660254, 0.0, 0.0, /// 0.0, 0.0, 1.0, 0.0, /// 0.0, 0.0, 0.0, 1.0); /// /// assert_relative_eq!(rot.to_homogeneous(), expected, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn to_homogeneous(self) -> Matrix4 { self.to_rotation_matrix().to_homogeneous() } /// Rotate a point by this unit quaternion. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Point3}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), f32::consts::FRAC_PI_2); /// let transformed_point = rot.transform_point(&Point3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point3) -> Point3 { self * pt } /// Rotate a vector by this unit quaternion. /// /// This is the same as the multiplication `self * v`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), f32::consts::FRAC_PI_2); /// let transformed_vector = rot.transform_vector(&Vector3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_vector, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_vector(&self, v: &Vector3) -> Vector3 { self * v } /// Rotate a point by the inverse of this unit quaternion. This may be /// cheaper than inverting the unit quaternion and transforming the /// point. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3, Point3}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), f32::consts::FRAC_PI_2); /// let transformed_point = rot.inverse_transform_point(&Point3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_point, Point3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point3) -> Point3 { // TODO: would it be useful performance-wise not to call inverse explicitly (i-e. implement // the inverse transformation explicitly here) ? self.inverse() * pt } /// Rotate a vector by the inverse of this unit quaternion. This may be /// cheaper than inverting the unit quaternion and transforming the /// vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), f32::consts::FRAC_PI_2); /// let transformed_vector = rot.inverse_transform_vector(&Vector3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 { self.inverse() * v } /// Rotate a vector by the inverse of this unit quaternion. This may be /// cheaper than inverting the unit quaternion and transforming the /// vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let rot = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_2); /// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis()); /// /// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.inverse() * v } /// Appends to `self` a rotation given in the axis-angle form, using a linearized formulation. /// /// This is faster, but approximate, way to compute `UnitQuaternion::new(axisangle) * self`. #[inline] #[must_use] pub fn append_axisangle_linearized(&self, axisangle: &Vector3) -> Self { let half: T = crate::convert(0.5); let q1 = self.clone().into_inner(); let q2 = Quaternion::from_imag(axisangle * half); Unit::new_normalize(&q1 + q2 * &q1) } } impl Default for UnitQuaternion { fn default() -> Self { Self::identity() } } impl fmt::Display for UnitQuaternion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(axis) = self.axis() { let axis = axis.into_inner(); write!( f, "UnitQuaternion angle: {} − axis: ({}, {}, {})", self.angle(), axis[0], axis[1], axis[2] ) } else { write!( f, "UnitQuaternion angle: {} − axis: (undefined)", self.angle() ) } } } impl> AbsDiffEq for UnitQuaternion { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.as_ref().abs_diff_eq(other.as_ref(), epsilon) } } impl> RelativeEq for UnitQuaternion { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.as_ref() .relative_eq(other.as_ref(), epsilon, max_relative) } } impl> UlpsEq for UnitQuaternion { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) } } nalgebra-0.33.0/src/geometry/quaternion_construction.rs000064400000000000000000001022200072674642500214760ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::dimension::U4; #[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{uniform::SampleUniform, Distribution, OpenClosed01, Standard, Uniform}, Rng, }; use num::{One, Zero}; use simba::scalar::{RealField, SupersetOf}; use simba::simd::SimdBool; use crate::base::dimension::U3; use crate::base::storage::Storage; use crate::base::{Matrix3, Matrix4, Unit, Vector, Vector3, Vector4}; use crate::{Scalar, SimdRealField}; use crate::geometry::{Quaternion, Rotation3, UnitQuaternion}; impl Quaternion { /// Creates a quaternion from a 4D vector. The quaternion scalar part corresponds to the `w` /// vector component. #[inline] // #[deprecated(note = "Use `::from` instead.")] // Don't deprecate because this one can be a const-fn. pub const fn from_vector(vector: Vector4) -> Self { Self { coords: vector } } /// Creates a new quaternion from its individual components. Note that the arguments order does /// **not** follow the storage order. /// /// The storage order is `[ i, j, k, w ]` while the arguments for this functions are in the /// order `(w, i, j, k)`. /// /// # Example /// ``` /// # use nalgebra::{Quaternion, Vector4}; /// let q = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// assert!(q.i == 2.0 && q.j == 3.0 && q.k == 4.0 && q.w == 1.0); /// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0)); /// ``` #[inline] pub const fn new(w: T, i: T, j: T, k: T) -> Self { Self::from_vector(Vector4::new(i, j, k, w)) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q = Quaternion::new(1.0f64, 2.0, 3.0, 4.0); /// let q2 = q.cast::(); /// assert_eq!(q2, Quaternion::new(1.0f32, 2.0, 3.0, 4.0)); /// ``` pub fn cast(self) -> Quaternion where T: Scalar, To: SupersetOf, { crate::convert(self) } } impl Quaternion { /// Constructs a pure quaternion. #[inline] pub fn from_imag(vector: Vector3) -> Self { Self::from_parts(T::zero(), vector) } /// Creates a new quaternion from its scalar and vector parts. Note that the arguments order does /// **not** follow the storage order. /// /// The storage order is [ vector, scalar ]. /// /// # Example /// ``` /// # use nalgebra::{Quaternion, Vector3, Vector4}; /// let w = 1.0; /// let ijk = Vector3::new(2.0, 3.0, 4.0); /// let q = Quaternion::from_parts(w, ijk); /// assert!(q.i == 2.0 && q.j == 3.0 && q.k == 4.0 && q.w == 1.0); /// assert_eq!(*q.as_vector(), Vector4::new(2.0, 3.0, 4.0, 1.0)); /// ``` #[inline] // TODO: take a reference to `vector`? pub fn from_parts(scalar: T, vector: Vector) -> Self where SB: Storage, { Self::new( scalar, vector[0].clone(), vector[1].clone(), vector[2].clone(), ) } /// Constructs a real quaternion. #[inline] pub fn from_real(r: T) -> Self { Self::from_parts(r, Vector3::zero()) } /// The quaternion multiplicative identity. /// /// # Example /// ``` /// # use nalgebra::Quaternion; /// let q = Quaternion::identity(); /// let q2 = Quaternion::new(1.0, 2.0, 3.0, 4.0); /// /// assert_eq!(q * q2, q2); /// assert_eq!(q2 * q, q2); /// ``` #[inline] pub fn identity() -> Self { Self::from_real(T::one()) } } // TODO: merge with the previous block. impl Quaternion where T::Element: SimdRealField, { /// Creates a new quaternion from its polar decomposition. /// /// Note that `axis` is assumed to be a unit vector. // TODO: take a reference to `axis`? pub fn from_polar_decomposition(scale: T, theta: T, axis: Unit>) -> Self where SB: Storage, { let rot = UnitQuaternion::::from_axis_angle(&axis, theta * crate::convert(2.0f64)); rot.into_inner() * scale } } impl One for Quaternion where T::Element: SimdRealField, { #[inline] fn one() -> Self { Self::identity() } } impl Zero for Quaternion where T::Element: SimdRealField, { #[inline] fn zero() -> Self { Self::from(Vector4::zero()) } #[inline] fn is_zero(&self) -> bool { self.coords.is_zero() } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where Standard: Distribution, { #[inline] fn sample(&self, rng: &mut R) -> Quaternion { Quaternion::new(rng.gen(), rng.gen(), rng.gen(), rng.gen()) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Quaternion where Owned: Send, { #[inline] fn arbitrary(g: &mut Gen) -> Self { Self::new( T::arbitrary(g), T::arbitrary(g), T::arbitrary(g), T::arbitrary(g), ) } } impl UnitQuaternion where T::Element: SimdRealField, { /// The rotation identity. /// /// # Example /// ``` /// # use nalgebra::{UnitQuaternion, Vector3, Point3}; /// let q = UnitQuaternion::identity(); /// let q2 = UnitQuaternion::new(Vector3::new(1.0, 2.0, 3.0)); /// let v = Vector3::new_random(); /// let p = Point3::from(v); /// /// assert_eq!(q * q2, q2); /// assert_eq!(q2 * q, q2); /// assert_eq!(q * v, v); /// assert_eq!(q * p, p); /// ``` #[inline] pub fn identity() -> Self { Self::new_unchecked(Quaternion::identity()) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::UnitQuaternion; /// # use approx::assert_relative_eq; /// let q = UnitQuaternion::from_euler_angles(1.0f64, 2.0, 3.0); /// let q2 = q.cast::(); /// assert_relative_eq!(q2, UnitQuaternion::from_euler_angles(1.0f32, 2.0, 3.0), epsilon = 1.0e-6); /// ``` pub fn cast(self) -> UnitQuaternion where To: SupersetOf, { crate::convert(self) } /// Creates a new quaternion from a unit vector (the rotation axis) and an angle /// (the rotation angle). /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axis = Vector3::y_axis(); /// let angle = f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let q = UnitQuaternion::from_axis_angle(&axis, angle); /// /// assert_eq!(q.axis().unwrap(), axis); /// assert_eq!(q.angle(), angle); /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // A zero vector yields an identity. /// assert_eq!(UnitQuaternion::from_scaled_axis(Vector3::::zeros()), UnitQuaternion::identity()); /// ``` #[inline] pub fn from_axis_angle(axis: &Unit>, angle: T) -> Self where SB: Storage, { let (sang, cang) = (angle / crate::convert(2.0f64)).simd_sin_cos(); let q = Quaternion::from_parts(cang, axis.as_ref() * sang); Self::new_unchecked(q) } /// Creates a new unit quaternion from a quaternion. /// /// The input quaternion will be normalized. #[inline] pub fn from_quaternion(q: Quaternion) -> Self { Self::new_normalize(q) } /// Creates a new unit quaternion from Euler angles. /// /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitQuaternion; /// let rot = UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); /// ``` #[inline] pub fn from_euler_angles(roll: T, pitch: T, yaw: T) -> Self { let (sr, cr) = (roll * crate::convert(0.5f64)).simd_sin_cos(); let (sp, cp) = (pitch * crate::convert(0.5f64)).simd_sin_cos(); let (sy, cy) = (yaw * crate::convert(0.5f64)).simd_sin_cos(); let q = Quaternion::new( cr.clone() * cp.clone() * cy.clone() + sr.clone() * sp.clone() * sy.clone(), sr.clone() * cp.clone() * cy.clone() - cr.clone() * sp.clone() * sy.clone(), cr.clone() * sp.clone() * cy.clone() + sr.clone() * cp.clone() * sy.clone(), cr * cp * sy - sr * sp * cy, ); Self::new_unchecked(q) } /// Builds an unit quaternion from a basis assumed to be orthonormal. /// /// In order to get a valid unit-quaternion, the input must be an /// orthonormal basis, i.e., all vectors are normalized, and the are /// all orthogonal to each other. These invariants are not checked /// by this method. pub fn from_basis_unchecked(basis: &[Vector3; 3]) -> Self { let rot = Rotation3::from_basis_unchecked(basis); Self::from_rotation_matrix(&rot) } /// Builds an unit quaternion from a rotation matrix. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation3, UnitQuaternion, Vector3}; /// let axis = Vector3::y_axis(); /// let angle = 0.1; /// let rot = Rotation3::from_axis_angle(&axis, angle); /// let q = UnitQuaternion::from_rotation_matrix(&rot); /// assert_relative_eq!(q.to_rotation_matrix(), rot, epsilon = 1.0e-6); /// assert_relative_eq!(q.axis().unwrap(), rot.axis().unwrap(), epsilon = 1.0e-6); /// assert_relative_eq!(q.angle(), rot.angle(), epsilon = 1.0e-6); /// ``` #[inline] pub fn from_rotation_matrix(rotmat: &Rotation3) -> Self { // Robust matrix to quaternion transformation. // See https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion let tr = rotmat[(0, 0)].clone() + rotmat[(1, 1)].clone() + rotmat[(2, 2)].clone(); let quarter: T = crate::convert(0.25); let res = tr.clone().simd_gt(T::zero()).if_else3( || { let denom = (tr.clone() + T::one()).simd_sqrt() * crate::convert(2.0); Quaternion::new( quarter.clone() * denom.clone(), (rotmat[(2, 1)].clone() - rotmat[(1, 2)].clone()) / denom.clone(), (rotmat[(0, 2)].clone() - rotmat[(2, 0)].clone()) / denom.clone(), (rotmat[(1, 0)].clone() - rotmat[(0, 1)].clone()) / denom, ) }, ( || { rotmat[(0, 0)].clone().simd_gt(rotmat[(1, 1)].clone()) & rotmat[(0, 0)].clone().simd_gt(rotmat[(2, 2)].clone()) }, || { let denom = (T::one() + rotmat[(0, 0)].clone() - rotmat[(1, 1)].clone() - rotmat[(2, 2)].clone()) .simd_sqrt() * crate::convert(2.0); Quaternion::new( (rotmat[(2, 1)].clone() - rotmat[(1, 2)].clone()) / denom.clone(), quarter.clone() * denom.clone(), (rotmat[(0, 1)].clone() + rotmat[(1, 0)].clone()) / denom.clone(), (rotmat[(0, 2)].clone() + rotmat[(2, 0)].clone()) / denom, ) }, ), ( || rotmat[(1, 1)].clone().simd_gt(rotmat[(2, 2)].clone()), || { let denom = (T::one() + rotmat[(1, 1)].clone() - rotmat[(0, 0)].clone() - rotmat[(2, 2)].clone()) .simd_sqrt() * crate::convert(2.0); Quaternion::new( (rotmat[(0, 2)].clone() - rotmat[(2, 0)].clone()) / denom.clone(), (rotmat[(0, 1)].clone() + rotmat[(1, 0)].clone()) / denom.clone(), quarter.clone() * denom.clone(), (rotmat[(1, 2)].clone() + rotmat[(2, 1)].clone()) / denom, ) }, ), || { let denom = (T::one() + rotmat[(2, 2)].clone() - rotmat[(0, 0)].clone() - rotmat[(1, 1)].clone()) .simd_sqrt() * crate::convert(2.0); Quaternion::new( (rotmat[(1, 0)].clone() - rotmat[(0, 1)].clone()) / denom.clone(), (rotmat[(0, 2)].clone() + rotmat[(2, 0)].clone()) / denom.clone(), (rotmat[(1, 2)].clone() + rotmat[(2, 1)].clone()) / denom.clone(), quarter.clone() * denom, ) }, ); Self::new_unchecked(res) } /// Builds an unit quaternion by extracting the rotation part of the given transformation `m`. /// /// This is an iterative method. See `.from_matrix_eps` to provide mover /// convergence parameters and starting solution. /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. pub fn from_matrix(m: &Matrix3) -> Self where T: RealField, { Rotation3::from_matrix(m).into() } /// Builds an unit quaternion by extracting the rotation part of the given transformation `m`. /// /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. /// /// # Parameters /// /// * `m`: the matrix from which the rotational part is to be extracted. /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. /// * `guess`: an estimate of the solution. Convergence will be significantly faster if an initial solution close /// to the actual solution is provided. Can be set to `UnitQuaternion::identity()` if no other /// guesses come to mind. pub fn from_matrix_eps(m: &Matrix3, eps: T, max_iter: usize, guess: Self) -> Self where T: RealField, { let guess = Rotation3::from(guess); Rotation3::from_matrix_eps(m, eps, max_iter, guess).into() } /// The unit quaternion needed to make `a` and `b` be collinear and point toward the same /// direction. Returns `None` if both `a` and `b` are collinear and point to opposite directions, as then the /// rotation desired is not unique. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); /// let q = UnitQuaternion::rotation_between(&a, &b).unwrap(); /// assert_relative_eq!(q * a, b); /// assert_relative_eq!(q.inverse() * b, a); /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Option where T: RealField, SB: Storage, SC: Storage, { Self::scaled_rotation_between(a, b, T::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector3, UnitQuaternion}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); /// let q2 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.2).unwrap(); /// let q5 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.5).unwrap(); /// assert_relative_eq!(q2 * q2 * q2 * q2 * q2 * a, b, epsilon = 1.0e-6); /// assert_relative_eq!(q5 * q5 * a, b, epsilon = 1.0e-6); /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, b: &Vector, s: T, ) -> Option where T: RealField, SB: Storage, SC: Storage, { // TODO: code duplication with Rotation. if let (Some(na), Some(nb)) = ( Unit::try_new(a.clone_owned(), T::zero()), Unit::try_new(b.clone_owned(), T::zero()), ) { Self::scaled_rotation_between_axis(&na, &nb, s) } else { Some(Self::identity()) } } /// The unit quaternion needed to make `a` and `b` be collinear and point toward the same /// direction. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); /// let q = UnitQuaternion::rotation_between(&a, &b).unwrap(); /// assert_relative_eq!(q * a, b); /// assert_relative_eq!(q.inverse() * b, a); /// ``` #[inline] pub fn rotation_between_axis( a: &Unit>, b: &Unit>, ) -> Option where T: RealField, SB: Storage, SC: Storage, { Self::scaled_rotation_between_axis(a, b, T::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Unit, Vector3, UnitQuaternion}; /// let a = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let b = Unit::new_normalize(Vector3::new(3.0, 1.0, 2.0)); /// let q2 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.2).unwrap(); /// let q5 = UnitQuaternion::scaled_rotation_between(&a, &b, 0.5).unwrap(); /// assert_relative_eq!(q2 * q2 * q2 * q2 * q2 * a, b, epsilon = 1.0e-6); /// assert_relative_eq!(q5 * q5 * a, b, epsilon = 1.0e-6); /// ``` #[inline] pub fn scaled_rotation_between_axis( na: &Unit>, nb: &Unit>, s: T, ) -> Option where T: RealField, SB: Storage, SC: Storage, { // TODO: code duplication with Rotation. let c = na.cross(nb); if let Some(axis) = Unit::try_new(c, T::default_epsilon()) { let cos = na.dot(nb); // The cosinus may be out of [-1, 1] because of inaccuracies. if cos <= -T::one() { None } else if cos >= T::one() { Some(Self::identity()) } else { Some(Self::from_axis_angle(&axis, cos.acos() * s)) } } else if na.dot(nb) < T::zero() { // PI // // The rotation axis is undefined but the angle not zero. This is not a // simple rotation. None } else { // Zero Some(Self::identity()) } } /// Creates an unit quaternion that corresponds to the local frame of an observer standing at the /// origin and looking toward `dir`. /// /// It maps the `z` axis to the direction `dir`. /// /// # Arguments /// * dir - The look direction. It does not need to be normalized. /// * up - The vertical direction. It does not need to be normalized. /// The only requirement of this parameter is to not be collinear to `dir`. Non-collinearity /// is not checked. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// let q = UnitQuaternion::face_towards(&dir, &up); /// assert_relative_eq!(q * Vector3::z(), dir.normalize()); /// ``` #[inline] pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { Self::from_rotation_matrix(&Rotation3::face_towards(dir, up)) } /// Deprecated: Use [`UnitQuaternion::face_towards`] instead. #[deprecated(note = "renamed to `face_towards`")] pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { Self::face_towards(dir, up) } /// Builds a right-handed look-at view matrix without translation. /// /// It maps the view direction `dir` to the **negative** `z` axis. /// This conforms to the common notion of right handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * dir − The view direction. It does not need to be normalized. /// * up - A vector approximately aligned with required the vertical axis. It does not need /// to be normalized. The only requirement of this parameter is to not be collinear to `dir`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// let q = UnitQuaternion::look_at_rh(&dir, &up); /// assert_relative_eq!(q * dir.normalize(), -Vector3::z()); /// ``` #[inline] pub fn look_at_rh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { Self::face_towards(&-dir, up).inverse() } /// Builds a left-handed look-at view matrix without translation. /// /// It maps the view direction `dir` to the **positive** `z` axis. /// This conforms to the common notion of left handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * dir − The view direction. It does not need to be normalized. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `dir`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// let q = UnitQuaternion::look_at_lh(&dir, &up); /// assert_relative_eq!(q * dir.normalize(), Vector3::z()); /// ``` #[inline] pub fn look_at_lh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { Self::face_towards(dir, up).inverse() } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` has a magnitude smaller than `T::default_epsilon()`, this returns the identity rotation. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let q = UnitQuaternion::new(axisangle); /// /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // A zero vector yields an identity. /// assert_eq!(UnitQuaternion::new(Vector3::::zeros()), UnitQuaternion::identity()); /// ``` #[inline] pub fn new(axisangle: Vector) -> Self where SB: Storage, { let two: T = crate::convert(2.0f64); let q = Quaternion::::from_imag(axisangle / two).exp(); Self::new_unchecked(q) } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` has a magnitude smaller than `eps`, this returns the identity rotation. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let q = UnitQuaternion::new_eps(axisangle, 1.0e-6); /// /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // An almost zero vector yields an identity. /// assert_eq!(UnitQuaternion::new_eps(Vector3::new(1.0e-8, 1.0e-9, 1.0e-7), 1.0e-6), UnitQuaternion::identity()); /// ``` #[inline] pub fn new_eps(axisangle: Vector, eps: T) -> Self where SB: Storage, { let two: T = crate::convert(2.0f64); let q = Quaternion::::from_imag(axisangle / two).exp_eps(eps); Self::new_unchecked(q) } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` has a magnitude smaller than `T::default_epsilon()`, this returns the identity rotation. /// Same as `Self::new(axisangle)`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let q = UnitQuaternion::from_scaled_axis(axisangle); /// /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // A zero vector yields an identity. /// assert_eq!(UnitQuaternion::from_scaled_axis(Vector3::::zeros()), UnitQuaternion::identity()); /// ``` #[inline] pub fn from_scaled_axis(axisangle: Vector) -> Self where SB: Storage, { Self::new(axisangle) } /// Creates a new unit quaternion rotation from a rotation axis scaled by the rotation angle. /// /// If `axisangle` has a magnitude smaller than `eps`, this returns the identity rotation. /// Same as `Self::new_eps(axisangle, eps)`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let q = UnitQuaternion::from_scaled_axis_eps(axisangle, 1.0e-6); /// /// assert_relative_eq!(q * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(q * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // An almost zero vector yields an identity. /// assert_eq!(UnitQuaternion::from_scaled_axis_eps(Vector3::new(1.0e-8, 1.0e-9, 1.0e-7), 1.0e-6), UnitQuaternion::identity()); /// ``` #[inline] pub fn from_scaled_axis_eps(axisangle: Vector, eps: T) -> Self where SB: Storage, { Self::new_eps(axisangle, eps) } /// Create the mean unit quaternion from a data structure implementing `IntoIterator` /// returning unit quaternions. /// /// The method will panic if the iterator does not return any quaternions. /// /// Algorithm from: Oshman, Yaakov, and Avishy Carmi. "Attitude estimation from vector /// observations using a genetic-algorithm-embedded quaternion particle filter." Journal of /// Guidance, Control, and Dynamics 29.4 (2006): 879-891. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitQuaternion}; /// let q1 = UnitQuaternion::from_euler_angles(0.0, 0.0, 0.0); /// let q2 = UnitQuaternion::from_euler_angles(-0.1, 0.0, 0.0); /// let q3 = UnitQuaternion::from_euler_angles(0.1, 0.0, 0.0); /// /// let quat_vec = vec![q1, q2, q3]; /// let q_mean = UnitQuaternion::mean_of(quat_vec); /// /// let euler_angles_mean = q_mean.euler_angles(); /// assert_relative_eq!(euler_angles_mean.0, 0.0, epsilon = 1.0e-7) /// ``` #[inline] pub fn mean_of(unit_quaternions: impl IntoIterator) -> Self where T: RealField, { let quaternions_matrix: Matrix4 = unit_quaternions .into_iter() .map(|q| q.as_vector() * q.as_vector().transpose()) .sum(); assert!(!quaternions_matrix.is_zero()); let eigen_matrix = quaternions_matrix .try_symmetric_eigen(T::RealField::default_epsilon(), 10) .expect("Quaternions matrix could not be diagonalized. This behavior should not be possible."); let max_eigenvalue_index = eigen_matrix .eigenvalues .iter() .position(|v| *v == eigen_matrix.eigenvalues.max()) .unwrap(); let max_eigenvector = eigen_matrix.eigenvectors.column(max_eigenvalue_index); UnitQuaternion::from_quaternion(Quaternion::new( max_eigenvector[0].clone(), max_eigenvector[1].clone(), max_eigenvector[2].clone(), max_eigenvector[3].clone(), )) } } impl One for UnitQuaternion where T::Element: SimdRealField, { #[inline] fn one() -> Self { Self::identity() } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where T::Element: SimdRealField, OpenClosed01: Distribution, T: SampleUniform, { /// Generate a uniformly distributed random rotation quaternion. #[inline] fn sample(&self, rng: &mut R) -> UnitQuaternion { // Ken Shoemake's Subgroup Algorithm // Uniform random rotations. // In D. Kirk, editor, Graphics Gems III, pages 124-132. Academic, New York, 1992. let x0 = rng.sample(OpenClosed01); let twopi = Uniform::new(T::zero(), T::simd_two_pi()); let theta1 = rng.sample(&twopi); let theta2 = rng.sample(&twopi); let s1 = theta1.clone().simd_sin(); let c1 = theta1.simd_cos(); let s2 = theta2.clone().simd_sin(); let c2 = theta2.simd_cos(); let r1 = (T::one() - x0.clone()).simd_sqrt(); let r2 = x0.simd_sqrt(); Unit::new_unchecked(Quaternion::new( s1 * r1.clone(), c1 * r1, s2 * r2.clone(), c2 * r2, )) } } #[cfg(feature = "arbitrary")] impl Arbitrary for UnitQuaternion where Owned: Send, Owned: Send, { #[inline] fn arbitrary(g: &mut Gen) -> Self { let axisangle = Vector3::arbitrary(g); Self::from_scaled_axis(axisangle) } } #[cfg(test)] #[cfg(feature = "rand")] mod tests { use super::*; use rand::SeedableRng; use rand_xorshift; #[test] fn random_unit_quats_are_unit() { let mut rng = rand_xorshift::XorShiftRng::from_seed([0xAB; 16]); for _ in 0..1000 { let x = rng.gen::>(); assert!(relative_eq!(x.into_inner().norm(), 1.0)) } } } nalgebra-0.33.0/src/geometry/quaternion_conversion.rs000064400000000000000000000260740072674642500211450ustar 00000000000000use num::Zero; use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::{PrimitiveSimdValue, SimdRealField, SimdValue}; use crate::base::{Matrix3, Matrix4, Scalar, Vector4}; use crate::geometry::{ AbstractRotation, Isometry, Quaternion, Rotation, Rotation3, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, UnitDualQuaternion, UnitQuaternion, }; /* * This file provides the following conversions: * ============================================= * * Quaternion -> Quaternion * UnitQuaternion -> UnitQuaternion * UnitQuaternion -> Rotation<3> * UnitQuaternion -> Isometry<3> * UnitQuaternion -> UnitDualQuaternion * UnitQuaternion -> Similarity<3> * UnitQuaternion -> Transform<3> * UnitQuaternion -> Matrix (homogeneous) * * NOTE: * UnitQuaternion -> Quaternion is already provided by: Unit -> T */ impl SubsetOf> for Quaternion where T1: Scalar, T2: Scalar + SupersetOf, { #[inline] fn to_superset(&self) -> Quaternion { Quaternion::from(self.coords.to_superset()) } #[inline] fn is_in_subset(q: &Quaternion) -> bool { crate::is_convertible::<_, Vector4>(&q.coords) } #[inline] fn from_superset_unchecked(q: &Quaternion) -> Self { Self { coords: q.coords.to_subset_unchecked(), } } } impl SubsetOf> for UnitQuaternion where T1: Scalar, T2: Scalar + SupersetOf, { #[inline] fn to_superset(&self) -> UnitQuaternion { UnitQuaternion::new_unchecked(self.as_ref().to_superset()) } #[inline] fn is_in_subset(uq: &UnitQuaternion) -> bool { crate::is_convertible::<_, Quaternion>(uq.as_ref()) } #[inline] fn from_superset_unchecked(uq: &UnitQuaternion) -> Self { Self::new_unchecked(crate::convert_ref_unchecked(uq.as_ref())) } } impl SubsetOf> for UnitQuaternion where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> Rotation3 { let q: UnitQuaternion = self.to_superset(); q.to_rotation_matrix() } #[inline] fn is_in_subset(rot: &Rotation3) -> bool { crate::is_convertible::<_, Rotation3>(rot) } #[inline] fn from_superset_unchecked(rot: &Rotation3) -> Self { let q = UnitQuaternion::::from_rotation_matrix(rot); crate::convert_unchecked(q) } } impl SubsetOf> for UnitQuaternion where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SupersetOf, { #[inline] fn to_superset(&self) -> Isometry { Isometry::from_parts(Translation::identity(), crate::convert_ref(self)) } #[inline] fn is_in_subset(iso: &Isometry) -> bool { iso.translation.vector.is_zero() } #[inline] fn from_superset_unchecked(iso: &Isometry) -> Self { crate::convert_ref_unchecked(&iso.rotation) } } impl SubsetOf> for UnitQuaternion where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitDualQuaternion { let q: UnitQuaternion = crate::convert_ref(self); UnitDualQuaternion::from_rotation(q) } #[inline] fn is_in_subset(dq: &UnitDualQuaternion) -> bool { dq.translation().vector.is_zero() } #[inline] fn from_superset_unchecked(dq: &UnitDualQuaternion) -> Self { crate::convert_unchecked(dq.rotation()) } } impl SubsetOf> for UnitQuaternion where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SupersetOf, { #[inline] fn to_superset(&self) -> Similarity { Similarity::from_isometry(crate::convert_ref(self), T2::one()) } #[inline] fn is_in_subset(sim: &Similarity) -> bool { sim.isometry.translation.vector.is_zero() && sim.scaling() == T2::one() } #[inline] fn from_superset_unchecked(sim: &Similarity) -> Self { crate::convert_ref_unchecked(&sim.isometry) } } impl SubsetOf> for UnitQuaternion where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.clone().to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl> SubsetOf> for UnitQuaternion { #[inline] fn to_superset(&self) -> Matrix4 { self.clone().to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &Matrix4) -> bool { crate::is_convertible::<_, Rotation3>(m) } #[inline] fn from_superset_unchecked(m: &Matrix4) -> Self { let rot: Rotation3 = crate::convert_ref_unchecked(m); Self::from_rotation_matrix(&rot) } } impl From> for Matrix4 where T::Element: SimdRealField, { #[inline] fn from(q: UnitQuaternion) -> Self { q.to_homogeneous() } } impl From> for Rotation3 where T::Element: SimdRealField, { #[inline] fn from(q: UnitQuaternion) -> Self { q.to_rotation_matrix() } } impl From> for UnitQuaternion where T::Element: SimdRealField, { #[inline] fn from(q: Rotation3) -> Self { Self::from_rotation_matrix(&q) } } impl From> for Matrix3 where T::Element: SimdRealField, { #[inline] fn from(q: UnitQuaternion) -> Self { q.to_rotation_matrix().into_inner() } } impl From> for Quaternion { #[inline] fn from(coords: Vector4) -> Self { Self { coords } } } impl From<[T; 4]> for Quaternion { #[inline] fn from(coords: [T; 4]) -> Self { Self { coords: coords.into(), } } } impl From<[Quaternion; 2]> for Quaternion where T: From<[::Element; 2]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Quaternion; 2]) -> Self { Self::from(Vector4::from([arr[0].coords, arr[1].coords])) } } impl From<[Quaternion; 4]> for Quaternion where T: From<[::Element; 4]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Quaternion; 4]) -> Self { Self::from(Vector4::from([ arr[0].coords, arr[1].coords, arr[2].coords, arr[3].coords, ])) } } impl From<[Quaternion; 8]> for Quaternion where T: From<[::Element; 8]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Quaternion; 8]) -> Self { Self::from(Vector4::from([ arr[0].coords, arr[1].coords, arr[2].coords, arr[3].coords, arr[4].coords, arr[5].coords, arr[6].coords, arr[7].coords, ])) } } impl From<[Quaternion; 16]> for Quaternion where T: From<[::Element; 16]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Quaternion; 16]) -> Self { Self::from(Vector4::from([ arr[0].coords, arr[1].coords, arr[2].coords, arr[3].coords, arr[4].coords, arr[5].coords, arr[6].coords, arr[7].coords, arr[8].coords, arr[9].coords, arr[10].coords, arr[11].coords, arr[12].coords, arr[13].coords, arr[14].coords, arr[15].coords, ])) } } impl From<[UnitQuaternion; 2]> for UnitQuaternion where T: From<[::Element; 2]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitQuaternion; 2]) -> Self { Self::new_unchecked(Quaternion::from([arr[0].into_inner(), arr[1].into_inner()])) } } impl From<[UnitQuaternion; 4]> for UnitQuaternion where T: From<[::Element; 4]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitQuaternion; 4]) -> Self { Self::new_unchecked(Quaternion::from([ arr[0].into_inner(), arr[1].into_inner(), arr[2].into_inner(), arr[3].into_inner(), ])) } } impl From<[UnitQuaternion; 8]> for UnitQuaternion where T: From<[::Element; 8]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitQuaternion; 8]) -> Self { Self::new_unchecked(Quaternion::from([ arr[0].into_inner(), arr[1].into_inner(), arr[2].into_inner(), arr[3].into_inner(), arr[4].into_inner(), arr[5].into_inner(), arr[6].into_inner(), arr[7].into_inner(), ])) } } impl From<[UnitQuaternion; 16]> for UnitQuaternion where T: From<[::Element; 16]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitQuaternion; 16]) -> Self { Self::new_unchecked(Quaternion::from([ arr[0].into_inner(), arr[1].into_inner(), arr[2].into_inner(), arr[3].into_inner(), arr[4].into_inner(), arr[5].into_inner(), arr[6].into_inner(), arr[7].into_inner(), arr[8].into_inner(), arr[9].into_inner(), arr[10].into_inner(), arr[11].into_inner(), arr[12].into_inner(), arr[13].into_inner(), arr[14].into_inner(), arr[15].into_inner(), ])) } } nalgebra-0.33.0/src/geometry/quaternion_coordinates.rs000064400000000000000000000011070072674642500212600ustar 00000000000000use std::ops::{Deref, DerefMut}; use simba::simd::SimdValue; use crate::base::coordinates::IJKW; use crate::Scalar; use crate::geometry::Quaternion; impl Deref for Quaternion { type Target = IJKW; #[inline] fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } } } impl DerefMut for Quaternion { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *(self as *mut Self as *mut Self::Target) } } } nalgebra-0.33.0/src/geometry/quaternion_ops.rs000064400000000000000000000435450072674642500175630ustar 00000000000000// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] /* * This file provides: * =================== * * * (Quaternion) * * Index * IndexMut * Quaternion × Quaternion * Quaternion + Quaternion * Quaternion - Quaternion * -Quaternion * Quaternion × Scalar * Quaternion ÷ Scalar * Scalar × Quaternion * * (Unit Quaternion) * UnitQuaternion × UnitQuaternion * UnitQuaternion × Rotation -> UnitQuaternion * Rotation × UnitQuaternion -> UnitQuaternion * * UnitQuaternion ÷ UnitQuaternion * UnitQuaternion ÷ Rotation -> UnitQuaternion * Rotation ÷ UnitQuaternion -> UnitQuaternion * * * UnitQuaternion × Point * UnitQuaternion × Vector * UnitQuaternion × Unit * * NOTE: -UnitQuaternion is already provided by `Unit`. * * * (Assignment Operators) * * Quaternion ×= Scalar * Quaternion ×= Quaternion * Quaternion += Quaternion * Quaternion -= Quaternion * * UnitQuaternion ×= UnitQuaternion * UnitQuaternion ×= Rotation * * UnitQuaternion ÷= UnitQuaternion * UnitQuaternion ÷= Rotation * * TODO: Rotation ×= UnitQuaternion * TODO: Rotation ÷= UnitQuaternion * */ use std::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; use crate::base::dimension::U3; use crate::base::storage::Storage; use crate::base::{Const, Scalar, Unit, Vector, Vector3}; use crate::SimdRealField; use crate::geometry::{Point3, Quaternion, Rotation, UnitQuaternion}; impl Index for Quaternion { type Output = T; #[inline] fn index(&self, i: usize) -> &Self::Output { &self.coords[i] } } impl IndexMut for Quaternion { #[inline] fn index_mut(&mut self, i: usize) -> &mut T { &mut self.coords[i] } } macro_rules! quaternion_op_impl( ($Op: ident, $op: ident; $($Storage: ident: $StoragesBound: ident $(<$($BoundParam: ty),*>)*),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Result: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField $(, $Storage: $StoragesBound $(<$($BoundParam),*>)*)*> $Op<$Rhs> for $Lhs where T::Element: SimdRealField { type Output = $Result; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); // Quaternion + Quaternion quaternion_op_impl!( Add, add; ; self: &'a Quaternion, rhs: &'b Quaternion, Output = Quaternion; Quaternion::from(&self.coords + &rhs.coords); 'a, 'b); quaternion_op_impl!( Add, add; ; self: &'a Quaternion, rhs: Quaternion, Output = Quaternion; Quaternion::from(&self.coords + rhs.coords); 'a); quaternion_op_impl!( Add, add; ; self: Quaternion, rhs: &'b Quaternion, Output = Quaternion; Quaternion::from(self.coords + &rhs.coords); 'b); quaternion_op_impl!( Add, add; ; self: Quaternion, rhs: Quaternion, Output = Quaternion; Quaternion::from(self.coords + rhs.coords); ); // Quaternion - Quaternion quaternion_op_impl!( Sub, sub; ; self: &'a Quaternion, rhs: &'b Quaternion, Output = Quaternion; Quaternion::from(&self.coords - &rhs.coords); 'a, 'b); quaternion_op_impl!( Sub, sub; ; self: &'a Quaternion, rhs: Quaternion, Output = Quaternion; Quaternion::from(&self.coords - rhs.coords); 'a); quaternion_op_impl!( Sub, sub; ; self: Quaternion, rhs: &'b Quaternion, Output = Quaternion; Quaternion::from(self.coords - &rhs.coords); 'b); quaternion_op_impl!( Sub, sub; ; self: Quaternion, rhs: Quaternion, Output = Quaternion; Quaternion::from(self.coords - rhs.coords); ); // Quaternion × Quaternion quaternion_op_impl!( Mul, mul; ; self: &'a Quaternion, rhs: &'b Quaternion, Output = Quaternion; Quaternion::new( self[3].clone() * rhs[3].clone() - self[0].clone() * rhs[0].clone() - self[1].clone() * rhs[1].clone() - self[2].clone() * rhs[2].clone(), self[3].clone() * rhs[0].clone() + self[0].clone() * rhs[3].clone() + self[1].clone() * rhs[2].clone() - self[2].clone() * rhs[1].clone(), self[3].clone() * rhs[1].clone() - self[0].clone() * rhs[2].clone() + self[1].clone() * rhs[3].clone() + self[2].clone() * rhs[0].clone(), self[3].clone() * rhs[2].clone() + self[0].clone() * rhs[1].clone() - self[1].clone() * rhs[0].clone() + self[2].clone() * rhs[3].clone()); 'a, 'b); quaternion_op_impl!( Mul, mul; ; self: &'a Quaternion, rhs: Quaternion, Output = Quaternion; self * &rhs; 'a); quaternion_op_impl!( Mul, mul; ; self: Quaternion, rhs: &'b Quaternion, Output = Quaternion; &self * rhs; 'b); quaternion_op_impl!( Mul, mul; ; self: Quaternion, rhs: Quaternion, Output = Quaternion; &self * &rhs; ); // UnitQuaternion × UnitQuaternion quaternion_op_impl!( Mul, mul; ; self: &'a UnitQuaternion, rhs: &'b UnitQuaternion, Output = UnitQuaternion; UnitQuaternion::new_unchecked(self.quaternion() * rhs.quaternion()); 'a, 'b); quaternion_op_impl!( Mul, mul; ; self: &'a UnitQuaternion, rhs: UnitQuaternion, Output = UnitQuaternion; self * &rhs; 'a); quaternion_op_impl!( Mul, mul; ; self: UnitQuaternion, rhs: &'b UnitQuaternion, Output = UnitQuaternion; &self * rhs; 'b); quaternion_op_impl!( Mul, mul; ; self: UnitQuaternion, rhs: UnitQuaternion, Output = UnitQuaternion; &self * &rhs; ); // UnitQuaternion ÷ UnitQuaternion quaternion_op_impl!( Div, div; ; self: &'a UnitQuaternion, rhs: &'b UnitQuaternion, Output = UnitQuaternion; #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; 'a, 'b); quaternion_op_impl!( Div, div; ; self: &'a UnitQuaternion, rhs: UnitQuaternion, Output = UnitQuaternion; self / &rhs; 'a); quaternion_op_impl!( Div, div; ; self: UnitQuaternion, rhs: &'b UnitQuaternion, Output = UnitQuaternion; &self / rhs; 'b); quaternion_op_impl!( Div, div; ; self: UnitQuaternion, rhs: UnitQuaternion, Output = UnitQuaternion; &self / &rhs; ); // UnitQuaternion × Rotation quaternion_op_impl!( Mul, mul; ; self: &'a UnitQuaternion, rhs: &'b Rotation, Output = UnitQuaternion; // TODO: can we avoid the conversion from a rotation matrix? self * UnitQuaternion::::from_rotation_matrix(rhs); 'a, 'b); quaternion_op_impl!( Mul, mul; ; self: &'a UnitQuaternion, rhs: Rotation, Output = UnitQuaternion; self * UnitQuaternion::::from_rotation_matrix(&rhs); 'a); quaternion_op_impl!( Mul, mul; ; self: UnitQuaternion, rhs: &'b Rotation, Output = UnitQuaternion; self * UnitQuaternion::::from_rotation_matrix(rhs); 'b); quaternion_op_impl!( Mul, mul; ; self: UnitQuaternion, rhs: Rotation, Output = UnitQuaternion; self * UnitQuaternion::::from_rotation_matrix(&rhs); ); // UnitQuaternion ÷ Rotation quaternion_op_impl!( Div, div; ; self: &'a UnitQuaternion, rhs: &'b Rotation, Output = UnitQuaternion; // TODO: can we avoid the conversion to a rotation matrix? self / UnitQuaternion::::from_rotation_matrix(rhs); 'a, 'b); quaternion_op_impl!( Div, div; ; self: &'a UnitQuaternion, rhs: Rotation, Output = UnitQuaternion; self / UnitQuaternion::::from_rotation_matrix(&rhs); 'a); quaternion_op_impl!( Div, div; ; self: UnitQuaternion, rhs: &'b Rotation, Output = UnitQuaternion; self / UnitQuaternion::::from_rotation_matrix(rhs); 'b); quaternion_op_impl!( Div, div; ; self: UnitQuaternion, rhs: Rotation, Output = UnitQuaternion; self / UnitQuaternion::::from_rotation_matrix(&rhs); ); // Rotation × UnitQuaternion quaternion_op_impl!( Mul, mul; ; self: &'a Rotation, rhs: &'b UnitQuaternion, Output = UnitQuaternion; // TODO: can we avoid the conversion from a rotation matrix? UnitQuaternion::::from_rotation_matrix(self) * rhs; 'a, 'b); quaternion_op_impl!( Mul, mul; ; self: &'a Rotation, rhs: UnitQuaternion, Output = UnitQuaternion; UnitQuaternion::::from_rotation_matrix(self) * rhs; 'a); quaternion_op_impl!( Mul, mul; ; self: Rotation, rhs: &'b UnitQuaternion, Output = UnitQuaternion; UnitQuaternion::::from_rotation_matrix(&self) * rhs; 'b); quaternion_op_impl!( Mul, mul; ; self: Rotation, rhs: UnitQuaternion, Output = UnitQuaternion; UnitQuaternion::::from_rotation_matrix(&self) * rhs; ); // Rotation ÷ UnitQuaternion quaternion_op_impl!( Div, div; ; self: &'a Rotation, rhs: &'b UnitQuaternion, Output = UnitQuaternion; // TODO: can we avoid the conversion from a rotation matrix? UnitQuaternion::::from_rotation_matrix(self) / rhs; 'a, 'b); quaternion_op_impl!( Div, div; ; self: &'a Rotation, rhs: UnitQuaternion, Output = UnitQuaternion; UnitQuaternion::::from_rotation_matrix(self) / rhs; 'a); quaternion_op_impl!( Div, div; ; self: Rotation, rhs: &'b UnitQuaternion, Output = UnitQuaternion; UnitQuaternion::::from_rotation_matrix(&self) / rhs; 'b); quaternion_op_impl!( Div, div; ; self: Rotation, rhs: UnitQuaternion, Output = UnitQuaternion; UnitQuaternion::::from_rotation_matrix(&self) / rhs; ); // UnitQuaternion × Vector quaternion_op_impl!( Mul, mul; SB: Storage> ; self: &'a UnitQuaternion, rhs: &'b Vector, SB>, Output = Vector3; { let two: T = crate::convert(2.0f64); let t = self.as_ref().vector().cross(rhs) * two; let cross = self.as_ref().vector().cross(&t); t * self.as_ref().scalar() + cross + rhs }; 'a, 'b); quaternion_op_impl!( Mul, mul; SB: Storage> ; self: &'a UnitQuaternion, rhs: Vector, Output = Vector3; self * &rhs; 'a); quaternion_op_impl!( Mul, mul; SB: Storage> ; self: UnitQuaternion, rhs: &'b Vector, Output = Vector3; &self * rhs; 'b); quaternion_op_impl!( Mul, mul; SB: Storage> ; self: UnitQuaternion, rhs: Vector, Output = Vector3; &self * &rhs; ); // UnitQuaternion × Point quaternion_op_impl!( Mul, mul; ; self: &'a UnitQuaternion, rhs: &'b Point3, Output = Point3; Point3::from(self * &rhs.coords); 'a, 'b); quaternion_op_impl!( Mul, mul; ; self: &'a UnitQuaternion, rhs: Point3, Output = Point3; Point3::from(self * rhs.coords); 'a); quaternion_op_impl!( Mul, mul; ; self: UnitQuaternion, rhs: &'b Point3, Output = Point3; Point3::from(self * &rhs.coords); 'b); quaternion_op_impl!( Mul, mul; ; self: UnitQuaternion, rhs: Point3, Output = Point3; Point3::from(self * rhs.coords); ); // UnitQuaternion × Unit quaternion_op_impl!( Mul, mul; SB: Storage> ; self: &'a UnitQuaternion, rhs: &'b Unit>, Output = Unit>; Unit::new_unchecked(self * rhs.as_ref()); 'a, 'b); quaternion_op_impl!( Mul, mul; SB: Storage> ; self: &'a UnitQuaternion, rhs: Unit>, Output = Unit>; Unit::new_unchecked(self * rhs.into_inner()); 'a); quaternion_op_impl!( Mul, mul; SB: Storage> ; self: UnitQuaternion, rhs: &'b Unit>, Output = Unit>; Unit::new_unchecked(self * rhs.as_ref()); 'b); quaternion_op_impl!( Mul, mul; SB: Storage> ; self: UnitQuaternion, rhs: Unit>, Output = Unit>; Unit::new_unchecked(self * rhs.into_inner()); ); macro_rules! scalar_op_impl( ($($Op: ident, $op: ident, $OpAssign: ident, $op_assign: ident);* $(;)*) => {$( impl $Op for Quaternion where T::Element: SimdRealField { type Output = Quaternion; #[inline] fn $op(self, n: T) -> Self::Output { Quaternion::from(self.coords.$op(n)) } } impl<'a, T: SimdRealField> $Op for &'a Quaternion where T::Element: SimdRealField { type Output = Quaternion; #[inline] fn $op(self, n: T) -> Self::Output { Quaternion::from((&self.coords).$op(n)) } } impl $OpAssign for Quaternion where T::Element: SimdRealField { #[inline] fn $op_assign(&mut self, n: T) { self.coords.$op_assign(n) } } )*} ); scalar_op_impl!( Mul, mul, MulAssign, mul_assign; Div, div, DivAssign, div_assign; ); macro_rules! left_scalar_mul_impl( ($($T: ty),* $(,)*) => {$( impl Mul> for $T { type Output = Quaternion<$T>; #[inline] fn mul(self, right: Quaternion<$T>) -> Self::Output { Quaternion::from(self * right.coords) } } impl<'b> Mul<&'b Quaternion<$T>> for $T { type Output = Quaternion<$T>; #[inline] fn mul(self, right: &'b Quaternion<$T>) -> Self::Output { Quaternion::from(self * &right.coords) } } )*} ); left_scalar_mul_impl!(f32, f64); impl Neg for Quaternion where T::Element: SimdRealField, { type Output = Quaternion; #[inline] fn neg(self) -> Self::Output { Self::Output::from(-self.coords) } } impl<'a, T: SimdRealField> Neg for &'a Quaternion where T::Element: SimdRealField, { type Output = Quaternion; #[inline] fn neg(self) -> Self::Output { Self::Output::from(-&self.coords) } } macro_rules! quaternion_op_impl( ($OpAssign: ident, $op_assign: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty $(=> $VDimA: ty, $VDimB: ty)*; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField> $OpAssign<$Rhs> for $Lhs where T::Element: SimdRealField { #[inline] fn $op_assign(&mut $lhs, $rhs: $Rhs) { $action } } } ); // Quaternion += Quaternion quaternion_op_impl!( AddAssign, add_assign; self: Quaternion, rhs: &'b Quaternion; self.coords += &rhs.coords; 'b); quaternion_op_impl!( AddAssign, add_assign; self: Quaternion, rhs: Quaternion; self.coords += rhs.coords; ); // Quaternion -= Quaternion quaternion_op_impl!( SubAssign, sub_assign; self: Quaternion, rhs: &'b Quaternion; self.coords -= &rhs.coords; 'b); quaternion_op_impl!( SubAssign, sub_assign; self: Quaternion, rhs: Quaternion; self.coords -= rhs.coords; ); // Quaternion ×= Quaternion quaternion_op_impl!( MulAssign, mul_assign; self: Quaternion, rhs: &'b Quaternion; { let res = &*self * rhs; // TODO: will this be optimized away? self.coords.copy_from(&res.coords); }; 'b); quaternion_op_impl!( MulAssign, mul_assign; self: Quaternion, rhs: Quaternion; *self *= &rhs; ); // UnitQuaternion ×= UnitQuaternion quaternion_op_impl!( MulAssign, mul_assign; self: UnitQuaternion, rhs: &'b UnitQuaternion; { let res = &*self * rhs; self.as_mut_unchecked().coords.copy_from(&res.as_ref().coords); }; 'b); quaternion_op_impl!( MulAssign, mul_assign; self: UnitQuaternion, rhs: UnitQuaternion; *self *= &rhs; ); // UnitQuaternion ÷= UnitQuaternion quaternion_op_impl!( DivAssign, div_assign; self: UnitQuaternion, rhs: &'b UnitQuaternion; { let res = &*self / rhs; self.as_mut_unchecked().coords.copy_from(&res.as_ref().coords); }; 'b); quaternion_op_impl!( DivAssign, div_assign; self: UnitQuaternion, rhs: UnitQuaternion; *self /= &rhs; ); // UnitQuaternion ×= Rotation quaternion_op_impl!( MulAssign, mul_assign; self: UnitQuaternion, rhs: &'b Rotation; { let res = &*self * rhs; self.as_mut_unchecked().coords.copy_from(&res.as_ref().coords); }; 'b); quaternion_op_impl!( MulAssign, mul_assign; self: UnitQuaternion, rhs: Rotation; *self *= &rhs; ); // UnitQuaternion ÷= Rotation quaternion_op_impl!( DivAssign, div_assign; self: UnitQuaternion, rhs: &'b Rotation; { let res = &*self / rhs; self.as_mut_unchecked().coords.copy_from(&res.as_ref().coords); }; 'b); quaternion_op_impl!( DivAssign, div_assign; self: UnitQuaternion, rhs: Rotation; *self /= &rhs; ); nalgebra-0.33.0/src/geometry/quaternion_simba.rs000064400000000000000000000044170072674642500200500ustar 00000000000000use simba::simd::SimdValue; use crate::base::Vector4; use crate::geometry::{Quaternion, UnitQuaternion}; use crate::Scalar; impl SimdValue for Quaternion where T::Element: Scalar, { const LANES: usize = T::LANES; type Element = Quaternion; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { Vector4::splat(val.coords).into() } #[inline] fn extract(&self, i: usize) -> Self::Element { self.coords.extract(i).into() } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { self.coords.extract_unchecked(i).into() } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.coords.replace(i, val.coords) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.coords.replace_unchecked(i, val.coords) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { self.coords.select(cond, other.coords).into() } } impl SimdValue for UnitQuaternion where T::Element: Scalar, { const LANES: usize = T::LANES; type Element = UnitQuaternion; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { UnitQuaternion::new_unchecked(Quaternion::splat(val.into_inner())) } #[inline] fn extract(&self, i: usize) -> Self::Element { UnitQuaternion::new_unchecked(self.as_ref().extract(i)) } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { UnitQuaternion::new_unchecked(self.as_ref().extract_unchecked(i)) } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.as_mut_unchecked().replace(i, val.into_inner()) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.as_mut_unchecked() .replace_unchecked(i, val.into_inner()) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { UnitQuaternion::new_unchecked(self.into_inner().select(cond, other.into_inner())) } } nalgebra-0.33.0/src/geometry/reflection.rs000064400000000000000000000110350072674642500166340ustar 00000000000000use crate::base::constraint::{AreMultipliable, DimEq, SameNumberOfRows, ShapeConstraint}; use crate::base::{Const, Matrix, Unit, Vector}; use crate::dimension::{Dim, U1}; use crate::storage::{Storage, StorageMut}; use simba::scalar::ComplexField; use crate::geometry::Point; /// A reflection wrt. a plane. pub struct Reflection { axis: Vector, bias: T, } impl>, const D: usize> Reflection, S> { /// Creates a new reflection wrt. the plane orthogonal to the given axis and that contains the /// point `pt`. pub fn new_containing_point(axis: Unit, S>>, pt: &Point) -> Self { let bias = axis.dotc(&pt.coords); Self::new(axis, bias) } } impl> Reflection { /// Creates a new reflection wrt. the plane orthogonal to the given axis and bias. /// /// The bias is the position of the plane on the axis. In particular, a bias equal to zero /// represents a plane that passes through the origin. pub fn new(axis: Unit>, bias: T) -> Self { Self { axis: axis.into_inner(), bias, } } /// The reflection axis. #[must_use] pub fn axis(&self) -> &Vector { &self.axis } /// The reflection bias. /// /// The bias is the position of the plane on the axis. In particular, a bias equal to zero /// represents a plane that passes through the origin. #[must_use] pub fn bias(&self) -> T { self.bias.clone() } // TODO: naming convention: reflect_to, reflect_assign ? /// Applies the reflection to the columns of `rhs`. pub fn reflect(&self, rhs: &mut Matrix) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..rhs.ncols() { // NOTE: we borrow the column twice here. First it is borrowed immutably for the // dot product, and then mutably. Somehow, this allows significantly // better optimizations of the dot product from the compiler. let m_two: T = crate::convert(-2.0f64); let factor = (self.axis.dotc(&rhs.column(i)) - self.bias.clone()) * m_two; rhs.column_mut(i).axpy(factor, &self.axis, T::one()); } } // TODO: naming convention: reflect_to, reflect_assign ? /// Applies the reflection to the columns of `rhs`. pub fn reflect_with_sign(&self, rhs: &mut Matrix, sign: T) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..rhs.ncols() { // NOTE: we borrow the column twice here. First it is borrowed immutably for the // dot product, and then mutably. Somehow, this allows significantly // better optimizations of the dot product from the compiler. let m_two = sign.clone().scale(crate::convert(-2.0f64)); let factor = (self.axis.dotc(&rhs.column(i)) - self.bias.clone()) * m_two; rhs.column_mut(i).axpy(factor, &self.axis, sign.clone()); } } /// Applies the reflection to the rows of `lhs`. pub fn reflect_rows( &self, lhs: &mut Matrix, work: &mut Vector, ) where S2: StorageMut, S3: StorageMut, ShapeConstraint: DimEq + AreMultipliable, { lhs.mul_to(&self.axis, work); if !self.bias.is_zero() { work.add_scalar_mut(-self.bias.clone()); } let m_two: T = crate::convert(-2.0f64); lhs.gerc(m_two, work, &self.axis, T::one()); } /// Applies the reflection to the rows of `lhs`. pub fn reflect_rows_with_sign( &self, lhs: &mut Matrix, work: &mut Vector, sign: T, ) where S2: StorageMut, S3: StorageMut, ShapeConstraint: DimEq + AreMultipliable, { lhs.mul_to(&self.axis, work); if !self.bias.is_zero() { work.add_scalar_mut(-self.bias.clone()); } let m_two = sign.clone().scale(crate::convert(-2.0f64)); lhs.gerc(m_two, work, &self.axis, sign); } } nalgebra-0.33.0/src/geometry/reflection_alias.rs000064400000000000000000000013510072674642500200050ustar 00000000000000use crate::base::ArrayStorage; use crate::geometry::Reflection; use crate::Const; /// A 1-dimensional reflection. pub type Reflection1 = Reflection, ArrayStorage>; /// A 2-dimensional reflection. pub type Reflection2 = Reflection, ArrayStorage>; /// A 3-dimensional reflection. pub type Reflection3 = Reflection, ArrayStorage>; /// A 4-dimensional reflection. pub type Reflection4 = Reflection, ArrayStorage>; /// A 5-dimensional reflection. pub type Reflection5 = Reflection, ArrayStorage>; /// A 6-dimensional reflection. pub type Reflection6 = Reflection, ArrayStorage>; nalgebra-0.33.0/src/geometry/rotation.rs000064400000000000000000000521540072674642500163500ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use num::{One, Zero}; use std::fmt; use std::hash; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde-serialize-no-std")] use crate::base::storage::Owned; use simba::scalar::RealField; use simba::simd::SimdRealField; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix, SMatrix, SVector, Scalar, Unit}; use crate::geometry::Point; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A rotation matrix. /// /// This is also known as an element of a Special Orthogonal (SO) group. /// The `Rotation` type can either represent a 2D or 3D rotation, represented as a matrix. /// For a rotation based on quaternions, see [`UnitQuaternion`](crate::UnitQuaternion) instead. /// /// Note that instead of using the [`Rotation`](crate::Rotation) type in your code directly, you should use one /// of its aliases: [`Rotation2`](crate::Rotation2), or [`Rotation3`](crate::Rotation3). Though /// keep in mind that all the documentation of all the methods of these aliases will also appears on /// this page. /// /// # Construction /// * [Identity `identity`](#identity) /// * [From a 2D rotation angle `new`…](#construction-from-a-2d-rotation-angle) /// * [From an existing 2D matrix or rotations `from_matrix`, `rotation_between`, `powf`…](#construction-from-an-existing-2d-matrix-or-rotations) /// * [From a 3D axis and/or angles `new`, `from_euler_angles`, `from_axis_angle`…](#construction-from-a-3d-axis-andor-angles) /// * [From a 3D eye position and target point `look_at`, `look_at_lh`, `rotation_between`…](#construction-from-a-3d-eye-position-and-target-point) /// * [From an existing 3D matrix or rotations `from_matrix`, `rotation_between`, `powf`…](#construction-from-an-existing-3d-matrix-or-rotations) /// /// # Transformation and composition /// Note that transforming vectors and points can be done by multiplication, e.g., `rotation * point`. /// Composing an rotation with another transformation can also be done by multiplication or division. /// * [3D axis and angle extraction `angle`, `euler_angles`, `scaled_axis`, `angle_to`…](#3d-axis-and-angle-extraction) /// * [2D angle extraction `angle`, `angle_to`…](#2d-angle-extraction) /// * [Transformation of a vector or a point `transform_vector`, `inverse_transform_point`…](#transformation-of-a-vector-or-a-point) /// * [Transposition and inversion `transpose`, `inverse`…](#transposition-and-inversion) /// * [Interpolation `slerp`…](#interpolation) /// /// # Conversion /// * [Conversion to a matrix `matrix`, `to_homogeneous`…](#conversion-to-a-matrix) /// #[repr(C)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Rotation", bound(archive = " T: rkyv::Archive, SMatrix: rkyv::Archive> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[derive(Copy, Clone)] pub struct Rotation { matrix: SMatrix, } impl fmt::Debug for Rotation { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.matrix.fmt(formatter) } } impl hash::Hash for Rotation where , Const>>::Buffer: hash::Hash, { fn hash(&self, state: &mut H) { self.matrix.hash(state) } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Rotation where T: Scalar + bytemuck::Zeroable, SMatrix: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Rotation where T: Scalar + bytemuck::Pod, SMatrix: bytemuck::Pod, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Rotation where Owned, Const>: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.matrix.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: Scalar, const D: usize> Deserialize<'a> for Rotation where Owned, Const>: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let matrix = SMatrix::::deserialize(deserializer)?; Ok(Self::from_matrix_unchecked(matrix)) } } impl Rotation { /// Creates a new rotation from the given square matrix. /// /// The matrix orthonormality is not checked. /// /// # Example /// ``` /// # use nalgebra::{Rotation2, Rotation3, Matrix2, Matrix3}; /// # use std::f32; /// let mat = Matrix3::new(0.8660254, -0.5, 0.0, /// 0.5, 0.8660254, 0.0, /// 0.0, 0.0, 1.0); /// let rot = Rotation3::from_matrix_unchecked(mat); /// /// assert_eq!(*rot.matrix(), mat); /// /// /// let mat = Matrix2::new(0.8660254, -0.5, /// 0.5, 0.8660254); /// let rot = Rotation2::from_matrix_unchecked(mat); /// /// assert_eq!(*rot.matrix(), mat); /// ``` #[inline] pub const fn from_matrix_unchecked(matrix: SMatrix) -> Self { Self { matrix } } } /// # Conversion to a matrix impl Rotation { /// A reference to the underlying matrix representation of this rotation. /// /// # Example /// ``` /// # use nalgebra::{Rotation2, Rotation3, Vector3, Matrix2, Matrix3}; /// # use std::f32; /// let rot = Rotation3::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, /// 0.5, 0.8660254, 0.0, /// 0.0, 0.0, 1.0); /// assert_eq!(*rot.matrix(), expected); /// /// /// let rot = Rotation2::new(f32::consts::FRAC_PI_6); /// let expected = Matrix2::new(0.8660254, -0.5, /// 0.5, 0.8660254); /// assert_eq!(*rot.matrix(), expected); /// ``` #[inline] #[must_use] pub fn matrix(&self) -> &SMatrix { &self.matrix } /// 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 { &mut self.matrix } /// A mutable reference to the underlying matrix representation of this rotation. /// /// This is suffixed by "_unchecked" because this allows the user to replace the /// matrix by another one that is non-inversible or non-orthonormal. If one of /// those properties is broken, subsequent method calls may return bogus results. #[inline] pub fn matrix_mut_unchecked(&mut self) -> &mut SMatrix { &mut self.matrix } /// Unwraps the underlying matrix. /// /// # Example /// ``` /// # use nalgebra::{Rotation2, Rotation3, Vector3, Matrix2, Matrix3}; /// # use std::f32; /// let rot = Rotation3::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); /// let mat = rot.into_inner(); /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, /// 0.5, 0.8660254, 0.0, /// 0.0, 0.0, 1.0); /// assert_eq!(mat, expected); /// /// /// let rot = Rotation2::new(f32::consts::FRAC_PI_6); /// let mat = rot.into_inner(); /// let expected = Matrix2::new(0.8660254, -0.5, /// 0.5, 0.8660254); /// assert_eq!(mat, expected); /// ``` #[inline] pub fn into_inner(self) -> SMatrix { self.matrix } /// Unwraps the underlying matrix. /// Deprecated: Use [`Rotation::into_inner`] instead. #[deprecated(note = "use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> SMatrix { self.matrix } /// Converts this rotation into its equivalent homogeneous transformation matrix. /// /// This is the same as `self.into()`. /// /// # Example /// ``` /// # use nalgebra::{Rotation2, Rotation3, Vector3, Matrix3, Matrix4}; /// # use std::f32; /// let rot = Rotation3::from_axis_angle(&Vector3::z_axis(), f32::consts::FRAC_PI_6); /// let expected = Matrix4::new(0.8660254, -0.5, 0.0, 0.0, /// 0.5, 0.8660254, 0.0, 0.0, /// 0.0, 0.0, 1.0, 0.0, /// 0.0, 0.0, 0.0, 1.0); /// assert_eq!(rot.to_homogeneous(), expected); /// /// /// let rot = Rotation2::new(f32::consts::FRAC_PI_6); /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, /// 0.5, 0.8660254, 0.0, /// 0.0, 0.0, 1.0); /// assert_eq!(rot.to_homogeneous(), expected); /// ``` #[inline] #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where T: Zero + One, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { // We could use `SMatrix::to_homogeneous()` here, but that would imply // adding the additional traits `DimAdd` and `IsNotStaticOne`. Maybe // these things will get nicer once specialization lands in Rust. let mut res = OMatrix::, U1>, DimNameSum, U1>>::identity(); res.fixed_view_mut::(0, 0).copy_from(&self.matrix); res } } /// # Transposition and inversion impl Rotation { /// Transposes `self`. /// /// Same as `.inverse()` because the inverse of a rotation matrix is its transpose. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let tr_rot = rot.transpose(); /// assert_relative_eq!(rot * tr_rot, Rotation3::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(tr_rot * rot, Rotation3::identity(), epsilon = 1.0e-6); /// /// let rot = Rotation2::new(1.2); /// let tr_rot = rot.transpose(); /// assert_relative_eq!(rot * tr_rot, Rotation2::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(tr_rot * rot, Rotation2::identity(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use = "Did you mean to use transpose_mut()?"] pub fn transpose(&self) -> Self { Self::from_matrix_unchecked(self.matrix.transpose()) } /// Inverts `self`. /// /// Same as `.transpose()` because the inverse of a rotation matrix is its transpose. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let inv = rot.inverse(); /// assert_relative_eq!(rot * inv, Rotation3::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(inv * rot, Rotation3::identity(), epsilon = 1.0e-6); /// /// let rot = Rotation2::new(1.2); /// let inv = rot.inverse(); /// assert_relative_eq!(rot * inv, Rotation2::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(inv * rot, Rotation2::identity(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(&self) -> Self { self.transpose() } /// Transposes `self` in-place. /// /// Same as `.inverse_mut()` because the inverse of a rotation matrix is its transpose. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let mut tr_rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// tr_rot.transpose_mut(); /// /// assert_relative_eq!(rot * tr_rot, Rotation3::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(tr_rot * rot, Rotation3::identity(), epsilon = 1.0e-6); /// /// let rot = Rotation2::new(1.2); /// let mut tr_rot = Rotation2::new(1.2); /// tr_rot.transpose_mut(); /// /// assert_relative_eq!(rot * tr_rot, Rotation2::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(tr_rot * rot, Rotation2::identity(), epsilon = 1.0e-6); /// ``` #[inline] pub fn transpose_mut(&mut self) { self.matrix.transpose_mut() } /// Inverts `self` in-place. /// /// Same as `.transpose_mut()` because the inverse of a rotation matrix is its transpose. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation2, Rotation3, Vector3}; /// let rot = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// let mut inv = Rotation3::new(Vector3::new(1.0, 2.0, 3.0)); /// inv.inverse_mut(); /// /// assert_relative_eq!(rot * inv, Rotation3::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(inv * rot, Rotation3::identity(), epsilon = 1.0e-6); /// /// let rot = Rotation2::new(1.2); /// let mut inv = Rotation2::new(1.2); /// inv.inverse_mut(); /// /// assert_relative_eq!(rot * inv, Rotation2::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(inv * rot, Rotation2::identity(), epsilon = 1.0e-6); /// ``` #[inline] pub fn inverse_mut(&mut self) { self.transpose_mut() } } /// # Transformation of a vector or a point impl Rotation where T::Element: SimdRealField, { /// Rotate the given point. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Point3, Rotation2, Rotation3, UnitQuaternion, Vector3}; /// let rot = Rotation3::new(Vector3::y() * f32::consts::FRAC_PI_2); /// let transformed_point = rot.transform_point(&Point3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } /// Rotate the given vector. /// /// This is the same as the multiplication `self * v`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation2, Rotation3, UnitQuaternion, Vector3}; /// let rot = Rotation3::new(Vector3::y() * f32::consts::FRAC_PI_2); /// let transformed_vector = rot.transform_vector(&Vector3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_vector, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } /// Rotate the given point by the inverse of this rotation. This may be /// cheaper than inverting the rotation and then transforming the given /// point. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Point3, Rotation2, Rotation3, UnitQuaternion, Vector3}; /// let rot = Rotation3::new(Vector3::y() * f32::consts::FRAC_PI_2); /// let transformed_point = rot.inverse_transform_point(&Point3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_point, Point3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { Point::from(self.inverse_transform_vector(&pt.coords)) } /// Rotate the given vector by the inverse of this rotation. This may be /// cheaper than inverting the rotation and then transforming the given /// vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation2, Rotation3, UnitQuaternion, Vector3}; /// let rot = Rotation3::new(Vector3::y() * f32::consts::FRAC_PI_2); /// let transformed_vector = rot.inverse_transform_vector(&Vector3::new(1.0, 2.0, 3.0)); /// /// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.matrix().tr_mul(v) } /// Rotate the given vector by the inverse of this rotation. This may be /// cheaper than inverting the rotation and then transforming the given /// vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation2, Rotation3, UnitQuaternion, Vector3}; /// let rot = Rotation3::new(Vector3::z() * f32::consts::FRAC_PI_2); /// let transformed_vector = rot.inverse_transform_unit_vector(&Vector3::x_axis()); /// /// assert_relative_eq!(transformed_vector, -Vector3::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { Unit::new_unchecked(self.inverse_transform_vector(&**v)) } } impl Eq for Rotation {} impl PartialEq for Rotation { #[inline] fn eq(&self, right: &Self) -> bool { self.matrix == right.matrix } } impl AbsDiffEq for Rotation where T: Scalar + AbsDiffEq, T::Epsilon: Clone, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.matrix.abs_diff_eq(&other.matrix, epsilon) } } impl RelativeEq for Rotation where T: Scalar + RelativeEq, T::Epsilon: Clone, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.matrix .relative_eq(&other.matrix, epsilon, max_relative) } } impl UlpsEq for Rotation where T: Scalar + UlpsEq, T::Epsilon: Clone, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps) } } /* * * Display * */ impl fmt::Display for Rotation where T: RealField + fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let precision = f.precision().unwrap_or(3); writeln!(f, "Rotation matrix {{")?; write!(f, "{:.*}", precision, self.matrix)?; writeln!(f, "}}") } } // // /* // // * // // * Absolute // // * // // */ // // impl Absolute for $t { // // type AbsoluteValue = $submatrix; // // // // #[inline] // // fn abs(m: &$t) -> $submatrix { // // Absolute::abs(&m.submatrix) // // } // // } nalgebra-0.33.0/src/geometry/rotation_alias.rs000064400000000000000000000006740072674642500175210ustar 00000000000000use crate::geometry::Rotation; /// A 2-dimensional rotation matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Rotation`](crate::Rotation) type too.** pub type Rotation2 = Rotation; /// A 3-dimensional rotation matrix. /// /// **Because this is an alias, not all its methods are listed here. See the [`Rotation`](crate::Rotation) type too.** pub type Rotation3 = Rotation; nalgebra-0.33.0/src/geometry/rotation_construction.rs000064400000000000000000000035560072674642500211640ustar 00000000000000use num::{One, Zero}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, SupersetOf}; use crate::base::{SMatrix, Scalar}; use crate::geometry::Rotation; impl Default for Rotation where T: Scalar + Zero + One, { fn default() -> Self { Self::identity() } } /// # Identity impl Rotation where T: Scalar + Zero + One, { /// Creates a new square identity rotation of the given `dimension`. /// /// # Example /// ``` /// # use nalgebra::{Rotation2, Rotation3}; /// # use nalgebra::Vector3; /// let rot1 = Rotation2::identity(); /// let rot2 = Rotation2::new(std::f32::consts::FRAC_PI_2); /// /// assert_eq!(rot1 * rot2, rot2); /// assert_eq!(rot2 * rot1, rot2); /// /// let rot1 = Rotation3::identity(); /// let rot2 = Rotation3::from_axis_angle(&Vector3::z_axis(), std::f32::consts::FRAC_PI_2); /// /// assert_eq!(rot1 * rot2, rot2); /// assert_eq!(rot2 * rot1, rot2); /// ``` #[inline] pub fn identity() -> Rotation { Self::from_matrix_unchecked(SMatrix::::identity()) } } impl Rotation { /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Rotation2; /// let rot = Rotation2::::identity(); /// let rot2 = rot.cast::(); /// assert_eq!(rot2, Rotation2::::identity()); /// ``` pub fn cast(self) -> Rotation where Rotation: SupersetOf, { crate::convert(self) } } impl One for Rotation where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, { #[inline] fn one() -> Self { Self::identity() } } nalgebra-0.33.0/src/geometry/rotation_conversion.rs000064400000000000000000000242430072674642500206130ustar 00000000000000use num::Zero; use crate::ArrayStorage; use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::{PrimitiveSimdValue, SimdValue}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimMin, DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, Matrix2, Matrix3, Matrix4, OMatrix, SMatrix, Scalar}; use crate::geometry::{ AbstractRotation, Isometry, Rotation, Rotation2, Rotation3, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, UnitComplex, UnitDualQuaternion, UnitQuaternion, }; /* * This file provides the following conversions: * ============================================= * * Rotation -> Rotation * Rotation3 -> UnitQuaternion * Rotation3 -> UnitDualQuaternion * Rotation2 -> UnitComplex * Rotation -> Isometry * Rotation -> Similarity * Rotation -> Transform * Rotation -> Matrix (homogeneous) */ impl SubsetOf> for Rotation where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> Rotation { Rotation::from_matrix_unchecked(self.matrix().to_superset()) } #[inline] fn is_in_subset(rot: &Rotation) -> bool { crate::is_convertible::<_, SMatrix>(rot.matrix()) } #[inline] fn from_superset_unchecked(rot: &Rotation) -> Self { Rotation::from_matrix_unchecked(rot.matrix().to_subset_unchecked()) } } impl SubsetOf> for Rotation3 where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitQuaternion { let q = UnitQuaternion::::from_rotation_matrix(self); q.to_superset() } #[inline] fn is_in_subset(q: &UnitQuaternion) -> bool { crate::is_convertible::<_, UnitQuaternion>(q) } #[inline] fn from_superset_unchecked(q: &UnitQuaternion) -> Self { let q: UnitQuaternion = crate::convert_ref_unchecked(q); q.to_rotation_matrix() } } impl SubsetOf> for Rotation3 where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitDualQuaternion { let q = UnitQuaternion::::from_rotation_matrix(self); let dq = UnitDualQuaternion::from_rotation(q); dq.to_superset() } #[inline] fn is_in_subset(dq: &UnitDualQuaternion) -> bool { crate::is_convertible::<_, UnitQuaternion>(&dq.rotation()) && dq.translation().vector.is_zero() } #[inline] fn from_superset_unchecked(dq: &UnitDualQuaternion) -> Self { let dq: UnitDualQuaternion = crate::convert_ref_unchecked(dq); dq.rotation().to_rotation_matrix() } } impl SubsetOf> for Rotation2 where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitComplex { let q = UnitComplex::::from_rotation_matrix(self); q.to_superset() } #[inline] fn is_in_subset(q: &UnitComplex) -> bool { crate::is_convertible::<_, UnitComplex>(q) } #[inline] fn from_superset_unchecked(q: &UnitComplex) -> Self { let q: UnitComplex = crate::convert_ref_unchecked(q); q.to_rotation_matrix() } } impl SubsetOf> for Rotation where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SupersetOf, { #[inline] fn to_superset(&self) -> Isometry { Isometry::from_parts(Translation::identity(), crate::convert_ref(self)) } #[inline] fn is_in_subset(iso: &Isometry) -> bool { iso.translation.vector.is_zero() } #[inline] fn from_superset_unchecked(iso: &Isometry) -> Self { crate::convert_ref_unchecked(&iso.rotation) } } impl SubsetOf> for Rotation where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SupersetOf, { #[inline] fn to_superset(&self) -> Similarity { Similarity::from_parts(Translation::identity(), crate::convert_ref(self), T2::one()) } #[inline] fn is_in_subset(sim: &Similarity) -> bool { sim.isometry.translation.vector.is_zero() && sim.scaling() == T2::one() } #[inline] fn from_superset_unchecked(sim: &Similarity) -> Self { crate::convert_ref_unchecked(&sim.isometry.rotation) } } impl SubsetOf> for Rotation where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, Const: DimNameAdd + DimMin, Output = Const>, // needed by .is_special_orthogonal() DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, // + Allocator, // + Allocator { // needed by .is_special_orthogonal() #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl SubsetOf, U1>, DimNameSum, U1>>> for Rotation where T1: RealField, T2: RealField + SupersetOf, Const: DimNameAdd + DimMin, Output = Const>, // needed by .is_special_orthogonal() DefaultAllocator: Allocator, Const, Buffer = ArrayStorage> + Allocator, U1>, DimNameSum, U1>>, // + Allocator, // + Allocator { // needed by .is_special_orthogonal() #[inline] fn to_superset(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &OMatrix, U1>, DimNameSum, U1>>) -> bool { let rot = m.fixed_view::(0, 0); let bottom = m.fixed_view::<1, D>(D, 0); // Scalar types agree. m.iter().all(|e| SupersetOf::::is_in_subset(e)) && // The block part is a rotation. rot.is_special_orthogonal(T2::default_epsilon() * crate::convert(100.0)) && // The bottom row is (0, 0, ..., 1) bottom.iter().all(|e| e.is_zero()) && m[(D, D)] == T2::one() } #[inline] fn from_superset_unchecked( m: &OMatrix, U1>, DimNameSum, U1>>, ) -> Self { let r = m.fixed_view::(0, 0); Self::from_matrix_unchecked(crate::convert_unchecked(r.into_owned())) } } impl From> for Matrix3 { #[inline] fn from(q: Rotation2) -> Self { q.to_homogeneous() } } impl From> for Matrix2 { #[inline] fn from(q: Rotation2) -> Self { q.into_inner() } } impl From> for Matrix4 { #[inline] fn from(q: Rotation3) -> Self { q.to_homogeneous() } } impl From> for Matrix3 { #[inline] fn from(q: Rotation3) -> Self { q.into_inner() } } impl From<[Rotation; 2]> for Rotation where T: From<[::Element; 2]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Rotation; 2]) -> Self { Self::from_matrix_unchecked(OMatrix::from([arr[0].into_inner(), arr[1].into_inner()])) } } impl From<[Rotation; 4]> for Rotation where T: From<[::Element; 4]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Rotation; 4]) -> Self { Self::from_matrix_unchecked(OMatrix::from([ arr[0].into_inner(), arr[1].into_inner(), arr[2].into_inner(), arr[3].into_inner(), ])) } } impl From<[Rotation; 8]> for Rotation where T: From<[::Element; 8]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Rotation; 8]) -> Self { Self::from_matrix_unchecked(OMatrix::from([ arr[0].into_inner(), arr[1].into_inner(), arr[2].into_inner(), arr[3].into_inner(), arr[4].into_inner(), arr[5].into_inner(), arr[6].into_inner(), arr[7].into_inner(), ])) } } impl From<[Rotation; 16]> for Rotation where T: From<[::Element; 16]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [Rotation; 16]) -> Self { Self::from_matrix_unchecked(OMatrix::from([ arr[0].into_inner(), arr[1].into_inner(), arr[2].into_inner(), arr[3].into_inner(), arr[4].into_inner(), arr[5].into_inner(), arr[6].into_inner(), arr[7].into_inner(), arr[8].into_inner(), arr[9].into_inner(), arr[10].into_inner(), arr[11].into_inner(), arr[12].into_inner(), arr[13].into_inner(), arr[14].into_inner(), arr[15].into_inner(), ])) } } nalgebra-0.33.0/src/geometry/rotation_interpolation.rs000064400000000000000000000054360072674642500213200ustar 00000000000000use crate::{RealField, Rotation2, Rotation3, SimdRealField, UnitComplex, UnitQuaternion}; /// # Interpolation impl Rotation2 { /// Spherical linear interpolation between two rotation matrices. /// /// # Examples: /// /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::geometry::Rotation2; /// /// let rot1 = Rotation2::new(std::f32::consts::FRAC_PI_4); /// let rot2 = Rotation2::new(-std::f32::consts::PI); /// /// let rot = rot1.slerp(&rot2, 1.0 / 3.0); /// /// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self where T::Element: SimdRealField, { let c1 = UnitComplex::from(self.clone()); let c2 = UnitComplex::from(other.clone()); c1.slerp(&c2, t).into() } } impl Rotation3 { /// Spherical linear interpolation between two rotation matrices. /// /// Panics if the angle between both rotations is 180 degrees (in which case the interpolation /// is not well-defined). Use `.try_slerp` instead to avoid the panic. /// /// # Examples: /// /// ``` /// # use nalgebra::geometry::Rotation3; /// /// let q1 = Rotation3::from_euler_angles(std::f32::consts::FRAC_PI_4, 0.0, 0.0); /// let q2 = Rotation3::from_euler_angles(-std::f32::consts::PI, 0.0, 0.0); /// /// let q = q1.slerp(&q2, 1.0 / 3.0); /// /// assert_eq!(q.euler_angles(), (std::f32::consts::FRAC_PI_2, 0.0, 0.0)); /// ``` #[inline] #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self where T: RealField, { let q1 = UnitQuaternion::from(self.clone()); let q2 = UnitQuaternion::from(other.clone()); q1.slerp(&q2, t).into() } /// Computes the spherical linear interpolation between two rotation matrices or returns `None` /// if both rotations are approximately 180 degrees apart (in which case the interpolation is /// not well-defined). /// /// # Arguments /// * `self`: the first rotation to interpolate from. /// * `other`: the second rotation to interpolate toward. /// * `t`: the interpolation parameter. Should be between 0 and 1. /// * `epsilon`: the value below which the sinus of the angle separating both rotations /// must be to return `None`. #[inline] #[must_use] pub fn try_slerp(&self, other: &Self, t: T, epsilon: T) -> Option where T: RealField, { let q1 = UnitQuaternion::from(self.clone()); let q2 = UnitQuaternion::from(other.clone()); q1.try_slerp(&q2, t, epsilon).map(|q| q.into()) } } nalgebra-0.33.0/src/geometry/rotation_ops.rs000064400000000000000000000156420072674642500172320ustar 00000000000000/* * * This provides the following operator overladings: * * Index<(usize, usize)> * * Rotation × Rotation * Rotation ÷ Rotation * Rotation × Matrix * Matrix × Rotation * Matrix ÷ Rotation * Rotation × Point * Rotation × Unit * * * Rotation ×= Rotation * Matrix ×= Rotation */ use num::{One, Zero}; use std::ops::{Div, DivAssign, Index, Mul, MulAssign}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; use crate::base::allocator::Allocator; use crate::base::constraint::{AreMultipliable, ShapeConstraint}; use crate::base::dimension::{Dim, U1}; use crate::base::storage::Storage; use crate::base::{ Const, DefaultAllocator, Matrix, OMatrix, SMatrix, SVector, Scalar, Unit, Vector, }; use crate::geometry::{Point, Rotation}; impl Index<(usize, usize)> for Rotation { type Output = T; #[inline] fn index(&self, row_col: (usize, usize)) -> &T { self.matrix().index(row_col) } } // Rotation × Rotation md_impl_all!( Mul, mul; (Const, Const), (Const, Const) const D; for; where; self: Rotation, right: Rotation, Output = Rotation; [val val] => Rotation::from_matrix_unchecked(self.into_inner() * right.into_inner()); [ref val] => Rotation::from_matrix_unchecked(self.matrix() * right.into_inner()); [val ref] => Rotation::from_matrix_unchecked(self.into_inner() * right.matrix()); [ref ref] => Rotation::from_matrix_unchecked(self.matrix() * right.matrix()); ); // Rotation ÷ Rotation // TODO: instead of calling inverse explicitly, could we just add a `mul_tr` or `mul_inv` method? md_impl_all!( Div, div; (Const, Const), (Const, Const) const D; for; where; self: Rotation, right: Rotation, Output = Rotation; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Rotation × Matrix md_impl_all!( Mul, mul; (Const, Const), (R2, C2) const D1; for R2, C2, SB; where R2: Dim, C2: Dim, SB: Storage, DefaultAllocator: Allocator, C2>, ShapeConstraint: AreMultipliable, Const, R2, C2>; self: Rotation, right: Matrix, Output = OMatrix, C2>; [val val] => self.into_inner() * right; [ref val] => self.matrix() * right; [val ref] => self.into_inner() * right; [ref ref] => self.matrix() * right; ); // Matrix × Rotation md_impl_all!( Mul, mul; (R1, C1), (Const, Const) const D2; for R1, C1, SA; where R1: Dim, C1: Dim, SA: Storage, DefaultAllocator: Allocator>, ShapeConstraint: AreMultipliable, Const>; self: Matrix, right: Rotation, Output = OMatrix>; [val val] => self * right.into_inner(); [ref val] => self * right.into_inner(); [val ref] => self * right.matrix(); [ref ref] => self * right.matrix(); ); // Matrix ÷ Rotation md_impl_all!( Div, div; (R1, C1), (Const, Const) const D2; for R1, C1, SA; where R1: Dim, C1: Dim, SA: Storage, DefaultAllocator: Allocator>, ShapeConstraint: AreMultipliable, Const>; self: Matrix, right: Rotation, Output = OMatrix>; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Rotation × Point // TODO: we don't handle properly non-zero origins here. Do we want this to be the intended // behavior? md_impl_all!( Mul, mul; (Const, Const), (Const, U1) const D; for; where ShapeConstraint: AreMultipliable, Const, Const, U1>; self: Rotation, right: Point, Output = Point; [val val] => self.into_inner() * right; [ref val] => self.matrix() * right; [val ref] => self.into_inner() * right; [ref ref] => self.matrix() * right; ); // Rotation × Unit md_impl_all!( Mul, mul; (Const, Const), (Const, U1) const D; for S; where S: Storage>, ShapeConstraint: AreMultipliable, Const, Const, U1>; self: Rotation, right: Unit, S>>, Output = Unit>; [val val] => Unit::new_unchecked(self.into_inner() * right.into_inner()); [ref val] => Unit::new_unchecked(self.matrix() * right.into_inner()); [val ref] => Unit::new_unchecked(self.into_inner() * right.as_ref()); [ref ref] => Unit::new_unchecked(self.matrix() * right.as_ref()); ); // Rotation ×= Rotation // TODO: try not to call `inverse()` explicitly. md_assign_impl_all!( MulAssign, mul_assign; (Const, Const), (Const, Const) const D; for; where; self: Rotation, right: Rotation; [val] => self.matrix_mut_unchecked().mul_assign(right.into_inner()); [ref] => self.matrix_mut_unchecked().mul_assign(right.matrix()); ); md_assign_impl_all!( DivAssign, div_assign; (Const, Const), (Const, Const) const D; for; where; self: Rotation, right: Rotation; [val] => self.matrix_mut_unchecked().mul_assign(right.inverse().into_inner()); [ref] => self.matrix_mut_unchecked().mul_assign(right.inverse().matrix()); ); // Matrix *= Rotation // TODO: try not to call `inverse()` explicitly. // TODO: this shares the same limitations as for the current impl. of MulAssign for matrices. // (In particular the number of matrix column must be equal to the number of rotation columns, // i.e., equal to the rotation dimension. md_assign_impl_all!( MulAssign, mul_assign; (Const, Const), (Const, Const) const R1, C1; for; where; self: SMatrix, right: Rotation; [val] => self.mul_assign(right.into_inner()); [ref] => self.mul_assign(right.matrix()); ); md_assign_impl_all!( DivAssign, div_assign; (Const, Const), (Const, Const) const R1, C1; for; where; self: SMatrix, right: Rotation; [val] => self.mul_assign(right.inverse().into_inner()); [ref] => self.mul_assign(right.inverse().matrix()); ); nalgebra-0.33.0/src/geometry/rotation_simba.rs000064400000000000000000000024450072674642500175210ustar 00000000000000use simba::simd::SimdValue; use crate::base::{OMatrix, Scalar}; use crate::geometry::Rotation; impl SimdValue for Rotation where T: Scalar + SimdValue, T::Element: Scalar, { const LANES: usize = T::LANES; type Element = Rotation; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { Rotation::from_matrix_unchecked(OMatrix::splat(val.into_inner())) } #[inline] fn extract(&self, i: usize) -> Self::Element { Rotation::from_matrix_unchecked(self.matrix().extract(i)) } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { Rotation::from_matrix_unchecked(self.matrix().extract_unchecked(i)) } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.matrix_mut_unchecked().replace(i, val.into_inner()) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.matrix_mut_unchecked() .replace_unchecked(i, val.into_inner()) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { Rotation::from_matrix_unchecked(self.into_inner().select(cond, other.into_inner())) } } nalgebra-0.33.0/src/geometry/rotation_specialization.rs000064400000000000000000001254470072674642500214540ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::Zero; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{uniform::SampleUniform, Distribution, OpenClosed01, Standard, Uniform}, Rng, }; use simba::scalar::RealField; use simba::simd::{SimdBool, SimdRealField}; use std::ops::Neg; use crate::base::dimension::{U1, U2, U3}; use crate::base::storage::Storage; use crate::base::{ Matrix2, Matrix3, SMatrix, SVector, Unit, UnitVector3, Vector, Vector1, Vector2, Vector3, }; use crate::geometry::{Rotation2, Rotation3, UnitComplex, UnitQuaternion}; /* * * 2D Rotation matrix. * */ /// # Construction from a 2D rotation angle impl Rotation2 { /// Builds a 2 dimensional rotation matrix from an angle in radian. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation2, Point2}; /// let rot = Rotation2::new(f32::consts::FRAC_PI_2); /// /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); /// ``` pub fn new(angle: T) -> Self { let (sia, coa) = angle.simd_sin_cos(); Self::from_matrix_unchecked(Matrix2::new(coa.clone(), -sia.clone(), sia, coa)) } /// Builds a 2 dimensional rotation matrix from an angle in radian wrapped in a 1-dimensional vector. /// /// /// This is generally used in the context of generic programming. Using /// the `::new(angle)` method instead is more common. #[inline] pub fn from_scaled_axis>(axisangle: Vector) -> Self { Self::new(axisangle[0].clone()) } } /// # Construction from an existing 2D matrix or rotations impl Rotation2 { /// Builds a rotation from a basis assumed to be orthonormal. /// /// In order to get a valid rotation matrix, the input must be an /// orthonormal basis, i.e., all vectors are normalized, and the are /// all orthogonal to each other. These invariants are not checked /// by this method. pub fn from_basis_unchecked(basis: &[Vector2; 2]) -> Self { let mat = Matrix2::from_columns(&basis[..]); Self::from_matrix_unchecked(mat) } /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. /// /// This is an iterative method. See `.from_matrix_eps` to provide mover /// convergence parameters and starting solution. /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. pub fn from_matrix(m: &Matrix2) -> Self where T: RealField, { Self::from_matrix_eps(m, T::default_epsilon(), 0, Self::identity()) } /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. /// /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. /// /// # Parameters /// /// * `m`: the matrix from which the rotational part is to be extracted. /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. /// * `guess`: an estimate of the solution. Convergence will be significantly faster if an initial solution close /// to the actual solution is provided. Can be set to `Rotation2::identity()` if no other /// guesses come to mind. pub fn from_matrix_eps(m: &Matrix2, eps: T, mut max_iter: usize, guess: Self) -> Self where T: RealField, { if max_iter == 0 { max_iter = usize::max_value(); } let mut rot = guess.into_inner(); for _ in 0..max_iter { let axis = rot.column(0).perp(&m.column(0)) + rot.column(1).perp(&m.column(1)); let denom = rot.column(0).dot(&m.column(0)) + rot.column(1).dot(&m.column(1)); let angle = axis / (denom.abs() + T::default_epsilon()); if angle.clone().abs() > eps { rot = Self::new(angle) * rot; } else { break; } } Self::from_matrix_unchecked(rot) } /// The rotation matrix required to align `a` and `b` but with its angle. /// /// This is the rotation `R` such that `(R * a).angle(b) == 0 && (R * a).dot(b).is_positive()`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector2, Rotation2}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); /// let rot = Rotation2::rotation_between(&a, &b); /// assert_relative_eq!(rot * a, b); /// assert_relative_eq!(rot.inverse() * b, a); /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Self where T: RealField, SB: Storage, SC: Storage, { crate::convert(UnitComplex::rotation_between(a, b).to_rotation_matrix()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector2, Rotation2}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); /// let rot2 = Rotation2::scaled_rotation_between(&a, &b, 0.2); /// let rot5 = Rotation2::scaled_rotation_between(&a, &b, 0.5); /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, b: &Vector, s: T, ) -> Self where T: RealField, SB: Storage, SC: Storage, { crate::convert(UnitComplex::scaled_rotation_between(a, b, s).to_rotation_matrix()) } /// The rotation matrix needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation2; /// let rot1 = Rotation2::new(0.1); /// let rot2 = Rotation2::new(1.7); /// let rot_to = rot1.rotation_to(&rot2); /// /// assert_relative_eq!(rot_to * rot1, rot2); /// assert_relative_eq!(rot_to.inverse() * rot2, rot1); /// ``` #[inline] #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other * self.inverse() } /// Ensure this rotation is an orthonormal rotation matrix. This is useful when repeated /// computations might cause the matrix from progressively not being orthonormal anymore. #[inline] pub fn renormalize(&mut self) where T: RealField, { let mut c = UnitComplex::from(self.clone()); let _ = c.renormalize(); *self = Self::from_matrix_eps(self.matrix(), T::default_epsilon(), 0, c.into()) } /// Raise the rotation to a given floating power, i.e., returns the rotation with the angle /// of `self` multiplied by `n`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation2; /// let rot = Rotation2::new(0.78); /// let pow = rot.powf(2.0); /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); /// ``` #[inline] #[must_use] pub fn powf(&self, n: T) -> Self { Self::new(self.angle() * n) } } /// # 2D angle extraction impl Rotation2 { /// The rotation angle. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation2; /// let rot = Rotation2::new(1.78); /// assert_relative_eq!(rot.angle(), 1.78); /// ``` #[inline] #[must_use] pub fn angle(&self) -> T { self.matrix()[(1, 0)] .clone() .simd_atan2(self.matrix()[(0, 0)].clone()) } /// The rotation angle needed to make `self` and `other` coincide. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation2; /// let rot1 = Rotation2::new(0.1); /// let rot2 = Rotation2::new(1.7); /// assert_relative_eq!(rot1.angle_to(&rot2), 1.6); /// ``` #[inline] #[must_use] pub fn angle_to(&self, other: &Self) -> T { self.rotation_to(other).angle() } /// The rotation angle returned as a 1-dimensional vector. /// /// This is generally used in the context of generic programming. Using /// the `.angle()` method instead is more common. #[inline] #[must_use] pub fn scaled_axis(&self) -> SVector { Vector1::new(self.angle()) } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where T::Element: SimdRealField, T: SampleUniform, { /// Generate a uniformly distributed random rotation. #[inline] fn sample(&self, rng: &mut R) -> Rotation2 { let twopi = Uniform::new(T::zero(), T::simd_two_pi()); Rotation2::new(rng.sample(twopi)) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Rotation2 where T::Element: SimdRealField, Owned: Send, { #[inline] fn arbitrary(g: &mut Gen) -> Self { Self::new(T::arbitrary(g)) } } /* * * 3D Rotation matrix. * */ /// # Construction from a 3D axis and/or angles impl Rotation3 where T::Element: SimdRealField, { /// Builds a 3 dimensional rotation matrix from an axis and an angle. /// /// # Arguments /// * `axisangle` - A vector representing the rotation. Its magnitude is the amount of rotation /// in radian. Its direction is the axis of rotation. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let rot = Rotation3::new(axisangle); /// /// assert_relative_eq!(rot * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(rot * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // A zero vector yields an identity. /// assert_eq!(Rotation3::new(Vector3::::zeros()), Rotation3::identity()); /// ``` pub fn new>(axisangle: Vector) -> Self { let axisangle = axisangle.into_owned(); let (axis, angle) = Unit::new_and_get(axisangle); Self::from_axis_angle(&axis, angle) } /// Builds a 3D rotation matrix from an axis scaled by the rotation angle. /// /// This is the same as `Self::new(axisangle)`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let rot = Rotation3::new(axisangle); /// /// assert_relative_eq!(rot * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(rot * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // A zero vector yields an identity. /// assert_eq!(Rotation3::from_scaled_axis(Vector3::::zeros()), Rotation3::identity()); /// ``` pub fn from_scaled_axis>(axisangle: Vector) -> Self { Self::new(axisangle) } /// Builds a 3D rotation matrix from an axis and a rotation angle. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation3, Point3, Vector3}; /// let axis = Vector3::y_axis(); /// let angle = f32::consts::FRAC_PI_2; /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// let rot = Rotation3::from_axis_angle(&axis, angle); /// /// assert_eq!(rot.axis().unwrap(), axis); /// assert_eq!(rot.angle(), angle); /// assert_relative_eq!(rot * pt, Point3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// assert_relative_eq!(rot * vec, Vector3::new(6.0, 5.0, -4.0), epsilon = 1.0e-6); /// /// // A zero vector yields an identity. /// assert_eq!(Rotation3::from_scaled_axis(Vector3::::zeros()), Rotation3::identity()); /// ``` pub fn from_axis_angle(axis: &Unit>, angle: T) -> Self where SB: Storage, { angle.clone().simd_ne(T::zero()).if_else( || { let ux = axis.as_ref()[0].clone(); let uy = axis.as_ref()[1].clone(); let uz = axis.as_ref()[2].clone(); let sqx = ux.clone() * ux.clone(); let sqy = uy.clone() * uy.clone(); let sqz = uz.clone() * uz.clone(); let (sin, cos) = angle.simd_sin_cos(); let one_m_cos = T::one() - cos.clone(); Self::from_matrix_unchecked(SMatrix::::new( sqx.clone() + (T::one() - sqx) * cos.clone(), ux.clone() * uy.clone() * one_m_cos.clone() - uz.clone() * sin.clone(), ux.clone() * uz.clone() * one_m_cos.clone() + uy.clone() * sin.clone(), ux.clone() * uy.clone() * one_m_cos.clone() + uz.clone() * sin.clone(), sqy.clone() + (T::one() - sqy) * cos.clone(), uy.clone() * uz.clone() * one_m_cos.clone() - ux.clone() * sin.clone(), ux.clone() * uz.clone() * one_m_cos.clone() - uy.clone() * sin.clone(), uy * uz * one_m_cos + ux * sin, sqz.clone() + (T::one() - sqz) * cos, )) }, Self::identity, ) } /// Creates a new rotation from Euler angles. /// /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation3; /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); /// ``` pub fn from_euler_angles(roll: T, pitch: T, yaw: T) -> Self { let (sr, cr) = roll.simd_sin_cos(); let (sp, cp) = pitch.simd_sin_cos(); let (sy, cy) = yaw.simd_sin_cos(); Self::from_matrix_unchecked(SMatrix::::new( cy.clone() * cp.clone(), cy.clone() * sp.clone() * sr.clone() - sy.clone() * cr.clone(), cy.clone() * sp.clone() * cr.clone() + sy.clone() * sr.clone(), sy.clone() * cp.clone(), sy.clone() * sp.clone() * sr.clone() + cy.clone() * cr.clone(), sy * sp.clone() * cr.clone() - cy * sr.clone(), -sp, cp.clone() * sr, cp * cr, )) } } /// # Construction from a 3D eye position and target point impl Rotation3 where T::Element: SimdRealField, { /// Creates a rotation that corresponds to the local frame of an observer standing at the /// origin and looking toward `dir`. /// /// It maps the `z` axis to the direction `dir`. /// /// # Arguments /// * dir - The look direction, that is, direction the matrix `z` axis will be aligned with. /// * up - The vertical direction. The only requirement of this parameter is to not be /// collinear to `dir`. Non-collinearity is not checked. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// let rot = Rotation3::face_towards(&dir, &up); /// assert_relative_eq!(rot * Vector3::z(), dir.normalize()); /// ``` #[inline] pub fn face_towards(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { // Gram–Schmidt process let zaxis = dir.normalize(); let xaxis = up.cross(&zaxis).normalize(); let yaxis = zaxis.cross(&xaxis); Self::from_matrix_unchecked(SMatrix::::new( xaxis.x.clone(), yaxis.x.clone(), zaxis.x.clone(), xaxis.y.clone(), yaxis.y.clone(), zaxis.y.clone(), xaxis.z.clone(), yaxis.z.clone(), zaxis.z.clone(), )) } /// Deprecated: Use [`Rotation3::face_towards`] instead. #[deprecated(note = "renamed to `face_towards`")] pub fn new_observer_frames(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { Self::face_towards(dir, up) } /// Builds a right-handed look-at view matrix without translation. /// /// It maps the view direction `dir` to the **negative** `z` axis. /// This conforms to the common notion of right handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * dir - The direction toward which the camera looks. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `dir`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// let rot = Rotation3::look_at_rh(&dir, &up); /// assert_relative_eq!(rot * dir.normalize(), -Vector3::z()); /// ``` #[inline] pub fn look_at_rh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { Self::face_towards(&dir.neg(), up).inverse() } /// Builds a left-handed look-at view matrix without translation. /// /// It maps the view direction `dir` to the **positive** `z` axis. /// This conforms to the common notion of left handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * dir - The direction toward which the camera looks. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `dir`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Rotation3, Vector3}; /// let dir = Vector3::new(1.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// let rot = Rotation3::look_at_lh(&dir, &up); /// assert_relative_eq!(rot * dir.normalize(), Vector3::z()); /// ``` #[inline] pub fn look_at_lh(dir: &Vector, up: &Vector) -> Self where SB: Storage, SC: Storage, { Self::face_towards(dir, up).inverse() } } /// # Construction from an existing 3D matrix or rotations impl Rotation3 where T::Element: SimdRealField, { /// The rotation matrix required to align `a` and `b` but with its angle. /// /// This is the rotation `R` such that `(R * a).angle(b) == 0 && (R * a).dot(b).is_positive()`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector3, Rotation3}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); /// let rot = Rotation3::rotation_between(&a, &b).unwrap(); /// assert_relative_eq!(rot * a, b, epsilon = 1.0e-6); /// assert_relative_eq!(rot.inverse() * b, a, epsilon = 1.0e-6); /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Option where T: RealField, SB: Storage, SC: Storage, { Self::scaled_rotation_between(a, b, T::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector3, Rotation3}; /// let a = Vector3::new(1.0, 2.0, 3.0); /// let b = Vector3::new(3.0, 1.0, 2.0); /// let rot2 = Rotation3::scaled_rotation_between(&a, &b, 0.2).unwrap(); /// let rot5 = Rotation3::scaled_rotation_between(&a, &b, 0.5).unwrap(); /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, b: &Vector, n: T, ) -> Option where T: RealField, SB: Storage, SC: Storage, { // TODO: code duplication with Rotation. if let (Some(na), Some(nb)) = (a.try_normalize(T::zero()), b.try_normalize(T::zero())) { let c = na.cross(&nb); if let Some(axis) = Unit::try_new(c, T::default_epsilon()) { return Some(Self::from_axis_angle(&axis, na.dot(&nb).acos() * n)); } // Zero or PI. if na.dot(&nb) < T::zero() { // PI // // The rotation axis is undefined but the angle not zero. This is not a // simple rotation. return None; } } Some(Self::identity()) } /// The rotation matrix needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation3, Vector3}; /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); /// let rot_to = rot1.rotation_to(&rot2); /// assert_relative_eq!(rot_to * rot1, rot2, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other * self.inverse() } /// Raise the rotation to a given floating power, i.e., returns the rotation with the same /// axis as `self` and an angle equal to `self.angle()` multiplied by `n`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; /// let rot = Rotation3::from_axis_angle(&axis, angle); /// let pow = rot.powf(2.0); /// assert_relative_eq!(pow.axis().unwrap(), axis, epsilon = 1.0e-6); /// assert_eq!(pow.angle(), 2.4); /// ``` #[inline] #[must_use] pub fn powf(&self, n: T) -> Self where T: RealField, { if let Some(axis) = self.axis() { Self::from_axis_angle(&axis, self.angle() * n) } else if self.matrix()[(0, 0)] < T::zero() { let minus_id = SMatrix::::from_diagonal_element(-T::one()); Self::from_matrix_unchecked(minus_id) } else { Self::identity() } } /// Builds a rotation from a basis assumed to be orthonormal. /// /// In order to get a valid rotation matrix, the input must be an /// orthonormal basis, i.e., all vectors are normalized, and the are /// all orthogonal to each other. These invariants are not checked /// by this method. pub fn from_basis_unchecked(basis: &[Vector3; 3]) -> Self { let mat = Matrix3::from_columns(&basis[..]); Self::from_matrix_unchecked(mat) } /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. /// /// This is an iterative method. See `.from_matrix_eps` to provide mover /// convergence parameters and starting solution. /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. pub fn from_matrix(m: &Matrix3) -> Self where T: RealField, { Self::from_matrix_eps(m, T::default_epsilon(), 0, Self::identity()) } /// Builds a rotation matrix by extracting the rotation part of the given transformation `m`. /// /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. /// /// # Parameters /// /// * `m`: the matrix from which the rotational part is to be extracted. /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. /// * `guess`: a guess of the solution. Convergence will be significantly faster if an initial solution close /// to the actual solution is provided. Can be set to `Rotation3::identity()` if no other /// guesses come to mind. pub fn from_matrix_eps(m: &Matrix3, eps: T, mut max_iter: usize, guess: Self) -> Self where T: RealField, { if max_iter == 0 { max_iter = usize::MAX; } // Using sqrt(eps) ensures we perturb with something larger than eps; clamp to eps to handle the case of eps > 1.0 let eps_disturbance = eps.clone().sqrt().max(eps.clone() * eps.clone()); let mut perturbation_axes = Vector3::x_axis(); let mut rot = guess.into_inner(); for _ in 0..max_iter { let axis = rot.column(0).cross(&m.column(0)) + rot.column(1).cross(&m.column(1)) + rot.column(2).cross(&m.column(2)); let denom = rot.column(0).dot(&m.column(0)) + rot.column(1).dot(&m.column(1)) + rot.column(2).dot(&m.column(2)); let axisangle = axis / (denom.abs() + T::default_epsilon()); if let Some((axis, angle)) = Unit::try_new_and_get(axisangle, eps.clone()) { rot = Rotation3::from_axis_angle(&axis, angle) * rot; } else { // Check if stuck in a maximum w.r.t. the norm (m - rot).norm() let mut perturbed = rot.clone(); let norm_squared = (m - &rot).norm_squared(); let mut new_norm_squared: T; // Perturb until the new norm is significantly different loop { perturbed *= Rotation3::from_axis_angle(&perturbation_axes, eps_disturbance.clone()); new_norm_squared = (m - &perturbed).norm_squared(); if abs_diff_ne!( norm_squared, new_norm_squared, epsilon = T::default_epsilon() ) { break; } } // If new norm is larger, it's a minimum if norm_squared < new_norm_squared { break; } // If not, continue from perturbed rotation, but use a different axes for the next perturbation perturbation_axes = UnitVector3::new_unchecked(perturbation_axes.yzx()); rot = perturbed; } } Self::from_matrix_unchecked(rot) } /// Ensure this rotation is an orthonormal rotation matrix. This is useful when repeated /// computations might cause the matrix from progressively not being orthonormal anymore. #[inline] pub fn renormalize(&mut self) where T: RealField, { let mut c = UnitQuaternion::from(self.clone()); let _ = c.renormalize(); *self = Self::from_matrix_eps(self.matrix(), T::default_epsilon(), 0, c.into()) } } /// # 3D axis and angle extraction impl Rotation3 { /// The rotation angle in [0; pi]. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Unit, Rotation3, Vector3}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let rot = Rotation3::from_axis_angle(&axis, 1.78); /// assert_relative_eq!(rot.angle(), 1.78); /// ``` #[inline] #[must_use] pub fn angle(&self) -> T { ((self.matrix()[(0, 0)].clone() + self.matrix()[(1, 1)].clone() + self.matrix()[(2, 2)].clone() - T::one()) / crate::convert(2.0)) .simd_acos() } /// The rotation axis. Returns `None` if the rotation angle is zero or PI. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; /// let rot = Rotation3::from_axis_angle(&axis, angle); /// assert_relative_eq!(rot.axis().unwrap(), axis); /// /// // Case with a zero angle. /// let rot = Rotation3::from_axis_angle(&axis, 0.0); /// assert!(rot.axis().is_none()); /// ``` #[inline] #[must_use] pub fn axis(&self) -> Option>> where T: RealField, { let rotmat = self.matrix(); let axis = SVector::::new( rotmat[(2, 1)].clone() - rotmat[(1, 2)].clone(), rotmat[(0, 2)].clone() - rotmat[(2, 0)].clone(), rotmat[(1, 0)].clone() - rotmat[(0, 1)].clone(), ); Unit::try_new(axis, T::default_epsilon()) } /// The rotation axis multiplied by the rotation angle. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axisangle = Vector3::new(0.1, 0.2, 0.3); /// let rot = Rotation3::new(axisangle); /// assert_relative_eq!(rot.scaled_axis(), axisangle, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn scaled_axis(&self) -> Vector3 where T: RealField, { if let Some(axis) = self.axis() { axis.into_inner() * self.angle() } else { Vector::zero() } } /// The rotation axis and angle in (0, pi] of this rotation matrix. /// /// Returns `None` if the angle is zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation3, Vector3, Unit}; /// let axis = Unit::new_normalize(Vector3::new(1.0, 2.0, 3.0)); /// let angle = 1.2; /// let rot = Rotation3::from_axis_angle(&axis, angle); /// let axis_angle = rot.axis_angle().unwrap(); /// assert_relative_eq!(axis_angle.0, axis); /// assert_relative_eq!(axis_angle.1, angle); /// /// // Case with a zero angle. /// let rot = Rotation3::from_axis_angle(&axis, 0.0); /// assert!(rot.axis_angle().is_none()); /// ``` #[inline] #[must_use] pub fn axis_angle(&self) -> Option<(Unit>, T)> where T: RealField, { self.axis().map(|axis| (axis, self.angle())) } /// The rotation angle needed to make `self` and `other` coincide. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Rotation3, Vector3}; /// let rot1 = Rotation3::from_axis_angle(&Vector3::y_axis(), 1.0); /// let rot2 = Rotation3::from_axis_angle(&Vector3::x_axis(), 0.1); /// assert_relative_eq!(rot1.angle_to(&rot2), 1.0045657, epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn angle_to(&self, other: &Self) -> T where T::Element: SimdRealField, { self.rotation_to(other).angle() } /// Creates Euler angles from a rotation. /// /// The angles are produced in the form (roll, pitch, yaw). #[deprecated(note = "This is renamed to use `.euler_angles()`.")] pub fn to_euler_angles(self) -> (T, T, T) where T: RealField, { self.euler_angles() } /// Euler angles corresponding to this rotation from a rotation. /// /// The angles are produced in the form (roll, pitch, yaw). /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::Rotation3; /// let rot = Rotation3::from_euler_angles(0.1, 0.2, 0.3); /// let euler = rot.euler_angles(); /// assert_relative_eq!(euler.0, 0.1, epsilon = 1.0e-6); /// assert_relative_eq!(euler.1, 0.2, epsilon = 1.0e-6); /// assert_relative_eq!(euler.2, 0.3, epsilon = 1.0e-6); /// ``` #[must_use] pub fn euler_angles(&self) -> (T, T, T) where T: RealField, { // Implementation informed by "Computing Euler angles from a rotation matrix", by Gregory G. Slabaugh // https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.371.6578 // where roll, pitch, yaw angles are referred to as ψ, θ, ϕ, if self[(2, 0)].clone().abs() < T::one() { let pitch = -self[(2, 0)].clone().asin(); let theta_cos = pitch.clone().cos(); let roll = (self[(2, 1)].clone() / theta_cos.clone()) .atan2(self[(2, 2)].clone() / theta_cos.clone()); let yaw = (self[(1, 0)].clone() / theta_cos.clone()).atan2(self[(0, 0)].clone() / theta_cos); (roll, pitch, yaw) } else if self[(2, 0)].clone() <= -T::one() { ( self[(0, 1)].clone().atan2(self[(0, 2)].clone()), T::frac_pi_2(), T::zero(), ) } else { ( -self[(0, 1)].clone().atan2(-self[(0, 2)].clone()), -T::frac_pi_2(), T::zero(), ) } } /// Represent this rotation as Euler angles. /// /// Returns the angles produced in the order provided by seq parameter, along with the /// observability flag. The Euler axes passed to seq must form an orthonormal basis. If the /// rotation is gimbal locked, then the observability flag is false. /// /// # Panics /// /// Panics if the Euler axes in `seq` are not orthonormal. /// /// # Example 1: /// ``` /// use std::f64::consts::PI; /// use approx::assert_relative_eq; /// use nalgebra::{Matrix3, Rotation3, Unit, Vector3}; /// /// // 3-1-2 /// let n = [ /// Unit::new_unchecked(Vector3::new(0.0, 0.0, 1.0)), /// Unit::new_unchecked(Vector3::new(1.0, 0.0, 0.0)), /// Unit::new_unchecked(Vector3::new(0.0, 1.0, 0.0)), /// ]; /// /// let r1 = Rotation3::from_axis_angle(&n[2], 20.0 * PI / 180.0); /// let r2 = Rotation3::from_axis_angle(&n[1], 30.0 * PI / 180.0); /// let r3 = Rotation3::from_axis_angle(&n[0], 45.0 * PI / 180.0); /// /// let d = r3 * r2 * r1; /// /// let (angles, observable) = d.euler_angles_ordered(n, false); /// assert!(observable); /// assert_relative_eq!(angles[0] * 180.0 / PI, 45.0, epsilon = 1e-12); /// assert_relative_eq!(angles[1] * 180.0 / PI, 30.0, epsilon = 1e-12); /// assert_relative_eq!(angles[2] * 180.0 / PI, 20.0, epsilon = 1e-12); /// ``` /// /// # Example 2: /// ``` /// use std::f64::consts::PI; /// use approx::assert_relative_eq; /// use nalgebra::{Matrix3, Rotation3, Unit, Vector3}; /// /// let sqrt_2 = 2.0_f64.sqrt(); /// let n = [ /// Unit::new_unchecked(Vector3::new(1.0 / sqrt_2, 1.0 / sqrt_2, 0.0)), /// Unit::new_unchecked(Vector3::new(1.0 / sqrt_2, -1.0 / sqrt_2, 0.0)), /// Unit::new_unchecked(Vector3::new(0.0, 0.0, 1.0)), /// ]; /// /// let r1 = Rotation3::from_axis_angle(&n[2], 20.0 * PI / 180.0); /// let r2 = Rotation3::from_axis_angle(&n[1], 30.0 * PI / 180.0); /// let r3 = Rotation3::from_axis_angle(&n[0], 45.0 * PI / 180.0); /// /// let d = r3 * r2 * r1; /// /// let (angles, observable) = d.euler_angles_ordered(n, false); /// assert!(observable); /// assert_relative_eq!(angles[0] * 180.0 / PI, 45.0, epsilon = 1e-12); /// assert_relative_eq!(angles[1] * 180.0 / PI, 30.0, epsilon = 1e-12); /// assert_relative_eq!(angles[2] * 180.0 / PI, 20.0, epsilon = 1e-12); /// ``` /// /// Algorithm based on: /// Malcolm D. Shuster, F. Landis Markley, “General formula for extraction the Euler /// angles”, Journal of guidance, control, and dynamics, vol. 29.1, pp. 215-221. 2006, /// and modified to be able to produce extrinsic rotations. #[must_use] pub fn euler_angles_ordered( &self, mut seq: [Unit>; 3], extrinsic: bool, ) -> ([T; 3], bool) where T: RealField + Copy, { let mut angles = [T::zero(); 3]; let eps = T::from_subset(&1e-7); let two = T::from_subset(&2.0); if extrinsic { seq.reverse(); } let [n1, n2, n3] = &seq; assert_relative_eq!(n1.dot(n2), T::zero(), epsilon = eps); assert_relative_eq!(n3.dot(n1), T::zero(), epsilon = eps); let n1_c_n2 = n1.cross(n2); let s1 = n1_c_n2.dot(n3); let c1 = n1.dot(n3); let lambda = s1.atan2(c1); let mut c = Matrix3::zeros(); c.column_mut(0).copy_from(n2); c.column_mut(1).copy_from(&n1_c_n2); c.column_mut(2).copy_from(n1); c.transpose_mut(); let r1l = Matrix3::new( T::one(), T::zero(), T::zero(), T::zero(), c1, s1, T::zero(), -s1, c1, ); let o_t = c * self.matrix() * (c.transpose() * r1l); angles[1] = o_t.m33.acos(); let safe1 = angles[1].abs() >= eps; let safe2 = (angles[1] - T::pi()).abs() >= eps; let observable = safe1 && safe2; angles[1] += lambda; if observable { angles[0] = o_t.m13.atan2(-o_t.m23); angles[2] = o_t.m31.atan2(o_t.m32); } else { // gimbal lock detected if extrinsic { // angle1 is initialized to zero if !safe1 { angles[2] = (o_t.m12 - o_t.m21).atan2(o_t.m11 + o_t.m22); } else { angles[2] = -(o_t.m12 + o_t.m21).atan2(o_t.m11 - o_t.m22); }; } else { // angle3 is initialized to zero if !safe1 { angles[0] = (o_t.m12 - o_t.m21).atan2(o_t.m11 + o_t.m22); } else { angles[0] = (o_t.m12 + o_t.m21).atan2(o_t.m11 - o_t.m22); }; }; }; let adjust = if seq[0] == seq[2] { // lambda = 0, so ensure angle2 -> [0, pi] angles[1] < T::zero() || angles[1] > T::pi() } else { // lamda = + or - pi/2, so ensure angle2 -> [-pi/2, pi/2] angles[1] < -T::frac_pi_2() || angles[1] > T::frac_pi_2() }; // dont adjust gimbal locked rotation if adjust && observable { angles[0] += T::pi(); angles[1] = two * lambda - angles[1]; angles[2] -= T::pi(); } // ensure all angles are within [-pi, pi] for angle in angles.as_mut_slice().iter_mut() { if *angle < -T::pi() { *angle += T::two_pi(); } else if *angle > T::pi() { *angle -= T::two_pi(); } } if extrinsic { angles.reverse(); } (angles, observable) } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where T::Element: SimdRealField, OpenClosed01: Distribution, T: SampleUniform, { /// Generate a uniformly distributed random rotation. #[inline] fn sample<'a, R: Rng + ?Sized>(&self, rng: &mut R) -> Rotation3 { // James Arvo. // Fast random rotation matrices. // In D. Kirk, editor, Graphics Gems III, pages 117-120. Academic, New York, 1992. // Compute a random rotation around Z let twopi = Uniform::new(T::zero(), T::simd_two_pi()); let theta = rng.sample(&twopi); let (ts, tc) = theta.simd_sin_cos(); let a = SMatrix::::new( tc.clone(), ts.clone(), T::zero(), -ts, tc, T::zero(), T::zero(), T::zero(), T::one(), ); // Compute a random rotation *of* Z let phi = rng.sample(&twopi); let z = rng.sample(OpenClosed01); let (ps, pc) = phi.simd_sin_cos(); let sqrt_z = z.clone().simd_sqrt(); let v = Vector3::new(pc * sqrt_z.clone(), ps * sqrt_z, (T::one() - z).simd_sqrt()); let mut b = v.clone() * v.transpose(); b += b.clone(); b -= SMatrix::::identity(); Rotation3::from_matrix_unchecked(b * a) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Rotation3 where T::Element: SimdRealField, Owned: Send, Owned: Send, { #[inline] fn arbitrary(g: &mut Gen) -> Self { Self::new(SVector::arbitrary(g)) } } nalgebra-0.33.0/src/geometry/scale.rs000064400000000000000000000272200072674642500155740ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use num::{One, Zero}; use std::fmt; use std::hash; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::storage::Owned; use crate::base::{Const, DefaultAllocator, OMatrix, OVector, SVector, Scalar}; use crate::ClosedDivAssign; use crate::ClosedMulAssign; use crate::geometry::Point; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A scale which supports non-uniform scaling. #[repr(C)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Scale", bound(archive = " T: rkyv::Archive, SVector: rkyv::Archive> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[derive(Copy, Clone)] pub struct Scale { /// The scale coordinates, i.e., how much is multiplied to a point's coordinates when it is /// scaled. pub vector: SVector, } impl fmt::Debug for Scale { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.vector.as_slice().fmt(formatter) } } impl hash::Hash for Scale where Owned>: hash::Hash, { fn hash(&self, state: &mut H) { self.vector.hash(state) } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Scale where T: Scalar + bytemuck::Zeroable, SVector: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Scale where T: Scalar + bytemuck::Pod, SVector: bytemuck::Pod, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Scale where Owned>: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.vector.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: Scalar, const D: usize> Deserialize<'a> for Scale where Owned>: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let matrix = SVector::::deserialize(deserializer)?; Ok(Scale::from(matrix)) } } impl Scale { /// Inverts `self`. /// /// # Example /// ``` /// # use nalgebra::{Scale2, Scale3}; /// let t = Scale3::new(1.0, 2.0, 3.0); /// assert_eq!(t * t.try_inverse().unwrap(), Scale3::identity()); /// assert_eq!(t.try_inverse().unwrap() * t, Scale3::identity()); /// /// // Work in all dimensions. /// let t = Scale2::new(1.0, 2.0); /// assert_eq!(t * t.try_inverse().unwrap(), Scale2::identity()); /// assert_eq!(t.try_inverse().unwrap() * t, Scale2::identity()); /// /// // Returns None if any coordinate is 0. /// let t = Scale2::new(0.0, 2.0); /// assert_eq!(t.try_inverse(), None); /// ``` #[inline] #[must_use = "Did you mean to use try_inverse_mut()?"] pub fn try_inverse(&self) -> Option> where T: ClosedDivAssign + One + Zero, { for i in 0..D { if self.vector[i] == T::zero() { return None; } } Some(self.vector.map(|e| T::one() / e).into()) } /// Inverts `self`. /// /// # Example /// ``` /// # use nalgebra::{Scale2, Scale3}; /// /// unsafe { /// let t = Scale3::new(1.0, 2.0, 3.0); /// assert_eq!(t * t.inverse_unchecked(), Scale3::identity()); /// assert_eq!(t.inverse_unchecked() * t, Scale3::identity()); /// /// // Work in all dimensions. /// let t = Scale2::new(1.0, 2.0); /// assert_eq!(t * t.inverse_unchecked(), Scale2::identity()); /// 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 where T: ClosedDivAssign + One, { self.vector.map(|e| T::one() / e).into() } /// Inverts `self`. /// /// # Example /// ``` /// # use nalgebra::{Scale2, Scale3}; /// let t = Scale3::new(1.0, 2.0, 3.0); /// assert_eq!(t * t.pseudo_inverse(), Scale3::identity()); /// assert_eq!(t.pseudo_inverse() * t, Scale3::identity()); /// /// // Work in all dimensions. /// let t = Scale2::new(1.0, 2.0); /// assert_eq!(t * t.pseudo_inverse(), Scale2::identity()); /// assert_eq!(t.pseudo_inverse() * t, Scale2::identity()); /// /// // Inverts only non-zero coordinates. /// let t = Scale2::new(0.0, 2.0); /// assert_eq!(t * t.pseudo_inverse(), Scale2::new(0.0, 1.0)); /// assert_eq!(t.pseudo_inverse() * t, Scale2::new(0.0, 1.0)); /// ``` #[inline] #[must_use] pub fn pseudo_inverse(&self) -> Scale where T: ClosedDivAssign + One + Zero, { self.vector .map(|e| { if e != T::zero() { T::one() / e } else { T::zero() } }) .into() } /// Converts this Scale into its equivalent homogeneous transformation matrix. /// /// # Example /// ``` /// # use nalgebra::{Scale2, Scale3, Matrix3, Matrix4}; /// let t = Scale3::new(10.0, 20.0, 30.0); /// let expected = Matrix4::new(10.0, 0.0, 0.0, 0.0, /// 0.0, 20.0, 0.0, 0.0, /// 0.0, 0.0, 30.0, 0.0, /// 0.0, 0.0, 0.0, 1.0); /// assert_eq!(t.to_homogeneous(), expected); /// /// let t = Scale2::new(10.0, 20.0); /// let expected = Matrix3::new(10.0, 0.0, 0.0, /// 0.0, 20.0, 0.0, /// 0.0, 0.0, 1.0); /// assert_eq!(t.to_homogeneous(), expected); /// ``` #[inline] #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where T: Zero + One + Clone, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>, U1>, { // TODO: use self.vector.push() instead. We can’t right now because // that would require the DimAdd bound (but here we use DimNameAdd). // This should be fixable once Rust gets a more complete support of // const-generics. let mut v = OVector::from_element(T::one()); for i in 0..D { v[i] = self.vector[i].clone(); } OMatrix::from_diagonal(&v) } /// Inverts `self` in-place. /// /// # Example /// ``` /// # use nalgebra::{Scale2, Scale3}; /// let t = Scale3::new(1.0, 2.0, 3.0); /// let mut inv_t = Scale3::new(1.0, 2.0, 3.0); /// assert!(inv_t.try_inverse_mut()); /// assert_eq!(t * inv_t, Scale3::identity()); /// assert_eq!(inv_t * t, Scale3::identity()); /// /// // Work in all dimensions. /// let t = Scale2::new(1.0, 2.0); /// let mut inv_t = Scale2::new(1.0, 2.0); /// assert!(inv_t.try_inverse_mut()); /// assert_eq!(t * inv_t, Scale2::identity()); /// assert_eq!(inv_t * t, Scale2::identity()); /// /// // Does not perform any operation if a coordinate is 0. /// let mut t = Scale2::new(0.0, 2.0); /// assert!(!t.try_inverse_mut()); /// ``` #[inline] pub fn try_inverse_mut(&mut self) -> bool where T: ClosedDivAssign + One + Zero, { if let Some(v) = self.try_inverse() { self.vector = v.vector; true } else { false } } } impl Scale { /// Translate the given point. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// ``` /// # use nalgebra::{Scale3, Point3}; /// let t = Scale3::new(1.0, 2.0, 3.0); /// let transformed_point = t.transform_point(&Point3::new(4.0, 5.0, 6.0)); /// assert_eq!(transformed_point, Point3::new(4.0, 10.0, 18.0)); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } } impl Scale { /// Translate the given point by the inverse of this Scale. /// /// # Example /// ``` /// # use nalgebra::{Scale3, Point3}; /// let t = Scale3::new(1.0, 2.0, 3.0); /// let transformed_point = t.try_inverse_transform_point(&Point3::new(4.0, 6.0, 6.0)).unwrap(); /// assert_eq!(transformed_point, Point3::new(4.0, 3.0, 2.0)); /// /// // Returns None if the inverse doesn't exist. /// let t = Scale3::new(1.0, 0.0, 3.0); /// let transformed_point = t.try_inverse_transform_point(&Point3::new(4.0, 6.0, 6.0)); /// assert_eq!(transformed_point, None); /// ``` #[inline] #[must_use] pub fn try_inverse_transform_point(&self, pt: &Point) -> Option> { self.try_inverse().map(|s| s * pt) } } impl Eq for Scale {} impl PartialEq for Scale { #[inline] fn eq(&self, right: &Scale) -> bool { self.vector == right.vector } } impl AbsDiffEq for Scale where T::Epsilon: Clone, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.vector.abs_diff_eq(&other.vector, epsilon) } } impl RelativeEq for Scale where T::Epsilon: Clone, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.vector .relative_eq(&other.vector, epsilon, max_relative) } } impl UlpsEq for Scale where T::Epsilon: Clone, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.vector.ulps_eq(&other.vector, epsilon, max_ulps) } } /* * * Display * */ impl fmt::Display for Scale { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let precision = f.precision().unwrap_or(3); writeln!(f, "Scale {{")?; write!(f, "{:.*}", precision, self.vector)?; writeln!(f, "}}") } } nalgebra-0.33.0/src/geometry/scale_alias.rs000064400000000000000000000006430072674642500167450ustar 00000000000000use crate::geometry::Scale; /// A 1-dimensional scale. pub type Scale1 = Scale; /// A 2-dimensional scale. pub type Scale2 = Scale; /// A 3-dimensional scale. pub type Scale3 = Scale; /// A 4-dimensional scale. pub type Scale4 = Scale; /// A 5-dimensional scale. pub type Scale5 = Scale; /// A 6-dimensional scale. pub type Scale6 = Scale; nalgebra-0.33.0/src/geometry/scale_construction.rs000064400000000000000000000075470072674642500204200ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::One; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use simba::scalar::{ClosedMulAssign, SupersetOf}; use crate::base::{SVector, Scalar}; use crate::geometry::Scale; impl Scale { /// Creates a new identity scale. /// /// # Example /// ``` /// # use nalgebra::{Point2, Point3, Scale2, Scale3}; /// let t = Scale2::identity(); /// let p = Point2::new(1.0, 2.0); /// assert_eq!(t * p, p); /// /// // Works in all dimensions. /// let t = Scale3::identity(); /// let p = Point3::new(1.0, 2.0, 3.0); /// assert_eq!(t * p, p); /// ``` #[inline] pub fn identity() -> Scale where T: One, { Scale::from(SVector::from_element(T::one())) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Scale2; /// let tra = Scale2::new(1.0f64, 2.0); /// let tra2 = tra.cast::(); /// assert_eq!(tra2, Scale2::new(1.0f32, 2.0)); /// ``` pub fn cast(self) -> Scale where Scale: SupersetOf, { crate::convert(self) } } impl One for Scale { #[inline] fn one() -> Self { Self::identity() } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where Standard: Distribution, { /// Generate an arbitrary random variate for testing purposes. #[inline] fn sample(&self, rng: &mut G) -> Scale { Scale::from(rng.gen::>()) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Scale where Owned>: Send, { #[inline] fn arbitrary(rng: &mut Gen) -> Self { let v: SVector = Arbitrary::arbitrary(rng); Self::from(v) } } /* * * Small Scale construction from components. * */ macro_rules! componentwise_constructors_impl( ($($doc: expr; $D: expr, $($args: ident:$irow: expr),*);* $(;)*) => {$( impl Scale { #[doc = "Initializes this Scale from its components."] #[doc = "# Example\n```"] #[doc = $doc] #[doc = "```"] #[inline] pub const fn new($($args: T),*) -> Self { Self { vector: SVector::::new($($args),*) } } } )*} ); componentwise_constructors_impl!( "# use nalgebra::Scale1;\nlet t = Scale1::new(1.0);\nassert!(t.vector.x == 1.0);"; 1, x:0; "# use nalgebra::Scale2;\nlet t = Scale2::new(1.0, 2.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0);"; 2, x:0, y:1; "# use nalgebra::Scale3;\nlet t = Scale3::new(1.0, 2.0, 3.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0);"; 3, x:0, y:1, z:2; "# use nalgebra::Scale4;\nlet t = Scale4::new(1.0, 2.0, 3.0, 4.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0);"; 4, x:0, y:1, z:2, w:3; "# use nalgebra::Scale5;\nlet t = Scale5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0);"; 5, x:0, y:1, z:2, w:3, a:4; "# use nalgebra::Scale6;\nlet t = Scale6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0 && t.vector.b == 6.0);"; 6, x:0, y:1, z:2, w:3, a:4, b:5; ); nalgebra-0.33.0/src/geometry/scale_conversion.rs000064400000000000000000000150530072674642500200420ustar 00000000000000use num::{One, Zero}; use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::PrimitiveSimdValue; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix, OVector, SVector, Scalar}; use crate::geometry::{Scale, SuperTCategoryOf, TAffine, Transform}; use crate::Point; /* * This file provides the following conversions: * ============================================= * * Scale -> Scale * Scale -> Transform * Scale -> Matrix (homogeneous) */ impl SubsetOf> for Scale where T1: Scalar, T2: Scalar + SupersetOf, { #[inline] fn to_superset(&self) -> Scale { Scale::from(self.vector.to_superset()) } #[inline] fn is_in_subset(rot: &Scale) -> bool { crate::is_convertible::<_, SVector>(&rot.vector) } #[inline] fn from_superset_unchecked(rot: &Scale) -> Self { Scale { vector: rot.vector.to_subset_unchecked(), } } } impl SubsetOf> for Scale where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>, U1>, { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl SubsetOf, U1>, DimNameSum, U1>>> for Scale where T1: RealField, T2: RealField + SupersetOf, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>, U1>, { #[inline] fn to_superset(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &OMatrix, U1>, DimNameSum, U1>>) -> bool { if m[(D, D)] != T2::one() { return false; } for i in 0..D + 1 { for j in 0..D + 1 { if i != j && m[(i, j)] != T2::zero() { return false; } } } true } #[inline] fn from_superset_unchecked( m: &OMatrix, U1>, DimNameSum, U1>>, ) -> Self { let v = m.fixed_view::(0, 0).diagonal(); Self { vector: crate::convert_unchecked(v), } } } impl From> for OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>, U1> + Allocator>, { #[inline] fn from(t: Scale) -> Self { t.to_homogeneous() } } impl From>> for Scale { #[inline] fn from(vector: OVector>) -> Self { Scale { vector } } } impl From<[T; D]> for Scale { #[inline] fn from(coords: [T; D]) -> Self { Scale { vector: coords.into(), } } } impl From> for Scale { #[inline] fn from(pt: Point) -> Self { Scale { vector: pt.coords } } } impl From> for [T; D] { #[inline] fn from(t: Scale) -> Self { t.vector.into() } } impl From<[Scale; 2]> for Scale where T: From<[::Element; 2]>, T::Element: Scalar, { #[inline] fn from(arr: [Scale; 2]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), ])) } } impl From<[Scale; 4]> for Scale where T: From<[::Element; 4]>, T::Element: Scalar, { #[inline] fn from(arr: [Scale; 4]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), arr[2].vector.clone(), arr[3].vector.clone(), ])) } } impl From<[Scale; 8]> for Scale where T: From<[::Element; 8]>, T::Element: Scalar, { #[inline] fn from(arr: [Scale; 8]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), arr[2].vector.clone(), arr[3].vector.clone(), arr[4].vector.clone(), arr[5].vector.clone(), arr[6].vector.clone(), arr[7].vector.clone(), ])) } } impl From<[Scale; 16]> for Scale where T: From<[::Element; 16]>, T::Element: Scalar, { #[inline] fn from(arr: [Scale; 16]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), arr[2].vector.clone(), arr[3].vector.clone(), arr[4].vector.clone(), arr[5].vector.clone(), arr[6].vector.clone(), arr[7].vector.clone(), arr[8].vector.clone(), arr[9].vector.clone(), arr[10].vector.clone(), arr[11].vector.clone(), arr[12].vector.clone(), arr[13].vector.clone(), arr[14].vector.clone(), arr[15].vector.clone(), ])) } } nalgebra-0.33.0/src/geometry/scale_coordinates.rs000064400000000000000000000016560072674642500201730ustar 00000000000000use std::ops::{Deref, DerefMut}; use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; use crate::base::Scalar; use crate::geometry::Scale; /* * * Give coordinates to Scale{1 .. 6} * */ macro_rules! deref_impl( ($D: expr, $Target: ident $(, $comps: ident)*) => { impl Deref for Scale { type Target = $Target; #[inline] fn deref(&self) -> &Self::Target { self.vector.deref() } } impl DerefMut for Scale { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { self.vector.deref_mut() } } } ); deref_impl!(1, X, x); deref_impl!(2, XY, x, y); deref_impl!(3, XYZ, x, y, z); deref_impl!(4, XYZW, x, y, z, w); deref_impl!(5, XYZWA, x, y, z, w, a); deref_impl!(6, XYZWAB, x, y, z, w, a, b); nalgebra-0.33.0/src/geometry/scale_ops.rs000064400000000000000000000105500072674642500164530ustar 00000000000000use std::ops::{Mul, MulAssign}; use simba::scalar::ClosedMulAssign; use crate::base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use crate::base::dimension::U1; use crate::base::{Const, SVector, Scalar}; use crate::geometry::{Point, Scale}; // Scale × Scale add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Scale, right: &'b Scale, Output = Scale; Scale::from(self.vector.component_mul(&right.vector)); 'a, 'b); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Scale, right: Scale, Output = Scale; Scale::from(self.vector.component_mul(&right.vector)); 'a); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Scale, right: &'b Scale, Output = Scale; Scale::from(self.vector.component_mul(&right.vector)); 'b); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Scale, right: Scale, Output = Scale; Scale::from(self.vector.component_mul(&right.vector)); ); // Scale × scalar add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Scale, right: T, Output = Scale; Scale::from(&self.vector * right); 'a); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Scale, right: T, Output = Scale; Scale::from(self.vector * right); ); // Scale × Point add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Scale, right: &'b Point, Output = Point; Point::from(self.vector.component_mul(&right.coords)); 'a, 'b); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Scale, right: Point, Output = Point; Point::from(self.vector.component_mul(&right.coords)); 'a); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Scale, right: &'b Point, Output = Point; Point::from(self.vector.component_mul(&right.coords)); 'b); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Scale, right: Point, Output = Point; Point::from(self.vector.component_mul(&right.coords)); ); // Scale * Vector add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Scale, right: &'b SVector, Output = SVector; self.vector.component_mul(right); 'a, 'b); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Scale, right: SVector, Output = SVector; self.vector.component_mul(&right); 'a); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Scale, right: &'b SVector, Output = SVector; self.vector.component_mul(right); 'b); add_sub_impl!(Mul, mul, ClosedMulAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Scale, right: SVector, Output = SVector; self.vector.component_mul(&right); ); // Scale *= Scale add_sub_assign_impl!(MulAssign, mul_assign, ClosedMulAssign; const D; self: Scale, right: &'b Scale; self.vector.component_mul_assign(&right.vector); 'b); add_sub_assign_impl!(MulAssign, mul_assign, ClosedMulAssign; const D; self: Scale, right: Scale; self.vector.component_mul_assign(&right.vector); ); // Scale ×= scalar add_sub_assign_impl!(MulAssign, mul_assign, ClosedMulAssign; const D; self: Scale, right: T; self.vector *= right; ); nalgebra-0.33.0/src/geometry/scale_simba.rs000064400000000000000000000021460072674642500167470ustar 00000000000000use simba::simd::SimdValue; use crate::base::OVector; use crate::Scalar; use crate::geometry::Scale; impl SimdValue for Scale where T::Element: Scalar, { const LANES: usize = T::LANES; type Element = Scale; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { OVector::splat(val.vector).into() } #[inline] fn extract(&self, i: usize) -> Self::Element { self.vector.extract(i).into() } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { self.vector.extract_unchecked(i).into() } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.vector.replace(i, val.vector) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.vector.replace_unchecked(i, val.vector) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { self.vector.select(cond, other.vector).into() } } nalgebra-0.33.0/src/geometry/similarity.rs000064400000000000000000000337130072674642500166770ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use num::Zero; use std::fmt; use std::hash; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use simba::scalar::{RealField, SubsetOf}; use simba::simd::SimdRealField; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::storage::Owned; use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar}; use crate::geometry::{AbstractRotation, Isometry, Point, Translation}; #[cfg(feature = "rkyv-serialize")] 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 = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "T: Scalar + Serialize, R: Serialize, DefaultAllocator: Allocator>, Owned>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "T: Scalar + Deserialize<'de>, R: Deserialize<'de>, DefaultAllocator: Allocator>, Owned>: Deserialize<'de>")) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Similarity", bound(archive = " T: rkyv::Archive, R: rkyv::Archive, Isometry: rkyv::Archive> ") ) )] pub struct Similarity { /// The part of this similarity that does not include the scaling factor. pub isometry: Isometry, scaling: T, } impl hash::Hash for Similarity where Owned>: hash::Hash, { fn hash(&self, state: &mut H) { self.isometry.hash(state); self.scaling.hash(state); } } impl Similarity where R: AbstractRotation, { /// Creates a new similarity from its rotational and translational parts. #[inline] pub fn from_parts(translation: Translation, rotation: R, scaling: T) -> Self { Self::from_isometry(Isometry::from_parts(translation, rotation), scaling) } /// Creates a new similarity from its rotational and translational parts. #[inline] pub fn from_isometry(isometry: Isometry, scaling: T) -> Self { assert!(!scaling.is_zero(), "The scaling factor must not be zero."); Self { isometry, scaling } } /// The scaling factor of this similarity transformation. #[inline] pub fn set_scaling(&mut self, scaling: T) { assert!( !scaling.is_zero(), "The similarity scaling factor must not be zero." ); self.scaling = scaling; } } impl Similarity { /// The scaling factor of this similarity transformation. #[inline] #[must_use] pub fn scaling(&self) -> T { self.scaling.clone() } } impl Similarity where T::Element: SimdRealField, R: AbstractRotation, { /// Creates a new similarity that applies only a scaling factor. #[inline] pub fn from_scaling(scaling: T) -> Self { Self::from_isometry(Isometry::identity(), scaling) } /// Inverts `self`. #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(&self) -> Self { let mut res = self.clone(); res.inverse_mut(); res } /// Inverts `self` in-place. #[inline] pub fn inverse_mut(&mut self) { self.scaling = T::one() / self.scaling.clone(); self.isometry.inverse_mut(); self.isometry.translation.vector *= self.scaling.clone(); } /// The similarity transformation that applies a scaling factor `scaling` before `self`. #[inline] #[must_use = "Did you mean to use prepend_scaling_mut()?"] pub fn prepend_scaling(&self, scaling: T) -> Self { assert!( !scaling.is_zero(), "The similarity scaling factor must not be zero." ); Self::from_isometry(self.isometry.clone(), self.scaling.clone() * scaling) } /// The similarity transformation that applies a scaling factor `scaling` after `self`. #[inline] #[must_use = "Did you mean to use append_scaling_mut()?"] pub fn append_scaling(&self, scaling: T) -> Self { assert!( !scaling.is_zero(), "The similarity scaling factor must not be zero." ); Self::from_parts( Translation::from(&self.isometry.translation.vector * scaling.clone()), self.isometry.rotation.clone(), self.scaling.clone() * scaling, ) } /// Sets `self` to the similarity transformation that applies a scaling factor `scaling` before `self`. #[inline] pub fn prepend_scaling_mut(&mut self, scaling: T) { assert!( !scaling.is_zero(), "The similarity scaling factor must not be zero." ); self.scaling *= scaling } /// Sets `self` to the similarity transformation that applies a scaling factor `scaling` after `self`. #[inline] pub fn append_scaling_mut(&mut self, scaling: T) { assert!( !scaling.is_zero(), "The similarity scaling factor must not be zero." ); self.isometry.translation.vector *= scaling.clone(); self.scaling *= scaling; } /// Appends to `self` the given translation in-place. #[inline] pub fn append_translation_mut(&mut self, t: &Translation) { self.isometry.append_translation_mut(t) } /// Appends to `self` the given rotation in-place. #[inline] pub fn append_rotation_mut(&mut self, r: &R) { self.isometry.append_rotation_mut(r) } /// Appends in-place to `self` a rotation centered at the point `p`, i.e., the rotation that /// lets `p` invariant. #[inline] pub fn append_rotation_wrt_point_mut(&mut self, r: &R, p: &Point) { self.isometry.append_rotation_wrt_point_mut(r, p) } /// Appends in-place to `self` a rotation centered at the point with coordinates /// `self.translation`. #[inline] pub fn append_rotation_wrt_center_mut(&mut self, r: &R) { self.isometry.append_rotation_wrt_center_mut(r) } /// Transform the given point by this similarity. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Point3, Similarity3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// let translation = Vector3::new(1.0, 2.0, 3.0); /// let sim = Similarity3::new(translation, axisangle, 3.0); /// let transformed_point = sim.transform_point(&Point3::new(4.0, 5.0, 6.0)); /// assert_relative_eq!(transformed_point, Point3::new(19.0, 17.0, -9.0), epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } /// Transform the given vector by this similarity, ignoring the translational /// component. /// /// This is the same as the multiplication `self * t`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// let translation = Vector3::new(1.0, 2.0, 3.0); /// let sim = Similarity3::new(translation, axisangle, 3.0); /// let transformed_vector = sim.transform_vector(&Vector3::new(4.0, 5.0, 6.0)); /// assert_relative_eq!(transformed_vector, Vector3::new(18.0, 15.0, -12.0), epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } /// Transform the given point by the inverse of this similarity. This may /// be cheaper than inverting the similarity and then transforming the /// given point. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Point3, Similarity3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// let translation = Vector3::new(1.0, 2.0, 3.0); /// let sim = Similarity3::new(translation, axisangle, 2.0); /// let transformed_point = sim.inverse_transform_point(&Point3::new(4.0, 5.0, 6.0)); /// assert_relative_eq!(transformed_point, Point3::new(-1.5, 1.5, 1.5), epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { self.isometry.inverse_transform_point(pt) / self.scaling() } /// Transform the given vector by the inverse of this similarity, /// ignoring the translational component. This may be cheaper than /// inverting the similarity and then transforming the given vector. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// let translation = Vector3::new(1.0, 2.0, 3.0); /// let sim = Similarity3::new(translation, axisangle, 2.0); /// let transformed_vector = sim.inverse_transform_vector(&Vector3::new(4.0, 5.0, 6.0)); /// assert_relative_eq!(transformed_vector, Vector3::new(-3.0, 2.5, 2.0), epsilon = 1.0e-5); /// ``` #[inline] #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.isometry.inverse_transform_vector(v) / self.scaling() } } // NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation // and makes it harder to use it, e.g., for Transform × Isometry implementation. // This is OK since all constructors of the isometry enforce the Rotation bound already (and // explicit struct construction is prevented by the private scaling factor). impl Similarity { /// Converts this similarity into its equivalent homogeneous transformation matrix. #[inline] #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, R: SubsetOf, U1>, DimNameSum, U1>>>, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { let mut res = self.isometry.to_homogeneous(); for e in res.fixed_view_mut::(0, 0).iter_mut() { *e *= self.scaling.clone() } res } } impl Eq for Similarity where R: AbstractRotation + Eq { } impl PartialEq for Similarity where R: AbstractRotation + PartialEq, { #[inline] fn eq(&self, right: &Self) -> bool { self.isometry == right.isometry && self.scaling == right.scaling } } impl AbsDiffEq for Similarity where R: AbstractRotation + AbsDiffEq, T::Epsilon: Clone, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.isometry.abs_diff_eq(&other.isometry, epsilon.clone()) && self.scaling.abs_diff_eq(&other.scaling, epsilon) } } impl RelativeEq for Similarity where R: AbstractRotation + RelativeEq, T::Epsilon: Clone, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.isometry .relative_eq(&other.isometry, epsilon.clone(), max_relative.clone()) && self .scaling .relative_eq(&other.scaling, epsilon, max_relative) } } impl UlpsEq for Similarity where R: AbstractRotation + UlpsEq, T::Epsilon: Clone, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.isometry .ulps_eq(&other.isometry, epsilon.clone(), max_ulps) && self.scaling.ulps_eq(&other.scaling, epsilon, max_ulps) } } /* * * Display * */ impl fmt::Display for Similarity where T: RealField + fmt::Display, R: AbstractRotation + fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let precision = f.precision().unwrap_or(3); writeln!(f, "Similarity {{")?; write!(f, "{:.*}", precision, self.isometry)?; write!(f, "Scaling: {:.*}", precision, self.scaling)?; writeln!(f, "}}") } } nalgebra-0.33.0/src/geometry/similarity_alias.rs000064400000000000000000000010760072674642500200450ustar 00000000000000use crate::geometry::{Rotation2, Rotation3, Similarity, UnitComplex, UnitQuaternion}; /// A 2-dimensional similarity. pub type Similarity2 = Similarity, 2>; /// A 3-dimensional similarity. pub type Similarity3 = Similarity, 3>; /// A 2-dimensional similarity using a rotation matrix for its rotation part. pub type SimilarityMatrix2 = Similarity, 2>; /// A 3-dimensional similarity using a rotation matrix for its rotation part. pub type SimilarityMatrix3 = Similarity, 3>; nalgebra-0.33.0/src/geometry/similarity_construction.rs000064400000000000000000000364230072674642500215120ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::One; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use simba::scalar::SupersetOf; use simba::simd::SimdRealField; use crate::base::{Vector2, Vector3}; use crate::{ AbstractRotation, Isometry, Point, Point3, Rotation2, Rotation3, Scalar, Similarity, Translation, UnitComplex, UnitQuaternion, }; impl Default for Similarity where T::Element: SimdRealField, R: AbstractRotation, { fn default() -> Self { Self::identity() } } impl Similarity where T::Element: SimdRealField, R: AbstractRotation, { /// Creates a new identity similarity. /// /// # Example /// ``` /// # use nalgebra::{Similarity2, Point2, Similarity3, Point3}; /// /// let sim = Similarity2::identity(); /// let pt = Point2::new(1.0, 2.0); /// assert_eq!(sim * pt, pt); /// /// let sim = Similarity3::identity(); /// let pt = Point3::new(1.0, 2.0, 3.0); /// assert_eq!(sim * pt, pt); /// ``` #[inline] pub fn identity() -> Self { Self::from_isometry(Isometry::identity(), T::one()) } } impl One for Similarity where T::Element: SimdRealField, R: AbstractRotation, { /// Creates a new identity similarity. #[inline] fn one() -> Self { Self::identity() } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where R: AbstractRotation, Standard: Distribution + Distribution, { /// Generate an arbitrary random variate for testing purposes. #[inline] fn sample<'a, G: Rng + ?Sized>(&self, rng: &mut G) -> Similarity { let mut s = rng.gen(); while relative_eq!(s, T::zero()) { s = rng.gen() } Similarity::from_isometry(rng.gen(), s) } } impl Similarity where T::Element: SimdRealField, R: AbstractRotation, { /// The similarity that applies the scaling factor `scaling`, followed by the rotation `r` with /// its axis passing through the point `p`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity2, Point2, UnitComplex}; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// let pt = Point2::new(3.0, 2.0); /// let sim = Similarity2::rotation_wrt_point(rot, pt, 4.0); /// /// assert_relative_eq!(sim * Point2::new(1.0, 2.0), Point2::new(-3.0, 3.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn rotation_wrt_point(r: R, p: Point, scaling: T) -> Self { let shift = r.transform_vector(&-&p.coords); Self::from_parts(Translation::from(shift + p.coords), r, scaling) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Similarity where T: crate::RealField + Arbitrary + Send, T::Element: crate::RealField, R: AbstractRotation + Arbitrary + Send, Owned>: Send, { #[inline] fn arbitrary(rng: &mut Gen) -> Self { let mut s: T = Arbitrary::arbitrary(rng); while s.is_zero() { s = Arbitrary::arbitrary(rng) } Self::from_isometry(Arbitrary::arbitrary(rng), s) } } /* * * Constructors for various static dimensions. * */ // 2D similarity. impl Similarity, 2> where T::Element: SimdRealField, { /// Creates a new similarity from a translation, a rotation, and an uniform scaling factor. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{SimilarityMatrix2, Vector2, Point2}; /// let sim = SimilarityMatrix2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); /// /// assert_relative_eq!(sim * Point2::new(2.0, 4.0), Point2::new(-11.0, 8.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn new(translation: Vector2, angle: T, scaling: T) -> Self { Self::from_parts( Translation::from(translation), Rotation2::new(angle), scaling, ) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::SimilarityMatrix2; /// let sim = SimilarityMatrix2::::identity(); /// let sim2 = sim.cast::(); /// assert_eq!(sim2, SimilarityMatrix2::::identity()); /// ``` pub fn cast(self) -> Similarity, 2> where Similarity, 2>: SupersetOf, { crate::convert(self) } } impl Similarity, 2> where T::Element: SimdRealField, { /// Creates a new similarity from a translation and a rotation angle. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity2, Vector2, Point2}; /// let sim = Similarity2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2, 3.0); /// /// assert_relative_eq!(sim * Point2::new(2.0, 4.0), Point2::new(-11.0, 8.0), epsilon = 1.0e-6); /// ``` #[inline] pub fn new(translation: Vector2, angle: T, scaling: T) -> Self { Self::from_parts( Translation::from(translation), UnitComplex::new(angle), scaling, ) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Similarity2; /// let sim = Similarity2::::identity(); /// let sim2 = sim.cast::(); /// assert_eq!(sim2, Similarity2::::identity()); /// ``` pub fn cast(self) -> Similarity, 2> where Similarity, 2>: SupersetOf, { crate::convert(self) } } // 3D rotation. macro_rules! similarity_construction_impl( ($Rot: ident) => { impl Similarity, 3> where T::Element: SimdRealField { /// Creates a new similarity from a translation, rotation axis-angle, and scaling /// factor. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let axisangle = Vector3::y() * f32::consts::FRAC_PI_2; /// let translation = Vector3::new(1.0, 2.0, 3.0); /// // Point and vector being transformed in the tests. /// let pt = Point3::new(4.0, 5.0, 6.0); /// let vec = Vector3::new(4.0, 5.0, 6.0); /// /// // Similarity with its rotation part represented as a UnitQuaternion /// let sim = Similarity3::new(translation, axisangle, 3.0); /// assert_relative_eq!(sim * pt, Point3::new(19.0, 17.0, -9.0), epsilon = 1.0e-5); /// assert_relative_eq!(sim * vec, Vector3::new(18.0, 15.0, -12.0), epsilon = 1.0e-5); /// /// // Similarity with its rotation part represented as a Rotation3 (a 3x3 rotation matrix). /// let sim = SimilarityMatrix3::new(translation, axisangle, 3.0); /// assert_relative_eq!(sim * pt, Point3::new(19.0, 17.0, -9.0), epsilon = 1.0e-5); /// assert_relative_eq!(sim * vec, Vector3::new(18.0, 15.0, -12.0), epsilon = 1.0e-5); /// ``` #[inline] pub fn new(translation: Vector3, axisangle: Vector3, scaling: T) -> Self { Self::from_isometry(Isometry::<_, $Rot, 3>::new(translation, axisangle), scaling) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Similarity3; /// let sim = Similarity3::::identity(); /// let sim2 = sim.cast::(); /// assert_eq!(sim2, Similarity3::::identity()); /// ``` pub fn cast(self) -> Similarity, 3> where Similarity, 3>: SupersetOf, { crate::convert(self) } /// Creates an similarity that corresponds to a scaling factor and a local frame of /// an observer standing at the point `eye` and looking toward `target`. /// /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the /// `eye`. /// /// # Arguments /// * eye - The observer position. /// * target - The target position. /// * up - Vertical direction. The only requirement of this parameter is to not be collinear /// to `eye - at`. Non-collinearity is not checked. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); /// let target = Point3::new(2.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// // Similarity with its rotation part represented as a UnitQuaternion /// let sim = Similarity3::face_towards(&eye, &target, &up, 3.0); /// assert_eq!(sim * Point3::origin(), eye); /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); /// /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). /// let sim = SimilarityMatrix3::face_towards(&eye, &target, &up, 3.0); /// assert_eq!(sim * Point3::origin(), eye); /// assert_relative_eq!(sim * Vector3::z(), Vector3::x() * 3.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn face_towards(eye: &Point3, target: &Point3, up: &Vector3, scaling: T) -> Self { Self::from_isometry(Isometry::<_, $Rot, 3>::face_towards(eye, target, up), scaling) } /// Deprecated: Use [`SimilarityMatrix3::face_towards`](Self::face_towards) instead. #[deprecated(note="renamed to `face_towards`")] pub fn new_observer_frames(eye: &Point3, target: &Point3, up: &Vector3, scaling: T) -> Self { Self::face_towards(eye, target, up, scaling) } /// Builds a right-handed look-at view matrix including scaling factor. /// /// This conforms to the common notion of right handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); /// let target = Point3::new(2.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// // Similarity with its rotation part represented as a UnitQuaternion /// let iso = Similarity3::look_at_rh(&eye, &target, &up, 3.0); /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z() * 3.0, epsilon = 1.0e-6); /// /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). /// let iso = SimilarityMatrix3::look_at_rh(&eye, &target, &up, 3.0); /// assert_relative_eq!(iso * Vector3::x(), -Vector3::z() * 3.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn look_at_rh(eye: &Point3, target: &Point3, up: &Vector3, scaling: T) -> Self { Self::from_isometry(Isometry::<_, $Rot, 3>::look_at_rh(eye, target, up), scaling) } /// Builds a left-handed look-at view matrix including a scaling factor. /// /// This conforms to the common notion of left handed look-at matrix from the computer /// graphics community. /// /// # Arguments /// * eye - The eye position. /// * target - The target position. /// * up - A vector approximately aligned with required the vertical axis. The only /// requirement of this parameter is to not be collinear to `target - eye`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{Similarity3, SimilarityMatrix3, Point3, Vector3}; /// let eye = Point3::new(1.0, 2.0, 3.0); /// let target = Point3::new(2.0, 2.0, 3.0); /// let up = Vector3::y(); /// /// // Similarity with its rotation part represented as a UnitQuaternion /// let sim = Similarity3::look_at_lh(&eye, &target, &up, 3.0); /// assert_relative_eq!(sim * Vector3::x(), Vector3::z() * 3.0, epsilon = 1.0e-6); /// /// // Similarity with its rotation part represented as Rotation3 (a 3x3 rotation matrix). /// let sim = SimilarityMatrix3::look_at_lh(&eye, &target, &up, 3.0); /// assert_relative_eq!(sim * Vector3::x(), Vector3::z() * 3.0, epsilon = 1.0e-6); /// ``` #[inline] pub fn look_at_lh(eye: &Point3, target: &Point3, up: &Vector3, scaling: T) -> Self { Self::from_isometry(Isometry::<_, $Rot, 3>::look_at_lh(eye, target, up), scaling) } } } ); similarity_construction_impl!(Rotation3); similarity_construction_impl!(UnitQuaternion); nalgebra-0.33.0/src/geometry/similarity_conversion.rs000064400000000000000000000254720072674642500211470ustar 00000000000000use num::Zero; use crate::ArrayStorage; use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::{PrimitiveSimdValue, SimdRealField, SimdValue}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimMin, DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix, Scalar}; use crate::geometry::{ AbstractRotation, Isometry, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, }; /* * This file provides the following conversions: * ============================================= * * Similarity -> Similarity * Similarity -> Transform * Similarity -> Matrix (homogeneous) */ impl SubsetOf> for Similarity where T1: RealField + SubsetOf, T2: RealField + SupersetOf, R1: AbstractRotation + SubsetOf, R2: AbstractRotation, { #[inline] fn to_superset(&self) -> Similarity { Similarity::from_isometry(self.isometry.to_superset(), self.scaling().to_superset()) } #[inline] fn is_in_subset(sim: &Similarity) -> bool { crate::is_convertible::<_, Isometry>(&sim.isometry) && crate::is_convertible::<_, T1>(&sim.scaling()) } #[inline] fn from_superset_unchecked(sim: &Similarity) -> Self { Similarity::from_isometry( sim.isometry.to_subset_unchecked(), sim.scaling().to_subset_unchecked(), ) } } impl SubsetOf> for Similarity where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, R: AbstractRotation + SubsetOf, U1>, DimNameSum, U1>>> + SubsetOf, U1>, DimNameSum, U1>>>, Const: DimNameAdd + DimMin, Output = Const>, // needed by .determinant() DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, // + Allocator // + Allocator // + Allocator // + Allocator // + Allocator, { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl SubsetOf, U1>, DimNameSum, U1>>> for Similarity where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SubsetOf, U1>, DimNameSum, U1>>> + SubsetOf, U1>, DimNameSum, U1>>>, Const: DimNameAdd + DimMin, Output = Const>, // needed by .determinant() DefaultAllocator: Allocator, Const<1>, Buffer = ArrayStorage> + Allocator, U1>, DimNameSum, U1>>, { #[inline] fn to_superset(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &OMatrix, U1>, DimNameSum, U1>>) -> bool { let mut rot = m.fixed_view::(0, 0).clone_owned(); if rot .fixed_columns_mut::<1>(0) .try_normalize_mut(T2::zero()) .is_some() && rot .fixed_columns_mut::<1>(1) .try_normalize_mut(T2::zero()) .is_some() && rot .fixed_columns_mut::<1>(2) .try_normalize_mut(T2::zero()) .is_some() { // TODO: could we avoid explicit the computation of the determinant? // (its sign is needed to see if the scaling factor is negative). if rot.determinant() < T2::zero() { rot.fixed_columns_mut::<1>(0).neg_mut(); rot.fixed_columns_mut::<1>(1).neg_mut(); rot.fixed_columns_mut::<1>(2).neg_mut(); } let bottom = m.fixed_view::<1, D>(D, 0); // Scalar types agree. m.iter().all(|e| SupersetOf::::is_in_subset(e)) && // The normalized block part is a rotation. // rot.is_special_orthogonal(T2::default_epsilon().sqrt()) && // The bottom row is (0, 0, ..., 1) bottom.iter().all(|e| e.is_zero()) && m[(D, D)] == T2::one() } else { false } } #[inline] fn from_superset_unchecked( m: &OMatrix, U1>, DimNameSum, U1>>, ) -> Self { let mut mm = m.clone_owned(); let na = mm.fixed_view_mut::(0, 0).normalize_mut(); let nb = mm.fixed_view_mut::(0, 1).normalize_mut(); let nc = mm.fixed_view_mut::(0, 2).normalize_mut(); let mut scale = (na + nb + nc) / crate::convert(3.0); // We take the mean, for robustness. // TODO: could we avoid the explicit computation of the determinant? // (its sign is needed to see if the scaling factor is negative). if mm.fixed_view::(0, 0).determinant() < T2::zero() { mm.fixed_view_mut::(0, 0).neg_mut(); mm.fixed_view_mut::(0, 1).neg_mut(); mm.fixed_view_mut::(0, 2).neg_mut(); scale = -scale; } let t = m.fixed_view::(0, D).into_owned(); let t = Translation { vector: crate::convert_unchecked(t), }; Self::from_parts( t, crate::convert_unchecked(mm), crate::convert_unchecked(scale), ) } } impl From> for OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, R: SubsetOf, U1>, DimNameSum, U1>>>, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, // + Allocator { #[inline] fn from(sim: Similarity) -> Self { sim.to_homogeneous() } } impl From<[Similarity; 2]> for Similarity where T: From<[::Element; 2]>, R: SimdValue + AbstractRotation + From<[::Element; 2]>, R::Element: AbstractRotation, T::Element: Scalar + Zero + Copy, R::Element: Scalar + Zero + Copy, { #[inline] fn from(arr: [Similarity; 2]) -> Self { let iso = Isometry::from([arr[0].isometry, arr[1].isometry]); let scale = T::from([arr[0].scaling(), arr[1].scaling()]); Self::from_isometry(iso, scale) } } impl From<[Similarity; 4]> for Similarity where T: From<[::Element; 4]>, R: SimdValue + AbstractRotation + From<[::Element; 4]>, R::Element: AbstractRotation, T::Element: Scalar + Zero + Copy, R::Element: Scalar + Zero + Copy, { #[inline] fn from(arr: [Similarity; 4]) -> Self { let iso = Isometry::from([ arr[0].isometry, arr[1].isometry, arr[2].isometry, arr[3].isometry, ]); let scale = T::from([ arr[0].scaling(), arr[1].scaling(), arr[2].scaling(), arr[3].scaling(), ]); Self::from_isometry(iso, scale) } } impl From<[Similarity; 8]> for Similarity where T: From<[::Element; 8]>, R: SimdValue + AbstractRotation + From<[::Element; 8]>, R::Element: AbstractRotation, T::Element: Scalar + Zero + Copy, R::Element: Scalar + Zero + Copy, { #[inline] fn from(arr: [Similarity; 8]) -> Self { let iso = Isometry::from([ arr[0].isometry, arr[1].isometry, arr[2].isometry, arr[3].isometry, arr[4].isometry, arr[5].isometry, arr[6].isometry, arr[7].isometry, ]); let scale = T::from([ arr[0].scaling(), arr[1].scaling(), arr[2].scaling(), arr[3].scaling(), arr[4].scaling(), arr[5].scaling(), arr[6].scaling(), arr[7].scaling(), ]); Self::from_isometry(iso, scale) } } impl From<[Similarity; 16]> for Similarity where T: From<[::Element; 16]>, R: SimdValue + AbstractRotation + From<[::Element; 16]>, R::Element: AbstractRotation, T::Element: Scalar + Zero + Copy, R::Element: Scalar + Zero + Copy, { #[inline] fn from(arr: [Similarity; 16]) -> Self { let iso = Isometry::from([ arr[0].isometry, arr[1].isometry, arr[2].isometry, arr[3].isometry, arr[4].isometry, arr[5].isometry, arr[6].isometry, arr[7].isometry, arr[8].isometry, arr[9].isometry, arr[10].isometry, arr[11].isometry, arr[12].isometry, arr[13].isometry, arr[14].isometry, arr[15].isometry, ]); let scale = T::from([ arr[0].scaling(), arr[1].scaling(), arr[2].scaling(), arr[3].scaling(), arr[4].scaling(), arr[5].scaling(), arr[6].scaling(), arr[7].scaling(), arr[8].scaling(), arr[9].scaling(), arr[10].scaling(), arr[11].scaling(), arr[12].scaling(), arr[13].scaling(), arr[14].scaling(), arr[15].scaling(), ]); Self::from_isometry(iso, scale) } } nalgebra-0.33.0/src/geometry/similarity_ops.rs000064400000000000000000000516200072674642500175550ustar 00000000000000// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] use num::{One, Zero}; use std::ops::{Div, DivAssign, Mul, MulAssign}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; use simba::simd::SimdRealField; use crate::base::{SVector, Scalar}; use crate::geometry::{ AbstractRotation, Isometry, Point, Rotation, Similarity, Translation, UnitComplex, UnitQuaternion, }; // TODO: there are several cloning of rotations that we could probably get rid of (but we didn't // yet because that would require to add a bound like `where for<'a, 'b> &'a R: Mul<&'b R, Output = R>` // which is quite ugly. /* * * In this file, we provide: * ========================= * * * (Operators) * * Similarity × Similarity * Similarity × R * Similarity × Isometry * * Isometry × Similarity * Isometry ÷ Similarity * * * Similarity ÷ Similarity * Similarity ÷ R * Similarity ÷ Isometry * * Similarity × Point * Similarity × Vector * * * Similarity × Translation * Translation × Similarity * * NOTE: The following are provided explicitly because we can't have R × Similarity. * Rotation × Similarity * UnitQuaternion × Similarity * * Rotation ÷ Similarity * UnitQuaternion ÷ Similarity * * (Assignment Operators) * * Similarity ×= Translation * * Similarity ×= Similarity * Similarity ×= Isometry * Similarity ×= R * * Similarity ÷= Similarity * Similarity ÷= Isometry * Similarity ÷= R * */ // XXX: code duplication: those macros are the same as for the isometry. macro_rules! similarity_binop_impl( ($Op: ident, $op: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField, R, const D: usize> $Op<$Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! similarity_binop_impl_all( ($Op: ident, $op: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { similarity_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); similarity_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); similarity_binop_impl!( $Op, $op; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); similarity_binop_impl!( $Op, $op; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); macro_rules! similarity_binop_assign_impl_all( ($OpAssign: ident, $op_assign: ident; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty; [val] => $action_val: expr; [ref] => $action_ref: expr;) => { impl $OpAssign<$Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation{ #[inline] fn $op_assign(&mut $lhs, $rhs: $Rhs) { $action_val } } impl<'b, T: SimdRealField, R, const D: usize> $OpAssign<&'b $Rhs> for $Lhs where T::Element: SimdRealField, R: AbstractRotation { #[inline] fn $op_assign(&mut $lhs, $rhs: &'b $Rhs) { $action_ref } } } ); // Similarity × Similarity // Similarity ÷ Similarity similarity_binop_impl_all!( Mul, mul; self: Similarity, rhs: Similarity, Output = Similarity; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let mut res = self * &rhs.isometry; res.prepend_scaling_mut(rhs.scaling()); res }; ); similarity_binop_impl_all!( Div, div; self: Similarity, rhs: Similarity, Output = Similarity; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // Similarity ×= Translation similarity_binop_assign_impl_all!( MulAssign, mul_assign; self: Similarity, rhs: Translation; [val] => *self *= &rhs; [ref] => { let shift = self.isometry.rotation.transform_vector(&rhs.vector) * self.scaling(); self.isometry.translation.vector += shift; }; ); // Similarity ×= Similarity // Similarity ÷= Similarity similarity_binop_assign_impl_all!( MulAssign, mul_assign; self: Similarity, rhs: Similarity; [val] => *self *= &rhs; [ref] => { *self *= &rhs.isometry; self.prepend_scaling_mut(rhs.scaling()); }; ); similarity_binop_assign_impl_all!( DivAssign, div_assign; self: Similarity, rhs: Similarity; [val] => *self /= &rhs; // TODO: don't invert explicitly. [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Similarity ×= Isometry // Similarity ÷= Isometry similarity_binop_assign_impl_all!( MulAssign, mul_assign; self: Similarity, rhs: Isometry; [val] => *self *= &rhs; [ref] => { let shift = self.isometry.rotation.transform_vector(&rhs.translation.vector) * self.scaling(); self.isometry.translation.vector += shift; self.isometry.rotation *= rhs.rotation.clone(); }; ); similarity_binop_assign_impl_all!( DivAssign, div_assign; self: Similarity, rhs: Isometry; [val] => *self /= &rhs; // TODO: don't invert explicitly. [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Similarity ×= R // Similarity ÷= R md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (Const, U1), (Const, Const) const D; for; where; self: Similarity, D>, rhs: Rotation; [val] => self.isometry.rotation *= rhs; [ref] => self.isometry.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (Const, U1), (Const, Const) const D; for; where; self: Similarity, D>, rhs: Rotation; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (U3, U3), (U3, U3) const; for; where; self: Similarity, 3>, rhs: UnitQuaternion; [val] => self.isometry.rotation *= rhs; [ref] => self.isometry.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (U3, U3), (U3, U3) const; for; where; self: Similarity, 3>, rhs: UnitQuaternion; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); md_assign_impl_all!( MulAssign, mul_assign where T: SimdRealField for T::Element: SimdRealField; (U2, U2), (U2, U2) const; for; where; self: Similarity, 2>, rhs: UnitComplex; [val] => self.isometry.rotation *= rhs; [ref] => self.isometry.rotation *= rhs.clone(); ); md_assign_impl_all!( DivAssign, div_assign where T: SimdRealField for T::Element: SimdRealField; (U2, U2), (U2, U2) const; for; where; self: Similarity, 2>, rhs: UnitComplex; // TODO: don't invert explicitly? [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Similarity × Isometry // Similarity ÷ Isometry similarity_binop_impl_all!( Mul, mul; self: Similarity, rhs: Isometry, Output = Similarity; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let shift = self.isometry.rotation.transform_vector(&rhs.translation.vector) * self.scaling(); Similarity::from_parts( #[allow(clippy::suspicious_arithmetic_impl)] Translation::from(&self.isometry.translation.vector + shift), self.isometry.rotation.clone() * rhs.rotation.clone(), self.scaling()) }; ); similarity_binop_impl_all!( Div, div; self: Similarity, rhs: Isometry, Output = Similarity; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // Isometry × Similarity // Isometry ÷ Similarity similarity_binop_impl_all!( Mul, mul; self: Isometry, rhs: Similarity, Output = Similarity; [val val] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * rhs.isometry, scaling) }; [ref val] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * rhs.isometry, scaling) }; [val ref] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * &rhs.isometry, scaling) }; [ref ref] => { let scaling = rhs.scaling(); Similarity::from_isometry(self * &rhs.isometry, scaling) }; ); similarity_binop_impl_all!( Div, div; self: Isometry, rhs: Similarity, Output = Similarity; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // Similarity × Point similarity_binop_impl_all!( Mul, mul; self: Similarity, right: Point, Output = Point; [val val] => { let scaling = self.scaling(); self.isometry.translation * (self.isometry.rotation.transform_point(&right) * scaling) }; [ref val] => &self.isometry.translation * (self.isometry.rotation.transform_point(&right) * self.scaling()); [val ref] => { let scaling = self.scaling(); self.isometry.translation * (self.isometry.rotation.transform_point(right) * scaling) }; [ref ref] => &self.isometry.translation * (self.isometry.rotation.transform_point(right) * self.scaling()); ); // Similarity × Vector similarity_binop_impl_all!( Mul, mul; self: Similarity, right: SVector, Output = SVector; [val val] => self.isometry.rotation.transform_vector(&right) * self.scaling(); [ref val] => self.isometry.rotation.transform_vector(&right) * self.scaling(); [val ref] => self.isometry.rotation.transform_vector(right) * self.scaling(); [ref ref] => self.isometry.rotation.transform_vector(right) * self.scaling(); ); // Similarity × Translation similarity_binop_impl_all!( Mul, mul; self: Similarity, right: Translation, Output = Similarity; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => { let shift = self.isometry.rotation.transform_vector(&right.vector) * self.scaling(); Similarity::from_parts( #[allow(clippy::suspicious_arithmetic_impl)] Translation::from(&self.isometry.translation.vector + shift), self.isometry.rotation.clone(), self.scaling()) }; ); // Translation × Similarity similarity_binop_impl_all!( Mul, mul; self: Translation, right: Similarity, Output = Similarity; [val val] => { let scaling = right.scaling(); Similarity::from_isometry(self * right.isometry, scaling) }; [ref val] => { let scaling = right.scaling(); Similarity::from_isometry(self * right.isometry, scaling) }; [val ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); [ref ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); ); macro_rules! similarity_from_composition_impl( ($Op: ident, $op: ident; $($Dims: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField $(, const $Dims: usize)*> $Op<$Rhs> for $Lhs where T::Element: SimdRealField { type Output = $Output; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! similarity_from_composition_impl_all( ($Op: ident, $op: ident; $($Dims: ident),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Output: ty; [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { similarity_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: $Lhs, $rhs: $Rhs, Output = $Output; $action_val_val; ); similarity_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Output; $action_ref_val; 'a); similarity_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_val_ref; 'b); similarity_from_composition_impl!( $Op, $op; $($Dims),*; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Output; $action_ref_ref; 'a, 'b); } ); // Similarity × Rotation similarity_from_composition_impl_all!( Mul, mul; D; self: Similarity, D>, rhs: Rotation, Output = Similarity, D>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); ); // Rotation × Similarity similarity_from_composition_impl_all!( Mul, mul; D; self: Rotation, right: Similarity, D>, Output = Similarity, D>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); ); // Similarity ÷ Rotation similarity_from_composition_impl_all!( Div, div; D; self: Similarity, D>, rhs: Rotation, Output = Similarity, D>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); ); // Rotation ÷ Similarity similarity_from_composition_impl_all!( Div, div; D; self: Rotation, right: Similarity, D>, Output = Similarity, D>; // TODO: don't call inverse explicitly? [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Similarity × UnitQuaternion similarity_from_composition_impl_all!( Mul, mul; ; self: Similarity, 3>, rhs: UnitQuaternion, Output = Similarity, 3>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); ); // UnitQuaternion × Similarity similarity_from_composition_impl_all!( Mul, mul; ; self: UnitQuaternion, right: Similarity, 3>, Output = Similarity, 3>; [val val] => &self * &right; [ref val] => self * &right; [val ref] => &self * right; [ref ref] => Similarity::from_isometry(self * &right.isometry, right.scaling()); ); // Similarity ÷ UnitQuaternion similarity_from_composition_impl_all!( Div, div; ; self: Similarity, 3>, rhs: UnitQuaternion, Output = Similarity, 3>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); ); // UnitQuaternion ÷ Similarity similarity_from_composition_impl_all!( Div, div; ; self: UnitQuaternion, right: Similarity, 3>, Output = Similarity, 3>; // TODO: don't call inverse explicitly? [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * right.inverse() }; ); // Similarity × UnitComplex similarity_from_composition_impl_all!( Mul, mul; ; self: Similarity, 2>, rhs: UnitComplex, Output = Similarity, 2>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry * rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry * rhs, self.scaling()); ); // Similarity ÷ UnitComplex similarity_from_composition_impl_all!( Div, div; ; self: Similarity, 2>, rhs: UnitComplex, Output = Similarity, 2>; [val val] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref val] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); [val ref] => { let scaling = self.scaling(); Similarity::from_isometry(self.isometry / rhs, scaling) }; [ref ref] => Similarity::from_isometry(&self.isometry / rhs, self.scaling()); ); nalgebra-0.33.0/src/geometry/similarity_simba.rs000064400000000000000000000034530072674642500200500ustar 00000000000000use simba::simd::{SimdRealField, SimdValue}; use crate::geometry::{AbstractRotation, Isometry, Similarity}; impl SimdValue for Similarity where T::Element: SimdRealField, R: SimdValue + AbstractRotation, R::Element: AbstractRotation, { const LANES: usize = T::LANES; type Element = Similarity; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { let scaling = T::splat(val.scaling()); Similarity::from_isometry(Isometry::splat(val.isometry), scaling) } #[inline] fn extract(&self, i: usize) -> Self::Element { Similarity::from_isometry(self.isometry.extract(i), self.scaling().extract(i)) } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { Similarity::from_isometry( self.isometry.extract_unchecked(i), self.scaling().extract_unchecked(i), ) } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { let mut s = self.scaling(); s.replace(i, val.scaling()); self.set_scaling(s); self.isometry.replace(i, val.isometry); } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { let mut s = self.scaling(); s.replace_unchecked(i, val.scaling()); self.set_scaling(s); self.isometry.replace_unchecked(i, val.isometry); } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { let scaling = self.scaling().select(cond, other.scaling()); Similarity::from_isometry(self.isometry.select(cond, other.isometry), scaling) } } nalgebra-0.33.0/src/geometry/swizzle.rs000064400000000000000000000045410072674642500162150ustar 00000000000000use crate::base::{Const, Scalar, ToTypenum}; use crate::geometry::{Point, Point2, Point3}; use typenum::{self, Cmp, Greater}; macro_rules! impl_swizzle { ($( where $BaseDim: ident: $( $name: ident() -> $Result: ident[$($i: expr),+] ),+ ;)* ) => { $( $( /// Builds a new point from components of `self`. #[inline] #[must_use] pub fn $name(&self) -> $Result where as ToTypenum>::Typenum: Cmp { $Result::new($(self[$i].clone()),*) } )* )* } } /// # Swizzling impl Point where Const: ToTypenum, { impl_swizzle!( where U0: xx() -> Point2[0, 0], xxx() -> Point3[0, 0, 0]; where U1: xy() -> Point2[0, 1], yx() -> Point2[1, 0], yy() -> Point2[1, 1], xxy() -> Point3[0, 0, 1], xyx() -> Point3[0, 1, 0], xyy() -> Point3[0, 1, 1], yxx() -> Point3[1, 0, 0], yxy() -> Point3[1, 0, 1], yyx() -> Point3[1, 1, 0], yyy() -> Point3[1, 1, 1]; where U2: xz() -> Point2[0, 2], yz() -> Point2[1, 2], zx() -> Point2[2, 0], zy() -> Point2[2, 1], zz() -> Point2[2, 2], xxz() -> Point3[0, 0, 2], xyz() -> Point3[0, 1, 2], xzx() -> Point3[0, 2, 0], xzy() -> Point3[0, 2, 1], xzz() -> Point3[0, 2, 2], yxz() -> Point3[1, 0, 2], yyz() -> Point3[1, 1, 2], yzx() -> Point3[1, 2, 0], yzy() -> Point3[1, 2, 1], yzz() -> Point3[1, 2, 2], zxx() -> Point3[2, 0, 0], zxy() -> Point3[2, 0, 1], zxz() -> Point3[2, 0, 2], zyx() -> Point3[2, 1, 0], zyy() -> Point3[2, 1, 1], zyz() -> Point3[2, 1, 2], zzx() -> Point3[2, 2, 0], zzy() -> Point3[2, 2, 1], zzz() -> Point3[2, 2, 2]; ); } nalgebra-0.33.0/src/geometry/transform.rs000064400000000000000000000534050072674642500165240ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::any::Any; use std::fmt::{self, Debug}; use std::hash; use std::marker::PhantomData; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use simba::scalar::RealField; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::storage::Owned; use crate::base::{Const, DefaultAllocator, DimName, OMatrix, SVector}; use crate::geometry::Point; /// Trait implemented by phantom types identifying the projective transformation type. /// /// NOTE: this trait is not intended to be implemented outside of the `nalgebra` crate. pub trait TCategory: Any + Debug + Copy + PartialEq + Send { /// Indicates whether a `Transform` with the category `Self` has a bottom-row different from /// `0 0 .. 1`. #[inline] fn has_normalizer() -> bool { true } /// Checks that the given matrix is a valid homogeneous representation of an element of the /// category `Self`. fn check_homogeneous_invariants(mat: &OMatrix) -> bool where T::Epsilon: Clone, DefaultAllocator: Allocator; } /// Traits that gives the `Transform` category that is compatible with the result of the /// multiplication of transformations with categories `Self` and `Other`. pub trait TCategoryMul: TCategory { /// The transform category that results from the multiplication of a `Transform` to a /// `Transform`. This is usually equal to `Self` or `Other`, whichever is the most /// general category. type Representative: TCategory; } /// Indicates that `Self` is a more general `Transform` category than `Other`. pub trait SuperTCategoryOf: TCategory {} /// Indicates that `Self` is a more specific `Transform` category than `Other`. /// /// Automatically implemented based on `SuperTCategoryOf`. pub trait SubTCategoryOf: TCategory {} impl SubTCategoryOf for T1 where T1: TCategory, T2: SuperTCategoryOf, { } /// Tag representing the most general (not necessarily inversible) `Transform` type. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum TGeneral {} /// Tag representing the most general inversible `Transform` type. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] 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)] pub enum TAffine {} impl TCategory for TGeneral { #[inline] fn check_homogeneous_invariants(_: &OMatrix) -> bool where T::Epsilon: Clone, DefaultAllocator: Allocator, { true } } impl TCategory for TProjective { #[inline] fn check_homogeneous_invariants(mat: &OMatrix) -> bool where T::Epsilon: Clone, DefaultAllocator: Allocator, { mat.is_invertible() } } impl TCategory for TAffine { #[inline] fn has_normalizer() -> bool { false } #[inline] fn check_homogeneous_invariants(mat: &OMatrix) -> bool where T::Epsilon: Clone, DefaultAllocator: Allocator, { let last = D::dim() - 1; mat.is_invertible() && mat[(last, last)] == T::one() && (0..last).all(|i| mat[(last, i)].is_zero()) } } macro_rules! category_mul_impl( ($($a: ident * $b: ident => $c: ty);* $(;)*) => {$( impl TCategoryMul<$a> for $b { type Representative = $c; } )*} ); // We require stability upon multiplication. impl TCategoryMul for T { type Representative = T; } category_mul_impl!( // TGeneral * TGeneral => TGeneral; TGeneral * TProjective => TGeneral; TGeneral * TAffine => TGeneral; TProjective * TGeneral => TGeneral; // TProjective * TProjective => TProjective; TProjective * TAffine => TProjective; TAffine * TGeneral => TGeneral; TAffine * TProjective => TProjective; // TAffine * TAffine => TAffine; ); macro_rules! super_tcategory_impl( ($($a: ident >= $b: ident);* $(;)*) => {$( impl SuperTCategoryOf<$b> for $a { } )*} ); impl SuperTCategoryOf for T {} super_tcategory_impl!( TGeneral >= TProjective; TGeneral >= TAffine; TProjective >= TAffine; ); /// A transformation matrix in homogeneous coordinates. /// /// It is stored as a matrix with dimensions `(D + 1, D + 1)`, e.g., it stores a 4x4 matrix for a /// 3D transformation. #[repr(C)] pub struct Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { matrix: OMatrix, U1>, DimNameSum, U1>>, _phantom: PhantomData, } impl Debug for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.matrix.fmt(formatter) } } impl hash::Hash for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, Owned, U1>, DimNameSum, U1>>: hash::Hash, { fn hash(&self, state: &mut H) { self.matrix.hash(state); } } impl Copy for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, Owned, U1>, DimNameSum, U1>>: Copy, { } impl Clone for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn clone(&self) -> Self { Transform::from_matrix_unchecked(self.matrix.clone()) } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Transform where T: RealField + bytemuck::Zeroable, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, OMatrix, U1>, DimNameSum, U1>>: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Transform where T: RealField + bytemuck::Pod, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, OMatrix, U1>, DimNameSum, U1>>: bytemuck::Pod, Owned, U1>, DimNameSum, U1>>: Copy, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, Owned, U1>, DimNameSum, U1>>: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.matrix.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: RealField, C: TCategory, const D: usize> Deserialize<'a> for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, Owned, U1>, DimNameSum, U1>>: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let matrix = OMatrix::, U1>, DimNameSum, U1>>::deserialize( deserializer, )?; Ok(Transform::from_matrix_unchecked(matrix)) } } impl Eq for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { } impl PartialEq for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn eq(&self, right: &Self) -> bool { self.matrix == right.matrix } } impl Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { /// Creates a new transformation from the given homogeneous matrix. The transformation category /// of `Self` is not checked to be verified by the given matrix. #[inline] pub fn from_matrix_unchecked( matrix: OMatrix, U1>, DimNameSum, U1>>, ) -> Self { Transform { matrix, _phantom: PhantomData, } } /// Retrieves the underlying matrix. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 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); /// ``` #[inline] pub fn into_inner(self) -> OMatrix, U1>, DimNameSum, U1>> { self.matrix } /// Retrieves the underlying matrix. /// Deprecated: Use [`Transform::into_inner`] instead. #[deprecated(note = "use `.into_inner()` instead")] #[inline] pub fn unwrap(self) -> OMatrix, U1>, DimNameSum, U1>> { self.matrix } /// A reference to the underlying matrix. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// assert_eq!(*t.matrix(), m); /// ``` #[inline] #[must_use] pub fn matrix(&self) -> &OMatrix, U1>, DimNameSum, U1>> { &self.matrix } /// A mutable reference to the underlying matrix. /// /// It is `_unchecked` because direct modifications of this matrix may break invariants /// identified by this transformation category. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let mut t = Transform2::from_matrix_unchecked(m); /// t.matrix_mut_unchecked().m12 = 42.0; /// t.matrix_mut_unchecked().m23 = 90.0; /// /// /// let expected = Matrix3::new(1.0, 42.0, 0.0, /// 3.0, 4.0, 90.0, /// 0.0, 0.0, 1.0); /// assert_eq!(*t.matrix(), expected); /// ``` #[inline] pub fn matrix_mut_unchecked( &mut self, ) -> &mut OMatrix, U1>, DimNameSum, U1>> { &mut self.matrix } /// Sets the category of this transform. /// /// This can be done only if the new category is more general than the current one, e.g., a /// transform with category `TProjective` cannot be converted to a transform with category /// `TAffine` because not all projective transformations are affine (the other way-round is /// valid though). #[inline] pub fn set_category>(self) -> Transform { Transform::from_matrix_unchecked(self.matrix) } /// Clones this transform into one that owns its data. #[inline] #[deprecated( note = "This method is redundant with automatic `Copy` and the `.clone()` method and will be removed in a future release." )] pub fn clone_owned(&self) -> Transform { Transform::from_matrix_unchecked(self.matrix.clone_owned()) } /// Converts this transform into its equivalent homogeneous transformation matrix. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// assert_eq!(t.to_homogeneous(), m); /// ``` #[inline] #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.matrix().clone_owned() } /// Attempts to invert this transformation. You may use `.inverse` instead of this /// transformation has a subcategory of `TProjective` (i.e. if it is a `Projective{2,3}` or `Affine{2,3}`). /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// let inv_t = t.try_inverse().unwrap(); /// assert_relative_eq!(t * inv_t, Transform2::identity()); /// assert_relative_eq!(inv_t * t, Transform2::identity()); /// /// // Non-invertible case. /// let m = Matrix3::new(0.0, 2.0, 1.0, /// 3.0, 0.0, 5.0, /// 0.0, 0.0, 0.0); /// let t = Transform2::from_matrix_unchecked(m); /// assert!(t.try_inverse().is_none()); /// ``` #[inline] #[must_use = "Did you mean to use try_inverse_mut()?"] pub fn try_inverse(self) -> Option> { self.matrix .try_inverse() .map(Transform::from_matrix_unchecked) } /// Inverts this transformation. Use `.try_inverse` if this transform has the `TGeneral` /// category (i.e., a `Transform{2,3}` may not be invertible). /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let proj = Projective2::from_matrix_unchecked(m); /// let inv_t = proj.inverse(); /// assert_relative_eq!(proj * inv_t, Projective2::identity()); /// assert_relative_eq!(inv_t * proj, Projective2::identity()); /// ``` #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(self) -> Transform where C: SubTCategoryOf, { // TODO: specialize for TAffine? Transform::from_matrix_unchecked(self.matrix.try_inverse().unwrap()) } /// Attempts to invert this transformation in-place. You may use `.inverse_mut` instead of this /// transformation has a subcategory of `TProjective`. /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// let mut inv_t = t; /// assert!(inv_t.try_inverse_mut()); /// assert_relative_eq!(t * inv_t, Transform2::identity()); /// assert_relative_eq!(inv_t * t, Transform2::identity()); /// /// // Non-invertible case. /// let m = Matrix3::new(0.0, 2.0, 1.0, /// 3.0, 0.0, 5.0, /// 0.0, 0.0, 0.0); /// let mut t = Transform2::from_matrix_unchecked(m); /// assert!(!t.try_inverse_mut()); /// ``` #[inline] pub fn try_inverse_mut(&mut self) -> bool { self.matrix.try_inverse_mut() } /// Inverts this transformation in-place. Use `.try_inverse_mut` if this transform has the /// `TGeneral` category (it may not be invertible). /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let proj = Projective2::from_matrix_unchecked(m); /// let mut inv_t = proj; /// inv_t.inverse_mut(); /// assert_relative_eq!(proj * inv_t, Projective2::identity()); /// assert_relative_eq!(inv_t * proj, Projective2::identity()); /// ``` #[inline] pub fn inverse_mut(&mut self) where C: SubTCategoryOf, { let _ = self.matrix.try_inverse_mut(); } } impl Transform where T: RealField, C: TCategory, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>>, // + Allocator // + Allocator { /// Transform the given point by this transformation. /// /// This is the same as the multiplication `self * pt`. #[inline] #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { self * pt } /// Transform the given vector by this transformation, ignoring the /// translational component of the transformation. /// /// This is the same as the multiplication `self * v`. #[inline] #[must_use] pub fn transform_vector(&self, v: &SVector) -> SVector { self * v } } impl Transform where Const: DimNameAdd, C: SubTCategoryOf, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>>, // + Allocator // + Allocator { /// Transform the given point by the inverse of this transformation. /// This may be cheaper than inverting the transformation and transforming /// the point. #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { self.clone().inverse() * pt } /// Transform the given vector by the inverse of this transformation. /// This may be cheaper than inverting the transformation and transforming /// the vector. #[inline] #[must_use] pub fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.clone().inverse() * v } } impl Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { /// A mutable reference to underlying matrix. Use `.matrix_mut_unchecked` instead if this /// transformation category is not `TGeneral`. #[inline] pub fn matrix_mut( &mut self, ) -> &mut OMatrix, U1>, DimNameSum, U1>> { self.matrix_mut_unchecked() } } impl AbsDiffEq for Transform where Const: DimNameAdd, T::Epsilon: Clone, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.matrix.abs_diff_eq(&other.matrix, epsilon) } } impl RelativeEq for Transform where Const: DimNameAdd, T::Epsilon: Clone, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.matrix .relative_eq(&other.matrix, epsilon, max_relative) } } impl UlpsEq for Transform where Const: DimNameAdd, T::Epsilon: Clone, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps) } } #[cfg(test)] mod tests { use super::*; use crate::base::Matrix4; #[test] fn checks_homogeneous_invariants_of_square_identity_matrix() { assert!(TAffine::check_homogeneous_invariants( &Matrix4::::identity() )); } } nalgebra-0.33.0/src/geometry/transform_alias.rs000064400000000000000000000015650072674642500176750ustar 00000000000000use crate::geometry::{TAffine, TGeneral, TProjective, Transform}; /// A 2D general transformation that may not be invertible. Stored as a homogeneous 3x3 matrix. pub type Transform2 = Transform; /// An invertible 2D general transformation. Stored as a homogeneous 3x3 matrix. pub type Projective2 = Transform; /// A 2D affine transformation. Stored as a homogeneous 3x3 matrix. pub type Affine2 = Transform; /// A 3D general transformation that may not be inversible. Stored as a homogeneous 4x4 matrix. pub type Transform3 = Transform; /// An invertible 3D general transformation. Stored as a homogeneous 4x4 matrix. pub type Projective3 = Transform; /// A 3D affine transformation. Stored as a homogeneous 4x4 matrix. pub type Affine3 = Transform; nalgebra-0.33.0/src/geometry/transform_construction.rs000064400000000000000000000041030072674642500213250ustar 00000000000000use num::One; use simba::scalar::RealField; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix}; use crate::geometry::{TCategory, Transform}; impl Default for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { fn default() -> Self { Self::identity() } } impl Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { /// Creates a new identity transform. /// /// # Example /// /// ``` /// # use nalgebra::{Transform2, Projective2, Affine2, Transform3, Projective3, Affine3, Point2, Point3}; /// /// let pt = Point2::new(1.0, 2.0); /// let t = Projective2::identity(); /// assert_eq!(t * pt, pt); /// /// let aff = Affine2::identity(); /// assert_eq!(aff * pt, pt); /// /// let aff = Transform2::identity(); /// assert_eq!(aff * pt, pt); /// /// // Also works in 3D. /// let pt = Point3::new(1.0, 2.0, 3.0); /// let t = Projective3::identity(); /// assert_eq!(t * pt, pt); /// /// let aff = Affine3::identity(); /// assert_eq!(aff * pt, pt); /// /// let aff = Transform3::identity(); /// assert_eq!(aff * pt, pt); /// ``` #[inline] pub fn identity() -> Self { Self::from_matrix_unchecked(OMatrix::< _, DimNameSum, U1>, DimNameSum, U1>, >::identity()) } } impl One for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { /// Creates a new identity transform. #[inline] fn one() -> Self { Self::identity() } } nalgebra-0.33.0/src/geometry/transform_conversion.rs000064400000000000000000000045420072674642500207670ustar 00000000000000use simba::scalar::{RealField, SubsetOf}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix}; use crate::geometry::{SuperTCategoryOf, TCategory, Transform}; impl SubsetOf> for Transform where T1: RealField + SubsetOf, T2: RealField, C1: TCategory, C2: SuperTCategoryOf, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, T1::Epsilon: Copy, T2::Epsilon: Copy, { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl SubsetOf, U1>, DimNameSum, U1>>> for Transform where T1: RealField + SubsetOf, T2: RealField, C: TCategory, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, T1::Epsilon: Copy, T2::Epsilon: Copy, { #[inline] fn to_superset(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.matrix().to_superset() } #[inline] fn is_in_subset(m: &OMatrix, U1>, DimNameSum, U1>>) -> bool { C::check_homogeneous_invariants(m) } #[inline] fn from_superset_unchecked( m: &OMatrix, U1>, DimNameSum, U1>>, ) -> Self { Self::from_matrix_unchecked(crate::convert_ref_unchecked(m)) } } impl From> for OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn from(t: Transform) -> Self { t.to_homogeneous() } } nalgebra-0.33.0/src/geometry/transform_ops.rs000064400000000000000000000773160072674642500174140ustar 00000000000000// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] use num::{One, Zero}; use std::ops::{Div, DivAssign, Index, IndexMut, Mul, MulAssign}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign, RealField, SubsetOf}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar}; use crate::geometry::{ Isometry, Point, Rotation, Similarity, SubTCategoryOf, SuperTCategoryOf, TAffine, TCategory, TCategoryMul, TGeneral, TProjective, Transform, Translation, UnitComplex, UnitQuaternion, }; /* * * In the following, we provide: * ========================= * * Index<(usize, usize)> * IndexMut<(usize, usize)> (where TCategory == TGeneral) * * (Operators) * * Transform × Isometry * Transform × Rotation * Transform × Similarity * Transform × Transform * Transform × UnitQuaternion * Transform × UnitComplex * Transform × Translation * Transform × Vector * Transform × Point * * Isometry × Transform * Rotation × Transform * Similarity × Transform * Translation × Transform * UnitQuaternion × Transform * UnitComplex × Transform * * TODO: Transform ÷ Isometry * Transform ÷ Rotation * TODO: Transform ÷ Similarity * Transform ÷ Transform * Transform ÷ UnitQuaternion * Transform ÷ Translation * * TODO: Isometry ÷ Transform * Rotation ÷ Transform * TODO: Similarity ÷ Transform * Translation ÷ Transform * UnitQuaternion ÷ Transform * TODO: UnitComplex ÷ Transform * * * (Assignment Operators) * * * Transform ×= Transform * Transform ×= Similarity * Transform ×= Isometry * Transform ×= Rotation * Transform ×= UnitQuaternion * Transform ×= UnitComplex * Transform ×= Translation * * Transform ÷= Transform * TODO: Transform ÷= Similarity * TODO: Transform ÷= Isometry * Transform ÷= Rotation * Transform ÷= UnitQuaternion * Transform ÷= UnitComplex * */ /* * * Indexing. * */ impl Index<(usize, usize)> for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { type Output = T; #[inline] fn index(&self, ij: (usize, usize)) -> &T { self.matrix().index(ij) } } // Only general transformations are mutably indexable. impl IndexMut<(usize, usize)> for Transform where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn index_mut(&mut self, ij: (usize, usize)) -> &mut T { self.matrix_mut().index_mut(ij) } } // Transform × Vector md_impl_all!( Mul, mul where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C; where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: SVector, Output = SVector; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let transform = self.matrix().fixed_view::(0, 0); if C::has_normalizer() { let normalizer = self.matrix().fixed_view::<1, D>(D, 0); let n = normalizer.tr_dot(rhs); if !n.is_zero() { return transform * (rhs / n); } } transform * rhs }; ); // Transform × Point md_impl_all!( Mul, mul where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C; where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Point, Output = Point; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let transform = self.matrix().fixed_view::(0, 0); let translation = self.matrix().fixed_view::(0, D); if C::has_normalizer() { let normalizer = self.matrix().fixed_view::<1, D>(D, 0); #[allow(clippy::suspicious_arithmetic_impl)] let n = normalizer.tr_dot(&rhs.coords) + unsafe { self.matrix().get_unchecked((D, D)).clone() }; if !n.is_zero() { return (transform * rhs + translation) / n; } } transform * rhs + translation }; ); // Transform × Transform md_impl_all!( Mul, mul where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (DimNameSum, U1>, DimNameSum, U1>) const D; for CA, CB; where Const: DimNameAdd, CA: TCategoryMul, CB: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Transform, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.into_inner()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.matrix()); ); // Transform × Rotation md_impl_all!( Mul, mul where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, Const) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Rotation, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); // Rotation × Transform md_impl_all!( Mul, mul where T: RealField; (Const, Const), (DimNameSum, U1>, DimNameSum, U1>) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Rotation, rhs: Transform, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); // Transform × UnitQuaternion md_impl_all!( Mul, mul where T: RealField; (U4, U4), (U4, U1) const; for C; where C: TCategoryMul; self: Transform, rhs: UnitQuaternion, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.clone().to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.clone().to_homogeneous()); ); // Transform × UnitComplex md_impl_all!( Mul, mul where T: RealField; (U3, U3), (U2, U1) const; for C; where C: TCategoryMul; self: Transform, rhs: UnitComplex, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.clone().to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.clone().to_homogeneous()); ); // UnitQuaternion × Transform md_impl_all!( Mul, mul where T: RealField; (U4, U1), (U4, U4) const; for C; where C: TCategoryMul; self: UnitQuaternion, rhs: Transform, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [ref val] => Self::Output::from_matrix_unchecked(self.clone().to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.clone().to_homogeneous() * rhs.matrix()); ); // UnitComplex × Transform md_impl_all!( Mul, mul where T: RealField; (U2, U1), (U3, U3) const; for C; where C: TCategoryMul; self: UnitComplex, rhs: Transform, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [ref val] => Self::Output::from_matrix_unchecked(self.clone().to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.clone().to_homogeneous() * rhs.matrix()); ); // Transform × Isometry md_impl_all!( Mul, mul where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C, R; where Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> >, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Isometry, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); // Isometry × Transform md_impl_all!( Mul, mul where T: RealField; (Const, U1), (DimNameSum, U1>, DimNameSum, U1>) const D; for C, R; where Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> >, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Isometry, rhs: Transform, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); // Transform × Similarity md_impl_all!( Mul, mul where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C, R; where Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> >, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Similarity, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); // Similarity × Transform md_impl_all!( Mul, mul where T: RealField; (Const, U1), (DimNameSum, U1>, DimNameSum, U1>) const D; for C, R; where Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> >, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Similarity, rhs: Transform, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); /* * * TODO: don't explicitly build the homogeneous translation matrix. * Directly apply the translation, just as in `Matrix::{append,prepend}_translation`. This has not * been done yet because of the `DimNameDiff` requirement (which is not automatically deduced from * `DimNameAdd` requirement). * */ // Transform × Translation md_impl_all!( Mul, mul where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Translation, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); ); // Translation × Transform md_impl_all!( Mul, mul where T: RealField; (Const, U1), (DimNameSum, U1>, DimNameSum, U1>) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Translation, rhs: Transform, Output = Transform; [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); ); // Transform ÷ Transform md_impl_all!( Div, div where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (DimNameSum, U1>, DimNameSum, U1>) const D; for CA, CB; where Const: DimNameAdd, CA: TCategoryMul, CB: SubTCategoryOf, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Transform, Output = Transform; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.clone().inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.clone().inverse() }; ); // Transform ÷ Rotation md_impl_all!( Div, div where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, Const) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Rotation, Output = Transform; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // Rotation ÷ Transform md_impl_all!( Div, div where T: RealField; (Const, Const), (DimNameSum, U1>, DimNameSum, U1>) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Rotation, rhs: Transform, Output = Transform; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; ); // Transform ÷ UnitQuaternion md_impl_all!( Div, div where T: RealField; (U4, U4), (U4, U1) const; for C; where C: TCategoryMul; self: Transform, rhs: UnitQuaternion, Output = Transform; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // UnitQuaternion ÷ Transform md_impl_all!( Div, div where T: RealField; (U4, U1), (U4, U4) const; for C; where C: TCategoryMul; self: UnitQuaternion, rhs: Transform, Output = Transform; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; ); // // Transform ÷ Isometry // md_impl_all!( // Div, div where T: RealField; // (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) // for Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> > // where SB::Alloc: Allocator, U1>, DimNameSum, U1> >; // self: Transform, rhs: Isometry, Output = Transform; // [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.inverse().to_homogeneous()); // [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.inverse().to_homogeneous()); // [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.inverse().to_homogeneous()); // [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.inverse().to_homogeneous()); // ); // // Isometry ÷ Transform // md_impl_all!( // Div, div where T: RealField; // (Const, U1), (DimNameSum, U1>, DimNameSum, U1>) // for Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> > // where SA::Alloc: Allocator, U1>, DimNameSum, U1> >; // self: Isometry, rhs: Transform, Output = Transform; // [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // ); // // Transform ÷ Similarity // md_impl_all!( // Div, div where T: RealField; // (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) // for Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> > // where SB::Alloc: Allocator // where SB::Alloc: Allocator, U1>, DimNameSum, U1> >; // self: Transform, rhs: Similarity, Output = Transform; // [val val] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); // [ref val] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); // [val ref] => Self::Output::from_matrix_unchecked(self.into_inner() * rhs.to_homogeneous()); // [ref ref] => Self::Output::from_matrix_unchecked(self.matrix() * rhs.to_homogeneous()); // ); // // Similarity ÷ Transform // md_impl_all!( // Div, div where T: RealField; // (Const, U1), (DimNameSum, U1>, DimNameSum, U1>) // for Const: DimNameAdd, C: TCategoryMul, R: SubsetOf, U1>, DimNameSum, U1>> > // where SA::Alloc: Allocator // where SA::Alloc: Allocator, U1>, DimNameSum, U1> >; // self: Similarity, rhs: Transform, Output = Transform; // [val val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [ref val] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.into_inner()); // [val ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // [ref ref] => Self::Output::from_matrix_unchecked(self.to_homogeneous() * rhs.matrix()); // ); // Transform ÷ Translation md_impl_all!( Div, div where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Translation, Output = Transform; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * rhs.inverse() }; ); // Translation ÷ Transform md_impl_all!( Div, div where T: RealField; (Const, U1), (DimNameSum, U1>, DimNameSum, U1>) const D; for C; where Const: DimNameAdd, C: TCategoryMul, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Translation, rhs: Transform, Output = Transform; [val val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [ref val] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [val ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self.inverse() * rhs }; ); // Transform ×= Transform md_assign_impl_all!( MulAssign, mul_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (DimNameSum, U1>, DimNameSum, U1>) const D; for CA, CB; where Const: DimNameAdd, CA: TCategory, CB: SubTCategoryOf, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Transform; [val] => *self.matrix_mut_unchecked() *= rhs.into_inner(); [ref] => *self.matrix_mut_unchecked() *= rhs.matrix(); ); // Transform ×= Similarity md_assign_impl_all!( MulAssign, mul_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C, R; where Const: DimNameAdd, C: TCategory, R: SubsetOf, U1>, DimNameSum, U1>> >, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Similarity; [val] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); [ref] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); ); // Transform ×= Isometry md_assign_impl_all!( MulAssign, mul_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C, R; where Const: DimNameAdd, C: TCategory, R: SubsetOf, U1>, DimNameSum, U1>> >, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Isometry; [val] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); [ref] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); ); /* * * TODO: don't explicitly build the homogeneous translation matrix. * Directly apply the translation, just as in `Matrix::{append,prepend}_translation`. This has not * been done yet because of the `DimNameDiff` requirement (which is not automatically deduced from * `DimNameAdd` requirement). * */ // Transform ×= Translation md_assign_impl_all!( MulAssign, mul_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C; where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Translation; [val] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); [ref] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); ); // Transform ×= Rotation md_assign_impl_all!( MulAssign, mul_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, Const) const D; for C; where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Rotation; [val] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); [ref] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); ); // Transform ×= UnitQuaternion md_assign_impl_all!( MulAssign, mul_assign where T: RealField; (U4, U4), (U4, U1) const; for C; where C: TCategory; self: Transform, rhs: UnitQuaternion; [val] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); [ref] => *self.matrix_mut_unchecked() *= rhs.clone().to_homogeneous(); ); // Transform ×= UnitComplex md_assign_impl_all!( MulAssign, mul_assign where T: RealField; (U3, U3), (U2, U1) const; for C; where C: TCategory; self: Transform, rhs: UnitComplex; [val] => *self.matrix_mut_unchecked() *= rhs.to_homogeneous(); [ref] => *self.matrix_mut_unchecked() *= rhs.clone().to_homogeneous(); ); // Transform ÷= Transform md_assign_impl_all!( DivAssign, div_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (DimNameSum, U1>, DimNameSum, U1>) const D; for CA, CB; where Const: DimNameAdd, CA: SuperTCategoryOf, CB: SubTCategoryOf, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Transform; [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.clone().inverse() }; ); // // Transform ÷= Similarity // md_assign_impl_all!( // DivAssign, div_assign; // (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) // for Const: DimNameAdd, C: TCategory, R: SubsetOf, U1>, DimNameSum, U1>> >; // self: Transform, rhs: Similarity; // [val] => *self *= rhs.inverse(); // [ref] => *self *= rhs.inverse(); // ); // // // // Transform ÷= Isometry // md_assign_impl_all!( // DivAssign, div_assign; // (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) // for Const: DimNameAdd, C: TCategory, R: SubsetOf, U1>, DimNameSum, U1>> >; // self: Transform, rhs: Isometry; // [val] => *self *= rhs.inverse(); // [ref] => *self *= rhs.inverse(); // ); // Transform ÷= Translation md_assign_impl_all!( DivAssign, div_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, U1) const D; for C; where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Translation; [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Transform ÷= Rotation md_assign_impl_all!( DivAssign, div_assign where T: RealField; (DimNameSum, U1>, DimNameSum, U1>), (Const, Const) const D; for C; where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>; self: Transform, rhs: Rotation; [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Transform ÷= UnitQuaternion md_assign_impl_all!( DivAssign, div_assign where T: RealField; (U4, U4), (U4, U1) const; for C; where C: TCategory; self: Transform, rhs: UnitQuaternion; [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); // Transform ÷= UnitComplex md_assign_impl_all!( DivAssign, div_assign where T: RealField; (U3, U3), (U2, U1) const; for C; where C: TCategory; self: Transform, rhs: UnitComplex; [val] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; [ref] => #[allow(clippy::suspicious_op_assign_impl)] { *self *= rhs.inverse() }; ); nalgebra-0.33.0/src/geometry/transform_simba.rs000064400000000000000000000031140072674642500176670ustar 00000000000000use simba::simd::SimdValue; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, OMatrix, Scalar}; use crate::RealField; use crate::geometry::{TCategory, Transform}; impl SimdValue for Transform where T::Element: Scalar, C: TCategory, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { const LANES: usize = T::LANES; type Element = Transform; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { Transform::from_matrix_unchecked(OMatrix::splat(val.into_inner())) } #[inline] fn extract(&self, i: usize) -> Self::Element { Transform::from_matrix_unchecked(self.matrix().extract(i)) } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { Transform::from_matrix_unchecked(self.matrix().extract_unchecked(i)) } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.matrix_mut_unchecked().replace(i, val.into_inner()) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.matrix_mut_unchecked() .replace_unchecked(i, val.into_inner()) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { Transform::from_matrix_unchecked(self.into_inner().select(cond, other.into_inner())) } } nalgebra-0.33.0/src/geometry/translation.rs000064400000000000000000000215410072674642500170430ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use num::{One, Zero}; use std::fmt; use std::hash; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use simba::scalar::{ClosedAddAssign, ClosedNeg, ClosedSubAssign}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::storage::Owned; use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar}; use crate::geometry::Point; #[cfg(feature = "rkyv-serialize")] use rkyv::bytecheck; /// A translation. #[repr(C)] #[cfg_attr( feature = "rkyv-serialize-no-std", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive( as = "Translation", bound(archive = " T: rkyv::Archive, SVector: rkyv::Archive> ") ) )] #[cfg_attr(feature = "rkyv-serialize", derive(bytecheck::CheckBytes))] #[derive(Copy, Clone)] pub struct Translation { /// The translation coordinates, i.e., how much is added to a point's coordinates when it is /// translated. pub vector: SVector, } impl fmt::Debug for Translation { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.vector.as_slice().fmt(formatter) } } impl hash::Hash for Translation where Owned>: hash::Hash, { fn hash(&self, state: &mut H) { self.vector.hash(state) } } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Zeroable for Translation where T: Scalar + bytemuck::Zeroable, SVector: bytemuck::Zeroable, { } #[cfg(feature = "bytemuck")] unsafe impl bytemuck::Pod for Translation where T: Scalar + bytemuck::Pod, SVector: bytemuck::Pod, { } #[cfg(feature = "serde-serialize-no-std")] impl Serialize for Translation where Owned>: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.vector.serialize(serializer) } } #[cfg(feature = "serde-serialize-no-std")] impl<'a, T: Scalar, const D: usize> Deserialize<'a> for Translation where Owned>: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a>, { let matrix = SVector::::deserialize(deserializer)?; Ok(Translation::from(matrix)) } } impl Translation { /// Creates a new translation from the given vector. #[inline] #[deprecated(note = "Use `::from` instead.")] pub fn from_vector(vector: SVector) -> Translation { Translation { vector } } /// Inverts `self`. /// /// # Example /// ``` /// # use nalgebra::{Translation2, Translation3}; /// let t = Translation3::new(1.0, 2.0, 3.0); /// assert_eq!(t * t.inverse(), Translation3::identity()); /// assert_eq!(t.inverse() * t, Translation3::identity()); /// /// // Work in all dimensions. /// let t = Translation2::new(1.0, 2.0); /// assert_eq!(t * t.inverse(), Translation2::identity()); /// assert_eq!(t.inverse() * t, Translation2::identity()); /// ``` #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(&self) -> Translation where T: ClosedNeg, { Translation::from(-&self.vector) } /// Converts this translation into its equivalent homogeneous transformation matrix. /// /// # Example /// ``` /// # use nalgebra::{Translation2, Translation3, Matrix3, Matrix4}; /// let t = Translation3::new(10.0, 20.0, 30.0); /// let expected = Matrix4::new(1.0, 0.0, 0.0, 10.0, /// 0.0, 1.0, 0.0, 20.0, /// 0.0, 0.0, 1.0, 30.0, /// 0.0, 0.0, 0.0, 1.0); /// assert_eq!(t.to_homogeneous(), expected); /// /// let t = Translation2::new(10.0, 20.0); /// let expected = Matrix3::new(1.0, 0.0, 10.0, /// 0.0, 1.0, 20.0, /// 0.0, 0.0, 1.0); /// assert_eq!(t.to_homogeneous(), expected); /// ``` #[inline] #[must_use] pub fn to_homogeneous(&self) -> OMatrix, U1>, DimNameSum, U1>> where T: Zero + One, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { let mut res = OMatrix::, U1>, DimNameSum, U1>>::identity(); res.fixed_view_mut::(0, D).copy_from(&self.vector); res } /// Inverts `self` in-place. /// /// # Example /// ``` /// # use nalgebra::{Translation2, Translation3}; /// let t = Translation3::new(1.0, 2.0, 3.0); /// let mut inv_t = Translation3::new(1.0, 2.0, 3.0); /// inv_t.inverse_mut(); /// assert_eq!(t * inv_t, Translation3::identity()); /// assert_eq!(inv_t * t, Translation3::identity()); /// /// // Work in all dimensions. /// let t = Translation2::new(1.0, 2.0); /// let mut inv_t = Translation2::new(1.0, 2.0); /// inv_t.inverse_mut(); /// assert_eq!(t * inv_t, Translation2::identity()); /// assert_eq!(inv_t * t, Translation2::identity()); /// ``` #[inline] pub fn inverse_mut(&mut self) where T: ClosedNeg, { self.vector.neg_mut() } } impl Translation { /// Translate the given point. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// ``` /// # use nalgebra::{Translation3, Point3}; /// let t = Translation3::new(1.0, 2.0, 3.0); /// let transformed_point = t.transform_point(&Point3::new(4.0, 5.0, 6.0)); /// assert_eq!(transformed_point, Point3::new(5.0, 7.0, 9.0)); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point) -> Point { pt + &self.vector } } impl Translation { /// Translate the given point by the inverse of this translation. /// /// # Example /// ``` /// # use nalgebra::{Translation3, Point3}; /// let t = Translation3::new(1.0, 2.0, 3.0); /// let transformed_point = t.inverse_transform_point(&Point3::new(4.0, 5.0, 6.0)); /// assert_eq!(transformed_point, Point3::new(3.0, 3.0, 3.0)); /// ``` #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point) -> Point { pt - &self.vector } } impl Eq for Translation {} impl PartialEq for Translation { #[inline] fn eq(&self, right: &Translation) -> bool { self.vector == right.vector } } impl AbsDiffEq for Translation where T::Epsilon: Clone, { type Epsilon = T::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.vector.abs_diff_eq(&other.vector, epsilon) } } impl RelativeEq for Translation where T::Epsilon: Clone, { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.vector .relative_eq(&other.vector, epsilon, max_relative) } } impl UlpsEq for Translation where T::Epsilon: Clone, { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.vector.ulps_eq(&other.vector, epsilon, max_ulps) } } /* * * Display * */ impl fmt::Display for Translation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let precision = f.precision().unwrap_or(3); writeln!(f, "Translation {{")?; write!(f, "{:.*}", precision, self.vector)?; writeln!(f, "}}") } } nalgebra-0.33.0/src/geometry/translation_alias.rs000064400000000000000000000010250072674642500202070ustar 00000000000000use crate::geometry::Translation; /// A 1-dimensional translation. pub type Translation1 = Translation; /// A 2-dimensional translation. pub type Translation2 = Translation; /// A 3-dimensional translation. pub type Translation3 = Translation; /// A 4-dimensional translation. pub type Translation4 = Translation; /// A 5-dimensional translation. pub type Translation5 = Translation; /// A 6-dimensional translation. pub type Translation6 = Translation; nalgebra-0.33.0/src/geometry/translation_construction.rs000064400000000000000000000103110072674642500216460ustar 00000000000000#[cfg(feature = "arbitrary")] use crate::base::storage::Owned; #[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; use num::{One, Zero}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use simba::scalar::{ClosedAddAssign, SupersetOf}; use crate::base::{SVector, Scalar}; use crate::geometry::Translation; impl Default for Translation { fn default() -> Self { Self::identity() } } impl Translation { /// Creates a new identity translation. /// /// # Example /// ``` /// # use nalgebra::{Point2, Point3, Translation2, Translation3}; /// let t = Translation2::identity(); /// let p = Point2::new(1.0, 2.0); /// assert_eq!(t * p, p); /// /// // Works in all dimensions. /// let t = Translation3::identity(); /// let p = Point3::new(1.0, 2.0, 3.0); /// assert_eq!(t * p, p); /// ``` #[inline] pub fn identity() -> Translation where T: Zero, { Self::from(SVector::::from_element(T::zero())) } /// Cast the components of `self` to another type. /// /// # Example /// ``` /// # use nalgebra::Translation2; /// let tra = Translation2::new(1.0f64, 2.0); /// let tra2 = tra.cast::(); /// assert_eq!(tra2, Translation2::new(1.0f32, 2.0)); /// ``` pub fn cast(self) -> Translation where Translation: SupersetOf, { crate::convert(self) } } impl One for Translation { #[inline] fn one() -> Self { Self::identity() } } #[cfg(feature = "rand-no-std")] impl Distribution> for Standard where Standard: Distribution, { /// Generate an arbitrary random variate for testing purposes. #[inline] fn sample(&self, rng: &mut G) -> Translation { Translation::from(rng.gen::>()) } } #[cfg(feature = "arbitrary")] impl Arbitrary for Translation where Owned>: Send, { #[inline] fn arbitrary(rng: &mut Gen) -> Self { let v: SVector = Arbitrary::arbitrary(rng); Self::from(v) } } /* * * Small translation construction from components. * */ macro_rules! componentwise_constructors_impl( ($($doc: expr; $D: expr, $($args: ident:$irow: expr),*);* $(;)*) => {$( impl Translation { #[doc = "Initializes this translation from its components."] #[doc = "# Example\n```"] #[doc = $doc] #[doc = "```"] #[inline] pub const fn new($($args: T),*) -> Self { Self { vector: SVector::::new($($args),*) } } } )*} ); componentwise_constructors_impl!( "# use nalgebra::Translation1;\nlet t = Translation1::new(1.0);\nassert!(t.vector.x == 1.0);"; 1, x:0; "# use nalgebra::Translation2;\nlet t = Translation2::new(1.0, 2.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0);"; 2, x:0, y:1; "# use nalgebra::Translation3;\nlet t = Translation3::new(1.0, 2.0, 3.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0);"; 3, x:0, y:1, z:2; "# use nalgebra::Translation4;\nlet t = Translation4::new(1.0, 2.0, 3.0, 4.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0);"; 4, x:0, y:1, z:2, w:3; "# use nalgebra::Translation5;\nlet t = Translation5::new(1.0, 2.0, 3.0, 4.0, 5.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0);"; 5, x:0, y:1, z:2, w:3, a:4; "# use nalgebra::Translation6;\nlet t = Translation6::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);\nassert!(t.vector.x == 1.0 && t.vector.y == 2.0 && t.vector.z == 3.0 && t.vector.w == 4.0 && t.vector.a == 5.0 && t.vector.b == 6.0);"; 6, x:0, y:1, z:2, w:3, a:4, b:5; ); nalgebra-0.33.0/src/geometry/translation_conversion.rs000064400000000000000000000217300072674642500213100ustar 00000000000000use num::{One, Zero}; use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::PrimitiveSimdValue; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, DimName, OMatrix, OVector, SVector, Scalar}; use crate::geometry::{ AbstractRotation, Isometry, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, Translation3, UnitDualQuaternion, UnitQuaternion, }; use crate::{ArrayStorage, Point}; /* * This file provides the following conversions: * ============================================= * * Translation -> Translation * Translation -> Isometry * Translation3 -> UnitDualQuaternion * Translation -> Similarity * Translation -> Transform * Translation -> Matrix (homogeneous) */ impl SubsetOf> for Translation where T1: Scalar, T2: Scalar + SupersetOf, { #[inline] fn to_superset(&self) -> Translation { Translation::from(self.vector.to_superset()) } #[inline] fn is_in_subset(rot: &Translation) -> bool { crate::is_convertible::<_, SVector>(&rot.vector) } #[inline] fn from_superset_unchecked(rot: &Translation) -> Self { Translation { vector: rot.vector.to_subset_unchecked(), } } } impl SubsetOf> for Translation where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation, { #[inline] fn to_superset(&self) -> Isometry { Isometry::from_parts(self.to_superset(), R::identity()) } #[inline] fn is_in_subset(iso: &Isometry) -> bool { iso.rotation == R::identity() } #[inline] fn from_superset_unchecked(iso: &Isometry) -> Self { Self::from_superset_unchecked(&iso.translation) } } impl SubsetOf> for Translation3 where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitDualQuaternion { let dq = UnitDualQuaternion::::from_parts(self.clone(), UnitQuaternion::identity()); dq.to_superset() } #[inline] fn is_in_subset(dq: &UnitDualQuaternion) -> bool { crate::is_convertible::<_, Translation>(&dq.translation()) && dq.rotation() == UnitQuaternion::identity() } #[inline] fn from_superset_unchecked(dq: &UnitDualQuaternion) -> Self { let dq: UnitDualQuaternion = crate::convert_ref_unchecked(dq); dq.translation() } } impl SubsetOf> for Translation where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation, { #[inline] fn to_superset(&self) -> Similarity { Similarity::from_parts(self.to_superset(), R::identity(), T2::one()) } #[inline] fn is_in_subset(sim: &Similarity) -> bool { sim.isometry.rotation == R::identity() && sim.scaling() == T2::one() } #[inline] fn from_superset_unchecked(sim: &Similarity) -> Self { Self::from_superset_unchecked(&sim.isometry.translation) } } impl SubsetOf> for Translation where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl SubsetOf, U1>, DimNameSum, U1>>> for Translation where T1: RealField, T2: RealField + SupersetOf, Const: DimNameAdd, DefaultAllocator: Allocator, Buffer = ArrayStorage> + Allocator, U1>, DimNameSum, U1>>, { #[inline] fn to_superset(&self) -> OMatrix, U1>, DimNameSum, U1>> { self.to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &OMatrix, U1>, DimNameSum, U1>>) -> bool { let id = m.generic_view((0, 0), (DimNameSum::, U1>::name(), Const::)); // Scalar types agree. m.iter().all(|e| SupersetOf::::is_in_subset(e)) && // The block part does nothing. id.is_identity(T2::zero()) && // The normalization factor is one. m[(D, D)] == T2::one() } #[inline] fn from_superset_unchecked( m: &OMatrix, U1>, DimNameSum, U1>>, ) -> Self { let t: OVector> = m.fixed_view::(0, D).into_owned(); Self { vector: crate::convert_unchecked(t), } } } impl From> for OMatrix, U1>, DimNameSum, U1>> where Const: DimNameAdd, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator>, { #[inline] fn from(t: Translation) -> Self { t.to_homogeneous() } } impl From>> for Translation { #[inline] fn from(vector: OVector>) -> Self { Translation { vector } } } impl From<[T; D]> for Translation { #[inline] fn from(coords: [T; D]) -> Self { Translation { vector: coords.into(), } } } impl From> for Translation { #[inline] fn from(pt: Point) -> Self { Translation { vector: pt.coords } } } impl From> for [T; D] { #[inline] fn from(t: Translation) -> Self { t.vector.into() } } impl From<[Translation; 2]> for Translation where T: From<[::Element; 2]>, T::Element: Scalar, { #[inline] fn from(arr: [Translation; 2]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), ])) } } impl From<[Translation; 4]> for Translation where T: From<[::Element; 4]>, T::Element: Scalar, { #[inline] fn from(arr: [Translation; 4]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), arr[2].vector.clone(), arr[3].vector.clone(), ])) } } impl From<[Translation; 8]> for Translation where T: From<[::Element; 8]>, T::Element: Scalar, { #[inline] fn from(arr: [Translation; 8]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), arr[2].vector.clone(), arr[3].vector.clone(), arr[4].vector.clone(), arr[5].vector.clone(), arr[6].vector.clone(), arr[7].vector.clone(), ])) } } impl From<[Translation; 16]> for Translation where T: From<[::Element; 16]>, T::Element: Scalar, { #[inline] fn from(arr: [Translation; 16]) -> Self { Self::from(OVector::from([ arr[0].vector.clone(), arr[1].vector.clone(), arr[2].vector.clone(), arr[3].vector.clone(), arr[4].vector.clone(), arr[5].vector.clone(), arr[6].vector.clone(), arr[7].vector.clone(), arr[8].vector.clone(), arr[9].vector.clone(), arr[10].vector.clone(), arr[11].vector.clone(), arr[12].vector.clone(), arr[13].vector.clone(), arr[14].vector.clone(), arr[15].vector.clone(), ])) } } nalgebra-0.33.0/src/geometry/translation_coordinates.rs000064400000000000000000000020520072674642500214310ustar 00000000000000use std::ops::{Deref, DerefMut}; use crate::base::coordinates::{X, XY, XYZ, XYZW, XYZWA, XYZWAB}; use crate::base::Scalar; use crate::geometry::Translation; /* * * Give coordinates to Translation{1 .. 6} * */ macro_rules! deref_impl( ($D: expr, $Target: ident $(, $comps: ident)*) => { impl Deref for Translation { type Target = $Target; #[inline] fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Translation as *const Self::Target) } } } impl DerefMut for Translation { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *(self as *mut Translation as *mut Self::Target) } } } } ); deref_impl!(1, X, x); deref_impl!(2, XY, x, y); deref_impl!(3, XYZ, x, y, z); deref_impl!(4, XYZW, x, y, z, w); deref_impl!(5, XYZWA, x, y, z, w, a); deref_impl!(6, XYZWAB, x, y, z, w, a, b); nalgebra-0.33.0/src/geometry/translation_ops.rs000064400000000000000000000122140072674642500177210ustar 00000000000000use std::ops::{Div, DivAssign, Mul, MulAssign}; use simba::scalar::{ClosedAddAssign, ClosedSubAssign}; use crate::base::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint}; use crate::base::dimension::U1; use crate::base::{Const, Scalar}; use crate::geometry::{Point, Translation}; // Translation × Translation add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Translation, right: &'b Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector + &right.vector) }; 'a, 'b); add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Translation, right: Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector + right.vector) }; 'a); add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Translation, right: &'b Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector + &right.vector) }; 'b); add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Translation, right: Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector + right.vector) }; ); // Translation ÷ Translation // TODO: instead of calling inverse explicitly, could we just add a `mul_tr` or `mul_inv` method? add_sub_impl!(Div, div, ClosedSubAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Translation, right: &'b Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector - &right.vector) }; 'a, 'b); add_sub_impl!(Div, div, ClosedSubAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Translation, right: Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(&self.vector - right.vector) }; 'a); add_sub_impl!(Div, div, ClosedSubAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Translation, right: &'b Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector - &right.vector) }; 'b); add_sub_impl!(Div, div, ClosedSubAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Translation, right: Translation, Output = Translation; #[allow(clippy::suspicious_arithmetic_impl)] { Translation::from(self.vector - right.vector) }; ); // Translation × Point // TODO: we don't handle properly non-zero origins here. Do we want this to be the intended // behavior? add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Translation, right: &'b Point, Output = Point; #[allow(clippy::suspicious_arithmetic_impl)] { right + &self.vector }; 'a, 'b); add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: &'a Translation, right: Point, Output = Point; #[allow(clippy::suspicious_arithmetic_impl)] { right + &self.vector }; 'a); add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Translation, right: &'b Point, Output = Point; #[allow(clippy::suspicious_arithmetic_impl)] { right + self.vector }; 'b); add_sub_impl!(Mul, mul, ClosedAddAssign; (Const, U1), (Const, U1) -> (Const, U1) const D; for; where; self: Translation, right: Point, Output = Point; #[allow(clippy::suspicious_arithmetic_impl)] { right + self.vector }; ); // Translation *= Translation add_sub_assign_impl!(MulAssign, mul_assign, ClosedAddAssign; const D; self: Translation, right: &'b Translation; #[allow(clippy::suspicious_op_assign_impl)] { self.vector += &right.vector }; 'b); add_sub_assign_impl!(MulAssign, mul_assign, ClosedAddAssign; const D; self: Translation, right: Translation; #[allow(clippy::suspicious_op_assign_impl)] { self.vector += right.vector }; ); add_sub_assign_impl!(DivAssign, div_assign, ClosedSubAssign; const D; self: Translation, right: &'b Translation; #[allow(clippy::suspicious_op_assign_impl)] { self.vector -= &right.vector }; 'b); add_sub_assign_impl!(DivAssign, div_assign, ClosedSubAssign; const D; self: Translation, right: Translation; #[allow(clippy::suspicious_op_assign_impl)] { self.vector -= right.vector }; ); nalgebra-0.33.0/src/geometry/translation_simba.rs000064400000000000000000000021700072674642500202130ustar 00000000000000use simba::simd::SimdValue; use crate::base::OVector; use crate::Scalar; use crate::geometry::Translation; impl SimdValue for Translation where T::Element: Scalar, { const LANES: usize = T::LANES; type Element = Translation; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { OVector::splat(val.vector).into() } #[inline] fn extract(&self, i: usize) -> Self::Element { self.vector.extract(i).into() } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { self.vector.extract_unchecked(i).into() } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.vector.replace(i, val.vector) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.vector.replace_unchecked(i, val.vector) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { self.vector.select(cond, other.vector).into() } } nalgebra-0.33.0/src/geometry/unit_complex.rs000064400000000000000000000357670072674642500172320ustar 00000000000000use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use num_complex::Complex; use std::fmt; use crate::base::{Matrix2, Matrix3, Normed, Unit, Vector1, Vector2}; use crate::geometry::{Point2, Rotation2}; use crate::Scalar; use simba::scalar::RealField; use simba::simd::SimdRealField; use std::cmp::{Eq, PartialEq}; /// A 2D rotation represented as a complex number with magnitude 1. /// /// All the methods specific [`UnitComplex`](crate::UnitComplex) are listed here. You may also /// read the documentation of the [`Complex`](crate::Complex) type which /// is used internally and accessible with `unit_complex.complex()`. /// /// # Construction /// * [Identity `identity`](#identity) /// * [From a 2D rotation angle `new`, `from_cos_sin_unchecked`…](#construction-from-a-2d-rotation-angle) /// * [From an existing 2D matrix or complex number `from_matrix`, `rotation_to`, `powf`…](#construction-from-an-existing-2d-matrix-or-complex-number) /// * [From two vectors `rotation_between`, `scaled_rotation_between_axis`…](#construction-from-two-vectors) /// /// # Transformation and composition /// * [Angle extraction `angle`, `angle_to`…](#angle-extraction) /// * [Transformation of a vector or a point `transform_vector`, `inverse_transform_point`…](#transformation-of-a-vector-or-a-point) /// * [Conjugation and inversion `conjugate`, `inverse_mut`…](#conjugation-and-inversion) /// * [Interpolation `slerp`…](#interpolation) /// /// # Conversion /// * [Conversion to a matrix `to_rotation_matrix`, `to_homogeneous`…](#conversion-to-a-matrix) pub type UnitComplex = Unit>; impl PartialEq for UnitComplex { #[inline] fn eq(&self, rhs: &Self) -> bool { (**self).eq(&**rhs) } } impl Eq for UnitComplex {} impl Normed for Complex { type Norm = T::SimdRealField; #[inline] fn norm(&self) -> T::SimdRealField { // We don't use `.norm_sqr()` because it requires // some very strong Num trait requirements. (self.re.clone() * self.re.clone() + self.im.clone() * self.im.clone()).simd_sqrt() } #[inline] fn norm_squared(&self) -> T::SimdRealField { // We don't use `.norm_sqr()` because it requires // some very strong Num trait requirements. self.re.clone() * self.re.clone() + self.im.clone() * self.im.clone() } #[inline] fn scale_mut(&mut self, n: Self::Norm) { self.re *= n.clone(); self.im *= n; } #[inline] fn unscale_mut(&mut self, n: Self::Norm) { self.re /= n.clone(); self.im /= n; } } /// # Angle extraction impl UnitComplex where T::Element: SimdRealField, { /// The rotation angle in `]-pi; pi]` of this unit complex number. /// /// # Example /// ``` /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(1.78); /// assert_eq!(rot.angle(), 1.78); /// ``` #[inline] #[must_use] pub fn angle(&self) -> T { self.im.clone().simd_atan2(self.re.clone()) } /// The sine of the rotation angle. /// /// # Example /// ``` /// # use nalgebra::UnitComplex; /// let angle = 1.78f32; /// let rot = UnitComplex::new(angle); /// assert_eq!(rot.sin_angle(), angle.sin()); /// ``` #[inline] #[must_use] pub fn sin_angle(&self) -> T { self.im.clone() } /// The cosine of the rotation angle. /// /// # Example /// ``` /// # use nalgebra::UnitComplex; /// let angle = 1.78f32; /// let rot = UnitComplex::new(angle); /// assert_eq!(rot.cos_angle(),angle.cos()); /// ``` #[inline] #[must_use] pub fn cos_angle(&self) -> T { self.re.clone() } /// The rotation angle returned as a 1-dimensional vector. /// /// This is generally used in the context of generic programming. Using /// the `.angle()` method instead is more common. #[inline] #[must_use] pub fn scaled_axis(&self) -> Vector1 { Vector1::new(self.angle()) } /// The rotation axis and angle in (0, pi] of this complex number. /// /// This is generally used in the context of generic programming. Using /// the `.angle()` method instead is more common. /// Returns `None` if the angle is zero. #[inline] #[must_use] pub fn axis_angle(&self) -> Option<(Unit>, T)> where T: RealField, { let ang = self.angle(); if ang.is_zero() { None } else if ang.is_sign_positive() { Some((Unit::new_unchecked(Vector1::x()), ang)) } else { Some((Unit::new_unchecked(-Vector1::::x()), -ang)) } } /// The rotation angle needed to make `self` and `other` coincide. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let rot1 = UnitComplex::new(0.1); /// let rot2 = UnitComplex::new(1.7); /// assert_relative_eq!(rot1.angle_to(&rot2), 1.6); /// ``` #[inline] #[must_use] pub fn angle_to(&self, other: &Self) -> T { let delta = self.rotation_to(other); delta.angle() } } /// # Conjugation and inversion impl UnitComplex where T::Element: SimdRealField, { /// Compute the conjugate of this unit complex number. /// /// # Example /// ``` /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(1.78); /// let conj = rot.conjugate(); /// assert_eq!(rot.complex().im, -conj.complex().im); /// assert_eq!(rot.complex().re, conj.complex().re); /// ``` #[inline] #[must_use = "Did you mean to use conjugate_mut()?"] pub fn conjugate(&self) -> Self { Self::new_unchecked(self.conj()) } /// Inverts this complex number if it is not zero. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(1.2); /// let inv = rot.inverse(); /// assert_relative_eq!(rot * inv, UnitComplex::identity(), epsilon = 1.0e-6); /// assert_relative_eq!(inv * rot, UnitComplex::identity(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use = "Did you mean to use inverse_mut()?"] pub fn inverse(&self) -> Self { self.conjugate() } /// Compute in-place the conjugate of this unit complex number. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let angle = 1.7; /// let rot = UnitComplex::new(angle); /// let mut conj = UnitComplex::new(angle); /// conj.conjugate_mut(); /// assert_eq!(rot.complex().im, -conj.complex().im); /// assert_eq!(rot.complex().re, conj.complex().re); /// ``` #[inline] pub fn conjugate_mut(&mut self) { let me = self.as_mut_unchecked(); me.im = -me.im.clone(); } /// Inverts in-place this unit complex number. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let angle = 1.7; /// let mut rot = UnitComplex::new(angle); /// rot.inverse_mut(); /// assert_relative_eq!(rot * UnitComplex::new(angle), UnitComplex::identity()); /// assert_relative_eq!(UnitComplex::new(angle) * rot, UnitComplex::identity()); /// ``` #[inline] pub fn inverse_mut(&mut self) { self.conjugate_mut() } } /// # Conversion to a matrix impl UnitComplex where T::Element: SimdRealField, { /// Builds the rotation matrix corresponding to this unit complex number. /// /// # Example /// ``` /// # use nalgebra::{UnitComplex, Rotation2}; /// # use std::f32; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_6); /// let expected = Rotation2::new(f32::consts::FRAC_PI_6); /// assert_eq!(rot.to_rotation_matrix(), expected); /// ``` #[inline] #[must_use] pub fn to_rotation_matrix(self) -> Rotation2 { let r = self.re.clone(); let i = self.im.clone(); Rotation2::from_matrix_unchecked(Matrix2::new(r.clone(), -i.clone(), i, r)) } /// Converts this unit complex number into its equivalent homogeneous transformation matrix. /// /// # Example /// ``` /// # use nalgebra::{UnitComplex, Matrix3}; /// # use std::f32; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_6); /// let expected = Matrix3::new(0.8660254, -0.5, 0.0, /// 0.5, 0.8660254, 0.0, /// 0.0, 0.0, 1.0); /// assert_eq!(rot.to_homogeneous(), expected); /// ``` #[inline] #[must_use] pub fn to_homogeneous(self) -> Matrix3 { self.to_rotation_matrix().to_homogeneous() } } /// # Transformation of a vector or a point impl UnitComplex where T::Element: SimdRealField, { /// Rotate the given point by this unit complex number. /// /// This is the same as the multiplication `self * pt`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitComplex, Point2}; /// # use std::f32; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// let transformed_point = rot.transform_point(&Point2::new(1.0, 2.0)); /// assert_relative_eq!(transformed_point, Point2::new(-2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_point(&self, pt: &Point2) -> Point2 { self * pt } /// Rotate the given vector by this unit complex number. /// /// This is the same as the multiplication `self * v`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitComplex, Vector2}; /// # use std::f32; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// let transformed_vector = rot.transform_vector(&Vector2::new(1.0, 2.0)); /// assert_relative_eq!(transformed_vector, Vector2::new(-2.0, 1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn transform_vector(&self, v: &Vector2) -> Vector2 { self * v } /// Rotate the given point by the inverse of this unit complex number. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitComplex, Point2}; /// # use std::f32; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// let transformed_point = rot.inverse_transform_point(&Point2::new(1.0, 2.0)); /// assert_relative_eq!(transformed_point, Point2::new(2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_point(&self, pt: &Point2) -> Point2 { // TODO: would it be useful performance-wise not to call inverse explicitly (i-e. implement // the inverse transformation explicitly here) ? self.inverse() * pt } /// Rotate the given vector by the inverse of this unit complex number. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitComplex, Vector2}; /// # use std::f32; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// let transformed_vector = rot.inverse_transform_vector(&Vector2::new(1.0, 2.0)); /// assert_relative_eq!(transformed_vector, Vector2::new(2.0, -1.0), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_vector(&self, v: &Vector2) -> Vector2 { self.inverse() * v } /// Rotate the given vector by the inverse of this unit complex number. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{UnitComplex, Vector2}; /// # use std::f32; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// let transformed_vector = rot.inverse_transform_unit_vector(&Vector2::x_axis()); /// assert_relative_eq!(transformed_vector, -Vector2::y_axis(), epsilon = 1.0e-6); /// ``` #[inline] #[must_use] pub fn inverse_transform_unit_vector(&self, v: &Unit>) -> Unit> { self.inverse() * v } } /// # Interpolation impl UnitComplex where T::Element: SimdRealField, { /// Spherical linear interpolation between two rotations represented as unit complex numbers. /// /// # Examples: /// /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::geometry::UnitComplex; /// /// let rot1 = UnitComplex::new(std::f32::consts::FRAC_PI_4); /// let rot2 = UnitComplex::new(-std::f32::consts::PI); /// /// let rot = rot1.slerp(&rot2, 1.0 / 3.0); /// /// assert_relative_eq!(rot.angle(), std::f32::consts::FRAC_PI_2); /// ``` #[inline] #[must_use] pub fn slerp(&self, other: &Self, t: T) -> Self { let delta = other / self; self * Self::new(delta.angle() * t) } } impl fmt::Display for UnitComplex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "UnitComplex angle: {}", self.angle()) } } impl AbsDiffEq for UnitComplex { type Epsilon = T; #[inline] fn default_epsilon() -> Self::Epsilon { T::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.re.abs_diff_eq(&other.re, epsilon.clone()) && self.im.abs_diff_eq(&other.im, epsilon) } } impl RelativeEq for UnitComplex { #[inline] fn default_max_relative() -> Self::Epsilon { T::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.re .relative_eq(&other.re, epsilon.clone(), max_relative.clone()) && self.im.relative_eq(&other.im, epsilon, max_relative) } } impl UlpsEq for UnitComplex { #[inline] fn default_max_ulps() -> u32 { T::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.re.ulps_eq(&other.re, epsilon.clone(), max_ulps) && self.im.ulps_eq(&other.im, epsilon, max_ulps) } } nalgebra-0.33.0/src/geometry/unit_complex_construction.rs000064400000000000000000000341640072674642500220320ustar 00000000000000#[cfg(feature = "arbitrary")] use quickcheck::{Arbitrary, Gen}; #[cfg(feature = "rand-no-std")] use rand::{ distributions::{Distribution, Standard}, Rng, }; use num::One; use num_complex::Complex; use crate::base::dimension::{U1, U2}; use crate::base::storage::Storage; use crate::base::{Matrix2, Scalar, Unit, Vector, Vector2}; use crate::geometry::{Rotation2, UnitComplex}; use simba::scalar::{RealField, SupersetOf}; use simba::simd::SimdRealField; impl Default for UnitComplex where T::Element: SimdRealField, { fn default() -> Self { Self::identity() } } /// # Identity impl UnitComplex where T::Element: SimdRealField, { /// The unit complex number multiplicative identity. /// /// # Example /// ``` /// # use nalgebra::UnitComplex; /// let rot1 = UnitComplex::identity(); /// let rot2 = UnitComplex::new(1.7); /// /// assert_eq!(rot1 * rot2, rot2); /// assert_eq!(rot2 * rot1, rot2); /// ``` #[inline] pub fn identity() -> Self { Self::new_unchecked(Complex::new(T::one(), T::zero())) } } /// # Construction from a 2D rotation angle impl UnitComplex where T::Element: SimdRealField, { /// Builds the unit complex number corresponding to the rotation with the given angle. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitComplex, Point2}; /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2); /// /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); /// ``` #[inline] pub fn new(angle: T) -> Self { let (sin, cos) = angle.simd_sin_cos(); Self::from_cos_sin_unchecked(cos, sin) } /// Builds the unit complex number corresponding to the rotation with the angle. /// /// Same as `Self::new(angle)`. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitComplex, Point2}; /// let rot = UnitComplex::from_angle(f32::consts::FRAC_PI_2); /// /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); /// ``` // TODO: deprecate this. #[inline] pub fn from_angle(angle: T) -> Self { Self::new(angle) } /// Builds the unit complex number from the sinus and cosinus of the rotation angle. /// /// The input values are not checked to actually be cosines and sine of the same value. /// Is is generally preferable to use the `::new(angle)` constructor instead. /// /// # Example /// /// ``` /// # #[macro_use] extern crate approx; /// # use std::f32; /// # use nalgebra::{UnitComplex, Vector2, Point2}; /// let angle = f32::consts::FRAC_PI_2; /// let rot = UnitComplex::from_cos_sin_unchecked(angle.cos(), angle.sin()); /// /// assert_relative_eq!(rot * Point2::new(3.0, 4.0), Point2::new(-4.0, 3.0)); /// ``` #[inline] pub fn from_cos_sin_unchecked(cos: T, sin: T) -> Self { Self::new_unchecked(Complex::new(cos, sin)) } /// Builds a unit complex rotation from an angle in radian wrapped in a 1-dimensional vector. /// /// This is generally used in the context of generic programming. Using /// the `::new(angle)` method instead is more common. #[inline] pub fn from_scaled_axis>(axisangle: Vector) -> Self { Self::from_angle(axisangle[0].clone()) } } /// # Construction from an existing 2D matrix or complex number impl UnitComplex where T::Element: SimdRealField, { /// Cast the components of `self` to another type. /// /// # Example /// ``` /// #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let c = UnitComplex::new(1.0f64); /// let c2 = c.cast::(); /// assert_relative_eq!(c2, UnitComplex::new(1.0f32)); /// ``` pub fn cast(self) -> UnitComplex where UnitComplex: SupersetOf, { crate::convert(self) } /// The underlying complex number. /// /// Same as `self.as_ref()`. /// /// # Example /// ``` /// # extern crate num_complex; /// # use num_complex::Complex; /// # use nalgebra::UnitComplex; /// let angle = 1.78f32; /// let rot = UnitComplex::new(angle); /// assert_eq!(*rot.complex(), Complex::new(angle.cos(), angle.sin())); /// ``` #[inline] #[must_use] pub fn complex(&self) -> &Complex { self.as_ref() } /// Creates a new unit complex number from a complex number. /// /// The input complex number will be normalized. #[inline] pub fn from_complex(q: Complex) -> Self { Self::from_complex_and_get(q).0 } /// Creates a new unit complex number from a complex number. /// /// The input complex number will be normalized. Returns the norm of the complex number as well. #[inline] pub fn from_complex_and_get(q: Complex) -> (Self, T) { let norm = (q.im.clone() * q.im.clone() + q.re.clone() * q.re.clone()).simd_sqrt(); (Self::new_unchecked(q / norm.clone()), norm) } /// Builds the unit complex number from the corresponding 2D rotation matrix. /// /// # Example /// ``` /// # use nalgebra::{Rotation2, UnitComplex}; /// let rot = Rotation2::new(1.7); /// let complex = UnitComplex::from_rotation_matrix(&rot); /// assert_eq!(complex, UnitComplex::new(1.7)); /// ``` // TODO: add UnitComplex::from(...) instead? #[inline] pub fn from_rotation_matrix(rotmat: &Rotation2) -> Self { Self::new_unchecked(Complex::new(rotmat[(0, 0)].clone(), rotmat[(1, 0)].clone())) } /// Builds a rotation from a basis assumed to be orthonormal. /// /// In order to get a valid unit-quaternion, the input must be an /// orthonormal basis, i.e., all vectors are normalized, and the are /// all orthogonal to each other. These invariants are not checked /// by this method. pub fn from_basis_unchecked(basis: &[Vector2; 2]) -> Self { let mat = Matrix2::from_columns(&basis[..]); let rot = Rotation2::from_matrix_unchecked(mat); Self::from_rotation_matrix(&rot) } /// Builds an unit complex by extracting the rotation part of the given transformation `m`. /// /// This is an iterative method. See `.from_matrix_eps` to provide mover /// convergence parameters and starting solution. /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. pub fn from_matrix(m: &Matrix2) -> Self where T: RealField, { Rotation2::from_matrix(m).into() } /// Builds an unit complex by extracting the rotation part of the given transformation `m`. /// /// This implements "A Robust Method to Extract the Rotational Part of Deformations" by Müller et al. /// /// # Parameters /// /// * `m`: the matrix from which the rotational part is to be extracted. /// * `eps`: the angular errors tolerated between the current rotation and the optimal one. /// * `max_iter`: the maximum number of iterations. Loops indefinitely until convergence if set to `0`. /// * `guess`: an estimate of the solution. Convergence will be significantly faster if an initial solution close /// to the actual solution is provided. Can be set to `UnitQuaternion::identity()` if no other /// guesses come to mind. pub fn from_matrix_eps(m: &Matrix2, eps: T, max_iter: usize, guess: Self) -> Self where T: RealField, { let guess = Rotation2::from(guess); Rotation2::from_matrix_eps(m, eps, max_iter, guess).into() } /// The unit complex number needed to make `self` and `other` coincide. /// /// The result is such that: `self.rotation_to(other) * self == other`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let rot1 = UnitComplex::new(0.1); /// let rot2 = UnitComplex::new(1.7); /// let rot_to = rot1.rotation_to(&rot2); /// /// assert_relative_eq!(rot_to * rot1, rot2); /// assert_relative_eq!(rot_to.inverse() * rot2, rot1); /// ``` #[inline] #[must_use] pub fn rotation_to(&self, other: &Self) -> Self { other / self } /// Raise this unit complex number to a given floating power. /// /// This returns the unit complex number that identifies a rotation angle equal to /// `self.angle() × n`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::UnitComplex; /// let rot = UnitComplex::new(0.78); /// let pow = rot.powf(2.0); /// assert_relative_eq!(pow.angle(), 2.0 * 0.78); /// ``` #[inline] #[must_use] pub fn powf(&self, n: T) -> Self { Self::from_angle(self.angle() * n) } } /// # Construction from two vectors impl UnitComplex where T::Element: SimdRealField, { /// The unit complex needed to make `a` and `b` be collinear and point toward the same /// direction. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector2, UnitComplex}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); /// let rot = UnitComplex::rotation_between(&a, &b); /// assert_relative_eq!(rot * a, b); /// assert_relative_eq!(rot.inverse() * b, a); /// ``` #[inline] pub fn rotation_between(a: &Vector, b: &Vector) -> Self where T: RealField, SB: Storage, SC: Storage, { Self::scaled_rotation_between(a, b, T::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Vector2, UnitComplex}; /// let a = Vector2::new(1.0, 2.0); /// let b = Vector2::new(2.0, 1.0); /// let rot2 = UnitComplex::scaled_rotation_between(&a, &b, 0.2); /// let rot5 = UnitComplex::scaled_rotation_between(&a, &b, 0.5); /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); /// ``` #[inline] pub fn scaled_rotation_between( a: &Vector, b: &Vector, s: T, ) -> Self where T: RealField, SB: Storage, SC: Storage, { // TODO: code duplication with Rotation. if let (Some(na), Some(nb)) = ( Unit::try_new(a.clone_owned(), T::zero()), Unit::try_new(b.clone_owned(), T::zero()), ) { Self::scaled_rotation_between_axis(&na, &nb, s) } else { Self::identity() } } /// The unit complex needed to make `a` and `b` be collinear and point toward the same /// direction. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Unit, Vector2, UnitComplex}; /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); /// let rot = UnitComplex::rotation_between_axis(&a, &b); /// assert_relative_eq!(rot * a, b); /// assert_relative_eq!(rot.inverse() * b, a); /// ``` #[inline] pub fn rotation_between_axis( a: &Unit>, b: &Unit>, ) -> Self where SB: Storage, SC: Storage, { Self::scaled_rotation_between_axis(a, b, T::one()) } /// The smallest rotation needed to make `a` and `b` collinear and point toward the same /// direction, raised to the power `s`. /// /// # Example /// ``` /// # #[macro_use] extern crate approx; /// # use nalgebra::{Unit, Vector2, UnitComplex}; /// let a = Unit::new_normalize(Vector2::new(1.0, 2.0)); /// let b = Unit::new_normalize(Vector2::new(2.0, 1.0)); /// let rot2 = UnitComplex::scaled_rotation_between_axis(&a, &b, 0.2); /// let rot5 = UnitComplex::scaled_rotation_between_axis(&a, &b, 0.5); /// assert_relative_eq!(rot2 * rot2 * rot2 * rot2 * rot2 * a, b, epsilon = 1.0e-6); /// assert_relative_eq!(rot5 * rot5 * a, b, epsilon = 1.0e-6); /// ``` #[inline] pub fn scaled_rotation_between_axis( na: &Unit>, nb: &Unit>, s: T, ) -> Self where SB: Storage, SC: Storage, { let sang = na.perp(nb); let cang = na.dot(nb); Self::from_angle(sang.simd_atan2(cang) * s) } } impl One for UnitComplex where T::Element: SimdRealField, { #[inline] fn one() -> Self { Self::identity() } } #[cfg(feature = "rand")] impl Distribution> for Standard where T::Element: SimdRealField, rand_distr::UnitCircle: Distribution<[T; 2]>, { /// Generate a uniformly distributed random `UnitComplex`. #[inline] fn sample<'a, R: Rng + ?Sized>(&self, rng: &mut R) -> UnitComplex { let x = rng.sample(rand_distr::UnitCircle); UnitComplex::new_unchecked(Complex::new(x[0].clone(), x[1].clone())) } } #[cfg(feature = "arbitrary")] impl Arbitrary for UnitComplex where T::Element: SimdRealField, { #[inline] fn arbitrary(g: &mut Gen) -> Self { UnitComplex::from_angle(T::arbitrary(g)) } } nalgebra-0.33.0/src/geometry/unit_complex_conversion.rs000064400000000000000000000167600072674642500214670ustar 00000000000000use num::Zero; use num_complex::Complex; use simba::scalar::{RealField, SubsetOf, SupersetOf}; use simba::simd::{PrimitiveSimdValue, SimdRealField}; use crate::base::{Matrix2, Matrix3, Scalar}; use crate::geometry::{ AbstractRotation, Isometry, Rotation2, Similarity, SuperTCategoryOf, TAffine, Transform, Translation, UnitComplex, }; /* * This file provides the following conversions: * ============================================= * * UnitComplex -> UnitComplex * UnitComplex -> Rotation * UnitComplex -> Isometry<2> * UnitComplex -> Similarity<2> * UnitComplex -> Transform<2> * UnitComplex -> Matrix (homogeneous) * * NOTE: * UnitComplex -> Complex is already provided by: Unit -> T */ impl SubsetOf> for UnitComplex where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> UnitComplex { UnitComplex::new_unchecked(self.as_ref().to_superset()) } #[inline] fn is_in_subset(uq: &UnitComplex) -> bool { crate::is_convertible::<_, Complex>(uq.as_ref()) } #[inline] fn from_superset_unchecked(uq: &UnitComplex) -> Self { Self::new_unchecked(crate::convert_ref_unchecked(uq.as_ref())) } } impl SubsetOf> for UnitComplex where T1: RealField, T2: RealField + SupersetOf, { #[inline] fn to_superset(&self) -> Rotation2 { let q: UnitComplex = self.to_superset(); q.to_rotation_matrix().to_superset() } #[inline] fn is_in_subset(rot: &Rotation2) -> bool { crate::is_convertible::<_, Rotation2>(rot) } #[inline] fn from_superset_unchecked(rot: &Rotation2) -> Self { let q = UnitComplex::::from_rotation_matrix(rot); crate::convert_unchecked(q) } } impl SubsetOf> for UnitComplex where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SupersetOf, { #[inline] fn to_superset(&self) -> Isometry { Isometry::from_parts(Translation::identity(), crate::convert_ref(self)) } #[inline] fn is_in_subset(iso: &Isometry) -> bool { iso.translation.vector.is_zero() } #[inline] fn from_superset_unchecked(iso: &Isometry) -> Self { crate::convert_ref_unchecked(&iso.rotation) } } impl SubsetOf> for UnitComplex where T1: RealField, T2: RealField + SupersetOf, R: AbstractRotation + SupersetOf, { #[inline] fn to_superset(&self) -> Similarity { Similarity::from_isometry(crate::convert_ref(self), T2::one()) } #[inline] fn is_in_subset(sim: &Similarity) -> bool { sim.isometry.translation.vector.is_zero() && sim.scaling() == T2::one() } #[inline] fn from_superset_unchecked(sim: &Similarity) -> Self { crate::convert_ref_unchecked(&sim.isometry) } } impl SubsetOf> for UnitComplex where T1: RealField, T2: RealField + SupersetOf, C: SuperTCategoryOf, { #[inline] fn to_superset(&self) -> Transform { Transform::from_matrix_unchecked(self.clone().to_homogeneous().to_superset()) } #[inline] fn is_in_subset(t: &Transform) -> bool { >::is_in_subset(t.matrix()) } #[inline] fn from_superset_unchecked(t: &Transform) -> Self { Self::from_superset_unchecked(t.matrix()) } } impl> SubsetOf> for UnitComplex { #[inline] fn to_superset(&self) -> Matrix3 { self.clone().to_homogeneous().to_superset() } #[inline] fn is_in_subset(m: &Matrix3) -> bool { crate::is_convertible::<_, Rotation2>(m) } #[inline] fn from_superset_unchecked(m: &Matrix3) -> Self { let rot: Rotation2 = crate::convert_ref_unchecked(m); Self::from_rotation_matrix(&rot) } } impl From> for Rotation2 where T::Element: SimdRealField, { #[inline] fn from(q: UnitComplex) -> Self { q.to_rotation_matrix() } } impl From> for UnitComplex where T::Element: SimdRealField, { #[inline] fn from(q: Rotation2) -> Self { Self::from_rotation_matrix(&q) } } impl From> for Matrix3 where T::Element: SimdRealField, { #[inline] fn from(q: UnitComplex) -> Matrix3 { q.to_homogeneous() } } impl From> for Matrix2 where T::Element: SimdRealField, { #[inline] fn from(q: UnitComplex) -> Self { q.to_rotation_matrix().into_inner() } } impl From<[UnitComplex; 2]> for UnitComplex where T: From<[::Element; 2]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitComplex; 2]) -> Self { Self::new_unchecked(Complex { re: T::from([arr[0].re, arr[1].re]), im: T::from([arr[0].im, arr[1].im]), }) } } impl From<[UnitComplex; 4]> for UnitComplex where T: From<[::Element; 4]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitComplex; 4]) -> Self { Self::new_unchecked(Complex { re: T::from([arr[0].re, arr[1].re, arr[2].re, arr[3].re]), im: T::from([arr[0].im, arr[1].im, arr[2].im, arr[3].im]), }) } } impl From<[UnitComplex; 8]> for UnitComplex where T: From<[::Element; 8]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitComplex; 8]) -> Self { Self::new_unchecked(Complex { re: T::from([ arr[0].re, arr[1].re, arr[2].re, arr[3].re, arr[4].re, arr[5].re, arr[6].re, arr[7].re, ]), im: T::from([ arr[0].im, arr[1].im, arr[2].im, arr[3].im, arr[4].im, arr[5].im, arr[6].im, arr[7].im, ]), }) } } impl From<[UnitComplex; 16]> for UnitComplex where T: From<[::Element; 16]>, T::Element: Scalar + Copy, { #[inline] fn from(arr: [UnitComplex; 16]) -> Self { Self::new_unchecked(Complex { re: T::from([ arr[0].re, arr[1].re, arr[2].re, arr[3].re, arr[4].re, arr[5].re, arr[6].re, arr[7].re, arr[8].re, arr[9].re, arr[10].re, arr[11].re, arr[12].re, arr[13].re, arr[14].re, arr[15].re, ]), im: T::from([ arr[0].im, arr[1].im, arr[2].im, arr[3].im, arr[4].im, arr[5].im, arr[6].im, arr[7].im, arr[8].im, arr[9].im, arr[10].im, arr[11].im, arr[12].im, arr[13].im, arr[14].im, arr[15].im, ]), }) } } nalgebra-0.33.0/src/geometry/unit_complex_ops.rs000064400000000000000000000313200072674642500200700ustar 00000000000000// The macros break if the references are taken out, for some reason. #![allow(clippy::op_ref)] use std::ops::{Div, DivAssign, Mul, MulAssign}; use crate::base::storage::Storage; use crate::base::{Const, Unit, Vector, Vector2}; use crate::geometry::{Isometry, Point2, Rotation, Similarity, Translation, UnitComplex}; use simba::simd::SimdRealField; /* * This file provides: * =================== * * UnitComplex × UnitComplex * UnitComplex × Rotation -> UnitComplex * Rotation × UnitComplex -> UnitComplex * * UnitComplex ÷ UnitComplex * UnitComplex ÷ Rotation -> UnitComplex * Rotation ÷ UnitComplex -> UnitComplex * * * UnitComplex × Point * UnitComplex × Vector * UnitComplex × Unit * * UnitComplex × Isometry * UnitComplex × Similarity * UnitComplex × Translation -> Isometry * * (Assignment Operators) * * UnitComplex ×= UnitComplex * UnitComplex ×= Rotation * * UnitComplex ÷= UnitComplex * UnitComplex ÷= Rotation * * Rotation ×= UnitComplex * Rotation ÷= UnitComplex * */ // UnitComplex × UnitComplex impl Mul for UnitComplex { type Output = Self; #[inline] fn mul(self, rhs: Self) -> Self { Unit::new_unchecked(self.into_inner() * rhs.into_inner()) } } impl<'a, T: SimdRealField> Mul> for &'a UnitComplex where T::Element: SimdRealField, { type Output = UnitComplex; #[inline] fn mul(self, rhs: UnitComplex) -> Self::Output { Unit::new_unchecked(self.complex() * rhs.into_inner()) } } impl<'b, T: SimdRealField> Mul<&'b UnitComplex> for UnitComplex where T::Element: SimdRealField, { type Output = Self; #[inline] fn mul(self, rhs: &'b UnitComplex) -> Self::Output { Unit::new_unchecked(self.into_inner() * rhs.as_ref()) } } impl<'a, 'b, T: SimdRealField> Mul<&'b UnitComplex> for &'a UnitComplex where T::Element: SimdRealField, { type Output = UnitComplex; #[inline] fn mul(self, rhs: &'b UnitComplex) -> Self::Output { Unit::new_unchecked(self.complex() * rhs.as_ref()) } } // UnitComplex ÷ UnitComplex impl Div for UnitComplex where T::Element: SimdRealField, { type Output = Self; #[inline] fn div(self, rhs: Self) -> Self::Output { #[allow(clippy::suspicious_arithmetic_impl)] Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } impl<'a, T: SimdRealField> Div> for &'a UnitComplex where T::Element: SimdRealField, { type Output = UnitComplex; #[inline] fn div(self, rhs: UnitComplex) -> Self::Output { #[allow(clippy::suspicious_arithmetic_impl)] Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } impl<'b, T: SimdRealField> Div<&'b UnitComplex> for UnitComplex where T::Element: SimdRealField, { type Output = Self; #[inline] fn div(self, rhs: &'b UnitComplex) -> Self::Output { #[allow(clippy::suspicious_arithmetic_impl)] Unit::new_unchecked(self.into_inner() * rhs.conjugate().into_inner()) } } impl<'a, 'b, T: SimdRealField> Div<&'b UnitComplex> for &'a UnitComplex where T::Element: SimdRealField, { type Output = UnitComplex; #[inline] fn div(self, rhs: &'b UnitComplex) -> Self::Output { #[allow(clippy::suspicious_arithmetic_impl)] Unit::new_unchecked(self.complex() * rhs.conjugate().into_inner()) } } macro_rules! complex_op_impl( ($Op: ident, $op: ident; $($Storage: ident: $StoragesBound: ident $(<$($BoundParam: ty),*>)*),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Result: ty; $action: expr; $($lives: tt),*) => { impl<$($lives ,)* T: SimdRealField $(, $Storage: $StoragesBound $(<$($BoundParam),*>)*)*> $Op<$Rhs> for $Lhs where T::Element: SimdRealField { type Output = $Result; #[inline] fn $op($lhs, $rhs: $Rhs) -> Self::Output { $action } } } ); macro_rules! complex_op_impl_all( ($Op: ident, $op: ident; $($Storage: ident: $StoragesBound: ident $(<$($BoundParam: ty),*>)*),*; $lhs: ident: $Lhs: ty, $rhs: ident: $Rhs: ty, Output = $Result: ty; [val val] => $action_val_val: expr; [ref val] => $action_ref_val: expr; [val ref] => $action_val_ref: expr; [ref ref] => $action_ref_ref: expr;) => { complex_op_impl!($Op, $op; $($Storage: $StoragesBound $(<$($BoundParam),*>)*),*; $lhs: $Lhs, $rhs: $Rhs, Output = $Result; $action_val_val; ); complex_op_impl!($Op, $op; $($Storage: $StoragesBound $(<$($BoundParam),*>)*),*; $lhs: &'a $Lhs, $rhs: $Rhs, Output = $Result; $action_ref_val; 'a); complex_op_impl!($Op, $op; $($Storage: $StoragesBound $(<$($BoundParam),*>)*),*; $lhs: $Lhs, $rhs: &'b $Rhs, Output = $Result; $action_val_ref; 'b); complex_op_impl!($Op, $op; $($Storage: $StoragesBound $(<$($BoundParam),*>)*),*; $lhs: &'a $Lhs, $rhs: &'b $Rhs, Output = $Result; $action_ref_ref; 'a, 'b); } ); // UnitComplex × Rotation complex_op_impl_all!( Mul, mul; ; self: UnitComplex, rhs: Rotation, Output = UnitComplex; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => self * UnitComplex::from_rotation_matrix(rhs); ); // UnitComplex ÷ Rotation complex_op_impl_all!( Div, div; ; self: UnitComplex, rhs: Rotation, Output = UnitComplex; [val val] => &self / &rhs; [ref val] => self / &rhs; [val ref] => &self / rhs; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { self * UnitComplex::from_rotation_matrix(rhs).inverse() }; ); // Rotation × UnitComplex complex_op_impl_all!( Mul, mul; ; self: Rotation, rhs: UnitComplex, Output = UnitComplex; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => UnitComplex::from_rotation_matrix(self) * rhs; ); // Rotation ÷ UnitComplex complex_op_impl_all!( Div, div; ; self: Rotation, rhs: UnitComplex, Output = UnitComplex; [val val] => &self / &rhs; [ref val] => self / &rhs; [val ref] => &self / rhs; [ref ref] => #[allow(clippy::suspicious_arithmetic_impl)] { UnitComplex::from_rotation_matrix(self) * rhs.inverse() }; ); // UnitComplex × Point complex_op_impl_all!( Mul, mul; ; self: UnitComplex, rhs: Point2, Output = Point2; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => Point2::from(self * &rhs.coords); ); // UnitComplex × Vector complex_op_impl_all!( Mul, mul; S: Storage>; self: UnitComplex, rhs: Vector, S>, Output = Vector2; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let i = self.as_ref().im.clone(); let r = self.as_ref().re.clone(); Vector2::new(r.clone() * rhs[0].clone() - i.clone() * rhs[1].clone(), i * rhs[0].clone() + r * rhs[1].clone()) }; ); // UnitComplex × Unit complex_op_impl_all!( Mul, mul; S: Storage>; self: UnitComplex, rhs: Unit, S>>, Output = Unit>; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => Unit::new_unchecked(self * rhs.as_ref()); ); // UnitComplex × Isometry complex_op_impl_all!( Mul, mul; ; self: UnitComplex, rhs: Isometry, 2>, Output = Isometry, 2>; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => { let shift = self * &rhs.translation.vector; Isometry::from_parts(Translation::from(shift), self * &rhs.rotation) }; ); // UnitComplex × Similarity complex_op_impl_all!( Mul, mul; ; self: UnitComplex, rhs: Similarity, 2>, Output = Similarity, 2>; [val val] => &self * &rhs; [ref val] => self * &rhs; [val ref] => &self * rhs; [ref ref] => Similarity::from_isometry(self * &rhs.isometry, rhs.scaling()); ); // UnitComplex × Translation complex_op_impl_all!( Mul, mul; ; self: UnitComplex, rhs: Translation, Output = Isometry, 2>; [val val] => Isometry::from_parts(Translation::from(&self * rhs.vector), self); [ref val] => Isometry::from_parts(Translation::from( self * rhs.vector), self.clone()); [val ref] => Isometry::from_parts(Translation::from(&self * &rhs.vector), self); [ref ref] => Isometry::from_parts(Translation::from( self * &rhs.vector), self.clone()); ); // Translation × UnitComplex complex_op_impl_all!( Mul, mul; ; self: Translation, right: UnitComplex, Output = Isometry, 2>; [val val] => Isometry::from_parts(self, right); [ref val] => Isometry::from_parts(self.clone(), right); [val ref] => Isometry::from_parts(self, right.clone()); [ref ref] => Isometry::from_parts(self.clone(), right.clone()); ); // UnitComplex ×= UnitComplex impl MulAssign> for UnitComplex where T::Element: SimdRealField, { #[inline] fn mul_assign(&mut self, rhs: UnitComplex) { *self = self.clone() * rhs } } impl<'b, T: SimdRealField> MulAssign<&'b UnitComplex> for UnitComplex where T::Element: SimdRealField, { #[inline] fn mul_assign(&mut self, rhs: &'b UnitComplex) { *self = self.clone() * rhs } } // UnitComplex /= UnitComplex impl DivAssign> for UnitComplex where T::Element: SimdRealField, { #[inline] fn div_assign(&mut self, rhs: UnitComplex) { *self = self.clone() / rhs } } impl<'b, T: SimdRealField> DivAssign<&'b UnitComplex> for UnitComplex where T::Element: SimdRealField, { #[inline] fn div_assign(&mut self, rhs: &'b UnitComplex) { *self = self.clone() / rhs } } // UnitComplex ×= Rotation impl MulAssign> for UnitComplex where T::Element: SimdRealField, { #[inline] fn mul_assign(&mut self, rhs: Rotation) { *self = self.clone() * rhs } } impl<'b, T: SimdRealField> MulAssign<&'b Rotation> for UnitComplex where T::Element: SimdRealField, { #[inline] fn mul_assign(&mut self, rhs: &'b Rotation) { *self = self.clone() * rhs } } // UnitComplex ÷= Rotation impl DivAssign> for UnitComplex where T::Element: SimdRealField, { #[inline] fn div_assign(&mut self, rhs: Rotation) { *self = self.clone() / rhs } } impl<'b, T: SimdRealField> DivAssign<&'b Rotation> for UnitComplex where T::Element: SimdRealField, { #[inline] fn div_assign(&mut self, rhs: &'b Rotation) { *self = self.clone() / rhs } } // Rotation ×= UnitComplex impl MulAssign> for Rotation where T::Element: SimdRealField, { #[inline] fn mul_assign(&mut self, rhs: UnitComplex) { self.mul_assign(rhs.to_rotation_matrix()) } } impl<'b, T: SimdRealField> MulAssign<&'b UnitComplex> for Rotation where T::Element: SimdRealField, { #[inline] fn mul_assign(&mut self, rhs: &'b UnitComplex) { self.mul_assign(rhs.clone().to_rotation_matrix()) } } // Rotation ÷= UnitComplex impl DivAssign> for Rotation where T::Element: SimdRealField, { #[inline] fn div_assign(&mut self, rhs: UnitComplex) { self.div_assign(rhs.to_rotation_matrix()) } } impl<'b, T: SimdRealField> DivAssign<&'b UnitComplex> for Rotation where T::Element: SimdRealField, { #[inline] fn div_assign(&mut self, rhs: &'b UnitComplex) { self.div_assign(rhs.clone().to_rotation_matrix()) } } nalgebra-0.33.0/src/geometry/unit_complex_simba.rs000064400000000000000000000024250072674642500203660ustar 00000000000000use num_complex::Complex; use simba::simd::SimdValue; use std::ops::Deref; use crate::base::Unit; use crate::geometry::UnitComplex; use crate::SimdRealField; impl SimdValue for UnitComplex where T::Element: SimdRealField, { const LANES: usize = T::LANES; type Element = UnitComplex; type SimdBool = T::SimdBool; #[inline] fn splat(val: Self::Element) -> Self { Unit::new_unchecked(Complex::splat(val.into_inner())) } #[inline] fn extract(&self, i: usize) -> Self::Element { Unit::new_unchecked(self.deref().extract(i)) } #[inline] unsafe fn extract_unchecked(&self, i: usize) -> Self::Element { Unit::new_unchecked(self.deref().extract_unchecked(i)) } #[inline] fn replace(&mut self, i: usize, val: Self::Element) { self.as_mut_unchecked().replace(i, val.into_inner()) } #[inline] unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) { self.as_mut_unchecked() .replace_unchecked(i, val.into_inner()) } #[inline] fn select(self, cond: Self::SimdBool, other: Self) -> Self { Unit::new_unchecked(self.into_inner().select(cond, other.into_inner())) } } nalgebra-0.33.0/src/io/matrix_market.pest000064400000000000000000000007030072674642500164540ustar 00000000000000WHITESPACE = _{ " " } Comments = _{ "%" ~ (!NEWLINE ~ ANY)* } Header = { "%%" ~ (!NEWLINE ~ ANY)* } Shape = { Dimension ~ Dimension ~ Dimension } Document = { SOI ~ NEWLINE* ~ Header ~ (NEWLINE ~ Comments)* ~ (NEWLINE ~ Shape) ~ (NEWLINE ~ Entry?)* } Dimension = @{ ASCII_DIGIT+ } Value = @{ ("+" | "-")? ~ NUMBER+ ~ ("." ~ NUMBER+)? ~ ("e" ~ ("+" | "-")? ~ NUMBER+)? } Entry = { Dimension ~ Dimension ~ Value }nalgebra-0.33.0/src/io/matrix_market.rs000064400000000000000000000037730072674642500161370ustar 00000000000000use std::fs; use std::path::Path; use crate::sparse::CsMatrix; use crate::RealField; use pest::Parser; #[derive(Parser)] #[grammar = "io/matrix_market.pest"] struct MatrixMarketParser; // TODO: return an Error instead of an Option. /// Parses a Matrix Market file at the given path, and returns the corresponding sparse matrix. pub fn cs_matrix_from_matrix_market>(path: P) -> Option> { let file = fs::read_to_string(path).ok()?; cs_matrix_from_matrix_market_str(&file) } // TODO: return an Error instead of an Option. /// Parses a Matrix Market file described by the given string, and returns the corresponding sparse matrix. pub fn cs_matrix_from_matrix_market_str(data: &str) -> Option> { let file = MatrixMarketParser::parse(Rule::Document, data) .unwrap() .next()?; let mut shape = (0, 0, 0); let mut rows: Vec = Vec::new(); let mut cols: Vec = Vec::new(); let mut data: Vec = Vec::new(); for line in file.into_inner() { match line.as_rule() { Rule::Header => {} Rule::Shape => { let mut inner = line.into_inner(); shape.0 = inner.next()?.as_str().parse::().ok()?; shape.1 = inner.next()?.as_str().parse::().ok()?; shape.2 = inner.next()?.as_str().parse::().ok()?; } Rule::Entry => { let mut inner = line.into_inner(); // NOTE: indices are 1-based. rows.push(inner.next()?.as_str().parse::().ok()? - 1); cols.push(inner.next()?.as_str().parse::().ok()? - 1); data.push(crate::convert(inner.next()?.as_str().parse::().ok()?)); } _ => return None, // TODO: return an Err instead. } } Some(CsMatrix::from_triplet( shape.0, shape.1, &rows, &cols, &data, )) } nalgebra-0.33.0/src/io/mod.rs000064400000000000000000000002410072674642500140320ustar 00000000000000//! Parsers for various matrix formats. pub use self::matrix_market::{cs_matrix_from_matrix_market, cs_matrix_from_matrix_market_str}; mod matrix_market; nalgebra-0.33.0/src/lib.rs000064400000000000000000000357100072674642500134230ustar 00000000000000/*! # nalgebra **nalgebra** is a linear algebra library written for Rust targeting: * General-purpose linear algebra (still lacks a lot of features…) * Real-time computer graphics. * Real-time computer physics. ## Using **nalgebra** You will need the last stable build of the [rust compiler](https://www.rust-lang.org) and the official package manager: [cargo](https://github.com/rust-lang/cargo). Simply add the following to your `Cargo.toml` file: ```ignore [dependencies] // TODO: replace the * by the latest version. nalgebra = "*" ``` Most useful functionalities of **nalgebra** are grouped in the root module `nalgebra::`. However, the recommended way to use **nalgebra** is to import types and traits explicitly, and call free-functions using the `na::` prefix: ``` #[macro_use] extern crate approx; // For the macro assert_relative_eq! extern crate nalgebra as na; use na::{Vector3, Rotation3}; fn main() { let axis = Vector3::x_axis(); let angle = 1.57; let b = Rotation3::from_axis_angle(&axis, angle); assert_relative_eq!(b.axis().unwrap(), axis); assert_relative_eq!(b.angle(), angle); } ``` ## Features **nalgebra** is meant to be a general-purpose, low-dimensional, linear algebra library, with an optimized set of tools for computer graphics and physics. Those features include: * A single parametrizable type [`Matrix`](Matrix) for vectors, (square or rectangular) matrices, and slices with dimensions known either at compile-time (using type-level integers) or at runtime. * Matrices and vectors with compile-time sizes are statically allocated while dynamic ones are allocated on the heap. * Convenient aliases for low-dimensional matrices and vectors: [`Vector1`](Vector1) to [`Vector6`](Vector6) and [`Matrix1x1`](Matrix1) to [`Matrix6x6`](Matrix6), including rectangular matrices like [`Matrix2x5`](Matrix2x5). * Points sizes known at compile time, and convenience aliases: [`Point1`](Point1) to [`Point6`](Point6). * Translation (seen as a transformation that composes by multiplication): [`Translation2`](Translation2), [`Translation3`](Translation3). * Rotation matrices: [`Rotation2`](Rotation2), [`Rotation3`](Rotation3). * Quaternions: [`Quaternion`](Quaternion), [`UnitQuaternion`](UnitQuaternion) (for 3D rotation). * Unit complex numbers can be used for 2D rotation: [`UnitComplex`](UnitComplex). * Algebraic entities with a norm equal to one: [`Unit`](Unit), e.g., `Unit>`. * Isometries (translation ⨯ rotation): [`Isometry2`](Isometry2), [`Isometry3`](Isometry3) * Similarity transformations (translation ⨯ rotation ⨯ uniform scale): [`Similarity2`](Similarity2), [`Similarity3`](Similarity3). * Affine transformations stored as a homogeneous matrix: [`Affine2`](Affine2), [`Affine3`](Affine3). * Projective (i.e. invertible) transformations stored as a homogeneous matrix: [`Projective2`](Projective2), [`Projective3`](Projective3). * General transformations that does not have to be invertible, stored as a homogeneous matrix: [`Transform2`](Transform2), [`Transform3`](Transform3). * 3D projections for computer graphics: [`Perspective3`](Perspective3), [`Orthographic3`](Orthographic3). * Matrix factorizations: [`Cholesky`](Cholesky), [`QR`](QR), [`LU`](LU), [`FullPivLU`](FullPivLU), [`SVD`](SVD), [`Schur`](Schur), [`Hessenberg`](Hessenberg), [`SymmetricEigen`](SymmetricEigen). * Insertion and removal of rows of columns of a matrix. */ #![deny( missing_docs, nonstandard_style, unused_variables, unused_mut, unused_parens, rust_2018_idioms, rust_2018_compatibility, future_incompatible, missing_copy_implementations )] #![cfg_attr(not(feature = "rkyv-serialize-no-std"), deny(unused_results))] // TODO: deny this globally once bytecheck stops generating unused results. #![doc( html_favicon_url = "https://nalgebra.org/img/favicon.ico", html_root_url = "https://docs.rs/nalgebra/0.25.0" )] #![cfg_attr(not(feature = "std"), no_std)] /// Generates an appropriate deprecation note with a suggestion for replacement. /// /// Used for deprecating slice types in various locations throughout the library. /// See #1076 for more information. macro_rules! slice_deprecation_note { ($replacement:ident) => { concat!("Use ", stringify!($replacement), r###" instead. See [issue #1076](https://github.com/dimforge/nalgebra/issues/1076) for more information."###) } } pub(crate) use slice_deprecation_note; #[cfg(feature = "rand-no-std")] extern crate rand_package as rand; #[cfg(feature = "serde-serialize-no-std")] #[macro_use] extern crate serde; #[macro_use] extern crate approx; extern crate num_traits as num; #[cfg(all(feature = "alloc", not(feature = "std")))] #[cfg_attr(test, macro_use)] extern crate alloc; #[cfg(not(feature = "std"))] extern crate core as std; #[macro_use] #[cfg(feature = "io")] extern crate pest_derive; pub mod base; #[cfg(feature = "debug")] pub mod debug; pub mod geometry; #[cfg(feature = "io")] pub mod io; pub mod linalg; #[cfg(feature = "proptest-support")] pub mod proptest; #[cfg(feature = "sparse")] pub mod sparse; mod third_party; pub use crate::base::*; pub use crate::geometry::*; pub use crate::linalg::*; #[cfg(feature = "sparse")] pub use crate::sparse::*; #[cfg(feature = "std")] #[deprecated( note = "The 'core' module is being renamed to 'base' to avoid conflicts with the 'core' crate." )] pub use base as core; #[cfg(feature = "macros")] pub use nalgebra_macros::{dmatrix, dvector, matrix, point, stack, vector}; use simba::scalar::SupersetOf; use std::cmp::{self, Ordering, PartialOrd}; use num::{One, Signed, Zero}; use base::allocator::Allocator; pub use num_complex::Complex; pub use simba::scalar::{ ClosedAddAssign, ClosedDivAssign, ClosedMulAssign, ClosedSubAssign, ComplexField, Field, RealField, }; pub use simba::simd::{SimdBool, SimdComplexField, SimdPartialOrd, SimdRealField, SimdValue}; /// Gets the multiplicative identity element. /// /// # See also: /// /// * [`origin()`](crate::OPoint::origin) /// * [`zero()`] #[inline] pub fn one() -> T { T::one() } /// Gets the additive identity element. /// /// # See also: /// /// * [`one()`] /// * [`origin()`](crate::OPoint::origin) #[inline] pub fn zero() -> T { T::zero() } /* * * Ordering * */ // XXX: this is very naive and could probably be optimized for specific types. // XXX: also, we might just want to use divisions, but assuming `val` is usually not far from `min` // or `max`, would it still be more efficient? /// Wraps `val` into the range `[min, max]` using modular arithmetics. /// /// The range must not be empty. #[must_use] #[inline] pub fn wrap(mut val: T, min: T, max: T) -> T where T: Copy + PartialOrd + ClosedAddAssign + ClosedSubAssign, { assert!(min < max, "Invalid wrapping bounds."); let width = max - min; if val < min { val += width; while val < min { val += width } } else if val > max { val -= width; while val > max { val -= width } } val } /// Returns a reference to the input value clamped to the interval `[min, max]`. /// /// In particular: /// * If `min < val < max`, this returns `val`. /// * If `val <= min`, this returns `min`. /// * If `val >= max`, this returns `max`. #[must_use] #[inline] pub fn clamp(val: T, min: T, max: T) -> T { if val > min { if val < max { val } else { max } } else { min } } /// Same as `cmp::max`. #[inline] pub fn max(a: T, b: T) -> T { cmp::max(a, b) } /// Same as `cmp::min`. #[inline] pub fn min(a: T, b: T) -> T { cmp::min(a, b) } /// The absolute value of `a`. /// /// Deprecated: Use [`Matrix::abs()`] or [`ComplexField::abs()`] instead. #[deprecated(note = "use the inherent method `Matrix::abs` or `ComplexField::abs` instead")] #[inline] pub fn abs(a: &T) -> T { a.abs() } /// Returns the infimum of `a` and `b`. #[deprecated(note = "use the inherent method `Matrix::inf` instead")] #[inline] pub fn inf(a: &OMatrix, b: &OMatrix) -> OMatrix where T: Scalar + SimdPartialOrd, DefaultAllocator: Allocator, { a.inf(b) } /// Returns the supremum of `a` and `b`. #[deprecated(note = "use the inherent method `Matrix::sup` instead")] #[inline] pub fn sup(a: &OMatrix, b: &OMatrix) -> OMatrix where T: Scalar + SimdPartialOrd, DefaultAllocator: Allocator, { a.sup(b) } /// Returns simultaneously the infimum and supremum of `a` and `b`. #[deprecated(note = "use the inherent method `Matrix::inf_sup` instead")] #[inline] pub fn inf_sup( a: &OMatrix, b: &OMatrix, ) -> (OMatrix, OMatrix) where T: Scalar + SimdPartialOrd, DefaultAllocator: Allocator, { a.inf_sup(b) } /// Compare `a` and `b` using a partial ordering relation. #[inline] pub fn partial_cmp(a: &T, b: &T) -> Option { a.partial_cmp(b) } /// Returns `true` iff `a` and `b` are comparable and `a < b`. #[inline] pub fn partial_lt(a: &T, b: &T) -> bool { a.lt(b) } /// Returns `true` iff `a` and `b` are comparable and `a <= b`. #[inline] pub fn partial_le(a: &T, b: &T) -> bool { a.le(b) } /// Returns `true` iff `a` and `b` are comparable and `a > b`. #[inline] pub fn partial_gt(a: &T, b: &T) -> bool { a.gt(b) } /// Returns `true` iff `a` and `b` are comparable and `a >= b`. #[inline] pub fn partial_ge(a: &T, b: &T) -> bool { a.ge(b) } /// Return the minimum of `a` and `b` if they are comparable. #[inline] pub fn partial_min<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<&'a T> { if let Some(ord) = a.partial_cmp(b) { match ord { Ordering::Greater => Some(b), _ => Some(a), } } else { None } } /// Return the maximum of `a` and `b` if they are comparable. #[inline] pub fn partial_max<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<&'a T> { if let Some(ord) = a.partial_cmp(b) { match ord { Ordering::Less => Some(b), _ => Some(a), } } else { None } } /// Clamp `value` between `min` and `max`. Returns `None` if `value` is not comparable to /// `min` or `max`. #[inline] pub fn partial_clamp<'a, T: PartialOrd>(value: &'a T, min: &'a T, max: &'a T) -> Option<&'a T> { if let (Some(cmp_min), Some(cmp_max)) = (value.partial_cmp(min), value.partial_cmp(max)) { if cmp_min == Ordering::Less { Some(min) } else if cmp_max == Ordering::Greater { Some(max) } else { Some(value) } } else { None } } /// Sorts two values in increasing order using a partial ordering. #[inline] pub fn partial_sort2<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<(&'a T, &'a T)> { if let Some(ord) = a.partial_cmp(b) { match ord { Ordering::Less => Some((a, b)), _ => Some((b, a)), } } else { None } } /* * * Point operations. * */ /// The center of two points. /// /// # See also: /// /// * [`distance()`] /// * [`distance_squared()`] #[inline] pub fn center( p1: &Point, p2: &Point, ) -> Point { ((&p1.coords + &p2.coords) * convert::<_, T>(0.5)).into() } /// The distance between two points. /// /// # See also: /// /// * [`center()`] /// * [`distance_squared()`] #[inline] pub fn distance( p1: &Point, p2: &Point, ) -> T::SimdRealField { (&p2.coords - &p1.coords).norm() } /// The squared distance between two points. /// /// # See also: /// /// * [`center()`] /// * [`distance()`] #[inline] pub fn distance_squared( p1: &Point, p2: &Point, ) -> T::SimdRealField { (&p2.coords - &p1.coords).norm_squared() } /* * Cast */ /// Converts an object from one type to an equivalent or more general one. /// /// See also [`try_convert()`] for conversion to more specific types. /// /// # See also: /// /// * [`convert_ref()`] /// * [`convert_ref_unchecked()`] /// * [`is_convertible()`] /// * [`try_convert()`] /// * [`try_convert_ref()`] #[inline] pub fn convert>(t: From) -> To { To::from_subset(&t) } /// Attempts to convert an object to a more specific one. /// /// See also [`convert()`] for conversion to more general types. /// /// # See also: /// /// * [`convert()`] /// * [`convert_ref()`] /// * [`convert_ref_unchecked()`] /// * [`is_convertible()`] /// * [`try_convert_ref()`] #[inline] pub fn try_convert, To>(t: From) -> Option { t.to_subset() } /// Indicates if [`try_convert()`] will succeed without /// actually performing the conversion. /// /// # See also: /// /// * [`convert()`] /// * [`convert_ref()`] /// * [`convert_ref_unchecked()`] /// * [`try_convert()`] /// * [`try_convert_ref()`] #[inline] pub fn is_convertible, To>(t: &From) -> bool { t.is_in_subset() } /// Use with care! Same as [`try_convert()`] but /// without any property checks. /// /// # See also: /// /// * [`convert()`] /// * [`convert_ref()`] /// * [`convert_ref_unchecked()`] /// * [`is_convertible()`] /// * [`try_convert()`] /// * [`try_convert_ref()`] #[inline] pub fn convert_unchecked, To>(t: From) -> To { t.to_subset_unchecked() } /// Converts an object from one type to an equivalent or more general one. /// /// # See also: /// /// * [`convert()`] /// * [`convert_ref_unchecked()`] /// * [`is_convertible()`] /// * [`try_convert()`] /// * [`try_convert_ref()`] #[inline] pub fn convert_ref>(t: &From) -> To { To::from_subset(t) } /// Attempts to convert an object to a more specific one. /// /// # See also: /// /// * [`convert()`] /// * [`convert_ref()`] /// * [`convert_ref_unchecked()`] /// * [`is_convertible()`] /// * [`try_convert()`] #[inline] pub fn try_convert_ref, To>(t: &From) -> Option { t.to_subset() } /// Use with care! Same as [`try_convert()`] but /// without any property checks. /// /// # See also: /// /// * [`convert()`] /// * [`convert_ref()`] /// * [`is_convertible()`] /// * [`try_convert()`] /// * [`try_convert_ref()`] #[inline] pub fn convert_ref_unchecked, To>(t: &From) -> To { t.to_subset_unchecked() } nalgebra-0.33.0/src/linalg/balancing.rs000064400000000000000000000054010072674642500160330ustar 00000000000000//! Functions for balancing a matrix. use simba::scalar::RealField; use std::ops::{DivAssign, MulAssign}; use crate::allocator::Allocator; use crate::base::dimension::Dim; use crate::base::{Const, DefaultAllocator, OMatrix, OVector}; /// Applies in-place a modified Parlett and Reinsch matrix balancing with 2-norm to the matrix and returns /// the corresponding diagonal transformation. /// /// See pub fn balance_parlett_reinsch(matrix: &mut OMatrix) -> OVector where DefaultAllocator: Allocator + Allocator, { assert!(matrix.is_square(), "Unable to balance a non-square matrix."); let dim = matrix.shape_generic().0; let radix: T = crate::convert(2.0f64); let mut d = OVector::from_element_generic(dim, Const::<1>, T::one()); let mut converged = false; while !converged { converged = true; for i in 0..dim.value() { let mut n_col = matrix.column(i).norm_squared(); let mut n_row = matrix.row(i).norm_squared(); let mut f = T::one(); let s = n_col.clone() + n_row.clone(); n_col = n_col.sqrt(); n_row = n_row.sqrt(); if n_col.clone().is_zero() || n_row.clone().is_zero() { continue; } while n_col.clone() < n_row.clone() / radix.clone() { n_col *= radix.clone(); n_row /= radix.clone(); f *= radix.clone(); } while n_col.clone() >= n_row.clone() * radix.clone() { n_col /= radix.clone(); n_row *= radix.clone(); f /= radix.clone(); } let eps: T = crate::convert(0.95); #[allow(clippy::suspicious_operation_groupings)] if n_col.clone() * n_col + n_row.clone() * n_row < eps * s { converged = false; d[i] *= f.clone(); matrix.column_mut(i).mul_assign(f.clone()); matrix.row_mut(i).div_assign(f.clone()); } } } d } /// Computes in-place `D * m * D.inverse()`, where `D` is the matrix with diagonal `d`. pub fn unbalance(m: &mut OMatrix, d: &OVector) where DefaultAllocator: Allocator + Allocator, { assert!(m.is_square(), "Unable to unbalance a non-square matrix."); assert_eq!(m.nrows(), d.len(), "Unbalancing: mismatched dimensions."); for j in 0..d.len() { let mut col = m.column_mut(j); let denom = T::one() / d[j].clone(); for i in 0..d.len() { col[i] *= d[i].clone() * denom.clone(); } } } nalgebra-0.33.0/src/linalg/bidiagonal.rs000064400000000000000000000331370072674642500162150ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, Matrix, OMatrix, OVector, Unit}; use crate::dimension::{Const, Dim, DimDiff, DimMin, DimMinimum, DimSub, U1}; use simba::scalar::ComplexField; use crate::geometry::Reflection; use crate::linalg::householder; use crate::num::Zero; use std::mem::MaybeUninit; /// The bidiagonalization of a general matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator, U1>>, OMatrix: Serialize, OVector>: Serialize, OVector, U1>>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator, U1>>, OMatrix: Deserialize<'de>, OVector>: Deserialize<'de>, OVector, U1>>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct Bidiagonal, C: Dim> where DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator, U1>>, { // TODO: perhaps we should pack the axes into different vectors so that axes for `v_t` are // contiguous. This prevents some useless copies. uv: OMatrix, /// The diagonal elements of the decomposed matrix. diagonal: OVector>, /// The off-diagonal elements of the decomposed matrix. off_diagonal: OVector, U1>>, upper_diagonal: bool, } impl, C: Dim> Copy for Bidiagonal where DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator, U1>>, OMatrix: Copy, OVector>: Copy, OVector, U1>>: Copy, { } impl, C: Dim> Bidiagonal where DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator + Allocator + Allocator> + Allocator, U1>>, { /// Computes the Bidiagonal decomposition using householder reflections. pub fn new(mut matrix: OMatrix) -> Self { let (nrows, ncols) = matrix.shape_generic(); let min_nrows_ncols = nrows.min(ncols); let dim = min_nrows_ncols.value(); assert!( dim != 0, "Cannot compute the bidiagonalization of an empty matrix." ); let mut diagonal = Matrix::uninit(min_nrows_ncols, Const::<1>); let mut off_diagonal = Matrix::uninit(min_nrows_ncols.sub(Const::<1>), Const::<1>); let mut axis_packed = Matrix::zeros_generic(ncols, Const::<1>); let mut work = Matrix::zeros_generic(nrows, Const::<1>); let upper_diagonal = nrows.value() >= ncols.value(); if upper_diagonal { for ite in 0..dim - 1 { diagonal[ite] = MaybeUninit::new(householder::clear_column_unchecked( &mut matrix, ite, 0, None, )); off_diagonal[ite] = MaybeUninit::new(householder::clear_row_unchecked( &mut matrix, &mut axis_packed, &mut work, ite, 1, )); } diagonal[dim - 1] = MaybeUninit::new(householder::clear_column_unchecked( &mut matrix, dim - 1, 0, None, )); } else { for ite in 0..dim - 1 { diagonal[ite] = MaybeUninit::new(householder::clear_row_unchecked( &mut matrix, &mut axis_packed, &mut work, ite, 0, )); off_diagonal[ite] = MaybeUninit::new(householder::clear_column_unchecked( &mut matrix, ite, 1, None, )); } diagonal[dim - 1] = MaybeUninit::new(householder::clear_row_unchecked( &mut matrix, &mut axis_packed, &mut work, dim - 1, 0, )); } // Safety: diagonal and off_diagonal have been fully initialized. let (diagonal, off_diagonal) = unsafe { (diagonal.assume_init(), off_diagonal.assume_init()) }; Bidiagonal { uv: matrix, diagonal, off_diagonal, upper_diagonal, } } /// Indicates whether this decomposition contains an upper-diagonal matrix. #[inline] #[must_use] pub fn is_upper_diagonal(&self) -> bool { self.upper_diagonal } #[inline] fn axis_shift(&self) -> (usize, usize) { if self.upper_diagonal { (0, 1) } else { (1, 0) } } /// Unpacks this decomposition into its three matrix factors `(U, D, V^t)`. /// /// The decomposed matrix `M` is equal to `U * D * V^t`. #[inline] pub fn unpack( self, ) -> ( OMatrix>, OMatrix, DimMinimum>, OMatrix, C>, ) where DefaultAllocator: Allocator, DimMinimum> + Allocator> + Allocator, C>, { // TODO: optimize by calling a reallocator. (self.u(), self.d(), self.v_t()) } /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. #[inline] #[must_use] pub fn d(&self) -> OMatrix, DimMinimum> where DefaultAllocator: Allocator, DimMinimum>, { let (nrows, ncols) = self.uv.shape_generic(); let d = nrows.min(ncols); let mut res = OMatrix::identity_generic(d, d); res.set_partial_diagonal( self.diagonal .iter() .map(|e| T::from_real(e.clone().modulus())), ); let start = self.axis_shift(); res.view_mut(start, (d.value() - 1, d.value() - 1)) .set_partial_diagonal( self.off_diagonal .iter() .map(|e| T::from_real(e.clone().modulus())), ); res } /// Computes the orthogonal matrix `U` of this `U * D * V` decomposition. // TODO: code duplication with householder::assemble_q. // Except that we are returning a rectangular matrix here. #[must_use] pub fn u(&self) -> OMatrix> where DefaultAllocator: Allocator>, { let (nrows, ncols) = self.uv.shape_generic(); let mut res = Matrix::identity_generic(nrows, nrows.min(ncols)); let dim = self.diagonal.len(); let shift = self.axis_shift().0; for i in (0..dim - shift).rev() { let axis = self.uv.view_range(i + shift.., i); // Sometimes, the axis might have a zero magnitude. if axis.norm_squared().is_zero() { continue; } let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); let mut res_rows = res.view_range_mut(i + shift.., i..); let sign = if self.upper_diagonal { self.diagonal[i].clone().signum() } else { self.off_diagonal[i].clone().signum() }; refl.reflect_with_sign(&mut res_rows, sign); } res } /// Computes the orthogonal matrix `V_t` of this `U * D * V_t` decomposition. #[must_use] pub fn v_t(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, { let (nrows, ncols) = self.uv.shape_generic(); let min_nrows_ncols = nrows.min(ncols); let mut res = Matrix::identity_generic(min_nrows_ncols, ncols); let mut work = Matrix::zeros_generic(min_nrows_ncols, Const::<1>); let mut axis_packed = Matrix::zeros_generic(ncols, Const::<1>); let shift = self.axis_shift().1; for i in (0..min_nrows_ncols.value() - shift).rev() { let axis = self.uv.view_range(i, i + shift..); let mut axis_packed = axis_packed.rows_range_mut(i + shift..); axis_packed.tr_copy_from(&axis); // Sometimes, the axis might have a zero magnitude. if axis_packed.norm_squared().is_zero() { continue; } let refl = Reflection::new(Unit::new_unchecked(axis_packed), T::zero()); let mut res_rows = res.view_range_mut(i.., i + shift..); let sign = if self.upper_diagonal { self.off_diagonal[i].clone().signum() } else { self.diagonal[i].clone().signum() }; refl.reflect_rows_with_sign(&mut res_rows, &mut work.rows_range_mut(i..), sign); } res } /// The diagonal part of this decomposed matrix. #[must_use] pub fn diagonal(&self) -> OVector> where DefaultAllocator: Allocator>, { self.diagonal.map(|e| e.modulus()) } /// The off-diagonal part of this decomposed matrix. #[must_use] pub fn off_diagonal(&self) -> OVector, U1>> where DefaultAllocator: Allocator, U1>>, { self.off_diagonal.map(|e| e.modulus()) } #[doc(hidden)] pub fn uv_internal(&self) -> &OMatrix { &self.uv } } // impl + DimSub> Bidiagonal // where DefaultAllocator: Allocator + // Allocator { // /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. // pub fn solve(&self, b: &Matrix) -> OMatrix // where S2: StorageMut, // ShapeConstraint: SameNumberOfRows { // let mut res = b.clone_owned(); // self.solve_mut(&mut res); // res // } // // /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. // pub fn solve_mut(&self, b: &mut Matrix) // where S2: StorageMut, // ShapeConstraint: SameNumberOfRows { // // assert_eq!(self.uv.nrows(), b.nrows(), "Bidiagonal solve matrix dimension mismatch."); // assert!(self.uv.is_square(), "Bidiagonal solve: unable to solve a non-square system."); // // self.q_tr_mul(b); // self.solve_upper_triangular_mut(b); // } // // // TODO: duplicate code from the `solve` module. // fn solve_upper_triangular_mut(&self, b: &mut Matrix) // where S2: StorageMut, // ShapeConstraint: SameNumberOfRows { // // let dim = self.uv.nrows(); // // for k in 0 .. b.ncols() { // let mut b = b.column_mut(k); // for i in (0 .. dim).rev() { // let coeff; // // unsafe { // let diag = *self.diag.vget_unchecked(i); // coeff = *b.vget_unchecked(i) / diag; // *b.vget_unchecked_mut(i) = coeff; // } // // b.rows_range_mut(.. i).axpy(-coeff, &self.uv.view_range(.. i, i), T::one()); // } // } // } // // /// Computes the inverse of the decomposed matrix. // pub fn inverse(&self) -> OMatrix { // assert!(self.uv.is_square(), "Bidiagonal inverse: unable to compute the inverse of a non-square matrix."); // // // TODO: is there a less naive method ? // let (nrows, ncols) = self.uv.shape_generic(); // let mut res = OMatrix::identity_generic(nrows, ncols); // self.solve_mut(&mut res); // res // } // // // /// Computes the determinant of the decomposed matrix. // // pub fn determinant(&self) -> T { // // let dim = self.uv.nrows(); // // assert!(self.uv.is_square(), "Bidiagonal determinant: unable to compute the determinant of a non-square matrix."); // // // let mut res = T::one(); // // for i in 0 .. dim { // // res *= unsafe { *self.diag.vget_unchecked(i) }; // // } // // // res self.q_determinant() // // } // } nalgebra-0.33.0/src/linalg/cholesky.rs000064400000000000000000000422510072674642500157420ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use num::{One, Zero}; use simba::scalar::ComplexField; use simba::simd::SimdComplexField; use crate::allocator::Allocator; use crate::base::{Const, DefaultAllocator, Matrix, OMatrix, Vector}; use crate::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::dimension::{Dim, DimAdd, DimDiff, DimSub, DimSum, U1}; use crate::storage::{Storage, StorageMut}; /// The Cholesky decomposition of a symmetric-definite-positive matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator, OMatrix: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator, OMatrix: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct Cholesky where DefaultAllocator: Allocator, { chol: OMatrix, } impl Copy for Cholesky where DefaultAllocator: Allocator, OMatrix: Copy, { } impl Cholesky where DefaultAllocator: Allocator, { /// Computes the Cholesky decomposition of `matrix` without checking that the matrix is definite-positive. /// /// If the input matrix is not definite-positive, the decomposition may contain trash values (Inf, NaN, etc.) pub fn new_unchecked(mut matrix: OMatrix) -> Self { assert!(matrix.is_square(), "The input matrix must be square."); let n = matrix.nrows(); for j in 0..n { for k in 0..j { let factor = unsafe { -matrix.get_unchecked((j, k)).clone() }; let (mut col_j, col_k) = matrix.columns_range_pair_mut(j, k); let mut col_j = col_j.rows_range_mut(j..); let col_k = col_k.rows_range(j..); col_j.axpy(factor.simd_conjugate(), &col_k, T::one()); } let diag = unsafe { matrix.get_unchecked((j, j)).clone() }; let denom = diag.simd_sqrt(); unsafe { *matrix.get_unchecked_mut((j, j)) = denom.clone(); } let mut col = matrix.view_range_mut(j + 1.., j); col /= denom; } Cholesky { chol: matrix } } /// Uses the given matrix as-is without any checks or modifications as the /// Cholesky decomposition. /// /// It is up to the user to ensure all invariants hold. pub fn pack_dirty(matrix: OMatrix) -> Self { Cholesky { chol: matrix } } /// Retrieves the lower-triangular factor of the Cholesky decomposition with its strictly /// upper-triangular part filled with zeros. pub fn unpack(mut self) -> OMatrix { self.chol.fill_upper_triangle(T::zero(), 1); self.chol } /// Retrieves the lower-triangular factor of the Cholesky decomposition, without zeroing-out /// its strict upper-triangular part. /// /// The values of the strict upper-triangular part are garbage and should be ignored by further /// computations. pub fn unpack_dirty(self) -> OMatrix { self.chol } /// Retrieves the lower-triangular factor of the Cholesky decomposition with its strictly /// uppen-triangular part filled with zeros. #[must_use] pub fn l(&self) -> OMatrix { self.chol.lower_triangle() } /// Retrieves the lower-triangular factor of the Cholesky decomposition, without zeroing-out /// its strict upper-triangular part. /// /// This is an allocation-less version of `self.l()`. The values of the strict upper-triangular /// part are garbage and should be ignored by further computations. #[must_use] pub fn l_dirty(&self) -> &OMatrix { &self.chol } /// Solves the system `self * x = b` where `self` is the decomposed matrix and `x` the unknown. /// /// The result is stored on `b`. pub fn solve_mut(&self, b: &mut Matrix) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { self.chol.solve_lower_triangular_unchecked_mut(b); self.chol.ad_solve_lower_triangular_unchecked_mut(b); } /// Returns the solution of the system `self * x = b` where `self` is the decomposed matrix and /// `x` the unknown. #[must_use = "Did you mean to use solve_mut()?"] pub fn solve(&self, b: &Matrix) -> OMatrix where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); self.solve_mut(&mut res); res } /// Computes the inverse of the decomposed matrix. #[must_use] pub fn inverse(&self) -> OMatrix { let shape = self.chol.shape_generic(); let mut res = OMatrix::identity_generic(shape.0, shape.1); self.solve_mut(&mut res); res } /// Computes the determinant of the decomposed matrix. #[must_use] pub fn determinant(&self) -> T::SimdRealField { let dim = self.chol.nrows(); let mut prod_diag = T::one(); for i in 0..dim { prod_diag *= unsafe { self.chol.get_unchecked((i, i)).clone() }; } prod_diag.simd_modulus_squared() } /// Computes the natural logarithm of determinant of the decomposed matrix. /// /// This method is more robust than `.determinant()` to very small or very /// large determinants since it returns the natural logarithm of the /// determinant rather than the determinant itself. #[must_use] pub fn ln_determinant(&self) -> T::SimdRealField { let dim = self.chol.nrows(); let mut sum_diag = T::SimdRealField::zero(); for i in 0..dim { sum_diag += unsafe { self.chol .get_unchecked((i, i)) .clone() .simd_modulus_squared() .simd_ln() }; } sum_diag } } impl Cholesky where DefaultAllocator: Allocator, { /// Attempts to compute the Cholesky decomposition of `matrix`. /// /// Returns `None` if the input matrix is not definite-positive. The input matrix is assumed /// to be symmetric and only the lower-triangular part is read. pub fn new(matrix: OMatrix) -> Option { Self::new_internal(matrix, None) } /// Attempts to approximate the Cholesky decomposition of `matrix` by /// replacing non-positive values on the diagonals during the decomposition /// with the given `substitute`. /// /// [`try_sqrt`](ComplexField::try_sqrt) will be applied to the `substitute` /// when it has to be used. /// /// If your input matrix results only in positive values on the diagonals /// during the decomposition, `substitute` is unused and the result is just /// the same as if you used [`new`](Cholesky::new). /// /// This method allows to compensate for matrices with very small or even /// negative values due to numerical errors but necessarily results in only /// an approximation: it is basically a hack. If you don't specifically need /// Cholesky, it may be better to consider alternatives like the /// [`LU`](crate::linalg::LU) decomposition/factorization. pub fn new_with_substitute(matrix: OMatrix, substitute: T) -> Option { Self::new_internal(matrix, Some(substitute)) } /// Common implementation for `new` and `new_with_substitute`. fn new_internal(mut matrix: OMatrix, substitute: Option) -> Option { assert!(matrix.is_square(), "The input matrix must be square."); let n = matrix.nrows(); for j in 0..n { for k in 0..j { let factor = unsafe { -matrix.get_unchecked((j, k)).clone() }; let (mut col_j, col_k) = matrix.columns_range_pair_mut(j, k); let mut col_j = col_j.rows_range_mut(j..); let col_k = col_k.rows_range(j..); col_j.axpy(factor.conjugate(), &col_k, T::one()); } let sqrt_denom = |v: T| { if v.is_zero() { return None; } v.try_sqrt() }; let diag = unsafe { matrix.get_unchecked((j, j)).clone() }; if let Some(denom) = sqrt_denom(diag).or_else(|| substitute.clone().and_then(sqrt_denom)) { unsafe { *matrix.get_unchecked_mut((j, j)) = denom.clone(); } let mut col = matrix.view_range_mut(j + 1.., j); col /= denom; continue; } // The diagonal element is either zero or its square root could not // be taken (e.g. for negative real numbers). return None; } Some(Cholesky { chol: matrix }) } /// Given the Cholesky decomposition of a matrix `M`, a scalar `sigma` and a vector `v`, /// performs a rank one update such that we end up with the decomposition of `M + sigma * (v * v.adjoint())`. #[inline] pub fn rank_one_update(&mut self, x: &Vector, sigma: T::RealField) where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { Self::xx_rank_one_update(&mut self.chol, &mut x.clone_owned(), sigma) } /// Updates the decomposition such that we get the decomposition of a matrix with the given column `col` in the `j`th position. /// Since the matrix is square, an identical row will be added in the `j`th row. pub fn insert_column( &self, j: usize, col: Vector, ) -> Cholesky> where D: DimAdd, R2: Dim, S2: Storage, DefaultAllocator: Allocator, DimSum> + Allocator, ShapeConstraint: SameNumberOfRows>, { let mut col = col.into_owned(); // for an explanation of the formulas, see https://en.wikipedia.org/wiki/Cholesky_decomposition#Updating_the_decomposition let n = col.nrows(); assert_eq!( n, self.chol.nrows() + 1, "The new column must have the size of the factored matrix plus one." ); assert!(j < n, "j needs to be within the bound of the new matrix."); // loads the data into a new matrix with an additional jth row/column // TODO: would it be worth it to avoid the zero-initialization? let mut chol = Matrix::zeros_generic( self.chol.shape_generic().0.add(Const::<1>), self.chol.shape_generic().1.add(Const::<1>), ); chol.view_range_mut(..j, ..j) .copy_from(&self.chol.view_range(..j, ..j)); chol.view_range_mut(..j, j + 1..) .copy_from(&self.chol.view_range(..j, j..)); chol.view_range_mut(j + 1.., ..j) .copy_from(&self.chol.view_range(j.., ..j)); chol.view_range_mut(j + 1.., j + 1..) .copy_from(&self.chol.view_range(j.., j..)); // update the jth row let top_left_corner = self.chol.view_range(..j, ..j); let col_j = col[j].clone(); let (mut new_rowj_adjoint, mut new_colj) = col.rows_range_pair_mut(..j, j + 1..); assert!( top_left_corner.solve_lower_triangular_mut(&mut new_rowj_adjoint), "Cholesky::insert_column : Unable to solve lower triangular system!" ); new_rowj_adjoint.adjoint_to(&mut chol.view_range_mut(j, ..j)); // update the center element let center_element = T::sqrt(col_j - T::from_real(new_rowj_adjoint.norm_squared())); chol[(j, j)] = center_element.clone(); // update the jth column let bottom_left_corner = self.chol.view_range(j.., ..j); // new_colj = (col_jplus - bottom_left_corner * new_rowj.adjoint()) / center_element; new_colj.gemm( -T::one() / center_element.clone(), &bottom_left_corner, &new_rowj_adjoint, T::one() / center_element, ); chol.view_range_mut(j + 1.., j).copy_from(&new_colj); // update the bottom right corner let mut bottom_right_corner = chol.view_range_mut(j + 1.., j + 1..); Self::xx_rank_one_update( &mut bottom_right_corner, &mut new_colj, -T::RealField::one(), ); Cholesky { chol } } /// Updates the decomposition such that we get the decomposition of the factored matrix with its `j`th column removed. /// Since the matrix is square, the `j`th row will also be removed. #[must_use] pub fn remove_column(&self, j: usize) -> Cholesky> where D: DimSub, DefaultAllocator: Allocator, DimDiff> + Allocator, { let n = self.chol.nrows(); assert!(n > 0, "The matrix needs at least one column."); assert!(j < n, "j needs to be within the bound of the matrix."); // loads the data into a new matrix except for the jth row/column // TODO: would it be worth it to avoid this zero initialization? let mut chol = Matrix::zeros_generic( self.chol.shape_generic().0.sub(Const::<1>), self.chol.shape_generic().1.sub(Const::<1>), ); chol.view_range_mut(..j, ..j) .copy_from(&self.chol.view_range(..j, ..j)); chol.view_range_mut(..j, j..) .copy_from(&self.chol.view_range(..j, j + 1..)); chol.view_range_mut(j.., ..j) .copy_from(&self.chol.view_range(j + 1.., ..j)); chol.view_range_mut(j.., j..) .copy_from(&self.chol.view_range(j + 1.., j + 1..)); // updates the bottom right corner let mut bottom_right_corner = chol.view_range_mut(j.., j..); let mut workspace = self.chol.column(j).clone_owned(); let mut old_colj = workspace.rows_range_mut(j + 1..); Self::xx_rank_one_update(&mut bottom_right_corner, &mut old_colj, T::RealField::one()); Cholesky { chol } } /// Given the Cholesky decomposition of a matrix `M`, a scalar `sigma` and a vector `x`, /// performs a rank one update such that we end up with the decomposition of `M + sigma * (x * x.adjoint())`. /// /// This helper method is called by `rank_one_update` but also `insert_column` and `remove_column` /// where it is used on a square view of the decomposition fn xx_rank_one_update( chol: &mut Matrix, x: &mut Vector, sigma: T::RealField, ) where //T: ComplexField, Dm: Dim, Rx: Dim, Sm: StorageMut, Sx: StorageMut, { // heavily inspired by Eigen's `llt_rank_update_lower` implementation https://eigen.tuxfamily.org/dox/LLT_8h_source.html let n = x.nrows(); assert_eq!( n, chol.nrows(), "The input vector must be of the same size as the factorized matrix." ); let mut beta = crate::one::(); for j in 0..n { // updates the diagonal let diag = T::real(unsafe { chol.get_unchecked((j, j)).clone() }); let diag2 = diag.clone() * diag.clone(); let xj = unsafe { x.get_unchecked(j).clone() }; let sigma_xj2 = sigma.clone() * T::modulus_squared(xj.clone()); let gamma = diag2.clone() * beta.clone() + sigma_xj2.clone(); let new_diag = (diag2.clone() + sigma_xj2.clone() / beta.clone()).sqrt(); unsafe { *chol.get_unchecked_mut((j, j)) = T::from_real(new_diag.clone()) }; beta += sigma_xj2 / diag2; // updates the terms of L let mut xjplus = x.rows_range_mut(j + 1..); let mut col_j = chol.view_range_mut(j + 1.., j); // temp_jplus -= (wj / T::from_real(diag)) * col_j; xjplus.axpy(-xj.clone() / T::from_real(diag.clone()), &col_j, T::one()); if gamma != crate::zero::() { // col_j = T::from_real(nljj / diag) * col_j + (T::from_real(nljj * sigma / gamma) * T::conjugate(wj)) * temp_jplus; col_j.axpy( T::from_real(new_diag.clone() * sigma.clone() / gamma) * T::conjugate(xj), &xjplus, T::from_real(new_diag / diag), ); } } } } nalgebra-0.33.0/src/linalg/col_piv_qr.rs000064400000000000000000000260110072674642500162520ustar 00000000000000use num::Zero; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::{Allocator, Reallocator}; use crate::base::{Const, DefaultAllocator, Matrix, OMatrix, OVector, Unit}; use crate::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::dimension::{Dim, DimMin, DimMinimum}; use crate::storage::StorageMut; use crate::ComplexField; use crate::geometry::Reflection; use crate::linalg::{householder, PermutationSequence}; use std::mem::MaybeUninit; /// The QR decomposition (with column pivoting) of a general matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Serialize, PermutationSequence>: Serialize, OVector>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Deserialize<'de>, PermutationSequence>: Deserialize<'de>, OVector>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct ColPivQR, C: Dim> where DefaultAllocator: Allocator + Allocator>, { col_piv_qr: OMatrix, p: PermutationSequence>, diag: OVector>, } impl, C: Dim> Copy for ColPivQR where DefaultAllocator: Allocator + Allocator>, OMatrix: Copy, PermutationSequence>: Copy, OVector>: Copy, { } impl, C: Dim> ColPivQR where DefaultAllocator: Allocator + Allocator + Allocator>, { /// Computes the `ColPivQR` decomposition using householder reflections. pub fn new(mut matrix: OMatrix) -> Self { let (nrows, ncols) = matrix.shape_generic(); let min_nrows_ncols = nrows.min(ncols); let mut p = PermutationSequence::identity_generic(min_nrows_ncols); if min_nrows_ncols.value() == 0 { return ColPivQR { col_piv_qr: matrix, p, diag: Matrix::zeros_generic(min_nrows_ncols, Const::<1>), }; } let mut diag = Matrix::uninit(min_nrows_ncols, Const::<1>); for i in 0..min_nrows_ncols.value() { let piv = matrix.view_range(i.., i..).icamax_full(); let col_piv = piv.1 + i; matrix.swap_columns(i, col_piv); p.append_permutation(i, col_piv); diag[i] = MaybeUninit::new(householder::clear_column_unchecked(&mut matrix, i, 0, None)); } // Safety: diag is now fully initialized. let diag = unsafe { diag.assume_init() }; ColPivQR { col_piv_qr: matrix, p, diag, } } /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. #[inline] #[must_use] pub fn r(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, { let (nrows, ncols) = self.col_piv_qr.shape_generic(); let mut res = self .col_piv_qr .rows_generic(0, nrows.min(ncols)) .upper_triangle(); res.set_partial_diagonal(self.diag.iter().map(|e| T::from_real(e.clone().modulus()))); res } /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. /// /// This is usually faster than `r` but consumes `self`. #[inline] pub fn unpack_r(self) -> OMatrix, C> where DefaultAllocator: Reallocator, C>, { let (nrows, ncols) = self.col_piv_qr.shape_generic(); let mut res = self .col_piv_qr .resize_generic(nrows.min(ncols), ncols, T::zero()); res.fill_lower_triangle(T::zero(), 1); res.set_partial_diagonal(self.diag.iter().map(|e| T::from_real(e.clone().modulus()))); res } /// Computes the orthogonal matrix `Q` of this decomposition. #[must_use] pub fn q(&self) -> OMatrix> where DefaultAllocator: Allocator>, { let (nrows, ncols) = self.col_piv_qr.shape_generic(); // NOTE: we could build the identity matrix and call q_mul on it. // Instead we don't so that we take in account the matrix sparseness. let mut res = Matrix::identity_generic(nrows, nrows.min(ncols)); let dim = self.diag.len(); for i in (0..dim).rev() { let axis = self.col_piv_qr.view_range(i.., i); // TODO: sometimes, the axis might have a zero magnitude. let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); let mut res_rows = res.view_range_mut(i.., i..); refl.reflect_with_sign(&mut res_rows, self.diag[i].clone().signum()); } res } /// Retrieves the column permutation of this decomposition. #[inline] #[must_use] pub fn p(&self) -> &PermutationSequence> { &self.p } /// Unpacks this decomposition into its two matrix factors. pub fn unpack( self, ) -> ( OMatrix>, OMatrix, C>, PermutationSequence>, ) where DimMinimum: DimMin>, DefaultAllocator: Allocator> + Reallocator, C> + Allocator>, { (self.q(), self.r(), self.p) } #[doc(hidden)] pub fn col_piv_qr_internal(&self) -> &OMatrix { &self.col_piv_qr } /// Multiplies the provided matrix by the transpose of the `Q` matrix of this decomposition. pub fn q_tr_mul(&self, rhs: &mut Matrix) where S2: StorageMut, { let dim = self.diag.len(); for i in 0..dim { let axis = self.col_piv_qr.view_range(i.., i); let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); let mut rhs_rows = rhs.rows_range_mut(i..); refl.reflect_with_sign(&mut rhs_rows, self.diag[i].clone().signum().conjugate()); } } } impl> ColPivQR where DefaultAllocator: Allocator + Allocator + Allocator>, { /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if `self` is not invertible. #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, ) -> Option> where S2: StorageMut, ShapeConstraint: SameNumberOfRows, DefaultAllocator: Allocator, { let mut res = b.clone_owned(); if self.solve_mut(&mut res) { Some(res) } else { None } } /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// If the decomposed matrix is not invertible, this returns `false` and its input `b` is /// overwritten with garbage. pub fn solve_mut(&self, b: &mut Matrix) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { assert_eq!( self.col_piv_qr.nrows(), b.nrows(), "ColPivQR solve matrix dimension mismatch." ); assert!( self.col_piv_qr.is_square(), "ColPivQR solve: unable to solve a non-square system." ); self.q_tr_mul(b); let solved = self.solve_upper_triangular_mut(b); self.p.inv_permute_rows(b); solved } // TODO: duplicate code from the `solve` module. fn solve_upper_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.col_piv_qr.nrows(); for k in 0..b.ncols() { let mut b = b.column_mut(k); for i in (0..dim).rev() { let coeff; unsafe { let diag = self.diag.vget_unchecked(i).clone().modulus(); if diag.is_zero() { return false; } coeff = b.vget_unchecked(i).clone().unscale(diag); *b.vget_unchecked_mut(i) = coeff.clone(); } b.rows_range_mut(..i) .axpy(-coeff, &self.col_piv_qr.view_range(..i, i), T::one()); } } true } /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the decomposed matrix is not invertible. #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.col_piv_qr.is_square(), "ColPivQR inverse: unable to compute the inverse of a non-square matrix." ); // TODO: is there a less naive method ? let (nrows, ncols) = self.col_piv_qr.shape_generic(); let mut res = OMatrix::identity_generic(nrows, ncols); if self.solve_mut(&mut res) { Some(res) } else { None } } /// Indicates if the decomposed matrix is invertible. #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.col_piv_qr.is_square(), "ColPivQR: unable to test the invertibility of a non-square matrix." ); for i in 0..self.diag.len() { if self.diag[i].is_zero() { return false; } } true } /// Computes the determinant of the decomposed matrix. #[must_use] pub fn determinant(&self) -> T { let dim = self.col_piv_qr.nrows(); assert!( self.col_piv_qr.is_square(), "ColPivQR determinant: unable to compute the determinant of a non-square matrix." ); let mut res = T::one(); for i in 0..dim { res *= unsafe { self.diag.vget_unchecked(i).clone() }; } res * self.p.determinant() } } nalgebra-0.33.0/src/linalg/convolution.rs000064400000000000000000000107120072674642500164750ustar 00000000000000use std::cmp; use crate::base::allocator::Allocator; use crate::base::default_allocator::DefaultAllocator; use crate::base::dimension::{Const, Dim, DimAdd, DimDiff, DimSub, DimSum}; use crate::storage::Storage; use crate::{zero, OVector, RealField, Vector, U1}; impl> Vector { /// Returns the convolution of the target vector and a kernel. /// /// # Arguments /// /// * `kernel` - A Vector with size > 0 /// /// # Errors /// Inputs must satisfy `vector.len() >= kernel.len() > 0`. /// pub fn convolve_full( &self, kernel: Vector, ) -> OVector, U1>> where D1: DimAdd, D2: DimAdd>, DimSum: DimSub, S2: Storage, DefaultAllocator: Allocator, U1>>, { let vec = self.len(); let ker = kernel.len(); if ker == 0 || ker > vec { panic!("convolve_full expects `self.len() >= kernel.len() > 0`, received {} and {} respectively.", vec, ker); } let result_len = self .data .shape() .0 .add(kernel.shape_generic().0) .sub(Const::<1>); let mut conv = OVector::zeros_generic(result_len, Const::<1>); for i in 0..(vec + ker - 1) { let u_i = if i > vec { i - ker } else { 0 }; let u_f = cmp::min(i, vec - 1); if u_i == u_f { conv[i] += self[u_i].clone() * kernel[i - u_i].clone(); } else { for u in u_i..(u_f + 1) { if i - u < ker { conv[i] += self[u].clone() * kernel[i - u].clone(); } } } } conv } /// Returns the convolution of the target vector and a kernel. /// /// The output convolution consists only of those elements that do not rely on the zero-padding. /// # Arguments /// /// * `kernel` - A Vector with size > 0 /// /// /// # Errors /// Inputs must satisfy `self.len() >= kernel.len() > 0`. /// pub fn convolve_valid( &self, kernel: Vector, ) -> OVector, D2>> where D1: DimAdd, D2: Dim, DimSum: DimSub, S2: Storage, DefaultAllocator: Allocator, D2>>, { let vec = self.len(); let ker = kernel.len(); if ker == 0 || ker > vec { panic!("convolve_valid expects `self.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); } let result_len = self .data .shape() .0 .add(Const::<1>) .sub(kernel.shape_generic().0); let mut conv = OVector::zeros_generic(result_len, Const::<1>); for i in 0..(vec - ker + 1) { for j in 0..ker { conv[i] += self[i + j].clone() * kernel[ker - j - 1].clone(); } } conv } /// Returns the convolution of the target vector and a kernel. /// /// The output convolution is the same size as vector, centered with respect to the ‘full’ output. /// # Arguments /// /// * `kernel` - A Vector with size > 0 /// /// # Errors /// Inputs must satisfy `self.len() >= kernel.len() > 0`. #[must_use] pub fn convolve_same(&self, kernel: Vector) -> OVector where D2: Dim, S2: Storage, DefaultAllocator: Allocator, { let vec = self.len(); let ker = kernel.len(); if ker == 0 || ker > vec { panic!("convolve_same expects `self.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); } let mut conv = OVector::zeros_generic(self.shape_generic().0, Const::<1>); for i in 0..vec { for j in 0..ker { let val = if i + j < 1 || i + j >= vec + 1 { zero::() } else { self[i + j - 1].clone() }; conv[i] += val * kernel[ker - j - 1].clone(); } } conv } } nalgebra-0.33.0/src/linalg/decomposition.rs000064400000000000000000000365320072674642500170020ustar 00000000000000use crate::storage::Storage; use crate::{ Allocator, Bidiagonal, Cholesky, ColPivQR, ComplexField, DefaultAllocator, Dim, DimDiff, DimMin, DimMinimum, DimSub, FullPivLU, Hessenberg, Matrix, OMatrix, RealField, Schur, SymmetricEigen, SymmetricTridiagonal, LU, QR, SVD, U1, UDU, }; /// # Rectangular matrix decomposition /// /// This section contains the methods for computing some common decompositions of rectangular /// matrices with real or complex components. The following are currently supported: /// /// | Decomposition | Factors | Details | /// | -------------------------|---------------------|--------------| /// | QR | `Q * R` | `Q` is an unitary matrix, and `R` is upper-triangular. | /// | QR with column pivoting | `Q * R * P⁻¹` | `Q` is an unitary matrix, and `R` is upper-triangular. `P` is a permutation matrix. | /// | LU with partial pivoting | `P⁻¹ * L * U` | `L` is lower-triangular with a diagonal filled with `1` and `U` is upper-triangular. `P` is a permutation matrix. | /// | LU with full pivoting | `P⁻¹ * L * U * Q⁻¹` | `L` is lower-triangular with a diagonal filled with `1` and `U` is upper-triangular. `P` and `Q` are permutation matrices. | /// | SVD | `U * Σ * Vᵀ` | `U` and `V` are two orthogonal matrices and `Σ` is a diagonal matrix containing the singular values. | /// | Polar (Left Polar) | `P' * U` | `U` is semi-unitary/unitary and `P'` is a positive semi-definite Hermitian Matrix impl> Matrix { /// Computes the bidiagonalization using householder reflections. pub fn bidiagonalize(self) -> Bidiagonal where R: DimMin, DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator + Allocator + Allocator> + Allocator, U1>>, { Bidiagonal::new(self.into_owned()) } /// Computes the LU decomposition with full pivoting of `matrix`. /// /// This effectively computes `P, L, U, Q` such that `P * matrix * Q = LU`. pub fn full_piv_lu(self) -> FullPivLU where R: DimMin, DefaultAllocator: Allocator + Allocator>, { FullPivLU::new(self.into_owned()) } /// Computes the LU decomposition with partial (row) pivoting of `matrix`. pub fn lu(self) -> LU where R: DimMin, DefaultAllocator: Allocator + Allocator>, { LU::new(self.into_owned()) } /// Computes the QR decomposition of this matrix. pub fn qr(self) -> QR where R: DimMin, DefaultAllocator: Allocator + Allocator + Allocator>, { QR::new(self.into_owned()) } /// Computes the QR decomposition (with column pivoting) of this matrix. pub fn col_piv_qr(self) -> ColPivQR where R: DimMin, DefaultAllocator: Allocator + Allocator + Allocator> + Allocator>, { ColPivQR::new(self.into_owned()) } /// Computes the Singular Value Decomposition using implicit shift. /// The singular values are guaranteed to be sorted in descending order. /// If this order is not required consider using `svd_unordered`. pub fn svd(self, compute_u: bool, compute_v: bool) -> SVD where R: DimMin, DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator>, { SVD::new(self.into_owned(), compute_u, compute_v) } /// Computes the Singular Value Decomposition using implicit shift. /// The singular values are not guaranteed to be sorted in any particular order. /// If a descending order is required, consider using `svd` instead. pub fn svd_unordered(self, compute_u: bool, compute_v: bool) -> SVD where R: DimMin, DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator>, { SVD::new_unordered(self.into_owned(), compute_u, compute_v) } /// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift. /// The singular values are guaranteed to be sorted in descending order. /// If this order is not required consider using `try_svd_unordered`. /// /// # Arguments /// /// * `compute_u` − set this to `true` to enable the computation of left-singular vectors. /// * `compute_v` − set this to `true` to enable the computation of right-singular vectors. /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_svd( self, compute_u: bool, compute_v: bool, eps: T::RealField, max_niter: usize, ) -> Option> where R: DimMin, DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator>, { SVD::try_new(self.into_owned(), compute_u, compute_v, eps, max_niter) } /// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift. /// The singular values are not guaranteed to be sorted in any particular order. /// If a descending order is required, consider using `try_svd` instead. /// /// # Arguments /// /// * `compute_u` − set this to `true` to enable the computation of left-singular vectors. /// * `compute_v` − set this to `true` to enable the computation of right-singular vectors. /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_svd_unordered( self, compute_u: bool, compute_v: bool, eps: T::RealField, max_niter: usize, ) -> Option> where R: DimMin, DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator> + Allocator> + Allocator, U1>>, { SVD::try_new_unordered(self.into_owned(), compute_u, compute_v, eps, max_niter) } /// Computes the Polar Decomposition of a `matrix` (indirectly uses SVD). pub fn polar(self) -> (OMatrix, OMatrix) where R: DimMin, DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator, R> + Allocator> + Allocator + Allocator, DimMinimum> + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator> + Allocator> + Allocator, U1>>, { SVD::new_unordered(self.into_owned(), true, true) .to_polar() .unwrap() } /// Attempts to compute the Polar Decomposition of a `matrix` (indirectly uses SVD). /// /// # Arguments /// /// * `eps` − tolerance used to determine when a value converged to 0 when computing the SVD. /// * `max_niter` − maximum total number of iterations performed by the SVD computation algorithm. pub fn try_polar( self, eps: T::RealField, max_niter: usize, ) -> Option<(OMatrix, OMatrix)> where R: DimMin, DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator, R> + Allocator> + Allocator + Allocator, DimMinimum> + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator> + Allocator> + Allocator, U1>>, { SVD::try_new_unordered(self.into_owned(), true, true, eps, max_niter) .and_then(|svd| svd.to_polar()) } } /// # Square matrix decomposition /// /// This section contains the methods for computing some common decompositions of square /// matrices with real or complex components. The following are currently supported: /// /// | Decomposition | Factors | Details | /// | -------------------------|---------------------------|--------------| /// | Hessenberg | `Q * H * Qᵀ` | `Q` is a unitary matrix and `H` an upper-Hessenberg matrix. | /// | Cholesky | `L * Lᵀ` | `L` is a lower-triangular matrix. | /// | UDU | `U * D * Uᵀ` | `U` is a upper-triangular matrix, and `D` a diagonal matrix. | /// | Schur decomposition | `Q * T * Qᵀ` | `Q` is an unitary matrix and `T` a quasi-upper-triangular matrix. | /// | Symmetric eigendecomposition | `Q ~ Λ ~ Qᵀ` | `Q` is an unitary matrix, and `Λ` is a real diagonal matrix. | /// | Symmetric tridiagonalization | `Q ~ T ~ Qᵀ` | `Q` is an unitary matrix, and `T` is a tridiagonal matrix. | impl> Matrix { /// Attempts to compute the Cholesky decomposition of this matrix. /// /// Returns `None` if the input matrix is not definite-positive. The input matrix is assumed /// to be symmetric and only the lower-triangular part is read. pub fn cholesky(self) -> Option> where DefaultAllocator: Allocator, { Cholesky::new(self.into_owned()) } /// Attempts to compute the UDU decomposition of this matrix. /// /// The input matrix `self` is assumed to be symmetric and this decomposition will only read /// the upper-triangular part of `self`. pub fn udu(self) -> Option> where T: RealField, DefaultAllocator: Allocator + Allocator, { UDU::new(self.into_owned()) } /// Computes the Hessenberg decomposition of this matrix using householder reflections. pub fn hessenberg(self) -> Hessenberg where D: DimSub, DefaultAllocator: Allocator + Allocator + Allocator>, { Hessenberg::new(self.into_owned()) } /// Computes the Schur decomposition of a square matrix. pub fn schur(self) -> Schur where D: DimSub, // For Hessenberg. DefaultAllocator: Allocator> + Allocator> + Allocator + Allocator, { Schur::new(self.into_owned()) } /// Attempts to compute the Schur decomposition of a square matrix. /// /// If only eigenvalues are needed, it is more efficient to call the matrix method /// `.eigenvalues()` instead. /// /// # Arguments /// /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_schur(self, eps: T::RealField, max_niter: usize) -> Option> where D: DimSub, // For Hessenberg. DefaultAllocator: Allocator> + Allocator> + Allocator + Allocator, { Schur::try_new(self.into_owned(), eps, max_niter) } /// Computes the eigendecomposition of this symmetric matrix. /// /// Only the lower-triangular part (including the diagonal) of `m` is read. pub fn symmetric_eigen(self) -> SymmetricEigen where D: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator + Allocator>, { SymmetricEigen::new(self.into_owned()) } /// Computes the eigendecomposition of the given symmetric matrix with user-specified /// convergence parameters. /// /// Only the lower-triangular part (including the diagonal) of `m` is read. /// /// # Arguments /// /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_symmetric_eigen( self, eps: T::RealField, max_niter: usize, ) -> Option> where D: DimSub, DefaultAllocator: Allocator + Allocator> + Allocator + Allocator>, { SymmetricEigen::try_new(self.into_owned(), eps, max_niter) } /// Computes the tridiagonalization of this symmetric matrix. /// /// Only the lower-triangular part (including the diagonal) of `m` is read. pub fn symmetric_tridiagonalize(self) -> SymmetricTridiagonal where D: DimSub, DefaultAllocator: Allocator + Allocator>, { SymmetricTridiagonal::new(self.into_owned()) } } nalgebra-0.33.0/src/linalg/determinant.rs000064400000000000000000000044460072674642500164370ustar 00000000000000use simba::scalar::ComplexField; use crate::base::allocator::Allocator; use crate::base::dimension::DimMin; use crate::base::storage::Storage; use crate::base::{DefaultAllocator, SquareMatrix}; use crate::linalg::LU; impl, S: Storage> SquareMatrix { /// Computes the matrix determinant. /// /// If the matrix has a dimension larger than 3, an LU decomposition is used. #[inline] #[must_use] pub fn determinant(&self) -> T where DefaultAllocator: Allocator + Allocator, { assert!( self.is_square(), "Unable to compute the determinant of a non-square matrix." ); let dim = self.shape().0; unsafe { match dim { 0 => T::one(), 1 => self.get_unchecked((0, 0)).clone(), 2 => { let m11 = self.get_unchecked((0, 0)).clone(); let m12 = self.get_unchecked((0, 1)).clone(); let m21 = self.get_unchecked((1, 0)).clone(); let m22 = self.get_unchecked((1, 1)).clone(); m11 * m22 - m21 * m12 } 3 => { let m11 = self.get_unchecked((0, 0)).clone(); let m12 = self.get_unchecked((0, 1)).clone(); let m13 = self.get_unchecked((0, 2)).clone(); let m21 = self.get_unchecked((1, 0)).clone(); let m22 = self.get_unchecked((1, 1)).clone(); let m23 = self.get_unchecked((1, 2)).clone(); let m31 = self.get_unchecked((2, 0)).clone(); let m32 = self.get_unchecked((2, 1)).clone(); let m33 = self.get_unchecked((2, 2)).clone(); let minor_m12_m23 = m22.clone() * m33.clone() - m32.clone() * m23.clone(); let minor_m11_m23 = m21.clone() * m33 - m31.clone() * m23; let minor_m11_m22 = m21 * m32 - m31 * m22; m11 * minor_m12_m23 - m12 * minor_m11_m23 + m13 * minor_m11_m22 } _ => LU::new(self.clone_owned()).determinant(), } } } } nalgebra-0.33.0/src/linalg/eigen.rs000064400000000000000000000067530072674642500152170ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use num_complex::Complex; use simba::scalar::ComplexField; use std::cmp; use std::fmt::Display; use std::ops::MulAssign; use crate::allocator::Allocator; use crate::base::dimension::{Dim, DimDiff, DimSub, Dyn, U1, U2, U3}; use crate::base::storage::Storage; use crate::base::{ DefaultAllocator, Hessenberg, OMatrix, OVector, SquareMatrix, Unit, Vector2, Vector3, }; use crate::constraint::{DimEq, ShapeConstraint}; use crate::geometry::{Reflection, UnitComplex}; use crate::linalg::householder; use crate::linalg::Schur; /// Eigendecomposition of a real matrix with real eigenvalues (or complex eigen values for complex matrices). #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator, OVector: Serialize, OMatrix: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator, OVector: Serialize, OMatrix: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct Eigen where DefaultAllocator: Allocator + Allocator, { pub eigenvectors: OMatrix, pub eigenvalues: OVector, } impl Copy for Eigen where DefaultAllocator: Allocator + Allocator, OMatrix: Copy, OVector: Copy, { } impl Eigen where D: DimSub, // For Hessenberg. ShapeConstraint: DimEq>, // For Hessenberg. DefaultAllocator: Allocator> + Allocator> + Allocator + Allocator, // XXX: for debug DefaultAllocator: Allocator, OMatrix: Display, { /// Computes the eigendecomposition of a diagonalizable matrix with Complex eigenvalues. pub fn new(m: OMatrix) -> Option> { assert!( m.is_square(), "Unable to compute the eigendecomposition of a non-square matrix." ); let dim = m.nrows(); let (mut eigenvectors, mut eigenvalues) = Schur::new(m, 0).unwrap().unpack(); println!("Schur eigenvalues: {}", eigenvalues); // Check that the eigenvalues are all Complex. for i in 0..dim - 1 { if !eigenvalues[(i + 1, i)].is_zero() { return None; } } for j in 1..dim { for i in 0..j { let diff = eigenvalues[(i, i)] - eigenvalues[(j, j)]; if diff.is_zero() && !eigenvalues[(i, j)].is_zero() { return None; } let z = -eigenvalues[(i, j)] / diff; for k in j + 1..dim { eigenvalues[(i, k)] -= z * eigenvalues[(j, k)]; } for k in 0..dim { eigenvectors[(k, j)] += z * eigenvectors[(k, i)]; } } } // Normalize the eigenvector basis. for i in 0..dim { let _ = eigenvectors.column_mut(i).normalize_mut(); } Some(Eigen { eigenvectors, eigenvalues: eigenvalues.diagonal(), }) } } nalgebra-0.33.0/src/linalg/exp.rs000064400000000000000000000406750072674642500147250ustar 00000000000000//! This module provides the matrix exponent (exp) function to square matrices. //! use crate::{ base::{ allocator::Allocator, dimension::{Const, Dim, DimMin, DimMinimum}, DefaultAllocator, }, convert, try_convert, ComplexField, OMatrix, RealField, }; use crate::num::Zero; /// Precomputed factorials for integers in range `0..=34`. /// Note: `35!` does not fit into 128 bits. // TODO: find a better place for this array? const FACTORIAL: [u128; 35] = [ 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000, 51090942171709440000, 1124000727777607680000, 25852016738884976640000, 620448401733239439360000, 15511210043330985984000000, 403291461126605635584000000, 10888869450418352160768000000, 304888344611713860501504000000, 8841761993739701954543616000000, 265252859812191058636308480000000, 8222838654177922817725562880000000, 263130836933693530167218012160000000, 8683317618811886495518194401280000000, 295232799039604140847618609643520000000, ]; // https://github.com/scipy/scipy/blob/c1372d8aa90a73d8a52f135529293ff4edb98fc8/scipy/sparse/linalg/matfuncs.py struct ExpmPadeHelper where T: ComplexField, D: DimMin, DefaultAllocator: Allocator + Allocator>, { use_exact_norm: bool, ident: OMatrix, a: OMatrix, a2: Option>, a4: Option>, a6: Option>, a8: Option>, a10: Option>, d4_exact: Option, d6_exact: Option, d8_exact: Option, d10_exact: Option, d4_approx: Option, d6_approx: Option, d8_approx: Option, d10_approx: Option, } impl ExpmPadeHelper where T: ComplexField, D: DimMin, DefaultAllocator: Allocator + Allocator>, { fn new(a: OMatrix, use_exact_norm: bool) -> Self { let (nrows, ncols) = a.shape_generic(); ExpmPadeHelper { use_exact_norm, ident: OMatrix::::identity_generic(nrows, ncols), a, a2: None, a4: None, a6: None, a8: None, a10: None, d4_exact: None, d6_exact: None, d8_exact: None, d10_exact: None, d4_approx: None, d6_approx: None, d8_approx: None, d10_approx: None, } } fn calc_a2(&mut self) { if self.a2.is_none() { self.a2 = Some(&self.a * &self.a); } } fn calc_a4(&mut self) { if self.a4.is_none() { self.calc_a2(); let a2 = self.a2.as_ref().unwrap(); self.a4 = Some(a2 * a2); } } fn calc_a6(&mut self) { if self.a6.is_none() { self.calc_a2(); self.calc_a4(); let a2 = self.a2.as_ref().unwrap(); let a4 = self.a4.as_ref().unwrap(); self.a6 = Some(a4 * a2); } } fn calc_a8(&mut self) { if self.a8.is_none() { self.calc_a2(); self.calc_a6(); let a2 = self.a2.as_ref().unwrap(); let a6 = self.a6.as_ref().unwrap(); self.a8 = Some(a6 * a2); } } fn calc_a10(&mut self) { if self.a10.is_none() { self.calc_a4(); self.calc_a6(); let a4 = self.a4.as_ref().unwrap(); let a6 = self.a6.as_ref().unwrap(); self.a10 = Some(a6 * a4); } } fn d4_tight(&mut self) -> T::RealField { if self.d4_exact.is_none() { self.calc_a4(); self.d4_exact = Some(one_norm(self.a4.as_ref().unwrap()).powf(convert(0.25))); } self.d4_exact.clone().unwrap() } fn d6_tight(&mut self) -> T::RealField { if self.d6_exact.is_none() { self.calc_a6(); self.d6_exact = Some(one_norm(self.a6.as_ref().unwrap()).powf(convert(1.0 / 6.0))); } self.d6_exact.clone().unwrap() } fn d8_tight(&mut self) -> T::RealField { if self.d8_exact.is_none() { self.calc_a8(); self.d8_exact = Some(one_norm(self.a8.as_ref().unwrap()).powf(convert(1.0 / 8.0))); } self.d8_exact.clone().unwrap() } fn d10_tight(&mut self) -> T::RealField { if self.d10_exact.is_none() { self.calc_a10(); self.d10_exact = Some(one_norm(self.a10.as_ref().unwrap()).powf(convert(1.0 / 10.0))); } self.d10_exact.clone().unwrap() } fn d4_loose(&mut self) -> T::RealField { if self.use_exact_norm { return self.d4_tight(); } if self.d4_exact.is_some() { return self.d4_exact.clone().unwrap(); } if self.d4_approx.is_none() { self.calc_a4(); self.d4_approx = Some(one_norm(self.a4.as_ref().unwrap()).powf(convert(0.25))); } self.d4_approx.clone().unwrap() } fn d6_loose(&mut self) -> T::RealField { if self.use_exact_norm { return self.d6_tight(); } if self.d6_exact.is_some() { return self.d6_exact.clone().unwrap(); } if self.d6_approx.is_none() { self.calc_a6(); self.d6_approx = Some(one_norm(self.a6.as_ref().unwrap()).powf(convert(1.0 / 6.0))); } self.d6_approx.clone().unwrap() } fn d8_loose(&mut self) -> T::RealField { if self.use_exact_norm { return self.d8_tight(); } if self.d8_exact.is_some() { return self.d8_exact.clone().unwrap(); } if self.d8_approx.is_none() { self.calc_a8(); self.d8_approx = Some(one_norm(self.a8.as_ref().unwrap()).powf(convert(1.0 / 8.0))); } self.d8_approx.clone().unwrap() } fn d10_loose(&mut self) -> T::RealField { if self.use_exact_norm { return self.d10_tight(); } if self.d10_exact.is_some() { return self.d10_exact.clone().unwrap(); } if self.d10_approx.is_none() { self.calc_a10(); self.d10_approx = Some(one_norm(self.a10.as_ref().unwrap()).powf(convert(1.0 / 10.0))); } self.d10_approx.clone().unwrap() } fn pade3(&mut self) -> (OMatrix, OMatrix) { let b: [T; 4] = [convert(120.0), convert(60.0), convert(12.0), convert(1.0)]; self.calc_a2(); let a2 = self.a2.as_ref().unwrap(); let u = &self.a * (a2 * b[3].clone() + &self.ident * b[1].clone()); let v = a2 * b[2].clone() + &self.ident * b[0].clone(); (u, v) } fn pade5(&mut self) -> (OMatrix, OMatrix) { let b: [T; 6] = [ convert(30240.0), convert(15120.0), convert(3360.0), convert(420.0), convert(30.0), convert(1.0), ]; self.calc_a2(); self.calc_a6(); let u = &self.a * (self.a4.as_ref().unwrap() * b[5].clone() + self.a2.as_ref().unwrap() * b[3].clone() + &self.ident * b[1].clone()); let v = self.a4.as_ref().unwrap() * b[4].clone() + self.a2.as_ref().unwrap() * b[2].clone() + &self.ident * b[0].clone(); (u, v) } fn pade7(&mut self) -> (OMatrix, OMatrix) { let b: [T; 8] = [ convert(17_297_280.0), convert(8_648_640.0), convert(1_995_840.0), convert(277_200.0), convert(25_200.0), convert(1_512.0), convert(56.0), convert(1.0), ]; self.calc_a2(); self.calc_a4(); self.calc_a6(); let u = &self.a * (self.a6.as_ref().unwrap() * b[7].clone() + self.a4.as_ref().unwrap() * b[5].clone() + self.a2.as_ref().unwrap() * b[3].clone() + &self.ident * b[1].clone()); let v = self.a6.as_ref().unwrap() * b[6].clone() + self.a4.as_ref().unwrap() * b[4].clone() + self.a2.as_ref().unwrap() * b[2].clone() + &self.ident * b[0].clone(); (u, v) } fn pade9(&mut self) -> (OMatrix, OMatrix) { let b: [T; 10] = [ convert(17_643_225_600.0), convert(8_821_612_800.0), convert(2_075_673_600.0), convert(302_702_400.0), convert(30_270_240.0), convert(2_162_160.0), convert(110_880.0), convert(3_960.0), convert(90.0), convert(1.0), ]; self.calc_a2(); self.calc_a4(); self.calc_a6(); self.calc_a8(); let u = &self.a * (self.a8.as_ref().unwrap() * b[9].clone() + self.a6.as_ref().unwrap() * b[7].clone() + self.a4.as_ref().unwrap() * b[5].clone() + self.a2.as_ref().unwrap() * b[3].clone() + &self.ident * b[1].clone()); let v = self.a8.as_ref().unwrap() * b[8].clone() + self.a6.as_ref().unwrap() * b[6].clone() + self.a4.as_ref().unwrap() * b[4].clone() + self.a2.as_ref().unwrap() * b[2].clone() + &self.ident * b[0].clone(); (u, v) } fn pade13_scaled(&mut self, s: u64) -> (OMatrix, OMatrix) { let b: [T; 14] = [ convert(64_764_752_532_480_000.0), convert(32_382_376_266_240_000.0), convert(7_771_770_303_897_600.0), convert(1_187_353_796_428_800.0), convert(129_060_195_264_000.0), convert(10_559_470_521_600.0), convert(670_442_572_800.0), convert(33_522_128_640.0), convert(1_323_241_920.0), convert(40_840_800.0), convert(960_960.0), convert(16_380.0), convert(182.0), convert(1.0), ]; let s = s as f64; let mb = &self.a * convert::(2.0_f64.powf(-s)); self.calc_a2(); self.calc_a4(); self.calc_a6(); let mb2 = self.a2.as_ref().unwrap() * convert::(2.0_f64.powf(-2.0 * s)); let mb4 = self.a4.as_ref().unwrap() * convert::(2.0.powf(-4.0 * s)); let mb6 = self.a6.as_ref().unwrap() * convert::(2.0.powf(-6.0 * s)); let u2 = &mb6 * (&mb6 * b[13].clone() + &mb4 * b[11].clone() + &mb2 * b[9].clone()); let u = &mb * (&u2 + &mb6 * b[7].clone() + &mb4 * b[5].clone() + &mb2 * b[3].clone() + &self.ident * b[1].clone()); let v2 = &mb6 * (&mb6 * b[12].clone() + &mb4 * b[10].clone() + &mb2 * b[8].clone()); let v = v2 + &mb6 * b[6].clone() + &mb4 * b[4].clone() + &mb2 * b[2].clone() + &self.ident * b[0].clone(); (u, v) } } /// Compute `n!` #[inline(always)] fn factorial(n: usize) -> u128 { match FACTORIAL.get(n) { Some(f) => *f, None => panic!("{}! is greater than u128::MAX", n), } } /// Compute the 1-norm of a non-negative integer power of a non-negative matrix. fn onenorm_matrix_power_nonm(a: &OMatrix, p: usize) -> T where T: RealField, D: Dim, DefaultAllocator: Allocator + Allocator, { let nrows = a.shape_generic().0; let mut v = crate::OVector::::repeat_generic(nrows, Const::<1>, convert(1.0)); let m = a.transpose(); for _ in 0..p { v = &m * v; } v.max() } fn ell(a: &OMatrix, m: usize) -> u64 where T: ComplexField, D: Dim, DefaultAllocator: Allocator + Allocator + Allocator + Allocator, { let a_abs = a.map(|x| x.abs()); let a_abs_onenorm = onenorm_matrix_power_nonm(&a_abs, 2 * m + 1); if a_abs_onenorm == ::RealField::zero() { return 0; } // 2m choose m = (2m)!/(m! * (2m-m)!) = (2m)!/((m!)^2) let m_factorial = factorial(m); let choose_2m_m = factorial(2 * m) / (m_factorial * m_factorial); let abs_c_recip = choose_2m_m * factorial(2 * m + 1); let alpha = a_abs_onenorm / one_norm(a); let alpha: f64 = try_convert::<_, f64>(alpha).unwrap() / abs_c_recip as f64; let u = 2_f64.powf(-53.0); let log2_alpha_div_u = (alpha / u).log2(); let value = (log2_alpha_div_u / (2.0 * m as f64)).ceil(); if value > 0.0 { value as u64 } else { 0 } } fn solve_p_q(u: OMatrix, v: OMatrix) -> OMatrix where T: ComplexField, D: DimMin, DefaultAllocator: Allocator + Allocator>, { let p = &u + &v; let q = &v - &u; q.lu().solve(&p).unwrap() } fn one_norm(m: &OMatrix) -> T::RealField where T: ComplexField, D: Dim, DefaultAllocator: Allocator, { let mut max = ::RealField::zero(); for i in 0..m.ncols() { let col = m.column(i); max = max.max( col.iter() .fold(::RealField::zero(), |a, b| { a + b.clone().abs() }), ); } max } impl OMatrix where D: DimMin, DefaultAllocator: Allocator + Allocator> + Allocator + Allocator + Allocator, { /// Computes exponential of this matrix #[must_use] pub fn exp(&self) -> Self { // Simple case if self.nrows() == 1 { return self.map(|v| v.exp()); } let mut helper = ExpmPadeHelper::new(self.clone(), true); let eta_1 = T::RealField::max(helper.d4_loose(), helper.d6_loose()); if eta_1 < convert(1.495_585_217_958_292e-2) && ell(&helper.a, 3) == 0 { let (u, v) = helper.pade3(); return solve_p_q(u, v); } let eta_2 = T::RealField::max(helper.d4_tight(), helper.d6_loose()); if eta_2 < convert(2.539_398_330_063_23e-1) && ell(&helper.a, 5) == 0 { let (u, v) = helper.pade5(); return solve_p_q(u, v); } let eta_3 = T::RealField::max(helper.d6_tight(), helper.d8_loose()); if eta_3 < convert(9.504_178_996_162_932e-1) && ell(&helper.a, 7) == 0 { let (u, v) = helper.pade7(); return solve_p_q(u, v); } if eta_3 < convert(2.097_847_961_257_068e0) && ell(&helper.a, 9) == 0 { let (u, v) = helper.pade9(); return solve_p_q(u, v); } let eta_4 = T::RealField::max(helper.d8_loose(), helper.d10_loose()); let eta_5 = T::RealField::min(eta_3, eta_4); let theta_13 = convert(4.25); let mut s = if eta_5 == T::RealField::zero() { 0 } else { let l2 = try_convert::<_, f64>((eta_5 / theta_13).log2().ceil()).unwrap(); if l2 < 0.0 { 0 } else { l2 as u64 } }; s += ell( &(&helper.a * convert::(2.0_f64.powf(-(s as f64)))), 13, ); let (u, v) = helper.pade13_scaled(s); let mut x = solve_p_q(u, v); for _ in 0..s { x = &x * &x; } x } } #[cfg(test)] mod tests { #[test] #[allow(clippy::float_cmp)] fn one_norm() { use crate::Matrix3; let m = Matrix3::new(-3.0, 5.0, 7.0, 2.0, 6.0, 4.0, 0.0, 2.0, 8.0); assert_eq!(super::one_norm(&m), 19.0); } } nalgebra-0.33.0/src/linalg/full_piv_lu.rs000064400000000000000000000206610072674642500164420ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, Matrix, OMatrix}; use crate::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::dimension::{Dim, DimMin, DimMinimum}; use crate::storage::{Storage, StorageMut}; use simba::scalar::ComplexField; use crate::linalg::lu; use crate::linalg::PermutationSequence; /// LU decomposition with full row and column pivoting. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Serialize, PermutationSequence>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Deserialize<'de>, PermutationSequence>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct FullPivLU, C: Dim> where DefaultAllocator: Allocator + Allocator>, { lu: OMatrix, p: PermutationSequence>, q: PermutationSequence>, } impl, C: Dim> Copy for FullPivLU where DefaultAllocator: Allocator + Allocator>, OMatrix: Copy, PermutationSequence>: Copy, { } impl, C: Dim> FullPivLU where DefaultAllocator: Allocator + Allocator>, { /// Computes the LU decomposition with full pivoting of `matrix`. /// /// This effectively computes `P, L, U, Q` such that `P * matrix * Q = LU`. pub fn new(mut matrix: OMatrix) -> Self { let (nrows, ncols) = matrix.shape_generic(); let min_nrows_ncols = nrows.min(ncols); let mut p = PermutationSequence::identity_generic(min_nrows_ncols); let mut q = PermutationSequence::identity_generic(min_nrows_ncols); if min_nrows_ncols.value() == 0 { return Self { lu: matrix, p, q }; } for i in 0..min_nrows_ncols.value() { let piv = matrix.view_range(i.., i..).icamax_full(); let row_piv = piv.0 + i; let col_piv = piv.1 + i; let diag = matrix[(row_piv, col_piv)].clone(); if diag.is_zero() { // The remaining of the matrix is zero. break; } matrix.swap_columns(i, col_piv); q.append_permutation(i, col_piv); if row_piv != i { p.append_permutation(i, row_piv); matrix.columns_range_mut(..i).swap_rows(i, row_piv); lu::gauss_step_swap(&mut matrix, diag, i, row_piv); } else { lu::gauss_step(&mut matrix, diag, i); } } Self { lu: matrix, p, q } } #[doc(hidden)] pub fn lu_internal(&self) -> &OMatrix { &self.lu } /// The lower triangular matrix of this decomposition. #[inline] #[must_use] pub fn l(&self) -> OMatrix> where DefaultAllocator: Allocator>, { let (nrows, ncols) = self.lu.shape_generic(); let mut m = self.lu.columns_generic(0, nrows.min(ncols)).into_owned(); m.fill_upper_triangle(T::zero(), 1); m.fill_diagonal(T::one()); m } /// The upper triangular matrix of this decomposition. #[inline] #[must_use] pub fn u(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, { let (nrows, ncols) = self.lu.shape_generic(); self.lu.rows_generic(0, nrows.min(ncols)).upper_triangle() } /// The row permutations of this decomposition. #[inline] #[must_use] pub fn p(&self) -> &PermutationSequence> { &self.p } /// The column permutations of this decomposition. #[inline] #[must_use] pub fn q(&self) -> &PermutationSequence> { &self.q } /// The two matrices of this decomposition and the row and column permutations: `(P, L, U, Q)`. #[inline] pub fn unpack( self, ) -> ( PermutationSequence>, OMatrix>, OMatrix, C>, PermutationSequence>, ) where DefaultAllocator: Allocator> + Allocator, C>, { // Use reallocation for either l or u. let l = self.l(); let u = self.u(); let p = self.p; let q = self.q; (p, l, u, q) } } impl> FullPivLU where DefaultAllocator: Allocator + Allocator, { /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if the decomposed matrix is not invertible. #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, ) -> Option> where S2: Storage, ShapeConstraint: SameNumberOfRows, DefaultAllocator: Allocator, { let mut res = b.clone_owned(); if self.solve_mut(&mut res) { Some(res) } else { None } } /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// If the decomposed matrix is not invertible, this returns `false` and its input `b` may /// be overwritten with garbage. pub fn solve_mut(&self, b: &mut Matrix) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { assert_eq!( self.lu.nrows(), b.nrows(), "FullPivLU solve matrix dimension mismatch." ); assert!( self.lu.is_square(), "FullPivLU solve: unable to solve a non-square system." ); if self.is_invertible() { self.p.permute_rows(b); let _ = self.lu.solve_lower_triangular_with_diag_mut(b, T::one()); let _ = self.lu.solve_upper_triangular_mut(b); self.q.inv_permute_rows(b); true } else { false } } /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the decomposed matrix is not invertible. #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.lu.is_square(), "FullPivLU inverse: unable to compute the inverse of a non-square matrix." ); let (nrows, ncols) = self.lu.shape_generic(); let mut res = OMatrix::identity_generic(nrows, ncols); if self.solve_mut(&mut res) { Some(res) } else { None } } /// Indicates if the decomposed matrix is invertible. #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.lu.is_square(), "FullPivLU: unable to test the invertibility of a non-square matrix." ); let dim = self.lu.nrows(); !self.lu[(dim - 1, dim - 1)].is_zero() } /// Computes the determinant of the decomposed matrix. #[must_use] pub fn determinant(&self) -> T { assert!( self.lu.is_square(), "FullPivLU determinant: unable to compute the determinant of a non-square matrix." ); let dim = self.lu.nrows(); let mut res = self.lu[(dim - 1, dim - 1)].clone(); if !res.is_zero() { for i in 0..dim - 1 { res *= unsafe { self.lu.get_unchecked((i, i)).clone() }; } res * self.p.determinant() * self.q.determinant() } else { T::zero() } } } nalgebra-0.33.0/src/linalg/givens.rs000064400000000000000000000132760072674642500154210ustar 00000000000000//! Construction of givens rotations. use num::{One, Zero}; use simba::scalar::ComplexField; use crate::base::constraint::{DimEq, ShapeConstraint}; use crate::base::dimension::{Dim, U2}; use crate::base::storage::{Storage, StorageMut}; use crate::base::{Matrix, Vector}; /// A Givens rotation. #[derive(Debug, Clone, Copy)] pub struct GivensRotation { c: T::RealField, s: T, } // Matrix = UnitComplex * Matrix impl GivensRotation { /// The Givens rotation that does nothing. pub fn identity() -> Self { Self { c: T::RealField::one(), s: T::zero(), } } /// Initializes a Givens rotation from its components. /// /// The components are copies as-is. It is not checked whether they describe /// an actually valid Givens rotation. pub fn new_unchecked(c: T::RealField, s: T) -> Self { Self { c, s } } /// Initializes a Givens rotation from its non-normalized cosine an sine components. pub fn new(c: T, s: T) -> (Self, T) { Self::try_new(c, s, T::RealField::zero()) .unwrap_or_else(|| (GivensRotation::identity(), T::zero())) } /// Initializes a Givens rotation form its non-normalized cosine an sine components. pub fn try_new(c: T, s: T, eps: T::RealField) -> Option<(Self, T)> { let (mod0, sign0) = c.to_exp(); let denom = (mod0.clone() * mod0.clone() + s.clone().modulus_squared()).sqrt(); if denom > eps { let norm = sign0.scale(denom.clone()); let c = mod0 / denom; let s = s / norm.clone(); Some((Self { c, s }, norm)) } else { None } } /// Computes the rotation `R` required such that the `y` component of `R * v` is zero. /// /// Returns `None` if no rotation is needed (i.e. if `v.y == 0`). Otherwise, this returns the norm /// of `v` and the rotation `r` such that `R * v = [ |v|, 0.0 ]^t` where `|v|` is the norm of `v`. pub fn cancel_y>(v: &Vector) -> Option<(Self, T)> { if !v[1].is_zero() { let (mod0, sign0) = v[0].clone().to_exp(); let denom = (mod0.clone() * mod0.clone() + v[1].clone().modulus_squared()).sqrt(); let c = mod0 / denom.clone(); let s = -v[1].clone() / sign0.clone().scale(denom.clone()); let r = sign0.scale(denom); Some((Self { c, s }, r)) } else { None } } /// Computes the rotation `R` required such that the `x` component of `R * v` is zero. /// /// Returns `None` if no rotation is needed (i.e. if `v.x == 0`). Otherwise, this returns the norm /// of `v` and the rotation `r` such that `R * v = [ 0.0, |v| ]^t` where `|v|` is the norm of `v`. pub fn cancel_x>(v: &Vector) -> Option<(Self, T)> { if !v[0].is_zero() { let (mod1, sign1) = v[1].clone().to_exp(); let denom = (mod1.clone() * mod1.clone() + v[0].clone().modulus_squared()).sqrt(); let c = mod1 / denom.clone(); let s = (v[0].clone().conjugate() * sign1.clone()).unscale(denom.clone()); let r = sign1.scale(denom); Some((Self { c, s }, r)) } else { None } } /// The cos part of this rotation. #[must_use] pub fn c(&self) -> T::RealField { self.c.clone() } /// The sin part of this rotation. #[must_use] pub fn s(&self) -> T { self.s.clone() } /// The inverse of this givens rotation. #[must_use = "This function does not mutate self."] pub fn inverse(&self) -> Self { Self { c: self.c.clone(), s: -self.s.clone(), } } /// Performs the multiplication `rhs = self * rhs` in-place. pub fn rotate>( &self, rhs: &mut Matrix, ) where ShapeConstraint: DimEq, { assert_eq!( rhs.nrows(), 2, "Unit complex rotation: the input matrix must have exactly two rows." ); let s = self.s.clone(); let c = self.c.clone(); for j in 0..rhs.ncols() { unsafe { let a = rhs.get_unchecked((0, j)).clone(); let b = rhs.get_unchecked((1, j)).clone(); *rhs.get_unchecked_mut((0, j)) = a.clone().scale(c.clone()) - s.clone().conjugate() * b.clone(); *rhs.get_unchecked_mut((1, j)) = s.clone() * a + b.scale(c.clone()); } } } /// Performs the multiplication `lhs = lhs * self` in-place. pub fn rotate_rows>( &self, lhs: &mut Matrix, ) where ShapeConstraint: DimEq, { assert_eq!( lhs.ncols(), 2, "Unit complex rotation: the input matrix must have exactly two columns." ); let s = self.s.clone(); let c = self.c.clone(); // TODO: can we optimize that to iterate on one column at a time ? for j in 0..lhs.nrows() { unsafe { let a = lhs.get_unchecked((j, 0)).clone(); let b = lhs.get_unchecked((j, 1)).clone(); *lhs.get_unchecked_mut((j, 0)) = a.clone().scale(c.clone()) + s.clone() * b.clone(); *lhs.get_unchecked_mut((j, 1)) = -s.clone().conjugate() * a + b.scale(c.clone()); } } } } nalgebra-0.33.0/src/linalg/hessenberg.rs000064400000000000000000000120010072674642500162340ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, OMatrix, OVector}; use crate::dimension::{Const, DimDiff, DimSub, U1}; use simba::scalar::ComplexField; use crate::linalg::householder; use crate::Matrix; use std::mem::MaybeUninit; /// Hessenberg decomposition of a general matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Serialize, OVector>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Deserialize<'de>, OVector>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct Hessenberg> where DefaultAllocator: Allocator + Allocator>, { hess: OMatrix, subdiag: OVector>, } impl> Copy for Hessenberg where DefaultAllocator: Allocator + Allocator>, OMatrix: Copy, OVector>: Copy, { } impl> Hessenberg where DefaultAllocator: Allocator + Allocator + Allocator>, { /// Computes the Hessenberg decomposition using householder reflections. pub fn new(hess: OMatrix) -> Self { let mut work = Matrix::zeros_generic(hess.shape_generic().0, Const::<1>); Self::new_with_workspace(hess, &mut work) } /// Computes the Hessenberg decomposition using householder reflections. /// /// The workspace containing `D` elements must be provided but its content does not have to be /// initialized. pub fn new_with_workspace(mut hess: OMatrix, work: &mut OVector) -> Self { assert!( hess.is_square(), "Cannot compute the hessenberg decomposition of a non-square matrix." ); let dim = hess.shape_generic().0; assert!( dim.value() != 0, "Cannot compute the hessenberg decomposition of an empty matrix." ); assert_eq!( dim.value(), work.len(), "Hessenberg: invalid workspace size." ); if dim.value() == 0 { return Hessenberg { hess, subdiag: Matrix::zeros_generic(dim.sub(Const::<1>), Const::<1>), }; } let mut subdiag = Matrix::uninit(dim.sub(Const::<1>), Const::<1>); for ite in 0..dim.value() - 1 { subdiag[ite] = MaybeUninit::new(householder::clear_column_unchecked( &mut hess, ite, 1, Some(work), )); } // Safety: subdiag is now fully initialized. let subdiag = unsafe { subdiag.assume_init() }; Hessenberg { hess, subdiag } } /// Retrieves `(q, h)` with `q` the orthogonal matrix of this decomposition and `h` the /// hessenberg matrix. #[inline] pub fn unpack(self) -> (OMatrix, OMatrix) { let q = self.q(); (q, self.unpack_h()) } /// Retrieves the upper trapezoidal submatrix `H` of this decomposition. #[inline] pub fn unpack_h(mut self) -> OMatrix { let dim = self.hess.nrows(); self.hess.fill_lower_triangle(T::zero(), 2); self.hess .view_mut((1, 0), (dim - 1, dim - 1)) .set_partial_diagonal( self.subdiag .iter() .map(|e| T::from_real(e.clone().modulus())), ); self.hess } // TODO: add a h that moves out of self. /// Retrieves the upper trapezoidal submatrix `H` of this decomposition. /// /// This is less efficient than `.unpack_h()` as it allocates a new matrix. #[inline] #[must_use] pub fn h(&self) -> OMatrix { let dim = self.hess.nrows(); let mut res = self.hess.clone(); res.fill_lower_triangle(T::zero(), 2); res.view_mut((1, 0), (dim - 1, dim - 1)) .set_partial_diagonal( self.subdiag .iter() .map(|e| T::from_real(e.clone().modulus())), ); res } /// Computes the orthogonal matrix `Q` of this decomposition. #[must_use] pub fn q(&self) -> OMatrix { householder::assemble_q(&self.hess, self.subdiag.as_slice()) } #[doc(hidden)] pub fn hess_internal(&self) -> &OMatrix { &self.hess } } nalgebra-0.33.0/src/linalg/householder.rs000064400000000000000000000124370072674642500164450ustar 00000000000000//! Construction of householder elementary reflections. use crate::allocator::Allocator; use crate::base::{DefaultAllocator, OMatrix, OVector, Unit, Vector}; use crate::dimension::Dim; use crate::storage::StorageMut; use num::Zero; use simba::scalar::ComplexField; use crate::geometry::Reflection; /// Replaces `column` by the axis of the householder reflection that transforms `column` into /// `(+/-|column|, 0, ..., 0)`. /// /// The unit-length axis is output to `column`. Returns what would be the first component of /// `column` after reflection and `false` if no reflection was necessary. #[doc(hidden)] #[inline(always)] pub fn reflection_axis_mut>( column: &mut Vector, ) -> (T, bool) { let reflection_sq_norm = column.norm_squared(); let reflection_norm = reflection_sq_norm.clone().sqrt(); let factor; let signed_norm; unsafe { let (modulus, sign) = column.vget_unchecked(0).clone().to_exp(); signed_norm = sign.scale(reflection_norm.clone()); factor = (reflection_sq_norm + modulus * reflection_norm) * crate::convert(2.0); *column.vget_unchecked_mut(0) += signed_norm.clone(); }; 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. (signed_norm, false) } } /// Uses an householder reflection to zero out the `icol`-th column, starting with the `shift + 1`-th /// subdiagonal element. /// /// Returns the signed norm of the column. #[doc(hidden)] #[must_use] pub fn clear_column_unchecked( matrix: &mut OMatrix, icol: usize, shift: usize, bilateral: Option<&mut OVector>, ) -> T where DefaultAllocator: Allocator + Allocator, { let (mut left, mut right) = matrix.columns_range_pair_mut(icol, icol + 1..); let mut axis = left.rows_range_mut(icol + shift..); let (reflection_norm, not_zero) = reflection_axis_mut(&mut axis); if not_zero { let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); let sign = reflection_norm.clone().signum(); if let Some(work) = bilateral { refl.reflect_rows_with_sign(&mut right, work, sign.clone()); } refl.reflect_with_sign(&mut right.rows_range_mut(icol + shift..), sign.conjugate()); } reflection_norm } /// Uses an householder reflection to zero out the `irow`-th row, ending before the `shift + 1`-th /// superdiagonal element. /// /// Returns the signed norm of the column. #[doc(hidden)] #[must_use] pub fn clear_row_unchecked( matrix: &mut OMatrix, axis_packed: &mut OVector, work: &mut OVector, irow: usize, shift: usize, ) -> T where DefaultAllocator: Allocator + Allocator + Allocator, { let (mut top, mut bottom) = matrix.rows_range_pair_mut(irow, irow + 1..); let mut axis = axis_packed.rows_range_mut(irow + shift..); axis.tr_copy_from(&top.columns_range(irow + shift..)); let (reflection_norm, not_zero) = reflection_axis_mut(&mut axis); axis.conjugate_mut(); // So that reflect_rows actually cancels the first row. if not_zero { let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); refl.reflect_rows_with_sign( &mut bottom.columns_range_mut(irow + shift..), &mut work.rows_range_mut(irow + 1..), reflection_norm.clone().signum().conjugate(), ); top.columns_range_mut(irow + shift..) .tr_copy_from(refl.axis()); } else { top.columns_range_mut(irow + shift..).tr_copy_from(&axis); } reflection_norm } /// Computes the orthogonal transformation described by the elementary reflector axii stored on /// the lower-diagonal element of the given matrix. /// matrices. #[doc(hidden)] pub fn assemble_q(m: &OMatrix, signs: &[T]) -> OMatrix where DefaultAllocator: Allocator, { assert!(m.is_square()); let dim = m.shape_generic().0; // NOTE: we could build the identity matrix and call p_mult on it. // Instead we don't so that we take in account the matrix sparseness. let mut res = OMatrix::identity_generic(dim, dim); for i in (0..dim.value() - 1).rev() { let axis = m.view_range(i + 1.., i); let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); let mut res_rows = res.view_range_mut(i + 1.., i..); refl.reflect_with_sign(&mut res_rows, signs[i].clone().signum()); } res } nalgebra-0.33.0/src/linalg/inverse.rs000064400000000000000000000272150072674642500155770ustar 00000000000000use simba::scalar::ComplexField; use crate::base::allocator::Allocator; use crate::base::dimension::Dim; use crate::base::storage::{Storage, StorageMut}; use crate::base::{DefaultAllocator, OMatrix, SquareMatrix}; use crate::linalg::lu; impl> SquareMatrix { /// Attempts to invert this square matrix. /// /// # Panics /// /// Panics if `self` isn’t a square matrix. #[inline] #[must_use = "Did you mean to use try_inverse_mut()?"] pub fn try_inverse(self) -> Option> where DefaultAllocator: Allocator, { let mut me = self.into_owned(); if me.try_inverse_mut() { Some(me) } else { None } } } impl> SquareMatrix { /// Attempts to invert this square matrix in-place. Returns `false` and leaves `self` untouched if /// inversion fails. /// /// # Panics /// /// Panics if `self` isn’t a square matrix. #[inline] pub fn try_inverse_mut(&mut self) -> bool where DefaultAllocator: Allocator, { assert!(self.is_square(), "Unable to invert a non-square matrix."); let dim = self.shape().0; unsafe { match dim { 0 => true, 1 => { let determinant = self.get_unchecked((0, 0)).clone(); if determinant.is_zero() { false } else { *self.get_unchecked_mut((0, 0)) = T::one() / determinant; true } } 2 => { let m11 = self.get_unchecked((0, 0)).clone(); let m12 = self.get_unchecked((0, 1)).clone(); let m21 = self.get_unchecked((1, 0)).clone(); let m22 = self.get_unchecked((1, 1)).clone(); let determinant = m11.clone() * m22.clone() - m21.clone() * m12.clone(); if determinant.is_zero() { false } else { *self.get_unchecked_mut((0, 0)) = m22 / determinant.clone(); *self.get_unchecked_mut((0, 1)) = -m12 / determinant.clone(); *self.get_unchecked_mut((1, 0)) = -m21 / determinant.clone(); *self.get_unchecked_mut((1, 1)) = m11 / determinant; true } } 3 => { let m11 = self.get_unchecked((0, 0)).clone(); let m12 = self.get_unchecked((0, 1)).clone(); let m13 = self.get_unchecked((0, 2)).clone(); let m21 = self.get_unchecked((1, 0)).clone(); let m22 = self.get_unchecked((1, 1)).clone(); let m23 = self.get_unchecked((1, 2)).clone(); let m31 = self.get_unchecked((2, 0)).clone(); let m32 = self.get_unchecked((2, 1)).clone(); let m33 = self.get_unchecked((2, 2)).clone(); let minor_m12_m23 = m22.clone() * m33.clone() - m32.clone() * m23.clone(); let minor_m11_m23 = m21.clone() * m33.clone() - m31.clone() * m23.clone(); let minor_m11_m22 = m21.clone() * m32.clone() - m31.clone() * m22.clone(); let determinant = m11.clone() * minor_m12_m23.clone() - m12.clone() * minor_m11_m23.clone() + m13.clone() * minor_m11_m22.clone(); if determinant.is_zero() { false } else { *self.get_unchecked_mut((0, 0)) = minor_m12_m23 / determinant.clone(); *self.get_unchecked_mut((0, 1)) = (m13.clone() * m32.clone() - m33.clone() * m12.clone()) / determinant.clone(); *self.get_unchecked_mut((0, 2)) = (m12.clone() * m23.clone() - m22.clone() * m13.clone()) / determinant.clone(); *self.get_unchecked_mut((1, 0)) = -minor_m11_m23 / determinant.clone(); *self.get_unchecked_mut((1, 1)) = (m11.clone() * m33 - m31.clone() * m13.clone()) / determinant.clone(); *self.get_unchecked_mut((1, 2)) = (m13 * m21.clone() - m23 * m11.clone()) / determinant.clone(); *self.get_unchecked_mut((2, 0)) = minor_m11_m22 / determinant.clone(); *self.get_unchecked_mut((2, 1)) = (m12.clone() * m31 - m32 * m11.clone()) / determinant.clone(); *self.get_unchecked_mut((2, 2)) = (m11 * m22 - m21 * m12) / determinant; true } } 4 => { let oself = self.clone_owned(); do_inverse4(&oself, self) } _ => { let oself = self.clone_owned(); lu::try_invert_to(oself, self) } } } } } // NOTE: this is an extremely efficient, loop-unrolled matrix inverse from MESA (MIT licensed). fn do_inverse4>( m: &OMatrix, out: &mut SquareMatrix, ) -> bool where DefaultAllocator: Allocator, { let m = m.as_slice(); let cofactor00 = m[5].clone() * m[10].clone() * m[15].clone() - m[5].clone() * m[11].clone() * m[14].clone() - m[9].clone() * m[6].clone() * m[15].clone() + m[9].clone() * m[7].clone() * m[14].clone() + m[13].clone() * m[6].clone() * m[11].clone() - m[13].clone() * m[7].clone() * m[10].clone(); let cofactor01 = -m[4].clone() * m[10].clone() * m[15].clone() + m[4].clone() * m[11].clone() * m[14].clone() + m[8].clone() * m[6].clone() * m[15].clone() - m[8].clone() * m[7].clone() * m[14].clone() - m[12].clone() * m[6].clone() * m[11].clone() + m[12].clone() * m[7].clone() * m[10].clone(); let cofactor02 = m[4].clone() * m[9].clone() * m[15].clone() - m[4].clone() * m[11].clone() * m[13].clone() - m[8].clone() * m[5].clone() * m[15].clone() + m[8].clone() * m[7].clone() * m[13].clone() + m[12].clone() * m[5].clone() * m[11].clone() - m[12].clone() * m[7].clone() * m[9].clone(); let cofactor03 = -m[4].clone() * m[9].clone() * m[14].clone() + m[4].clone() * m[10].clone() * m[13].clone() + m[8].clone() * m[5].clone() * m[14].clone() - m[8].clone() * m[6].clone() * m[13].clone() - m[12].clone() * m[5].clone() * m[10].clone() + m[12].clone() * m[6].clone() * m[9].clone(); let det = m[0].clone() * cofactor00.clone() + m[1].clone() * cofactor01.clone() + m[2].clone() * cofactor02.clone() + m[3].clone() * cofactor03.clone(); if det.is_zero() { return false; } out[(0, 0)] = cofactor00; out[(1, 0)] = -m[1].clone() * m[10].clone() * m[15].clone() + m[1].clone() * m[11].clone() * m[14].clone() + m[9].clone() * m[2].clone() * m[15].clone() - m[9].clone() * m[3].clone() * m[14].clone() - m[13].clone() * m[2].clone() * m[11].clone() + m[13].clone() * m[3].clone() * m[10].clone(); out[(2, 0)] = m[1].clone() * m[6].clone() * m[15].clone() - m[1].clone() * m[7].clone() * m[14].clone() - m[5].clone() * m[2].clone() * m[15].clone() + m[5].clone() * m[3].clone() * m[14].clone() + m[13].clone() * m[2].clone() * m[7].clone() - m[13].clone() * m[3].clone() * m[6].clone(); out[(3, 0)] = -m[1].clone() * m[6].clone() * m[11].clone() + m[1].clone() * m[7].clone() * m[10].clone() + m[5].clone() * m[2].clone() * m[11].clone() - m[5].clone() * m[3].clone() * m[10].clone() - m[9].clone() * m[2].clone() * m[7].clone() + m[9].clone() * m[3].clone() * m[6].clone(); out[(0, 1)] = cofactor01; out[(1, 1)] = m[0].clone() * m[10].clone() * m[15].clone() - m[0].clone() * m[11].clone() * m[14].clone() - m[8].clone() * m[2].clone() * m[15].clone() + m[8].clone() * m[3].clone() * m[14].clone() + m[12].clone() * m[2].clone() * m[11].clone() - m[12].clone() * m[3].clone() * m[10].clone(); out[(2, 1)] = -m[0].clone() * m[6].clone() * m[15].clone() + m[0].clone() * m[7].clone() * m[14].clone() + m[4].clone() * m[2].clone() * m[15].clone() - m[4].clone() * m[3].clone() * m[14].clone() - m[12].clone() * m[2].clone() * m[7].clone() + m[12].clone() * m[3].clone() * m[6].clone(); out[(3, 1)] = m[0].clone() * m[6].clone() * m[11].clone() - m[0].clone() * m[7].clone() * m[10].clone() - m[4].clone() * m[2].clone() * m[11].clone() + m[4].clone() * m[3].clone() * m[10].clone() + m[8].clone() * m[2].clone() * m[7].clone() - m[8].clone() * m[3].clone() * m[6].clone(); out[(0, 2)] = cofactor02; out[(1, 2)] = -m[0].clone() * m[9].clone() * m[15].clone() + m[0].clone() * m[11].clone() * m[13].clone() + m[8].clone() * m[1].clone() * m[15].clone() - m[8].clone() * m[3].clone() * m[13].clone() - m[12].clone() * m[1].clone() * m[11].clone() + m[12].clone() * m[3].clone() * m[9].clone(); out[(2, 2)] = m[0].clone() * m[5].clone() * m[15].clone() - m[0].clone() * m[7].clone() * m[13].clone() - m[4].clone() * m[1].clone() * m[15].clone() + m[4].clone() * m[3].clone() * m[13].clone() + m[12].clone() * m[1].clone() * m[7].clone() - m[12].clone() * m[3].clone() * m[5].clone(); out[(0, 3)] = cofactor03; out[(3, 2)] = -m[0].clone() * m[5].clone() * m[11].clone() + m[0].clone() * m[7].clone() * m[9].clone() + m[4].clone() * m[1].clone() * m[11].clone() - m[4].clone() * m[3].clone() * m[9].clone() - m[8].clone() * m[1].clone() * m[7].clone() + m[8].clone() * m[3].clone() * m[5].clone(); out[(1, 3)] = m[0].clone() * m[9].clone() * m[14].clone() - m[0].clone() * m[10].clone() * m[13].clone() - m[8].clone() * m[1].clone() * m[14].clone() + m[8].clone() * m[2].clone() * m[13].clone() + m[12].clone() * m[1].clone() * m[10].clone() - m[12].clone() * m[2].clone() * m[9].clone(); out[(2, 3)] = -m[0].clone() * m[5].clone() * m[14].clone() + m[0].clone() * m[6].clone() * m[13].clone() + m[4].clone() * m[1].clone() * m[14].clone() - m[4].clone() * m[2].clone() * m[13].clone() - m[12].clone() * m[1].clone() * m[6].clone() + m[12].clone() * m[2].clone() * m[5].clone(); out[(3, 3)] = m[0].clone() * m[5].clone() * m[10].clone() - m[0].clone() * m[6].clone() * m[9].clone() - m[4].clone() * m[1].clone() * m[10].clone() + m[4].clone() * m[2].clone() * m[9].clone() + m[8].clone() * m[1].clone() * m[6].clone() - m[8].clone() * m[2].clone() * m[5].clone(); let inv_det = T::one() / det; for j in 0..4 { for i in 0..4 { out[(i, j)] *= inv_det.clone(); } } true } nalgebra-0.33.0/src/linalg/lu.rs000064400000000000000000000300260072674642500145360ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::{Allocator, Reallocator}; use crate::base::{DefaultAllocator, Matrix, OMatrix, Scalar}; use crate::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::dimension::{Dim, DimMin, DimMinimum}; use crate::storage::{Storage, StorageMut}; use simba::scalar::{ComplexField, Field}; use std::mem; use crate::linalg::PermutationSequence; /// LU decomposition with partial (row) pivoting. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Serialize, PermutationSequence>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Deserialize<'de>, PermutationSequence>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct LU, C: Dim> where DefaultAllocator: Allocator + Allocator>, { lu: OMatrix, p: PermutationSequence>, } impl, C: Dim> Copy for LU where DefaultAllocator: Allocator + Allocator>, OMatrix: Copy, PermutationSequence>: Copy, { } /// Performs a LU decomposition to overwrite `out` with the inverse of `matrix`. /// /// If `matrix` is not invertible, `false` is returned and `out` may contain invalid data. pub fn try_invert_to( mut matrix: OMatrix, out: &mut Matrix, ) -> bool where S: StorageMut, DefaultAllocator: Allocator, { assert!( matrix.is_square(), "LU inversion: unable to invert a rectangular matrix." ); let dim = matrix.nrows(); out.fill_with_identity(); for i in 0..dim { let piv = matrix.view_range(i.., i).icamax() + i; let diag = matrix[(piv, i)].clone(); if diag.is_zero() { return false; } if piv != i { out.swap_rows(i, piv); matrix.columns_range_mut(..i).swap_rows(i, piv); gauss_step_swap(&mut matrix, diag, i, piv); } else { gauss_step(&mut matrix, diag, i); } } let _ = matrix.solve_lower_triangular_with_diag_mut(out, T::one()); matrix.solve_upper_triangular_mut(out) } impl, C: Dim> LU where DefaultAllocator: Allocator + Allocator>, { /// Computes the LU decomposition with partial (row) pivoting of `matrix`. pub fn new(mut matrix: OMatrix) -> Self { let (nrows, ncols) = matrix.shape_generic(); let min_nrows_ncols = nrows.min(ncols); let mut p = PermutationSequence::identity_generic(min_nrows_ncols); if min_nrows_ncols.value() == 0 { return LU { lu: matrix, p }; } for i in 0..min_nrows_ncols.value() { let piv = matrix.view_range(i.., i).icamax() + i; let diag = matrix[(piv, i)].clone(); if diag.is_zero() { // No non-zero entries on this column. continue; } if piv != i { p.append_permutation(i, piv); matrix.columns_range_mut(..i).swap_rows(i, piv); gauss_step_swap(&mut matrix, diag, i, piv); } else { gauss_step(&mut matrix, diag, i); } } LU { lu: matrix, p } } #[doc(hidden)] pub fn lu_internal(&self) -> &OMatrix { &self.lu } /// The lower triangular matrix of this decomposition. #[inline] #[must_use] pub fn l(&self) -> OMatrix> where DefaultAllocator: Allocator>, { let (nrows, ncols) = self.lu.shape_generic(); let mut m = self.lu.columns_generic(0, nrows.min(ncols)).into_owned(); m.fill_upper_triangle(T::zero(), 1); m.fill_diagonal(T::one()); m } /// The lower triangular matrix of this decomposition. fn l_unpack_with_p( self, ) -> ( OMatrix>, PermutationSequence>, ) where DefaultAllocator: Reallocator>, { let (nrows, ncols) = self.lu.shape_generic(); let mut m = self.lu.resize_generic(nrows, nrows.min(ncols), T::zero()); m.fill_upper_triangle(T::zero(), 1); m.fill_diagonal(T::one()); (m, self.p) } /// The lower triangular matrix of this decomposition. #[inline] pub fn l_unpack(self) -> OMatrix> where DefaultAllocator: Reallocator>, { let (nrows, ncols) = self.lu.shape_generic(); let mut m = self.lu.resize_generic(nrows, nrows.min(ncols), T::zero()); m.fill_upper_triangle(T::zero(), 1); m.fill_diagonal(T::one()); m } /// The upper triangular matrix of this decomposition. #[inline] #[must_use] pub fn u(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, { let (nrows, ncols) = self.lu.shape_generic(); self.lu.rows_generic(0, nrows.min(ncols)).upper_triangle() } /// The row permutations of this decomposition. #[inline] #[must_use] pub fn p(&self) -> &PermutationSequence> { &self.p } /// The row permutations and two triangular matrices of this decomposition: `(P, L, U)`. #[inline] pub fn unpack( self, ) -> ( PermutationSequence>, OMatrix>, OMatrix, C>, ) where DefaultAllocator: Allocator> + Allocator, C> + Reallocator>, { // Use reallocation for either l or u. let u = self.u(); let (l, p) = self.l_unpack_with_p(); (p, l, u) } } impl> LU where DefaultAllocator: Allocator + Allocator, { /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if `self` is not invertible. #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, ) -> Option> where S2: Storage, ShapeConstraint: SameNumberOfRows, DefaultAllocator: Allocator, { let mut res = b.clone_owned(); if self.solve_mut(&mut res) { Some(res) } else { None } } /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// If the decomposed matrix is not invertible, this returns `false` and its input `b` may /// be overwritten with garbage. pub fn solve_mut(&self, b: &mut Matrix) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { assert_eq!( self.lu.nrows(), b.nrows(), "LU solve matrix dimension mismatch." ); assert!( self.lu.is_square(), "LU solve: unable to solve a non-square system." ); self.p.permute_rows(b); let _ = self.lu.solve_lower_triangular_with_diag_mut(b, T::one()); self.lu.solve_upper_triangular_mut(b) } /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the matrix is not invertible. #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.lu.is_square(), "LU inverse: unable to compute the inverse of a non-square matrix." ); let (nrows, ncols) = self.lu.shape_generic(); let mut res = OMatrix::identity_generic(nrows, ncols); if self.try_inverse_to(&mut res) { Some(res) } else { None } } /// Computes the inverse of the decomposed matrix and outputs the result to `out`. /// /// If the decomposed matrix is not invertible, this returns `false` and `out` may be /// overwritten with garbage. pub fn try_inverse_to>(&self, out: &mut Matrix) -> bool { assert!( self.lu.is_square(), "LU inverse: unable to compute the inverse of a non-square matrix." ); assert!( self.lu.shape() == out.shape(), "LU inverse: mismatched output shape." ); out.fill_with_identity(); self.solve_mut(out) } /// Computes the determinant of the decomposed matrix. #[must_use] pub fn determinant(&self) -> T { let dim = self.lu.nrows(); assert!( self.lu.is_square(), "LU determinant: unable to compute the determinant of a non-square matrix." ); let mut res = T::one(); for i in 0..dim { res *= unsafe { self.lu.get_unchecked((i, i)).clone() }; } res * self.p.determinant() } /// Indicates if the decomposed matrix is invertible. #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.lu.is_square(), "LU: unable to test the invertibility of a non-square matrix." ); for i in 0..self.lu.nrows() { if self.lu[(i, i)].is_zero() { return false; } } true } } #[doc(hidden)] /// Executes one step of gaussian elimination on the i-th row and column of `matrix`. The diagonal /// element `matrix[(i, i)]` is provided as argument. pub fn gauss_step(matrix: &mut Matrix, diag: T, i: usize) where T: Scalar + Field, S: StorageMut, { let mut submat = matrix.view_range_mut(i.., i..); let inv_diag = T::one() / diag; let (mut coeffs, mut submat) = submat.columns_range_pair_mut(0, 1..); let mut coeffs = coeffs.rows_range_mut(1..); coeffs *= inv_diag; let (pivot_row, mut down) = submat.rows_range_pair_mut(0, 1..); for k in 0..pivot_row.ncols() { down.column_mut(k) .axpy(-pivot_row[k].clone(), &coeffs, T::one()); } } #[doc(hidden)] /// Swaps the rows `i` with the row `piv` and executes one step of gaussian elimination on the i-th /// row and column of `matrix`. The diagonal element `matrix[(i, i)]` is provided as argument. pub fn gauss_step_swap( matrix: &mut Matrix, diag: T, i: usize, piv: usize, ) where T: Scalar + Field, S: StorageMut, { let piv = piv - i; let mut submat = matrix.view_range_mut(i.., i..); let inv_diag = T::one() / diag; let (mut coeffs, mut submat) = submat.columns_range_pair_mut(0, 1..); coeffs.swap((0, 0), (piv, 0)); let mut coeffs = coeffs.rows_range_mut(1..); coeffs *= inv_diag; let (mut pivot_row, mut down) = submat.rows_range_pair_mut(0, 1..); for k in 0..pivot_row.ncols() { mem::swap(&mut pivot_row[k], &mut down[(piv - 1, k)]); down.column_mut(k) .axpy(-pivot_row[k].clone(), &coeffs, T::one()); } } nalgebra-0.33.0/src/linalg/mod.rs000064400000000000000000000022650072674642500147010ustar 00000000000000//! [Reexported at the root of this crate.] Factorization of real matrices. pub mod balancing; mod bidiagonal; mod cholesky; mod convolution; mod determinant; // TODO: this should not be needed. However, the exp uses // explicit float operations on `f32` and `f64`. We need to // get rid of these to allow exp to be used on a no-std context. mod col_piv_qr; mod decomposition; #[cfg(feature = "std")] mod exp; mod full_piv_lu; pub mod givens; mod hessenberg; pub mod householder; mod inverse; mod lu; mod permutation_sequence; mod pow; mod qr; mod schur; mod solve; mod svd; mod svd2; mod svd3; mod symmetric_eigen; mod symmetric_tridiagonal; mod udu; //// TODO: Not complete enough for publishing. //// This handles only cases where each eigenvalue has multiplicity one. // mod eigen; pub use self::bidiagonal::*; pub use self::cholesky::*; pub use self::col_piv_qr::*; pub use self::full_piv_lu::*; pub use self::hessenberg::*; pub use self::lu::*; pub use self::permutation_sequence::*; pub use self::qr::*; pub use self::schur::*; pub use self::svd::*; pub use self::symmetric_eigen::*; pub use self::symmetric_tridiagonal::*; pub use self::udu::*; nalgebra-0.33.0/src/linalg/permutation_sequence.rs000064400000000000000000000113630072674642500203600ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use num::One; use simba::scalar::ClosedNeg; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, Matrix, OVector, Scalar}; #[cfg(any(feature = "std", feature = "alloc"))] use crate::dimension::Dyn; use crate::dimension::{Const, Dim, DimName}; use crate::storage::StorageMut; /// A sequence of row or column permutations. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator, OVector<(usize, usize), D>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator, OVector<(usize, usize), D>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct PermutationSequence where DefaultAllocator: Allocator, { len: usize, ipiv: OVector<(usize, usize), D>, } impl Copy for PermutationSequence where DefaultAllocator: Allocator, OVector<(usize, usize), D>: Copy, { } impl PermutationSequence where DefaultAllocator: Allocator, { /// Creates a new statically-allocated sequence of `D` identity permutations. #[inline] pub fn identity() -> Self { Self::identity_generic(D::name()) } } #[cfg(any(feature = "std", feature = "alloc"))] impl PermutationSequence where DefaultAllocator: Allocator, { /// Creates a new dynamically-allocated sequence of `n` identity permutations. #[inline] pub fn identity(n: usize) -> Self { Self::identity_generic(Dyn(n)) } } impl PermutationSequence where DefaultAllocator: Allocator, { /// Creates a new sequence of D identity permutations. #[inline] pub fn identity_generic(dim: D) -> Self { Self { len: 0, // TODO: using a uninitialized matrix would save some computation, but // that loos difficult to setup with MaybeUninit. ipiv: Matrix::repeat_generic(dim, Const::<1>, (0, 0)), } } /// Adds the interchange of the row (or column) `i` with the row (or column) `i2` to this /// sequence of permutations. #[inline] pub fn append_permutation(&mut self, i: usize, i2: usize) { if i != i2 { assert!( self.len < self.ipiv.len(), "Maximum number of permutations exceeded." ); self.ipiv[self.len] = (i, i2); self.len += 1; } } /// Applies this sequence of permutations to the rows of `rhs`. #[inline] pub fn permute_rows(&self, rhs: &mut Matrix) where S2: StorageMut, { for i in self.ipiv.rows_range(..self.len).iter() { rhs.swap_rows(i.0, i.1) } } /// Applies this sequence of permutations in reverse to the rows of `rhs`. #[inline] pub fn inv_permute_rows(&self, rhs: &mut Matrix) where S2: StorageMut, { for i in 0..self.len { let (i1, i2) = self.ipiv[self.len - i - 1]; rhs.swap_rows(i1, i2) } } /// Applies this sequence of permutations to the columns of `rhs`. #[inline] pub fn permute_columns(&self, rhs: &mut Matrix) where S2: StorageMut, { for i in self.ipiv.rows_range(..self.len).iter() { rhs.swap_columns(i.0, i.1) } } /// Applies this sequence of permutations in reverse to the columns of `rhs`. #[inline] pub fn inv_permute_columns( &self, rhs: &mut Matrix, ) where S2: StorageMut, { for i in 0..self.len { let (i1, i2) = self.ipiv[self.len - i - 1]; rhs.swap_columns(i1, i2) } } /// The number of non-identity permutations applied by this sequence. #[must_use] pub fn len(&self) -> usize { self.len } /// Returns true if the permutation sequence contains no elements. #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// The determinant of the matrix corresponding to this permutation. #[inline] #[must_use] pub fn determinant(&self) -> T { if self.len % 2 == 0 { T::one() } else { -T::one() } } } nalgebra-0.33.0/src/linalg/pow.rs000064400000000000000000000042340072674642500147250ustar 00000000000000//! This module provides the matrix exponential (pow) function to square matrices. use crate::{ allocator::Allocator, storage::{Storage, StorageMut}, DefaultAllocator, DimMin, Matrix, OMatrix, Scalar, }; use num::{One, Zero}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; impl Matrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, D: DimMin, S: StorageMut, DefaultAllocator: Allocator + Allocator, { /// Raises this matrix to an integral power `exp` in-place. pub fn pow_mut(&mut self, mut exp: u32) { // A matrix raised to the zeroth power is just the identity. if exp == 0 { self.fill_with_identity(); } else if exp > 1 { // We use the buffer to hold the result of multiplier^2, thus avoiding // extra allocations. let mut x = self.clone_owned(); let mut workspace = self.clone_owned(); if exp % 2 == 0 { self.fill_with_identity(); } else { // Avoid an useless multiplication by the identity // if the exponent is odd. exp -= 1; } // Exponentiation by squares. loop { if exp % 2 == 1 { self.mul_to(&x, &mut workspace); self.copy_from(&workspace); } exp /= 2; if exp == 0 { break; } x.mul_to(&x, &mut workspace); x.copy_from(&workspace); } } } } impl> Matrix where T: Scalar + Zero + One + ClosedAddAssign + ClosedMulAssign, D: DimMin, S: StorageMut, DefaultAllocator: Allocator + Allocator, { /// Raise this matrix to an integral power `exp`. #[must_use] pub fn pow(&self, exp: u32) -> OMatrix { let mut result = self.clone_owned(); result.pow_mut(exp); result } } nalgebra-0.33.0/src/linalg/qr.rs000064400000000000000000000236410072674642500145450ustar 00000000000000use num::Zero; #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::{Allocator, Reallocator}; use crate::base::{DefaultAllocator, Matrix, OMatrix, OVector, Unit}; use crate::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::dimension::{Const, Dim, DimMin, DimMinimum}; use crate::storage::{Storage, StorageMut}; use simba::scalar::ComplexField; use crate::geometry::Reflection; use crate::linalg::householder; use std::mem::MaybeUninit; /// The QR decomposition of a general matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Serialize, OVector>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Deserialize<'de>, OVector>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct QR, C: Dim> where DefaultAllocator: Allocator + Allocator>, { qr: OMatrix, diag: OVector>, } impl, C: Dim> Copy for QR where DefaultAllocator: Allocator + Allocator>, OMatrix: Copy, OVector>: Copy, { } impl, C: Dim> QR where DefaultAllocator: Allocator + Allocator + Allocator>, { /// Computes the QR decomposition using householder reflections. pub fn new(mut matrix: OMatrix) -> Self { let (nrows, ncols) = matrix.shape_generic(); let min_nrows_ncols = nrows.min(ncols); if min_nrows_ncols.value() == 0 { return QR { qr: matrix, diag: Matrix::zeros_generic(min_nrows_ncols, Const::<1>), }; } let mut diag = Matrix::uninit(min_nrows_ncols, Const::<1>); for i in 0..min_nrows_ncols.value() { diag[i] = MaybeUninit::new(householder::clear_column_unchecked(&mut matrix, i, 0, None)); } // Safety: diag is now fully initialized. let diag = unsafe { diag.assume_init() }; QR { qr: matrix, diag } } /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. #[inline] #[must_use] pub fn r(&self) -> OMatrix, C> where DefaultAllocator: Allocator, C>, { let (nrows, ncols) = self.qr.shape_generic(); let mut res = self.qr.rows_generic(0, nrows.min(ncols)).upper_triangle(); res.set_partial_diagonal(self.diag.iter().map(|e| T::from_real(e.clone().modulus()))); res } /// Retrieves the upper trapezoidal submatrix `R` of this decomposition. /// /// This is usually faster than `r` but consumes `self`. #[inline] pub fn unpack_r(self) -> OMatrix, C> where DefaultAllocator: Reallocator, C>, { let (nrows, ncols) = self.qr.shape_generic(); let mut res = self.qr.resize_generic(nrows.min(ncols), ncols, T::zero()); res.fill_lower_triangle(T::zero(), 1); res.set_partial_diagonal(self.diag.iter().map(|e| T::from_real(e.clone().modulus()))); res } /// Computes the orthogonal matrix `Q` of this decomposition. #[must_use] pub fn q(&self) -> OMatrix> where DefaultAllocator: Allocator>, { let (nrows, ncols) = self.qr.shape_generic(); // NOTE: we could build the identity matrix and call q_mul on it. // Instead we don't so that we take in account the matrix sparseness. let mut res = Matrix::identity_generic(nrows, nrows.min(ncols)); let dim = self.diag.len(); for i in (0..dim).rev() { let axis = self.qr.view_range(i.., i); // TODO: sometimes, the axis might have a zero magnitude. let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); let mut res_rows = res.view_range_mut(i.., i..); refl.reflect_with_sign(&mut res_rows, self.diag[i].clone().signum()); } res } /// Unpacks this decomposition into its two matrix factors. pub fn unpack( self, ) -> ( OMatrix>, OMatrix, C>, ) where DimMinimum: DimMin>, DefaultAllocator: Allocator> + Reallocator, C>, { (self.q(), self.unpack_r()) } #[doc(hidden)] pub fn qr_internal(&self) -> &OMatrix { &self.qr } #[must_use] pub(crate) fn diag_internal(&self) -> &OVector> { &self.diag } /// Multiplies the provided matrix by the transpose of the `Q` matrix of this decomposition. pub fn q_tr_mul(&self, rhs: &mut Matrix) // TODO: do we need a static constraint on the number of rows of rhs? where S2: StorageMut, { let dim = self.diag.len(); for i in 0..dim { let axis = self.qr.view_range(i.., i); let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); let mut rhs_rows = rhs.rows_range_mut(i..); refl.reflect_with_sign(&mut rhs_rows, self.diag[i].clone().signum().conjugate()); } } } impl> QR where DefaultAllocator: Allocator + Allocator, { /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// Returns `None` if `self` is not invertible. #[must_use = "Did you mean to use solve_mut()?"] pub fn solve( &self, b: &Matrix, ) -> Option> where S2: Storage, ShapeConstraint: SameNumberOfRows, DefaultAllocator: Allocator, { let mut res = b.clone_owned(); if self.solve_mut(&mut res) { Some(res) } else { None } } /// Solves the linear system `self * x = b`, where `x` is the unknown to be determined. /// /// If the decomposed matrix is not invertible, this returns `false` and its input `b` is /// overwritten with garbage. pub fn solve_mut(&self, b: &mut Matrix) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { assert_eq!( self.qr.nrows(), b.nrows(), "QR solve matrix dimension mismatch." ); assert!( self.qr.is_square(), "QR solve: unable to solve a non-square system." ); self.q_tr_mul(b); self.solve_upper_triangular_mut(b) } // TODO: duplicate code from the `solve` module. fn solve_upper_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.qr.nrows(); for k in 0..b.ncols() { let mut b = b.column_mut(k); for i in (0..dim).rev() { let coeff; unsafe { let diag = self.diag.vget_unchecked(i).clone().modulus(); if diag.is_zero() { return false; } coeff = b.vget_unchecked(i).clone().unscale(diag); *b.vget_unchecked_mut(i) = coeff.clone(); } b.rows_range_mut(..i) .axpy(-coeff, &self.qr.view_range(..i, i), T::one()); } } true } /// Computes the inverse of the decomposed matrix. /// /// Returns `None` if the decomposed matrix is not invertible. #[must_use] pub fn try_inverse(&self) -> Option> { assert!( self.qr.is_square(), "QR inverse: unable to compute the inverse of a non-square matrix." ); // TODO: is there a less naive method ? let (nrows, ncols) = self.qr.shape_generic(); let mut res = OMatrix::identity_generic(nrows, ncols); if self.solve_mut(&mut res) { Some(res) } else { None } } /// Indicates if the decomposed matrix is invertible. #[must_use] pub fn is_invertible(&self) -> bool { assert!( self.qr.is_square(), "QR: unable to test the invertibility of a non-square matrix." ); for i in 0..self.diag.len() { if self.diag[i].is_zero() { return false; } } true } // /// Computes the determinant of the decomposed matrix. // pub fn determinant(&self) -> T { // let dim = self.qr.nrows(); // assert!(self.qr.is_square(), "QR determinant: unable to compute the determinant of a non-square matrix."); // let mut res = T::one(); // for i in 0 .. dim { // res *= unsafe { *self.diag.vget_unchecked(i) }; // } // res self.q_determinant() // } } nalgebra-0.33.0/src/linalg/schur.rs000064400000000000000000000465120072674642500152510ustar 00000000000000#![allow(clippy::suspicious_operation_groupings)] #[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use approx::AbsDiffEq; use num_complex::Complex as NumComplex; use simba::scalar::{ComplexField, RealField}; use std::cmp; use crate::allocator::Allocator; use crate::base::dimension::{Const, Dim, DimDiff, DimSub, Dyn, U1, U2}; use crate::base::storage::Storage; use crate::base::{DefaultAllocator, OMatrix, OVector, SquareMatrix, Unit, Vector2, Vector3}; use crate::geometry::Reflection; use crate::linalg::givens::GivensRotation; use crate::linalg::householder; use crate::linalg::Hessenberg; use crate::{Matrix, UninitVector}; use std::mem::MaybeUninit; /// Schur decomposition of a square matrix. /// /// If this is a real matrix, this will be a `RealField` Schur decomposition. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator, OMatrix: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator, OMatrix: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct Schur where DefaultAllocator: Allocator, { q: OMatrix, t: OMatrix, } impl Copy for Schur where DefaultAllocator: Allocator, OMatrix: Copy, { } impl Schur where D: DimSub, // For Hessenberg. DefaultAllocator: Allocator> + Allocator> + Allocator + Allocator, { /// Computes the Schur decomposition of a square matrix. pub fn new(m: OMatrix) -> Self { Self::try_new(m, T::RealField::default_epsilon(), 0).unwrap() } /// Attempts to compute the Schur decomposition of a square matrix. /// /// If only eigenvalues are needed, it is more efficient to call the matrix method /// `.eigenvalues()` instead. /// /// # Arguments /// /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_new(m: OMatrix, eps: T::RealField, max_niter: usize) -> Option { let mut work = Matrix::zeros_generic(m.shape_generic().0, Const::<1>); Self::do_decompose(m, &mut work, eps, max_niter, true) .map(|(q, t)| Schur { q: q.unwrap(), t }) } fn do_decompose( mut m: OMatrix, work: &mut OVector, eps: T::RealField, max_niter: usize, compute_q: bool, ) -> Option<(Option>, OMatrix)> { assert!( m.is_square(), "Unable to compute the eigenvectors and eigenvalues of a non-square matrix." ); let dim = m.shape_generic().0; // Specialization would make this easier. if dim.value() == 0 { let vecs = Some(OMatrix::from_element_generic(dim, dim, T::zero())); let vals = OMatrix::from_element_generic(dim, dim, T::zero()); return Some((vecs, vals)); } else if dim.value() == 1 { if compute_q { let q = OMatrix::from_element_generic(dim, dim, T::one()); return Some((Some(q), m)); } else { return Some((None, m)); } } else if dim.value() == 2 { return decompose_2x2(m, compute_q); } let amax_m = m.camax(); m.unscale_mut(amax_m.clone()); let hess = Hessenberg::new_with_workspace(m, work); let mut q; let mut t; if compute_q { // TODO: could we work without unpacking? Using only the internal representation of // hessenberg decomposition. let (vecs, vals) = hess.unpack(); q = Some(vecs); t = vals; } else { q = None; t = hess.unpack_h() } // Implicit double-shift QR method. let mut niter = 0; let (mut start, mut end) = Self::delimit_subproblem(&mut t, eps.clone(), dim.value() - 1); while end != start { let subdim = end - start + 1; if subdim > 2 { let m = end - 1; let n = end; let h11 = t[(start, start)].clone(); let h12 = t[(start, start + 1)].clone(); let h21 = t[(start + 1, start)].clone(); let h22 = t[(start + 1, start + 1)].clone(); let h32 = t[(start + 2, start + 1)].clone(); let hnn = t[(n, n)].clone(); let hmm = t[(m, m)].clone(); let hnm = t[(n, m)].clone(); let hmn = t[(m, n)].clone(); let tra = hnn.clone() + hmm.clone(); let det = hnn * hmm - hnm * hmn; let mut axis = Vector3::new( h11.clone() * h11.clone() + h12 * h21.clone() - tra.clone() * h11.clone() + det, h21.clone() * (h11 + h22 - tra), h21 * h32, ); for k in start..n - 1 { let (norm, not_zero) = householder::reflection_axis_mut(&mut axis); if not_zero { if k > start { t[(k, k - 1)] = norm; t[(k + 1, k - 1)] = T::zero(); t[(k + 2, k - 1)] = T::zero(); } let refl = Reflection::new(Unit::new_unchecked(axis.clone()), T::zero()); { let krows = cmp::min(k + 4, end + 1); let mut work = work.rows_mut(0, krows); refl.reflect( &mut t.generic_view_mut((k, k), (Const::<3>, Dyn(dim.value() - k))), ); refl.reflect_rows( &mut t.generic_view_mut((0, k), (Dyn(krows), Const::<3>)), &mut work, ); } if let Some(ref mut q) = q { refl.reflect_rows( &mut q.generic_view_mut((0, k), (dim, Const::<3>)), work, ); } } axis.x = t[(k + 1, k)].clone(); axis.y = t[(k + 2, k)].clone(); if k < n - 2 { axis.z = t[(k + 3, k)].clone(); } } let mut axis = Vector2::new(axis.x.clone(), axis.y.clone()); let (norm, not_zero) = householder::reflection_axis_mut(&mut axis); if not_zero { let refl = Reflection::new(Unit::new_unchecked(axis), T::zero()); t[(m, m - 1)] = norm; t[(n, m - 1)] = T::zero(); { let mut work = work.rows_mut(0, end + 1); refl.reflect( &mut t.generic_view_mut((m, m), (Const::<2>, Dyn(dim.value() - m))), ); refl.reflect_rows( &mut t.generic_view_mut((0, m), (Dyn(end + 1), Const::<2>)), &mut work, ); } if let Some(ref mut q) = q { refl.reflect_rows(&mut q.generic_view_mut((0, m), (dim, Const::<2>)), work); } } } else { // Decouple the 2x2 block if it has real eigenvalues. if let Some(rot) = compute_2x2_basis(&t.fixed_view::<2, 2>(start, start)) { let inv_rot = rot.inverse(); inv_rot.rotate( &mut t.generic_view_mut( (start, start), (Const::<2>, Dyn(dim.value() - start)), ), ); rot.rotate_rows( &mut t.generic_view_mut((0, start), (Dyn(end + 1), Const::<2>)), ); t[(end, start)] = T::zero(); if let Some(ref mut q) = q { rot.rotate_rows(&mut q.generic_view_mut((0, start), (dim, Const::<2>))); } } // Check if we reached the beginning of the matrix. if end > 2 { end -= 2; } else { break; } } let sub = Self::delimit_subproblem(&mut t, eps.clone(), end); start = sub.0; end = sub.1; niter += 1; if niter == max_niter { return None; } } t.scale_mut(amax_m); Some((q, t)) } /// Computes the eigenvalues of the decomposed matrix. fn do_eigenvalues(t: &OMatrix, out: &mut OVector) -> bool { let dim = t.nrows(); let mut m = 0; while m < dim - 1 { let n = m + 1; if t[(n, m)].is_zero() { out[m] = t[(m, m)].clone(); m += 1; } else { // Complex eigenvalue. return false; } } if m == dim - 1 { out[m] = t[(m, m)].clone(); } true } /// Computes the complex eigenvalues of the decomposed matrix. fn do_complex_eigenvalues(t: &OMatrix, out: &mut UninitVector, D>) where T: RealField, DefaultAllocator: Allocator, { let dim = t.nrows(); let mut m = 0; while m < dim - 1 { let n = m + 1; if t[(n, m)].is_zero() { out[m] = MaybeUninit::new(NumComplex::new(t[(m, m)].clone(), T::zero())); m += 1; } else { // Solve the 2x2 eigenvalue subproblem. let hmm = t[(m, m)].clone(); let hnm = t[(n, m)].clone(); let hmn = t[(m, n)].clone(); let hnn = t[(n, n)].clone(); // NOTE: use the same algorithm as in compute_2x2_eigvals. let val = (hmm.clone() - hnn.clone()) * crate::convert(0.5); let discr = hnm * hmn + val.clone() * val; // All 2x2 blocks have negative discriminant because we already decoupled those // with positive eigenvalues. let sqrt_discr = NumComplex::new(T::zero(), (-discr).sqrt()); let half_tra = (hnn + hmm) * crate::convert(0.5); out[m] = MaybeUninit::new( NumComplex::new(half_tra.clone(), T::zero()) + sqrt_discr.clone(), ); out[m + 1] = MaybeUninit::new(NumComplex::new(half_tra, T::zero()) - sqrt_discr.clone()); m += 2; } } if m == dim - 1 { out[m] = MaybeUninit::new(NumComplex::new(t[(m, m)].clone(), T::zero())); } } fn delimit_subproblem(t: &mut OMatrix, eps: T::RealField, end: usize) -> (usize, usize) where D: DimSub, DefaultAllocator: Allocator>, { let mut n = end; while n > 0 { let m = n - 1; if t[(n, m)].clone().norm1() <= eps.clone() * (t[(n, n)].clone().norm1() + t[(m, m)].clone().norm1()) { t[(n, m)] = T::zero(); } else { break; } n -= 1; } if n == 0 { return (0, 0); } let mut new_start = n - 1; while new_start > 0 { let m = new_start - 1; let off_diag = t[(new_start, m)].clone(); if off_diag.is_zero() || off_diag.norm1() <= eps.clone() * (t[(new_start, new_start)].clone().norm1() + t[(m, m)].clone().norm1()) { t[(new_start, m)] = T::zero(); break; } new_start -= 1; } (new_start, n) } /// Retrieves the unitary matrix `Q` and the upper-quasitriangular matrix `T` such that the /// decomposed matrix equals `Q * T * Q.transpose()`. pub fn unpack(self) -> (OMatrix, OMatrix) { (self.q, self.t) } /// Computes the real eigenvalues of the decomposed matrix. /// /// Return `None` if some eigenvalues are complex. #[must_use] pub fn eigenvalues(&self) -> Option> { let mut out = Matrix::zeros_generic(self.t.shape_generic().0, Const::<1>); if Self::do_eigenvalues(&self.t, &mut out) { Some(out) } else { None } } /// Computes the complex eigenvalues of the decomposed matrix. #[must_use] pub fn complex_eigenvalues(&self) -> OVector, D> where T: RealField, DefaultAllocator: Allocator, { let mut out = Matrix::uninit(self.t.shape_generic().0, Const::<1>); Self::do_complex_eigenvalues(&self.t, &mut out); // Safety: out has been fully initialized by do_complex_eigenvalues. unsafe { out.assume_init() } } } fn decompose_2x2( mut m: OMatrix, compute_q: bool, ) -> Option<(Option>, OMatrix)> where DefaultAllocator: Allocator, { let dim = m.shape_generic().0; let mut q = None; match compute_2x2_basis(&m.fixed_view::<2, 2>(0, 0)) { Some(rot) => { let mut m = m.fixed_view_mut::<2, 2>(0, 0); let inv_rot = rot.inverse(); inv_rot.rotate(&mut m); rot.rotate_rows(&mut m); m[(1, 0)] = T::zero(); if compute_q { // XXX: we have to build the matrix manually because // rot.to_rotation_matrix().unwrap() causes an ICE. let c = T::from_real(rot.c()); q = Some(OMatrix::from_column_slice_generic( dim, dim, &[c.clone(), rot.s(), -rot.s().conjugate(), c], )); } } None => { if compute_q { q = Some(OMatrix::identity_generic(dim, dim)); } } }; Some((q, m)) } fn compute_2x2_eigvals>( m: &SquareMatrix, ) -> Option<(T, T)> { // Solve the 2x2 eigenvalue subproblem. let h00 = m[(0, 0)].clone(); let h10 = m[(1, 0)].clone(); let h01 = m[(0, 1)].clone(); let h11 = m[(1, 1)].clone(); // NOTE: this discriminant computation is more stable than the // one based on the trace and determinant: 0.25 * tra * tra - det // because it ensures positiveness for symmetric matrices. let val = (h00.clone() - h11.clone()) * crate::convert(0.5); let discr = h10 * h01 + val.clone() * val; discr.try_sqrt().map(|sqrt_discr| { let half_tra = (h00 + h11) * crate::convert(0.5); (half_tra.clone() + sqrt_discr.clone(), half_tra - sqrt_discr) }) } // Computes the 2x2 transformation that upper-triangulates a 2x2 matrix with real eigenvalues. /// Computes the singular vectors for a 2x2 matrix. /// /// Returns `None` if the matrix has complex eigenvalues, or is upper-triangular. In both case, /// the basis is the identity. fn compute_2x2_basis>( m: &SquareMatrix, ) -> Option> { let h10 = m[(1, 0)].clone(); if h10.is_zero() { return None; } if let Some((eigval1, eigval2)) = compute_2x2_eigvals(m) { let x1 = eigval1 - m[(1, 1)].clone(); let x2 = eigval2 - m[(1, 1)].clone(); // NOTE: Choose the one that yields a larger x component. // This is necessary for numerical stability of the normalization of the complex // number. if x1.clone().norm1() > x2.clone().norm1() { Some(GivensRotation::new(x1, h10).0) } else { Some(GivensRotation::new(x2, h10).0) } } else { None } } impl> SquareMatrix where D: DimSub, // For Hessenberg. DefaultAllocator: Allocator> + Allocator> + Allocator + Allocator, { /// Computes the eigenvalues of this matrix. #[must_use] pub fn eigenvalues(&self) -> Option> { assert!( self.is_square(), "Unable to compute eigenvalues of a non-square matrix." ); let mut work = Matrix::zeros_generic(self.shape_generic().0, Const::<1>); // Special case for 2x2 matrices. if self.nrows() == 2 { // TODO: can we avoid this slicing // (which is needed here just to transform D to U2)? let me = self.fixed_view::<2, 2>(0, 0); return match compute_2x2_eigvals(&me) { Some((a, b)) => { work[0] = a; work[1] = b; Some(work) } None => None, }; } // TODO: add balancing? let schur = Schur::do_decompose( self.clone_owned(), &mut work, T::RealField::default_epsilon(), 0, false, ) .unwrap(); if Schur::do_eigenvalues(&schur.1, &mut work) { Some(work) } else { None } } /// Computes the eigenvalues of this matrix. #[must_use] pub fn complex_eigenvalues(&self) -> OVector, D> // TODO: add balancing? where T: RealField, DefaultAllocator: Allocator, { let dim = self.shape_generic().0; let mut work = Matrix::zeros_generic(dim, Const::<1>); let schur = Schur::do_decompose( self.clone_owned(), &mut work, T::default_epsilon(), 0, false, ) .unwrap(); let mut eig = Matrix::uninit(dim, Const::<1>); Schur::do_complex_eigenvalues(&schur.1, &mut eig); // Safety: eig has been fully initialized by do_complex_eigenvalues. unsafe { eig.assume_init() } } } nalgebra-0.33.0/src/linalg/solve.rs000064400000000000000000000631450072674642500152560ustar 00000000000000use simba::scalar::ComplexField; use simba::simd::SimdComplexField; use crate::base::allocator::Allocator; use crate::base::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::base::dimension::{Dim, U1}; use crate::base::storage::{Storage, StorageMut}; use crate::base::{DVectorView, DefaultAllocator, Matrix, OMatrix, SquareMatrix, Vector}; impl> SquareMatrix { /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use solve_lower_triangular_mut()?"] #[inline] pub fn solve_lower_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); if self.solve_lower_triangular_mut(&mut res) { Some(res) } else { None } } /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use solve_upper_triangular_mut()?"] #[inline] pub fn solve_upper_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); if self.solve_upper_triangular_mut(&mut res) { Some(res) } else { None } } /// Solves the linear system `self . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` (including the diagonal) is considered not-zero. pub fn solve_lower_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let cols = b.ncols(); for i in 0..cols { if !self.solve_lower_triangular_vector_mut(&mut b.column_mut(i)) { return false; } } true } fn solve_lower_triangular_vector_mut(&self, b: &mut Vector) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); for i in 0..dim { let coeff; unsafe { let diag = self.get_unchecked((i, i)).clone(); if diag.is_zero() { return false; } coeff = b.vget_unchecked(i).clone() / diag; *b.vget_unchecked_mut(i) = coeff.clone(); } b.rows_range_mut(i + 1..) .axpy(-coeff, &self.view_range(i + 1.., i), T::one()); } true } // TODO: add the same but for solving upper-triangular. /// Solves the linear system `self . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` is considered not-zero. The diagonal is never read as it is /// assumed to be equal to `diag`. Returns `false` and does not modify its inputs if `diag` is zero. pub fn solve_lower_triangular_with_diag_mut( &self, b: &mut Matrix, diag: T, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { if diag.is_zero() { return false; } let dim = self.nrows(); let cols = b.ncols(); for k in 0..cols { let mut bcol = b.column_mut(k); for i in 0..dim - 1 { let coeff = unsafe { bcol.vget_unchecked(i).clone() } / diag.clone(); bcol.rows_range_mut(i + 1..) .axpy(-coeff, &self.view_range(i + 1.., i), T::one()); } } true } /// Solves the linear system `self . x = b` where `x` is the unknown and only the /// upper-triangular part of `self` (including the diagonal) is considered not-zero. pub fn solve_upper_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let cols = b.ncols(); for i in 0..cols { if !self.solve_upper_triangular_vector_mut(&mut b.column_mut(i)) { return false; } } true } fn solve_upper_triangular_vector_mut(&self, b: &mut Vector) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); for i in (0..dim).rev() { let coeff; unsafe { let diag = self.get_unchecked((i, i)).clone(); if diag.is_zero() { return false; } coeff = b.vget_unchecked(i).clone() / diag; *b.vget_unchecked_mut(i) = coeff.clone(); } b.rows_range_mut(..i) .axpy(-coeff, &self.view_range(..i, i), T::one()); } true } /* * * Transpose and adjoint versions * */ /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use tr_solve_lower_triangular_mut()?"] #[inline] pub fn tr_solve_lower_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); if self.tr_solve_lower_triangular_mut(&mut res) { Some(res) } else { None } } /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use tr_solve_upper_triangular_mut()?"] #[inline] pub fn tr_solve_upper_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); if self.tr_solve_upper_triangular_mut(&mut res) { Some(res) } else { None } } /// Solves the linear system `self.transpose() . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` (including the diagonal) is considered not-zero. pub fn tr_solve_lower_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let cols = b.ncols(); for i in 0..cols { if !self.xx_solve_lower_triangular_vector_mut( &mut b.column_mut(i), |e| e, |a, b| a.dot(b), ) { return false; } } true } /// Solves the linear system `self.transpose() . x = b` where `x` is the unknown and only the /// upper-triangular part of `self` (including the diagonal) is considered not-zero. pub fn tr_solve_upper_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let cols = b.ncols(); for i in 0..cols { if !self.xx_solve_upper_triangular_vector_mut( &mut b.column_mut(i), |e| e, |a, b| a.dot(b), ) { return false; } } true } /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use ad_solve_lower_triangular_mut()?"] #[inline] pub fn ad_solve_lower_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); if self.ad_solve_lower_triangular_mut(&mut res) { Some(res) } else { None } } /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use ad_solve_upper_triangular_mut()?"] #[inline] pub fn ad_solve_upper_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); if self.ad_solve_upper_triangular_mut(&mut res) { Some(res) } else { None } } /// Solves the linear system `self.adjoint() . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` (including the diagonal) is considered not-zero. pub fn ad_solve_lower_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let cols = b.ncols(); for i in 0..cols { if !self.xx_solve_lower_triangular_vector_mut( &mut b.column_mut(i), |e| e.conjugate(), |a, b| a.dotc(b), ) { return false; } } true } /// Solves the linear system `self.adjoint() . x = b` where `x` is the unknown and only the /// upper-triangular part of `self` (including the diagonal) is considered not-zero. pub fn ad_solve_upper_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let cols = b.ncols(); for i in 0..cols { if !self.xx_solve_upper_triangular_vector_mut( &mut b.column_mut(i), |e| e.conjugate(), |a, b| a.dotc(b), ) { return false; } } true } #[inline(always)] fn xx_solve_lower_triangular_vector_mut( &self, b: &mut Vector, conjugate: impl Fn(T) -> T, dot: impl Fn( &DVectorView<'_, T, S::RStride, S::CStride>, &DVectorView<'_, T, S2::RStride, S2::CStride>, ) -> T, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); for i in (0..dim).rev() { let dot = dot(&self.view_range(i + 1.., i), &b.view_range(i + 1.., 0)); unsafe { let b_i = b.vget_unchecked_mut(i); let diag = conjugate(self.get_unchecked((i, i)).clone()); if diag.is_zero() { return false; } *b_i = (b_i.clone() - dot) / diag; } } true } #[inline(always)] fn xx_solve_upper_triangular_vector_mut( &self, b: &mut Vector, conjugate: impl Fn(T) -> T, dot: impl Fn( &DVectorView<'_, T, S::RStride, S::CStride>, &DVectorView<'_, T, S2::RStride, S2::CStride>, ) -> T, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); for i in 0..dim { let dot = dot(&self.view_range(..i, i), &b.view_range(..i, 0)); unsafe { let b_i = b.vget_unchecked_mut(i); let diag = conjugate(self.get_unchecked((i, i)).clone()); if diag.is_zero() { return false; } *b_i = (b_i.clone() - dot) / diag; } } true } } /* * * SIMD-compatible unchecked versions. * */ impl> SquareMatrix { /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use solve_lower_triangular_unchecked_mut()?"] #[inline] pub fn solve_lower_triangular_unchecked( &self, b: &Matrix, ) -> OMatrix where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); self.solve_lower_triangular_unchecked_mut(&mut res); res } /// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use solve_upper_triangular_unchecked_mut()?"] #[inline] pub fn solve_upper_triangular_unchecked( &self, b: &Matrix, ) -> OMatrix where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); self.solve_upper_triangular_unchecked_mut(&mut res); res } /// Solves the linear system `self . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` (including the diagonal) is considered not-zero. pub fn solve_lower_triangular_unchecked_mut( &self, b: &mut Matrix, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..b.ncols() { self.solve_lower_triangular_vector_unchecked_mut(&mut b.column_mut(i)); } } fn solve_lower_triangular_vector_unchecked_mut(&self, b: &mut Vector) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); for i in 0..dim { let coeff; unsafe { let diag = self.get_unchecked((i, i)).clone(); coeff = b.vget_unchecked(i).clone() / diag; *b.vget_unchecked_mut(i) = coeff.clone(); } b.rows_range_mut(i + 1..) .axpy(-coeff.clone(), &self.view_range(i + 1.., i), T::one()); } } // TODO: add the same but for solving upper-triangular. /// Solves the linear system `self . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` is considered not-zero. The diagonal is never read as it is /// assumed to be equal to `diag`. Returns `false` and does not modify its inputs if `diag` is zero. pub fn solve_lower_triangular_with_diag_unchecked_mut( &self, b: &mut Matrix, diag: T, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); let cols = b.ncols(); for k in 0..cols { let mut bcol = b.column_mut(k); for i in 0..dim - 1 { let coeff = unsafe { bcol.vget_unchecked(i).clone() } / diag.clone(); bcol.rows_range_mut(i + 1..) .axpy(-coeff, &self.view_range(i + 1.., i), T::one()); } } } /// Solves the linear system `self . x = b` where `x` is the unknown and only the /// upper-triangular part of `self` (including the diagonal) is considered not-zero. pub fn solve_upper_triangular_unchecked_mut( &self, b: &mut Matrix, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..b.ncols() { self.solve_upper_triangular_vector_unchecked_mut(&mut b.column_mut(i)) } } fn solve_upper_triangular_vector_unchecked_mut(&self, b: &mut Vector) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); for i in (0..dim).rev() { let coeff; unsafe { let diag = self.get_unchecked((i, i)).clone(); coeff = b.vget_unchecked(i).clone() / diag; *b.vget_unchecked_mut(i) = coeff.clone(); } b.rows_range_mut(..i) .axpy(-coeff, &self.view_range(..i, i), T::one()); } } /* * * Transpose and adjoint versions * */ /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use tr_solve_lower_triangular_unchecked_mut()?"] #[inline] pub fn tr_solve_lower_triangular_unchecked( &self, b: &Matrix, ) -> OMatrix where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); self.tr_solve_lower_triangular_unchecked_mut(&mut res); res } /// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use tr_solve_upper_triangular_unchecked_mut()?"] #[inline] pub fn tr_solve_upper_triangular_unchecked( &self, b: &Matrix, ) -> OMatrix where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); self.tr_solve_upper_triangular_unchecked_mut(&mut res); res } /// Solves the linear system `self.transpose() . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` (including the diagonal) is considered not-zero. pub fn tr_solve_lower_triangular_unchecked_mut( &self, b: &mut Matrix, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..b.ncols() { self.xx_solve_lower_triangular_vector_unchecked_mut( &mut b.column_mut(i), |e| e, |a, b| a.dot(b), ) } } /// Solves the linear system `self.transpose() . x = b` where `x` is the unknown and only the /// upper-triangular part of `self` (including the diagonal) is considered not-zero. pub fn tr_solve_upper_triangular_unchecked_mut( &self, b: &mut Matrix, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..b.ncols() { self.xx_solve_upper_triangular_vector_unchecked_mut( &mut b.column_mut(i), |e| e, |a, b| a.dot(b), ) } } /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the lower-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use ad_solve_lower_triangular_unchecked_mut()?"] #[inline] pub fn ad_solve_lower_triangular_unchecked( &self, b: &Matrix, ) -> OMatrix where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); self.ad_solve_lower_triangular_unchecked_mut(&mut res); res } /// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only /// the upper-triangular part of `self` (including the diagonal) is considered not-zero. #[must_use = "Did you mean to use ad_solve_upper_triangular_unchecked_mut()?"] #[inline] pub fn ad_solve_upper_triangular_unchecked( &self, b: &Matrix, ) -> OMatrix where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut res = b.clone_owned(); self.ad_solve_upper_triangular_unchecked_mut(&mut res); res } /// Solves the linear system `self.adjoint() . x = b` where `x` is the unknown and only the /// lower-triangular part of `self` (including the diagonal) is considered not-zero. pub fn ad_solve_lower_triangular_unchecked_mut( &self, b: &mut Matrix, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..b.ncols() { self.xx_solve_lower_triangular_vector_unchecked_mut( &mut b.column_mut(i), |e| e.simd_conjugate(), |a, b| a.dotc(b), ) } } /// Solves the linear system `self.adjoint() . x = b` where `x` is the unknown and only the /// upper-triangular part of `self` (including the diagonal) is considered not-zero. pub fn ad_solve_upper_triangular_unchecked_mut( &self, b: &mut Matrix, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..b.ncols() { self.xx_solve_upper_triangular_vector_unchecked_mut( &mut b.column_mut(i), |e| e.simd_conjugate(), |a, b| a.dotc(b), ) } } #[inline(always)] fn xx_solve_lower_triangular_vector_unchecked_mut( &self, b: &mut Vector, conjugate: impl Fn(T) -> T, dot: impl Fn( &DVectorView<'_, T, S::RStride, S::CStride>, &DVectorView<'_, T, S2::RStride, S2::CStride>, ) -> T, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let dim = self.nrows(); for i in (0..dim).rev() { let dot = dot(&self.view_range(i + 1.., i), &b.view_range(i + 1.., 0)); unsafe { let b_i = b.vget_unchecked_mut(i); let diag = conjugate(self.get_unchecked((i, i)).clone()); *b_i = (b_i.clone() - dot) / diag; } } } #[inline(always)] fn xx_solve_upper_triangular_vector_unchecked_mut( &self, b: &mut Vector, conjugate: impl Fn(T) -> T, dot: impl Fn( &DVectorView<'_, T, S::RStride, S::CStride>, &DVectorView<'_, T, S2::RStride, S2::CStride>, ) -> T, ) where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { for i in 0..self.nrows() { let dot = dot(&self.view_range(..i, i), &b.view_range(..i, 0)); unsafe { let b_i = b.vget_unchecked_mut(i); let diag = conjugate(self.get_unchecked((i, i)).clone()); *b_i = (b_i.clone() - dot) / diag; } } } } nalgebra-0.33.0/src/linalg/svd.rs000064400000000000000000001035460072674642500147220ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use std::any::TypeId; use approx::AbsDiffEq; use num::{One, Zero}; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, Matrix, Matrix2x3, OMatrix, OVector, Vector2}; use crate::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::dimension::{Dim, DimDiff, DimMin, DimMinimum, DimSub, U1}; use crate::storage::Storage; use crate::{Matrix2, Matrix3, RawStorage, U2, U3}; use simba::scalar::{ComplexField, RealField}; use crate::linalg::givens::GivensRotation; use crate::linalg::symmetric_eigen; use crate::linalg::Bidiagonal; /// Singular Value Decomposition of a general matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator> + Allocator, C> + Allocator>, OMatrix>: Serialize, OMatrix, C>: Serialize, OVector>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator> + Allocator, C> + Allocator>, OMatrix>: Deserialize<'de>, OMatrix, C>: Deserialize<'de>, OVector>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct SVD, C: Dim> where DefaultAllocator: Allocator, C> + Allocator> + Allocator>, { /// The left-singular vectors `U` of this SVD. pub u: Option>>, /// The right-singular vectors `V^t` of this SVD. pub v_t: Option, C>>, /// The singular values of this SVD. pub singular_values: OVector>, } impl, C: Dim> Copy for SVD where DefaultAllocator: Allocator, C> + Allocator> + Allocator>, OMatrix>: Copy, OMatrix, C>: Copy, OVector>: Copy, { } impl, C: Dim> SVD where DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator> + Allocator> + Allocator, U1>>, { fn use_special_always_ordered_svd2() -> bool { TypeId::of::>() == TypeId::of::>() && TypeId::of::() == TypeId::of::>() } fn use_special_always_ordered_svd3() -> bool { TypeId::of::>() == TypeId::of::>() && TypeId::of::() == TypeId::of::>() } /// Computes the Singular Value Decomposition of `matrix` using implicit shift. /// The singular values are not guaranteed to be sorted in any particular order. /// If a descending order is required, consider using `new` instead. pub fn new_unordered(matrix: OMatrix, compute_u: bool, compute_v: bool) -> Self { Self::try_new_unordered( matrix, compute_u, compute_v, T::RealField::default_epsilon() * crate::convert(5.0), 0, ) .unwrap() } /// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift. /// The singular values are not guaranteed to be sorted in any particular order. /// If a descending order is required, consider using `try_new` instead. /// /// # Arguments /// /// * `compute_u` − set this to `true` to enable the computation of left-singular vectors. /// * `compute_v` − set this to `true` to enable the computation of right-singular vectors. /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_new_unordered( mut matrix: OMatrix, compute_u: bool, compute_v: bool, eps: T::RealField, max_niter: usize, ) -> Option { assert!( !matrix.is_empty(), "Cannot compute the SVD of an empty matrix." ); let (nrows, ncols) = matrix.shape_generic(); let min_nrows_ncols = nrows.min(ncols); if Self::use_special_always_ordered_svd2() { // SAFETY: the reference transmutes are OK since we checked that the types match exactly. let matrix: &Matrix2 = unsafe { std::mem::transmute(&matrix) }; let result = super::svd2::svd_ordered2(matrix, compute_u, compute_v); let typed_result: &Self = unsafe { std::mem::transmute(&result) }; return Some(typed_result.clone()); } else if Self::use_special_always_ordered_svd3() { // SAFETY: the reference transmutes are OK since we checked that the types match exactly. let matrix: &Matrix3 = unsafe { std::mem::transmute(&matrix) }; let result = super::svd3::svd_ordered3(matrix, compute_u, compute_v, eps, max_niter); let typed_result: &Self = unsafe { std::mem::transmute(&result) }; return Some(typed_result.clone()); } let dim = min_nrows_ncols.value(); let m_amax = matrix.camax(); if !m_amax.is_zero() { matrix.unscale_mut(m_amax.clone()); } let bi_matrix = Bidiagonal::new(matrix); let mut u = if compute_u { Some(bi_matrix.u()) } else { None }; let mut v_t = if compute_v { Some(bi_matrix.v_t()) } else { None }; let mut diagonal = bi_matrix.diagonal(); let mut off_diagonal = bi_matrix.off_diagonal(); let mut niter = 0; let (mut start, mut end) = Self::delimit_subproblem( &mut diagonal, &mut off_diagonal, &mut u, &mut v_t, bi_matrix.is_upper_diagonal(), dim - 1, eps.clone(), ); while end != start { let subdim = end - start + 1; // Solve the subproblem. #[allow(clippy::comparison_chain)] if subdim > 2 { let m = end - 1; let n = end; let mut vec; { let dm = diagonal[m].clone(); let dn = diagonal[n].clone(); let fm = off_diagonal[m].clone(); let tmm = dm.clone() * dm.clone() + off_diagonal[m - 1].clone() * off_diagonal[m - 1].clone(); let tmn = dm * fm.clone(); let tnn = dn.clone() * dn + fm.clone() * fm; let shift = symmetric_eigen::wilkinson_shift(tmm, tnn, tmn); vec = Vector2::new( diagonal[start].clone() * diagonal[start].clone() - shift, diagonal[start].clone() * off_diagonal[start].clone(), ); } for k in start..n { let m12 = if k == n - 1 { T::RealField::zero() } else { off_diagonal[k + 1].clone() }; let mut subm = Matrix2x3::new( diagonal[k].clone(), off_diagonal[k].clone(), T::RealField::zero(), T::RealField::zero(), diagonal[k + 1].clone(), m12, ); if let Some((rot1, norm1)) = GivensRotation::cancel_y(&vec) { rot1.inverse() .rotate_rows(&mut subm.fixed_columns_mut::<2>(0)); let rot1 = GivensRotation::new_unchecked(rot1.c(), T::from_real(rot1.s())); if k > start { // This is not the first iteration. off_diagonal[k - 1] = norm1; } let v = Vector2::new(subm[(0, 0)].clone(), subm[(1, 0)].clone()); // TODO: does the case `v.y == 0` ever happen? let (rot2, norm2) = GivensRotation::cancel_y(&v) .unwrap_or((GivensRotation::identity(), subm[(0, 0)].clone())); rot2.rotate(&mut subm.fixed_columns_mut::<2>(1)); let rot2 = GivensRotation::new_unchecked(rot2.c(), T::from_real(rot2.s())); subm[(0, 0)] = norm2; if let Some(ref mut v_t) = v_t { if bi_matrix.is_upper_diagonal() { rot1.rotate(&mut v_t.fixed_rows_mut::<2>(k)); } else { rot2.rotate(&mut v_t.fixed_rows_mut::<2>(k)); } } if let Some(ref mut u) = u { if bi_matrix.is_upper_diagonal() { rot2.inverse().rotate_rows(&mut u.fixed_columns_mut::<2>(k)); } else { rot1.inverse().rotate_rows(&mut u.fixed_columns_mut::<2>(k)); } } diagonal[k] = subm[(0, 0)].clone(); diagonal[k + 1] = subm[(1, 1)].clone(); off_diagonal[k] = subm[(0, 1)].clone(); if k != n - 1 { off_diagonal[k + 1] = subm[(1, 2)].clone(); } vec.x = subm[(0, 1)].clone(); vec.y = subm[(0, 2)].clone(); } else { break; } } } else if subdim == 2 { // Solve the remaining 2x2 subproblem. let (u2, s, v2) = compute_2x2_uptrig_svd( diagonal[start].clone(), off_diagonal[start].clone(), diagonal[start + 1].clone(), compute_u && bi_matrix.is_upper_diagonal() || compute_v && !bi_matrix.is_upper_diagonal(), compute_v && bi_matrix.is_upper_diagonal() || compute_u && !bi_matrix.is_upper_diagonal(), ); let u2 = u2.map(|u2| GivensRotation::new_unchecked(u2.c(), T::from_real(u2.s()))); let v2 = v2.map(|v2| GivensRotation::new_unchecked(v2.c(), T::from_real(v2.s()))); diagonal[start] = s[0].clone(); diagonal[start + 1] = s[1].clone(); off_diagonal[start] = T::RealField::zero(); if let Some(ref mut u) = u { let rot = if bi_matrix.is_upper_diagonal() { u2.clone().unwrap() } else { v2.clone().unwrap() }; rot.rotate_rows(&mut u.fixed_columns_mut::<2>(start)); } if let Some(ref mut v_t) = v_t { let rot = if bi_matrix.is_upper_diagonal() { v2.unwrap() } else { u2.unwrap() }; rot.inverse().rotate(&mut v_t.fixed_rows_mut::<2>(start)); } end -= 1; } // Re-delimit the subproblem in case some decoupling occurred. let sub = Self::delimit_subproblem( &mut diagonal, &mut off_diagonal, &mut u, &mut v_t, bi_matrix.is_upper_diagonal(), end, eps.clone(), ); start = sub.0; end = sub.1; niter += 1; if niter == max_niter { return None; } } diagonal *= m_amax; // Ensure all singular value are non-negative. for i in 0..dim { let sval = diagonal[i].clone(); if sval < T::RealField::zero() { diagonal[i] = -sval; if let Some(ref mut u) = u { u.column_mut(i).neg_mut(); } } } Some(Self { u, v_t, singular_values: diagonal, }) } /* fn display_bidiag(b: &Bidiagonal, begin: usize, end: usize) { for i in begin .. end { for k in begin .. i { print!(" "); } println!("{} {}", b.diagonal[i], b.off_diagonal[i]); } for k in begin .. end { print!(" "); } println!("{}", b.diagonal[end]); } */ fn delimit_subproblem( diagonal: &mut OVector>, off_diagonal: &mut OVector, U1>>, u: &mut Option>>, v_t: &mut Option, C>>, is_upper_diagonal: bool, end: usize, eps: T::RealField, ) -> (usize, usize) { let mut n = end; while n > 0 { let m = n - 1; if off_diagonal[m].is_zero() || off_diagonal[m].clone().norm1() <= eps.clone() * (diagonal[n].clone().norm1() + diagonal[m].clone().norm1()) { off_diagonal[m] = T::RealField::zero(); } else if diagonal[m].clone().norm1() <= eps { diagonal[m] = T::RealField::zero(); Self::cancel_horizontal_off_diagonal_elt( diagonal, off_diagonal, u, v_t, is_upper_diagonal, m, m + 1, ); if m != 0 { Self::cancel_vertical_off_diagonal_elt( diagonal, off_diagonal, u, v_t, is_upper_diagonal, m - 1, ); } } else if diagonal[n].clone().norm1() <= eps { diagonal[n] = T::RealField::zero(); Self::cancel_vertical_off_diagonal_elt( diagonal, off_diagonal, u, v_t, is_upper_diagonal, m, ); } else { break; } n -= 1; } if n == 0 { return (0, 0); } let mut new_start = n - 1; while new_start > 0 { let m = new_start - 1; if off_diagonal[m].clone().norm1() <= eps.clone() * (diagonal[new_start].clone().norm1() + diagonal[m].clone().norm1()) { off_diagonal[m] = T::RealField::zero(); break; } // TODO: write a test that enters this case. else if diagonal[m].clone().norm1() <= eps { diagonal[m] = T::RealField::zero(); Self::cancel_horizontal_off_diagonal_elt( diagonal, off_diagonal, u, v_t, is_upper_diagonal, m, n, ); if m != 0 { Self::cancel_vertical_off_diagonal_elt( diagonal, off_diagonal, u, v_t, is_upper_diagonal, m - 1, ); } break; } new_start -= 1; } (new_start, n) } // Cancels the i-th off-diagonal element using givens rotations. fn cancel_horizontal_off_diagonal_elt( diagonal: &mut OVector>, off_diagonal: &mut OVector, U1>>, u: &mut Option>>, v_t: &mut Option, C>>, is_upper_diagonal: bool, i: usize, end: usize, ) { let mut v = Vector2::new(off_diagonal[i].clone(), diagonal[i + 1].clone()); off_diagonal[i] = T::RealField::zero(); for k in i..end { if let Some((rot, norm)) = GivensRotation::cancel_x(&v) { let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s())); diagonal[k + 1] = norm; if is_upper_diagonal { if let Some(ref mut u) = *u { rot.inverse() .rotate_rows(&mut u.fixed_columns_with_step_mut::<2>(i, k - i)); } } else if let Some(ref mut v_t) = *v_t { rot.rotate(&mut v_t.fixed_rows_with_step_mut::<2>(i, k - i)); } if k + 1 != end { v.x = -rot.s().real() * off_diagonal[k + 1].clone(); v.y = diagonal[k + 2].clone(); off_diagonal[k + 1] *= rot.c(); } } else { break; } } } // Cancels the i-th off-diagonal element using givens rotations. fn cancel_vertical_off_diagonal_elt( diagonal: &mut OVector>, off_diagonal: &mut OVector, U1>>, u: &mut Option>>, v_t: &mut Option, C>>, is_upper_diagonal: bool, i: usize, ) { let mut v = Vector2::new(diagonal[i].clone(), off_diagonal[i].clone()); off_diagonal[i] = T::RealField::zero(); for k in (0..i + 1).rev() { if let Some((rot, norm)) = GivensRotation::cancel_y(&v) { let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s())); diagonal[k] = norm; if is_upper_diagonal { if let Some(ref mut v_t) = *v_t { rot.rotate(&mut v_t.fixed_rows_with_step_mut::<2>(k, i - k)); } } else if let Some(ref mut u) = *u { rot.inverse() .rotate_rows(&mut u.fixed_columns_with_step_mut::<2>(k, i - k)); } if k > 0 { v.x = diagonal[k - 1].clone(); v.y = rot.s().real() * off_diagonal[k - 1].clone(); off_diagonal[k - 1] *= rot.c(); } } else { break; } } } /// Computes the rank of the decomposed matrix, i.e., the number of singular values greater /// than `eps`. #[must_use] pub fn rank(&self, eps: T::RealField) -> usize { assert!( eps >= T::RealField::zero(), "SVD rank: the epsilon must be non-negative." ); self.singular_values.iter().filter(|e| **e > eps).count() } /// Rebuild the original matrix. /// /// This is useful if some of the singular values have been manually modified. /// Returns `Err` if the right- and left- singular vectors have not been /// computed at construction-time. pub fn recompose(self) -> Result, &'static str> { match (self.u, self.v_t) { (Some(mut u), Some(v_t)) => { for i in 0..self.singular_values.len() { let val = self.singular_values[i].clone(); u.column_mut(i).scale_mut(val); } Ok(u * v_t) } (None, None) => Err("SVD recomposition: U and V^t have not been computed."), (None, _) => Err("SVD recomposition: U has not been computed."), (_, None) => Err("SVD recomposition: V^t has not been computed."), } } /// Computes the pseudo-inverse of the decomposed matrix. /// /// Any singular value smaller than `eps` is assumed to be zero. /// Returns `Err` if the right- and left- singular vectors have not /// been computed at construction-time. pub fn pseudo_inverse(mut self, eps: T::RealField) -> Result, &'static str> where DefaultAllocator: Allocator, { if eps < T::RealField::zero() { Err("SVD pseudo inverse: the epsilon must be non-negative.") } else { for i in 0..self.singular_values.len() { let val = self.singular_values[i].clone(); if val > eps { self.singular_values[i] = T::RealField::one() / val; } else { self.singular_values[i] = T::RealField::zero(); } } self.recompose().map(|m| m.adjoint()) } } /// Solves the system `self * x = b` where `self` is the decomposed matrix and `x` the unknown. /// /// Any singular value smaller than `eps` is assumed to be zero. /// Returns `Err` if the singular vectors `U` and `V` have not been computed. // TODO: make this more generic wrt the storage types and the dimensions for `b`. pub fn solve( &self, b: &Matrix, eps: T::RealField, ) -> Result, &'static str> where S2: Storage, DefaultAllocator: Allocator + Allocator, C2>, ShapeConstraint: SameNumberOfRows, { if eps < T::RealField::zero() { Err("SVD solve: the epsilon must be non-negative.") } else { match (&self.u, &self.v_t) { (Some(u), Some(v_t)) => { let mut ut_b = u.ad_mul(b); for j in 0..ut_b.ncols() { let mut col = ut_b.column_mut(j); for i in 0..self.singular_values.len() { let val = self.singular_values[i].clone(); if val > eps { col[i] = col[i].clone().unscale(val); } else { col[i] = T::zero(); } } } Ok(v_t.ad_mul(&ut_b)) } (None, None) => Err("SVD solve: U and V^t have not been computed."), (None, _) => Err("SVD solve: U has not been computed."), (_, None) => Err("SVD solve: V^t has not been computed."), } } } /// converts SVD results to Polar decomposition form of the original Matrix: `A = P' * U`. /// /// The polar decomposition used here is Left Polar Decomposition (or Reverse Polar Decomposition) /// Returns None if the singular vectors of the SVD haven't been calculated pub fn to_polar(&self) -> Option<(OMatrix, OMatrix)> where DefaultAllocator: Allocator //result + Allocator, R> // adjoint + Allocator> // mapped vals + Allocator // result + Allocator, DimMinimum>, // square matrix { match (&self.u, &self.v_t) { (Some(u), Some(v_t)) => Some(( u * OMatrix::from_diagonal(&self.singular_values.map(|e| T::from_real(e))) * u.adjoint(), u * v_t, )), _ => None, } } } impl, C: Dim> SVD where DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator>, // for sorted singular values { /// Computes the Singular Value Decomposition of `matrix` using implicit shift. /// The singular values are guaranteed to be sorted in descending order. /// If this order is not required consider using `new_unordered`. pub fn new(matrix: OMatrix, compute_u: bool, compute_v: bool) -> Self { let mut svd = Self::new_unordered(matrix, compute_u, compute_v); if !Self::use_special_always_ordered_svd3() && !Self::use_special_always_ordered_svd2() { svd.sort_by_singular_values(); } svd } /// Attempts to compute the Singular Value Decomposition of `matrix` using implicit shift. /// The singular values are guaranteed to be sorted in descending order. /// If this order is not required consider using `try_new_unordered`. /// /// # Arguments /// /// * `compute_u` − set this to `true` to enable the computation of left-singular vectors. /// * `compute_v` − set this to `true` to enable the computation of right-singular vectors. /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_new( matrix: OMatrix, compute_u: bool, compute_v: bool, eps: T::RealField, max_niter: usize, ) -> Option { Self::try_new_unordered(matrix, compute_u, compute_v, eps, max_niter).map(|mut svd| { if !Self::use_special_always_ordered_svd3() && !Self::use_special_always_ordered_svd2() { svd.sort_by_singular_values(); } svd }) } /// Sort the estimated components of the SVD by its singular values in descending order. /// Such an ordering is often implicitly required when the decompositions are used for estimation or fitting purposes. /// Using this function is only required if `new_unordered` or `try_new_unordered` were used and the specific sorting is required afterward. pub fn sort_by_singular_values(&mut self) { const VALUE_PROCESSED: usize = usize::MAX; // Collect the singular values with their original index, ... let mut singular_values = self.singular_values.map_with_location(|r, _, e| (e, r)); assert_ne!( singular_values.data.shape().0.value(), VALUE_PROCESSED, "Too many singular values" ); // ... sort the singular values, ... singular_values .as_mut_slice() .sort_unstable_by(|(a, _), (b, _)| b.partial_cmp(a).expect("Singular value was NaN")); // ... and store them. self.singular_values .zip_apply(&singular_values, |value, (new_value, _)| { value.clone_from(&new_value) }); // Calculate required permutations given the sorted indices. // We need to identify all circles to calculate the required swaps. let mut permutations = crate::PermutationSequence::identity_generic(singular_values.data.shape().0); for i in 0..singular_values.len() { let mut index_1 = i; let mut index_2 = singular_values[i].1; // Check whether the value was already visited ... while index_2 != VALUE_PROCESSED // ... or a "double swap" must be avoided. && singular_values[index_2].1 != VALUE_PROCESSED { // Add the permutation ... permutations.append_permutation(index_1, index_2); // ... and mark the value as visited. singular_values[index_1].1 = VALUE_PROCESSED; index_1 = index_2; index_2 = singular_values[index_1].1; } } // Permute the optional components if let Some(u) = self.u.as_mut() { permutations.permute_columns(u); } if let Some(v_t) = self.v_t.as_mut() { permutations.permute_rows(v_t); } } } impl, C: Dim, S: Storage> Matrix where DimMinimum: DimSub, // for Bidiagonal. DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator> + Allocator> + Allocator, U1>>, { /// Computes the singular values of this matrix. /// The singular values are not guaranteed to be sorted in any particular order. /// If a descending order is required, consider using `singular_values` instead. #[must_use] pub fn singular_values_unordered(&self) -> OVector> { SVD::new_unordered(self.clone_owned(), false, false).singular_values } /// Computes the rank of this matrix. /// /// All singular values below `eps` are considered equal to 0. #[must_use] pub fn rank(&self, eps: T::RealField) -> usize { let svd = SVD::new_unordered(self.clone_owned(), false, false); svd.rank(eps) } /// Computes the pseudo-inverse of this matrix. /// /// All singular values below `eps` are considered equal to 0. pub fn pseudo_inverse(self, eps: T::RealField) -> Result, &'static str> where DefaultAllocator: Allocator, { SVD::new_unordered(self.clone_owned(), true, true).pseudo_inverse(eps) } } impl, C: Dim, S: Storage> Matrix where DimMinimum: DimSub, DefaultAllocator: Allocator + Allocator + Allocator + Allocator, U1>> + Allocator, C> + Allocator> + Allocator>, { /// Computes the singular values of this matrix. /// The singular values are guaranteed to be sorted in descending order. /// If this order is not required consider using `singular_values_unordered`. #[must_use] pub fn singular_values(&self) -> OVector> { SVD::new(self.clone_owned(), false, false).singular_values } } // Explicit formulae inspired from the paper "Computing the Singular Values of 2-by-2 Complex // Matrices", Sanzheng Qiao and Xiaohong Wang. // http://www.cas.mcmaster.ca/sqrl/papers/sqrl5.pdf fn compute_2x2_uptrig_svd( m11: T, m12: T, m22: T, compute_u: bool, compute_v: bool, ) -> ( Option>, Vector2, Option>, ) { let two: T::RealField = crate::convert(2.0f64); let half: T::RealField = crate::convert(0.5f64); let denom = (m11.clone() + m22.clone()).hypot(m12.clone()) + (m11.clone() - m22.clone()).hypot(m12.clone()); // NOTE: v1 is the singular value that is the closest to m22. // This prevents cancellation issues when constructing the vector `csv` below. If we chose // otherwise, we would have v1 ~= m11 when m12 is small. This would cause catastrophic // cancellation on `v1 * v1 - m11 * m11` below. let mut v1 = m11.clone() * m22.clone() * two / denom.clone(); let mut v2 = half * denom; let mut u = None; let mut v_t = None; if compute_u || compute_v { let (csv, sgn_v) = GivensRotation::new( m11.clone() * m12.clone(), v1.clone() * v1.clone() - m11.clone() * m11.clone(), ); v1 *= sgn_v.clone(); v2 *= sgn_v; if compute_v { v_t = Some(csv.clone()); } let cu = (m11.scale(csv.c()) + m12 * csv.s()) / v1.clone(); let su = (m22 * csv.s()) / v1.clone(); let (csu, sgn_u) = GivensRotation::new(cu, su); v1 *= sgn_u.clone(); v2 *= sgn_u; if compute_u { u = Some(csu); } } (u, Vector2::new(v1, v2), v_t) } nalgebra-0.33.0/src/linalg/svd2.rs000064400000000000000000000034720072674642500150010ustar 00000000000000use crate::{Matrix2, RealField, Vector2, SVD, U2}; // Implementation of the 2D SVD from https://ieeexplore.ieee.org/document/486688 // See also https://scicomp.stackexchange.com/questions/8899/robust-algorithm-for-2-times-2-svd pub fn svd_ordered2( m: &Matrix2, compute_u: bool, compute_v: bool, ) -> SVD { let half: T = crate::convert(0.5); let one: T = crate::convert(1.0); let e = (m.m11.clone() + m.m22.clone()) * half.clone(); let f = (m.m11.clone() - m.m22.clone()) * half.clone(); let g = (m.m21.clone() + m.m12.clone()) * half.clone(); let h = (m.m21.clone() - m.m12.clone()) * half.clone(); let q = (e.clone() * e.clone() + h.clone() * h.clone()).sqrt(); let r = (f.clone() * f.clone() + g.clone() * g.clone()).sqrt(); // Note that the singular values are always sorted because sx >= sy // because q >= 0 and r >= 0. let sx = q.clone() + r.clone(); let sy = q - r; 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 { let a1 = g.atan2(f); let a2 = h.atan2(e); let theta = (a2.clone() - a1.clone()) * half.clone(); let phi = (a2 + a1) * half; let (st, ct) = theta.sin_cos(); let (sp, cp) = phi.sin_cos(); let u = Matrix2::new(cp.clone(), -sp.clone(), sp, cp); let v_t = Matrix2::new(ct.clone(), -st.clone(), st * sy_sign.clone(), ct * sy_sign); SVD { u: if compute_u { Some(u) } else { None }, singular_values, v_t: if compute_v { Some(v_t) } else { None }, } } else { SVD { u: None, singular_values, v_t: None, } } } nalgebra-0.33.0/src/linalg/svd3.rs000064400000000000000000000034340072674642500150000ustar 00000000000000use crate::{Matrix3, SVD, U3}; use simba::scalar::RealField; // For the 3x3 case, on the GPU, it is much more efficient to compute the SVD // using an eigendecomposition followed by a QR decomposition. // // This is based on the paper "Computing the Singular Value Decomposition of 3 x 3 matrices with // minimal branching and elementary floating point operations" from McAdams, et al. pub fn svd_ordered3( m: &Matrix3, compute_u: bool, compute_v: bool, eps: T, niter: usize, ) -> Option> { let s = m.tr_mul(m); let mut v = s.try_symmetric_eigen(eps, niter)?.eigenvectors; let mut b = m * &v; // Sort singular values. This is a necessary step to ensure that // the QR decompositions R matrix ends up diagonal. let mut rho0 = b.column(0).norm_squared(); let mut rho1 = b.column(1).norm_squared(); let mut rho2 = b.column(2).norm_squared(); if rho0 < rho1 { b.swap_columns(0, 1); b.column_mut(1).neg_mut(); v.swap_columns(0, 1); v.column_mut(1).neg_mut(); std::mem::swap(&mut rho0, &mut rho1); } if rho0 < rho2 { b.swap_columns(0, 2); b.column_mut(2).neg_mut(); v.swap_columns(0, 2); v.column_mut(2).neg_mut(); std::mem::swap(&mut rho0, &mut rho2); } if rho1 < rho2 { b.swap_columns(1, 2); b.column_mut(2).neg_mut(); v.swap_columns(1, 2); v.column_mut(2).neg_mut(); std::mem::swap(&mut rho0, &mut rho2); } let qr = b.qr(); Some(SVD { u: if compute_u { Some(qr.q()) } else { None }, singular_values: qr.diag_internal().map(|e| e.abs()), v_t: if compute_v { Some(v.transpose()) } else { None }, }) } nalgebra-0.33.0/src/linalg/symmetric_eigen.rs000064400000000000000000000325440072674642500173100ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use approx::AbsDiffEq; use num::Zero; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, Matrix2, OMatrix, OVector, SquareMatrix, Vector2}; use crate::dimension::{Dim, DimDiff, DimSub, U1}; use crate::storage::Storage; use simba::scalar::ComplexField; use crate::linalg::givens::GivensRotation; use crate::linalg::SymmetricTridiagonal; /// Eigendecomposition of a symmetric matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator, OVector: Serialize, OMatrix: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator, OVector: Deserialize<'de>, OMatrix: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct SymmetricEigen where DefaultAllocator: Allocator + Allocator, { /// The eigenvectors of the decomposed matrix. pub eigenvectors: OMatrix, /// The unsorted eigenvalues of the decomposed matrix. pub eigenvalues: OVector, } impl Copy for SymmetricEigen where DefaultAllocator: Allocator + Allocator, OMatrix: Copy, OVector: Copy, { } impl SymmetricEigen where DefaultAllocator: Allocator + Allocator, { /// Computes the eigendecomposition of the given symmetric matrix. /// /// Only the lower-triangular parts (including its diagonal) of `m` is read. pub fn new(m: OMatrix) -> Self where D: DimSub, DefaultAllocator: Allocator> + Allocator>, { Self::try_new(m, T::RealField::default_epsilon(), 0).unwrap() } /// Computes the eigendecomposition of the given symmetric matrix with user-specified /// convergence parameters. /// /// Only the lower-triangular part (including its diagonal) of `m` is read. /// /// # Arguments /// /// * `eps` − tolerance used to determine when a value converged to 0. /// * `max_niter` − maximum total number of iterations performed by the algorithm. If this /// number of iteration is exceeded, `None` is returned. If `niter == 0`, then the algorithm /// continues indefinitely until convergence. pub fn try_new(m: OMatrix, eps: T::RealField, max_niter: usize) -> Option where D: DimSub, DefaultAllocator: Allocator> + Allocator>, { Self::do_decompose(m, true, eps, max_niter).map(|(vals, vecs)| SymmetricEigen { eigenvectors: vecs.unwrap(), eigenvalues: vals, }) } fn do_decompose( mut matrix: OMatrix, eigenvectors: bool, eps: T::RealField, max_niter: usize, ) -> Option<(OVector, Option>)> where D: DimSub, DefaultAllocator: Allocator> + Allocator>, { assert!( matrix.is_square(), "Unable to compute the eigendecomposition of a non-square matrix." ); let dim = matrix.nrows(); let m_amax = matrix.camax(); if !m_amax.is_zero() { matrix.unscale_mut(m_amax.clone()); } let (mut q_mat, mut diag, mut off_diag); if eigenvectors { let res = SymmetricTridiagonal::new(matrix).unpack(); q_mat = Some(res.0); diag = res.1; off_diag = res.2; } else { let res = SymmetricTridiagonal::new(matrix).unpack_tridiagonal(); q_mat = None; diag = res.0; off_diag = res.1; } if dim == 1 { diag.scale_mut(m_amax); return Some((diag, q_mat)); } let mut niter = 0; let (mut start, mut end) = Self::delimit_subproblem(&diag, &mut off_diag, dim - 1, eps.clone()); while end != start { let subdim = end - start + 1; #[allow(clippy::comparison_chain)] if subdim > 2 { let m = end - 1; let n = end; let mut vec = Vector2::new( diag[start].clone() - wilkinson_shift( diag[m].clone().clone(), diag[n].clone(), off_diag[m].clone().clone(), ), off_diag[start].clone(), ); for i in start..n { let j = i + 1; if let Some((rot, norm)) = GivensRotation::cancel_y(&vec) { if i > start { // Not the first iteration. off_diag[i - 1] = norm; } let mii = diag[i].clone(); let mjj = diag[j].clone(); let mij = off_diag[i].clone(); let cc = rot.c() * rot.c(); let ss = rot.s() * rot.s(); let cs = rot.c() * rot.s(); let b = cs.clone() * crate::convert(2.0) * mij.clone(); diag[i] = (cc.clone() * mii.clone() + ss.clone() * mjj.clone()) - b.clone(); diag[j] = (ss.clone() * mii.clone() + cc.clone() * mjj.clone()) + b; off_diag[i] = cs * (mii - mjj) + mij * (cc - ss); if i != n - 1 { vec.x = off_diag[i].clone(); vec.y = -rot.s() * off_diag[i + 1].clone(); off_diag[i + 1] *= rot.c(); } if let Some(ref mut q) = q_mat { let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s())); rot.inverse().rotate_rows(&mut q.fixed_columns_mut::<2>(i)); } } else { break; } } if off_diag[m].clone().norm1() <= eps.clone() * (diag[m].clone().norm1() + diag[n].clone().norm1()) { end -= 1; } } else if subdim == 2 { let m = Matrix2::new( diag[start].clone(), off_diag[start].clone().conjugate(), off_diag[start].clone(), diag[start + 1].clone(), ); let eigvals = m.eigenvalues().unwrap(); let basis = Vector2::new( eigvals.x.clone() - diag[start + 1].clone(), off_diag[start].clone(), ); diag[start] = eigvals[0].clone(); diag[start + 1] = eigvals[1].clone(); if let Some(ref mut q) = q_mat { if let Some((rot, _)) = GivensRotation::try_new(basis.x.clone(), basis.y.clone(), eps.clone()) { let rot = GivensRotation::new_unchecked(rot.c(), T::from_real(rot.s())); rot.rotate_rows(&mut q.fixed_columns_mut::<2>(start)); } } end -= 1; } // Re-delimit the subproblem in case some decoupling occurred. let sub = Self::delimit_subproblem(&diag, &mut off_diag, end, eps.clone()); start = sub.0; end = sub.1; niter += 1; if niter == max_niter { return None; } } diag.scale_mut(m_amax); Some((diag, q_mat)) } fn delimit_subproblem( diag: &OVector, off_diag: &mut OVector>, end: usize, eps: T::RealField, ) -> (usize, usize) where D: DimSub, DefaultAllocator: Allocator>, { let mut n = end; while n > 0 { let m = n - 1; if off_diag[m].clone().norm1() > eps.clone() * (diag[n].clone().norm1() + diag[m].clone().norm1()) { break; } n -= 1; } if n == 0 { return (0, 0); } let mut new_start = n - 1; while new_start > 0 { let m = new_start - 1; if off_diag[m].clone().is_zero() || off_diag[m].clone().norm1() <= eps.clone() * (diag[new_start].clone().norm1() + diag[m].clone().norm1()) { off_diag[m] = T::RealField::zero(); break; } new_start -= 1; } (new_start, n) } /// Rebuild the original matrix. /// /// This is useful if some of the eigenvalues have been manually modified. #[must_use] pub fn recompose(&self) -> OMatrix { let mut u_t = self.eigenvectors.clone(); for i in 0..self.eigenvalues.len() { let val = self.eigenvalues[i].clone(); u_t.column_mut(i).scale_mut(val); } u_t.adjoint_mut(); &self.eigenvectors * u_t } } /// Computes the wilkinson shift, i.e., the 2x2 symmetric matrix eigenvalue to its tailing /// component `tnn`. /// /// The inputs are interpreted as the 2x2 matrix: /// tmm tmn /// tmn tnn pub fn wilkinson_shift(tmm: T, tnn: T, tmn: T) -> T { let sq_tmn = tmn.clone() * tmn; if !sq_tmn.is_zero() { // We have the guarantee that the denominator won't be zero. let d = (tmm - tnn.clone()) * crate::convert(0.5); tnn - sq_tmn.clone() / (d.clone() + d.clone().signum() * (d.clone() * d + sq_tmn).sqrt()) } else { tnn } } /* * * Computations of eigenvalues for symmetric matrices. * */ impl, S: Storage> SquareMatrix where DefaultAllocator: Allocator + Allocator> + Allocator + Allocator>, { /// Computes the eigenvalues of this symmetric matrix. /// /// Only the lower-triangular part of the matrix is read. #[must_use] pub fn symmetric_eigenvalues(&self) -> OVector { SymmetricEigen::do_decompose( self.clone_owned(), false, T::RealField::default_epsilon(), 0, ) .unwrap() .0 } } #[cfg(test)] mod test { use crate::base::Matrix2; fn expected_shift(m: Matrix2) -> f64 { let vals = m.eigenvalues().unwrap(); if (vals.x - m.m22).abs() < (vals.y - m.m22).abs() { vals.x } else { vals.y } } #[cfg(feature = "rand")] #[test] fn wilkinson_shift_random() { for _ in 0..1000 { let m = Matrix2::::new_random(); let m = m * m.transpose(); let expected = expected_shift(m); let computed = super::wilkinson_shift(m.m11, m.m22, m.m12); assert!(relative_eq!(expected, computed, epsilon = 1.0e-7)); } } #[test] fn wilkinson_shift_zero() { let m = Matrix2::new(0.0, 0.0, 0.0, 0.0); assert!(relative_eq!( expected_shift(m), super::wilkinson_shift(m.m11, m.m22, m.m12) )); } #[test] fn wilkinson_shift_zero_diagonal() { let m = Matrix2::new(0.0, 42.0, 42.0, 0.0); assert!(relative_eq!( expected_shift(m), super::wilkinson_shift(m.m11, m.m22, m.m12) )); } #[test] fn wilkinson_shift_zero_off_diagonal() { let m = Matrix2::new(42.0, 0.0, 0.0, 64.0); assert!(relative_eq!( expected_shift(m), super::wilkinson_shift(m.m11, m.m22, m.m12) )); } #[test] fn wilkinson_shift_zero_trace() { let m = Matrix2::new(42.0, 20.0, 20.0, -42.0); assert!(relative_eq!( expected_shift(m), super::wilkinson_shift(m.m11, m.m22, m.m12) )); } #[test] fn wilkinson_shift_zero_diag_diff_and_zero_off_diagonal() { let m = Matrix2::new(42.0, 0.0, 0.0, 42.0); assert!(relative_eq!( expected_shift(m), super::wilkinson_shift(m.m11, m.m22, m.m12) )); } #[test] fn wilkinson_shift_zero_det() { let m = Matrix2::new(2.0, 4.0, 4.0, 8.0); assert!(relative_eq!( expected_shift(m), super::wilkinson_shift(m.m11, m.m22, m.m12) )); } } nalgebra-0.33.0/src/linalg/symmetric_tridiagonal.rs000064400000000000000000000126640072674642500205170ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, OMatrix, OVector}; use crate::dimension::{Const, DimDiff, DimSub, U1}; use simba::scalar::ComplexField; use crate::linalg::householder; use crate::Matrix; use std::mem::MaybeUninit; /// Tridiagonalization of a symmetric matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Serialize, OVector>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Deserialize<'de>, OVector>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct SymmetricTridiagonal> where DefaultAllocator: Allocator + Allocator>, { tri: OMatrix, off_diagonal: OVector>, } impl> Copy for SymmetricTridiagonal where DefaultAllocator: Allocator + Allocator>, OMatrix: Copy, OVector>: Copy, { } impl> SymmetricTridiagonal where DefaultAllocator: Allocator + Allocator>, { /// Computes the tridiagonalization of the symmetric matrix `m`. /// /// Only the lower-triangular part (including the diagonal) of `m` is read. pub fn new(mut m: OMatrix) -> Self { let dim = m.shape_generic().0; assert!( m.is_square(), "Unable to compute the symmetric tridiagonal decomposition of a non-square matrix." ); assert!( dim.value() != 0, "Unable to compute the symmetric tridiagonal decomposition of an empty matrix." ); let mut off_diagonal = Matrix::uninit(dim.sub(Const::<1>), Const::<1>); let mut p = Matrix::zeros_generic(dim.sub(Const::<1>), Const::<1>); for i in 0..dim.value() - 1 { let mut m = m.rows_range_mut(i + 1..); let (mut axis, mut m) = m.columns_range_pair_mut(i, i + 1..); let (norm, not_zero) = householder::reflection_axis_mut(&mut axis); off_diagonal[i] = MaybeUninit::new(norm); if not_zero { let mut p = p.rows_range_mut(i..); p.hegemv(crate::convert(2.0), &m, &axis, T::zero()); let dot = axis.dotc(&p); m.hegerc(-T::one(), &p, &axis, T::one()); m.hegerc(-T::one(), &axis, &p, T::one()); m.hegerc(dot * crate::convert(2.0), &axis, &axis, T::one()); } } // Safety: off_diagonal has been fully initialized. let off_diagonal = unsafe { off_diagonal.assume_init() }; Self { tri: m, off_diagonal, } } #[doc(hidden)] // For debugging. pub fn internal_tri(&self) -> &OMatrix { &self.tri } /// Retrieve the orthogonal transformation, diagonal, and off diagonal elements of this /// decomposition. pub fn unpack( self, ) -> ( OMatrix, OVector, OVector>, ) where DefaultAllocator: Allocator + Allocator>, { let diag = self.diagonal(); let q = self.q(); (q, diag, self.off_diagonal.map(T::modulus)) } /// Retrieve the diagonal, and off diagonal elements of this decomposition. pub fn unpack_tridiagonal( self, ) -> ( OVector, OVector>, ) where DefaultAllocator: Allocator + Allocator>, { (self.diagonal(), self.off_diagonal.map(T::modulus)) } /// The diagonal components of this decomposition. #[must_use] pub fn diagonal(&self) -> OVector where DefaultAllocator: Allocator, { self.tri.map_diagonal(|e| e.real()) } /// The off-diagonal components of this decomposition. #[must_use] pub fn off_diagonal(&self) -> OVector> where DefaultAllocator: Allocator>, { self.off_diagonal.map(T::modulus) } /// Computes the orthogonal matrix `Q` of this decomposition. #[must_use] pub fn q(&self) -> OMatrix { householder::assemble_q(&self.tri, self.off_diagonal.as_slice()) } /// Recomputes the original symmetric matrix. pub fn recompose(mut self) -> OMatrix { let q = self.q(); self.tri.fill_lower_triangle(T::zero(), 2); self.tri.fill_upper_triangle(T::zero(), 2); for i in 0..self.off_diagonal.len() { let val = T::from_real(self.off_diagonal[i].clone().modulus()); self.tri[(i + 1, i)] = val.clone(); self.tri[(i, i + 1)] = val; } &q * self.tri * q.adjoint() } } nalgebra-0.33.0/src/linalg/udu.rs000064400000000000000000000056010072674642500147140ustar 00000000000000#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::Allocator; use crate::base::{Const, DefaultAllocator, OMatrix, OVector}; use crate::dimension::Dim; use simba::scalar::RealField; /// UDU factorization. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "OVector: Serialize, OMatrix: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound( deserialize = "OVector: Deserialize<'de>, OMatrix: Deserialize<'de>" )) )] #[derive(Clone, Debug)] pub struct UDU where DefaultAllocator: Allocator + Allocator, { /// The upper triangular matrix resulting from the factorization pub u: OMatrix, /// The diagonal matrix resulting from the factorization pub d: OVector, } impl Copy for UDU where DefaultAllocator: Allocator + Allocator, OVector: Copy, OMatrix: Copy, { } impl UDU where DefaultAllocator: Allocator + Allocator, { /// Computes the UDU^T factorization. /// /// The input matrix `p` is assumed to be symmetric and this decomposition will only read /// the upper-triangular part of `p`. /// /// Ref.: "Optimal control and estimation-Dover Publications", Robert F. Stengel, (1994) page 360 pub fn new(p: OMatrix) -> Option { let n = p.ncols(); let n_dim = p.shape_generic().1; let mut d = OVector::zeros_generic(n_dim, Const::<1>); let mut u = OMatrix::zeros_generic(n_dim, n_dim); d[n - 1] = p[(n - 1, n - 1)].clone(); if d[n - 1].is_zero() { return None; } u.column_mut(n - 1) .axpy(T::one() / d[n - 1].clone(), &p.column(n - 1), T::zero()); for j in (0..n - 1).rev() { let mut d_j = d[j].clone(); for k in j + 1..n { d_j += d[k].clone() * u[(j, k)].clone().powi(2); } d[j] = p[(j, j)].clone() - d_j; if d[j].is_zero() { return None; } for i in (0..=j).rev() { let mut u_ij = u[(i, j)].clone(); for k in j + 1..n { u_ij += d[k].clone() * u[(j, k)].clone() * u[(i, k)].clone(); } u[(i, j)] = (p[(i, j)].clone() - u_ij) / d[j].clone(); } u[(j, j)] = T::one(); } Some(Self { u, d }) } /// Returns the diagonal elements as a matrix #[must_use] pub fn d_matrix(&self) -> OMatrix { OMatrix::from_diagonal(&self.d) } } nalgebra-0.33.0/src/proptest/mod.rs000064400000000000000000000413070072674642500153130ustar 00000000000000//! `proptest`-related features for `nalgebra` data structures. //! //! **This module is only available when the `proptest-support` feature is enabled in `nalgebra`**. //! //! `proptest` is a library for *property-based testing*. While similar to `QuickCheck`, //! which may be more familiar to some users, it has a more sophisticated design that //! provides users with automatic invariant-preserving shrinking. This means that when using //! `proptest`, you rarely need to write your own shrinkers - which is usually very difficult - //! and can instead get this "for free". Moreover, `proptest` does not rely on a canonical //! `Arbitrary` trait implementation like `QuickCheck`, though it does also provide this. For //! more information, check out the [proptest docs](https://docs.rs/proptest/0.10.1/proptest/) //! and the [proptest book](https://altsysrq.github.io/proptest-book/intro.html). //! //! This module provides users of `nalgebra` with tools to work with `nalgebra` types in //! `proptest` tests. At present, this integration is at an early stage, and only //! provides tools for generating matrices and vectors, and not any of the geometry types. //! There are essentially two ways of using this functionality: //! //! - Using the [`matrix()`] function to generate matrices with constraints //! on dimensions and elements. //! - Relying on the [`Arbitrary`] implementation of [`OMatrix`]. //! //! The first variant is almost always preferred in practice. Read on to discover why. //! //! ### Using free function strategies //! //! In `proptest`, it is usually preferable to have free functions that generate *strategies*. //! Currently, the [`matrix()`] function fills this role. The analogous function for //! column vectors is [`vector()`]. Let's take a quick look at how it may be used: //! ``` //! use nalgebra::proptest::matrix; //! use proptest::prelude::*; //! //! proptest! { //! # /* //! #[test] //! # */ //! fn my_test(a in matrix(-5 ..= 5, 2 ..= 4, 1..=4)) { //! // Generates matrices with elements in the range -5 ..= 5, rows in 2..=4 and //! // columns in 1..=4. //! } //! } //! //! # fn main() { my_test(); } //! ``` //! //! In the above example, we generate matrices with constraints on the elements, as well as the //! on the allowed dimensions. When a failing example is found, the resulting shrinking process //! will preserve these invariants. We can use this to compose more advanced strategies. //! For example, let's consider a toy example where we need to generate pairs of matrices //! with exactly 3 rows fixed at compile-time and the same number of columns, but we want the //! number of columns to vary. One way to do this is to use `proptest` combinators in combination //! with [`matrix()`] as follows: //! //! ``` //! use nalgebra::{Dyn, OMatrix, Const}; //! use nalgebra::proptest::matrix; //! use proptest::prelude::*; //! //! type MyMatrix = OMatrix, Dyn>; //! //! /// Returns a strategy for pairs of matrices with `U3` rows and the same number of //! /// columns. //! fn matrix_pairs() -> impl Strategy { //! matrix(-5 ..= 5, Const::<3>, 0 ..= 10) //! // We first generate the initial matrix `a`, and then depending on the concrete //! // instances of `a`, we pick a second matrix with the same number of columns //! .prop_flat_map(|a| { //! let b = matrix(-5 .. 5, Const::<3>, a.ncols()); //! // This returns a new tuple strategy where we keep `a` fixed while //! // the second item is a strategy that generates instances with the same //! // dimensions as `a` //! (Just(a), b) //! }) //! } //! //! proptest! { //! # /* //! #[test] //! # */ //! fn my_test((a, b) in matrix_pairs()) { //! // Let's double-check that the two matrices do indeed have the same number of //! // columns //! prop_assert_eq!(a.ncols(), b.ncols()); //! } //! } //! //! # fn main() { my_test(); } //! ``` //! //! ### The `Arbitrary` implementation //! //! If you don't care about the dimensions of matrices, you can write tests like these: //! //! ``` //! use nalgebra::{DMatrix, DVector, Dyn, Matrix3, OMatrix, Vector3, U3}; //! use proptest::prelude::*; //! //! proptest! { //! # /* //! #[test] //! # */ //! fn test_dynamic(matrix: DMatrix) { //! // This will generate arbitrary instances of `DMatrix` and also attempt //! // to shrink/simplify them when test failures are encountered. //! } //! //! # /* //! #[test] //! # */ //! fn test_static_and_mixed(matrix: Matrix3, matrix2: OMatrix) { //! // Test some property involving these matrices //! } //! //! # /* //! #[test] //! # */ //! fn test_vectors(fixed_size_vector: Vector3, dyn_vector: DVector) { //! // Test some property involving these vectors //! } //! } //! //! # fn main() { test_dynamic(); test_static_and_mixed(); test_vectors(); } //! ``` //! //! While this may be convenient, the default strategies for built-in types in `proptest` can //! generate *any* number, including integers large enough to easily lead to overflow when used in //! matrix operations, or even infinity or NaN values for floating-point types. Therefore //! `Arbitrary` is rarely the method of choice for writing property-based tests. //! //! ### Notes on shrinking //! //! Due to some limitations of the current implementation, shrinking takes place by first //! shrinking the matrix elements before trying to shrink the dimensions of the matrix. //! This unfortunately often leads to the fact that a large number of shrinking iterations //! are necessary to find a (nearly) minimal failing test case. As a workaround for this, //! you can increase the maximum number of shrinking iterations when debugging. To do this, //! simply set the `PROPTEST_MAX_SHRINK_ITERS` variable to a high number. For example: //! //! ```text //! PROPTEST_MAX_SHRINK_ITERS=100000 cargo test my_failing_test //! ``` use crate::allocator::Allocator; use crate::{Const, DefaultAllocator, Dim, DimName, Dyn, OMatrix, Scalar, U1}; use proptest::arbitrary::Arbitrary; use proptest::collection::vec; use proptest::strategy::{BoxedStrategy, Just, NewTree, Strategy, ValueTree}; use proptest::test_runner::TestRunner; use std::ops::RangeInclusive; /// Parameters for arbitrary matrix generation. #[derive(Debug, Clone)] #[non_exhaustive] pub struct MatrixParameters { /// The range of rows that may be generated. pub rows: DimRange, /// The range of columns that may be generated. pub cols: DimRange, /// Parameters for the `Arbitrary` implementation of the scalar values. pub value_parameters: NParameters, } /// A range of allowed dimensions for use in generation of matrices. /// /// The `DimRange` type is used to encode the range of dimensions that can be used for generation /// of matrices with `proptest`. In most cases, you do not need to concern yourself with /// `DimRange` directly, as it supports conversion from other types such as `U3` or inclusive /// ranges such as `5 ..= 6`. The latter example corresponds to dimensions from (inclusive) /// `Dyn(5)` to `Dyn(6)` (inclusive). #[derive(Debug, Clone, PartialEq, Eq)] pub struct DimRange(RangeInclusive); impl DimRange { /// The lower bound for dimensions generated. pub fn lower_bound(&self) -> D { *self.0.start() } /// The upper bound for dimensions generated. pub fn upper_bound(&self) -> D { *self.0.end() } } impl From for DimRange { fn from(dim: D) -> Self { DimRange(dim..=dim) } } impl From> for DimRange { fn from(range: RangeInclusive) -> Self { DimRange(range) } } impl From> for DimRange { fn from(range: RangeInclusive) -> Self { DimRange::from(Dyn(*range.start())..=Dyn(*range.end())) } } impl DimRange { /// Converts the `DimRange` into an instance of `RangeInclusive`. pub fn to_range_inclusive(&self) -> RangeInclusive { self.lower_bound().value()..=self.upper_bound().value() } } impl From for DimRange { fn from(dim: usize) -> Self { DimRange::from(Dyn(dim)) } } /// The default range used for Dyn dimensions when generating arbitrary matrices. fn dynamic_dim_range() -> DimRange { DimRange::from(0..=6) } /// Create a strategy to generate matrices containing values drawn from the given strategy, /// with rows and columns in the provided ranges. /// /// ## Examples /// ``` /// use nalgebra::proptest::matrix; /// use nalgebra::{OMatrix, Const, Dyn}; /// use proptest::prelude::*; /// /// proptest! { /// # /* /// #[test] /// # */ /// fn my_test(a in matrix(0 .. 5i32, Const::<3>, 0 ..= 5)) { /// // Let's make sure we've got the correct type first /// let a: OMatrix<_, Const::<3>, Dyn> = a; /// prop_assert!(a.nrows() == 3); /// prop_assert!(a.ncols() <= 5); /// prop_assert!(a.iter().all(|x_ij| *x_ij >= 0 && *x_ij < 5)); /// } /// } /// /// # fn main() { my_test(); } /// ``` /// /// ## Limitations /// The current implementation has some limitations that lead to suboptimal shrinking behavior. /// See the [module-level documentation](index.html) for more. pub fn matrix( value_strategy: ScalarStrategy, rows: impl Into>, cols: impl Into>, ) -> MatrixStrategy where ScalarStrategy: Strategy + Clone + 'static, ScalarStrategy::Value: Scalar, R: Dim, C: Dim, DefaultAllocator: Allocator, { matrix_(value_strategy, rows.into(), cols.into()) } /// Same as `matrix`, but without the additional anonymous generic types fn matrix_( value_strategy: ScalarStrategy, rows: DimRange, cols: DimRange, ) -> MatrixStrategy where ScalarStrategy: Strategy + Clone + 'static, ScalarStrategy::Value: Scalar, R: Dim, C: Dim, DefaultAllocator: Allocator, { let nrows = rows.lower_bound().value()..=rows.upper_bound().value(); let ncols = cols.lower_bound().value()..=cols.upper_bound().value(); // Even though we can use this function to generate fixed-size matrices, // we currently generate all matrices with heap allocated Vec data. // TODO: Avoid heap allocation for fixed-size matrices. // Doing this *properly* would probably require us to implement a custom // strategy and valuetree with custom shrinking logic, which is not trivial // Perhaps more problematic, however, is the poor shrinking behavior the current setup leads to. // Shrinking in proptest basically happens in "reverse" of the combinators, so // by first generating the dimensions and then the elements, we get shrinking that first // tries to completely shrink the individual elements before trying to reduce the dimension. // This is clearly the opposite of what we want. I can't find any good way around this // short of writing our own custom value tree, which we should probably do at some point. // TODO: Custom implementation of value tree for better shrinking behavior. let strategy = nrows .prop_flat_map(move |nrows| (Just(nrows), ncols.clone())) .prop_flat_map(move |(nrows, ncols)| { ( Just(nrows), Just(ncols), vec(value_strategy.clone(), nrows * ncols), ) }) .prop_map(|(nrows, ncols, values)| { // Note: R/C::from_usize will panic if nrows/ncols does not fit in the dimension type. // However, this should never fail, because we should only be generating // this stuff in the first place OMatrix::from_iterator_generic(R::from_usize(nrows), C::from_usize(ncols), values) }) .boxed(); MatrixStrategy { strategy } } /// Create a strategy to generate column vectors containing values drawn from the given strategy, /// with length in the provided range. /// /// This is a convenience function for calling /// [`matrix(value_strategy, length, U1)`](crate::matrix) and should /// be used when you only want to generate column vectors, as it's simpler and makes the intent /// clear. pub fn vector( value_strategy: ScalarStrategy, length: impl Into>, ) -> MatrixStrategy where ScalarStrategy: Strategy + Clone + 'static, ScalarStrategy::Value: Scalar, D: Dim, DefaultAllocator: Allocator, { matrix_(value_strategy, length.into(), Const::<1>.into()) } impl Default for MatrixParameters where NParameters: Default, R: DimName, C: DimName, { fn default() -> Self { Self { rows: DimRange::from(R::name()), cols: DimRange::from(C::name()), value_parameters: NParameters::default(), } } } impl Default for MatrixParameters where NParameters: Default, R: DimName, { fn default() -> Self { Self { rows: DimRange::from(R::name()), cols: dynamic_dim_range(), value_parameters: NParameters::default(), } } } impl Default for MatrixParameters where NParameters: Default, C: DimName, { fn default() -> Self { Self { rows: dynamic_dim_range(), cols: DimRange::from(C::name()), value_parameters: NParameters::default(), } } } impl Default for MatrixParameters where NParameters: Default, { fn default() -> Self { Self { rows: dynamic_dim_range(), cols: dynamic_dim_range(), value_parameters: NParameters::default(), } } } impl Arbitrary for OMatrix where T: Scalar + Arbitrary, ::Strategy: Clone, R: Dim, C: Dim, MatrixParameters: Default, DefaultAllocator: Allocator, { type Parameters = MatrixParameters; fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { let value_strategy = T::arbitrary_with(args.value_parameters); matrix(value_strategy, args.rows, args.cols) } type Strategy = MatrixStrategy; } /// A strategy for generating matrices. #[derive(Debug, Clone)] pub struct MatrixStrategy where NStrategy: Strategy, NStrategy::Value: Scalar, DefaultAllocator: Allocator, { // For now we only internally hold a boxed strategy. The reason for introducing this // separate wrapper struct is so that we can replace the strategy logic with custom logic // later down the road without introducing significant breaking changes strategy: BoxedStrategy>, } impl Strategy for MatrixStrategy where NStrategy: Strategy, NStrategy::Value: Scalar, R: Dim, C: Dim, DefaultAllocator: Allocator, { type Tree = MatrixValueTree; type Value = OMatrix; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let underlying_tree = self.strategy.new_tree(runner)?; Ok(MatrixValueTree { value_tree: underlying_tree, }) } } /// A value tree for matrices. pub struct MatrixValueTree where T: Scalar, R: Dim, C: Dim, DefaultAllocator: Allocator, { // For now we only wrap a boxed value tree. The reason for wrapping is that this allows us // to swap out the value tree logic down the road without significant breaking changes. value_tree: Box>>, } impl ValueTree for MatrixValueTree where T: Scalar, R: Dim, C: Dim, DefaultAllocator: Allocator, { type Value = OMatrix; fn current(&self) -> Self::Value { self.value_tree.current() } fn simplify(&mut self) -> bool { self.value_tree.simplify() } fn complicate(&mut self) -> bool { self.value_tree.complicate() } } nalgebra-0.33.0/src/sparse/cs_matrix.rs000064400000000000000000000374450072674642500161520ustar 00000000000000use num::Zero; use simba::scalar::ClosedAddAssign; use std::iter; use std::marker::PhantomData; use std::ops::Range; use std::slice; use crate::allocator::Allocator; use crate::sparse::cs_utils; use crate::{Const, DefaultAllocator, Dim, Dyn, Matrix, OVector, Scalar, Vector, U1}; pub struct ColumnEntries<'a, T> { curr: usize, i: &'a [usize], v: &'a [T], } impl<'a, T> ColumnEntries<'a, T> { #[inline] pub fn new(i: &'a [usize], v: &'a [T]) -> Self { assert_eq!(i.len(), v.len()); Self { curr: 0, i, v } } } impl<'a, T: Clone> Iterator for ColumnEntries<'a, T> { type Item = (usize, T); #[inline] fn next(&mut self) -> Option { if self.curr >= self.i.len() { None } else { let res = Some((unsafe { *self.i.get_unchecked(self.curr) }, unsafe { self.v.get_unchecked(self.curr).clone() })); self.curr += 1; res } } } // TODO: this structure exists for now only because impl trait // cannot be used for trait method return types. /// Trait for iterable compressed-column matrix storage. pub trait CsStorageIter<'a, T, R, C = U1> { /// Iterator through all the rows of a specific columns. /// /// The elements are given as a tuple (`row_index`, value). type ColumnEntries: Iterator; /// Iterator through the row indices of a specific column. type ColumnRowIndices: Iterator; /// Iterates through all the row indices of the j-th column. fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices; /// Iterates through all the entries of the j-th column. fn column_entries(&'a self, j: usize) -> Self::ColumnEntries; } /// Trait for mutably iterable compressed-column sparse matrix storage. pub trait CsStorageIterMut<'a, T: 'a, R, C = U1> { /// Mutable iterator through all the values of the sparse matrix. type ValuesMut: Iterator; /// Mutable iterator through all the rows of a specific columns. /// /// The elements are given as a tuple (`row_index`, value). type ColumnEntriesMut: Iterator; /// A mutable iterator through the values buffer of the sparse matrix. fn values_mut(&'a mut self) -> Self::ValuesMut; /// Iterates mutably through all the entries of the j-th column. fn column_entries_mut(&'a mut self, j: usize) -> Self::ColumnEntriesMut; } /// Trait for compressed column sparse matrix storage. pub trait CsStorage: for<'a> CsStorageIter<'a, T, R, C> { /// The shape of the stored matrix. fn shape(&self) -> (R, C); /// Retrieve the i-th row index of the underlying row index buffer. /// /// # Safety /// No bound-checking is performed. unsafe fn row_index_unchecked(&self, i: usize) -> usize; /// The i-th value on the contiguous value buffer of this storage. /// /// # Safety /// No bound-checking is performed. unsafe fn get_value_unchecked(&self, i: usize) -> &T; /// The i-th value on the contiguous value buffer of this storage. fn get_value(&self, i: usize) -> &T; /// Retrieve the i-th row index of the underlying row index buffer. fn row_index(&self, i: usize) -> usize; /// The value indices for the `i`-th column. fn column_range(&self, i: usize) -> Range; /// The size of the value buffer (i.e. the entries known as possibly being non-zero). fn len(&self) -> usize; } /// Trait for compressed column sparse matrix mutable storage. pub trait CsStorageMut: CsStorage + for<'a> CsStorageIterMut<'a, T, R, C> { } /// A storage of column-compressed sparse matrix based on a Vec. #[derive(Clone, Debug, PartialEq)] pub struct CsVecStorage where DefaultAllocator: Allocator, { pub(crate) shape: (R, C), pub(crate) p: OVector, pub(crate) i: Vec, pub(crate) vals: Vec, } impl CsVecStorage where DefaultAllocator: Allocator, { /// The value buffer of this storage. #[must_use] pub fn values(&self) -> &[T] { &self.vals } /// The column shifts buffer. #[must_use] pub fn p(&self) -> &[usize] { self.p.as_slice() } /// The row index buffers. #[must_use] pub fn i(&self) -> &[usize] { &self.i } } impl CsVecStorage where DefaultAllocator: Allocator {} impl<'a, T: Scalar, R: Dim, C: Dim> CsStorageIter<'a, T, R, C> for CsVecStorage where DefaultAllocator: Allocator, { type ColumnEntries = ColumnEntries<'a, T>; type ColumnRowIndices = iter::Cloned>; #[inline] fn column_entries(&'a self, j: usize) -> Self::ColumnEntries { let rng = self.column_range(j); ColumnEntries::new(&self.i[rng.clone()], &self.vals[rng]) } #[inline] fn column_row_indices(&'a self, j: usize) -> Self::ColumnRowIndices { let rng = self.column_range(j); self.i[rng].iter().cloned() } } impl CsStorage for CsVecStorage where DefaultAllocator: Allocator, { #[inline] fn shape(&self) -> (R, C) { self.shape } #[inline] fn len(&self) -> usize { self.vals.len() } #[inline] fn row_index(&self, i: usize) -> usize { self.i[i] } #[inline] unsafe fn row_index_unchecked(&self, i: usize) -> usize { *self.i.get_unchecked(i) } #[inline] unsafe fn get_value_unchecked(&self, i: usize) -> &T { self.vals.get_unchecked(i) } #[inline] fn get_value(&self, i: usize) -> &T { &self.vals[i] } #[inline] fn column_range(&self, j: usize) -> Range { let end = if j + 1 == self.p.len() { self.len() } else { self.p[j + 1] }; self.p[j]..end } } impl<'a, T: Scalar, R: Dim, C: Dim> CsStorageIterMut<'a, T, R, C> for CsVecStorage where DefaultAllocator: Allocator, { type ValuesMut = slice::IterMut<'a, T>; type ColumnEntriesMut = iter::Zip>, slice::IterMut<'a, T>>; #[inline] fn values_mut(&'a mut self) -> Self::ValuesMut { self.vals.iter_mut() } #[inline] fn column_entries_mut(&'a mut self, j: usize) -> Self::ColumnEntriesMut { let rng = self.column_range(j); self.i[rng.clone()] .iter() .cloned() .zip(self.vals[rng].iter_mut()) } } impl CsStorageMut for CsVecStorage where DefaultAllocator: Allocator { } /* pub struct CsSliceStorage<'a, T: Scalar, R: Dim, C: DimAdd> { shape: (R, C), p: VectorSlice>, i: VectorSlice, vals: VectorSlice, }*/ /// A compressed sparse column matrix. #[derive(Clone, Debug, PartialEq)] pub struct CsMatrix< T: Scalar, R: Dim = Dyn, C: Dim = Dyn, S: CsStorage = CsVecStorage, > { pub(crate) data: S, _phantoms: PhantomData<(T, R, C)>, } /// A column compressed sparse vector. pub type CsVector> = CsMatrix; impl CsMatrix where DefaultAllocator: Allocator, { /// Creates a new compressed sparse column matrix with the specified dimension and /// `nvals` possible non-zero values. pub fn new_uninitialized_generic(nrows: R, ncols: C, nvals: usize) -> Self { let mut i = Vec::with_capacity(nvals); unsafe { i.set_len(nvals); } i.shrink_to_fit(); let mut vals = Vec::with_capacity(nvals); unsafe { vals.set_len(nvals); } vals.shrink_to_fit(); CsMatrix { data: CsVecStorage { shape: (nrows, ncols), p: OVector::zeros_generic(ncols, Const::<1>), i, vals, }, _phantoms: PhantomData, } } /* pub(crate) fn from_parts_generic( nrows: R, ncols: C, p: OVector, i: Vec, vals: Vec, ) -> Self where T: Zero + ClosedAddAssign, DefaultAllocator: Allocator, { assert_eq!(ncols.value(), p.len(), "Invalid inptr size."); assert_eq!(i.len(), vals.len(), "Invalid value size."); // Check p. for ptr in &p { assert!(*ptr < i.len(), "Invalid inptr value."); } for ptr in p.as_slice().windows(2) { assert!(ptr[0] <= ptr[1], "Invalid inptr ordering."); } // Check i. for i in &i { assert!(*i < nrows.value(), "Invalid row ptr value.") } let mut res = CsMatrix { data: CsVecStorage { shape: (nrows, ncols), p, i, vals, }, _phantoms: PhantomData, }; // Sort and remove duplicates. res.sort(); res.dedup(); res }*/ } /* impl CsMatrix { pub(crate) fn from_parts( nrows: usize, ncols: usize, p: Vec, i: Vec, vals: Vec, ) -> Self { let nrows = Dyn(nrows); let ncols = Dyn(ncols); let p = DVector::from_data(VecStorage::new(ncols, U1, p)); Self::from_parts_generic(nrows, ncols, p, i, vals) } } */ impl> CsMatrix { pub(crate) fn from_data(data: S) -> Self { CsMatrix { data, _phantoms: PhantomData, } } /// The size of the data buffer. #[must_use] pub fn len(&self) -> usize { self.data.len() } /// The number of rows of this matrix. #[must_use] pub fn nrows(&self) -> usize { self.data.shape().0.value() } /// The number of rows of this matrix. #[must_use] pub fn ncols(&self) -> usize { self.data.shape().1.value() } /// The shape of this matrix. #[must_use] pub fn shape(&self) -> (usize, usize) { let (nrows, ncols) = self.data.shape(); (nrows.value(), ncols.value()) } /// Whether this matrix is square or not. #[must_use] pub fn is_square(&self) -> bool { let (nrows, ncols) = self.data.shape(); nrows.value() == ncols.value() } /// Should always return `true`. /// /// This method is generally used for debugging and should typically not be called in user code. /// This checks that the row inner indices of this matrix are sorted. It takes `O(n)` time, /// where n` is `self.len()`. /// All operations of CSC matrices on nalgebra assume, and will return, sorted indices. /// If at any time this `is_sorted` method returns `false`, then, something went wrong /// and an issue should be open on the nalgebra repository with details on how to reproduce /// this. #[must_use] pub fn is_sorted(&self) -> bool { for j in 0..self.ncols() { let mut curr = None; for idx in self.data.column_row_indices(j) { if let Some(curr) = curr { if idx <= curr { return false; } } curr = Some(idx); } } true } /// Computes the transpose of this sparse matrix. #[must_use = "This function does not mutate the matrix. Consider using the return value or removing the function call. There's also transpose_mut() for square matrices."] pub fn transpose(&self) -> CsMatrix where DefaultAllocator: Allocator, { let (nrows, ncols) = self.data.shape(); let nvals = self.len(); let mut res = CsMatrix::new_uninitialized_generic(ncols, nrows, nvals); let mut workspace = Vector::zeros_generic(nrows, Const::<1>); // Compute p. for i in 0..nvals { let row_id = self.data.row_index(i); workspace[row_id] += 1; } let _ = cs_utils::cumsum(&mut workspace, &mut res.data.p); // Fill the result. for j in 0..ncols.value() { for (row_id, value) in self.data.column_entries(j) { let shift = workspace[row_id]; res.data.vals[shift] = value; res.data.i[shift] = j; workspace[row_id] += 1; } } res } } impl> CsMatrix { /// Iterator through all the mutable values of this sparse matrix. #[inline] pub fn values_mut(&mut self) -> impl Iterator { self.data.values_mut() } } impl CsMatrix where DefaultAllocator: Allocator, { pub(crate) fn sort(&mut self) where T: Zero, DefaultAllocator: Allocator, { // Size = R let nrows = self.data.shape().0; let mut workspace = Matrix::zeros_generic(nrows, Const::<1>); self.sort_with_workspace(workspace.as_mut_slice()); } pub(crate) fn sort_with_workspace(&mut self, workspace: &mut [T]) { assert!( workspace.len() >= self.nrows(), "Workspace must be able to hold at least self.nrows() elements." ); for j in 0..self.ncols() { // Scatter the row in the workspace. for (irow, val) in self.data.column_entries(j) { workspace[irow] = val; } // Sort the index vector. let range = self.data.column_range(j); self.data.i[range.clone()].sort_unstable(); // Permute the values too. for (i, irow) in range.clone().zip(self.data.i[range].iter().cloned()) { self.data.vals[i] = workspace[irow].clone(); } } } // Remove duplicate entries on a sorted CsMatrix. pub(crate) fn dedup(&mut self) where T: Zero + ClosedAddAssign, { let mut curr_i = 0; for j in 0..self.ncols() { let range = self.data.column_range(j); self.data.p[j] = curr_i; if range.start != range.end { let mut value = T::zero(); let mut irow = self.data.i[range.start]; for idx in range { let curr_irow = self.data.i[idx]; if curr_irow == irow { value += self.data.vals[idx].clone(); } else { self.data.i[curr_i] = irow; self.data.vals[curr_i] = value; value = self.data.vals[idx].clone(); irow = curr_irow; curr_i += 1; } } // Handle the last entry. self.data.i[curr_i] = irow; self.data.vals[curr_i] = value; curr_i += 1; } } self.data.i.truncate(curr_i); self.data.i.shrink_to_fit(); self.data.vals.truncate(curr_i); self.data.vals.shrink_to_fit(); } } nalgebra-0.33.0/src/sparse/cs_matrix_cholesky.rs000064400000000000000000000331550072674642500200450ustar 00000000000000use std::iter; use std::mem; use crate::allocator::Allocator; use crate::sparse::{CsMatrix, CsStorage, CsStorageIter, CsStorageIterMut, CsVecStorage}; use crate::{Const, DefaultAllocator, Dim, Matrix, OVector, RealField}; /// The cholesky decomposition of a column compressed sparse matrix. pub struct CsCholesky where DefaultAllocator: Allocator, { // Non-zero pattern of the original matrix upper-triangular part. // Unlike the original matrix, the `original_p` array does contain the last sentinel value // equal to `original_i.len()` at the end. original_p: Vec, original_i: Vec, // Decomposition result. l: CsMatrix, // Used only for the pattern. // TODO: store only the nonzero pattern instead. u: CsMatrix, ok: bool, // Workspaces. work_x: OVector, work_c: OVector, } impl CsCholesky where DefaultAllocator: Allocator + Allocator, { /// Computes the cholesky decomposition of the sparse matrix `m`. pub fn new(m: &CsMatrix) -> Self { let mut me = Self::new_symbolic(m); let _ = me.decompose_left_looking(&m.data.vals); me } /// Perform symbolic analysis for the given matrix. /// /// This does not access the numerical values of `m`. pub fn new_symbolic(m: &CsMatrix) -> Self { assert!( m.is_square(), "The matrix `m` must be square to compute its elimination tree." ); let (l, u) = Self::nonzero_pattern(m); // Workspaces. let work_x = Matrix::zeros_generic(m.data.shape().0, Const::<1>); let work_c = Matrix::zeros_generic(m.data.shape().1, Const::<1>); let mut original_p = m.data.p.as_slice().to_vec(); original_p.push(m.data.i.len()); CsCholesky { original_p, original_i: m.data.i.clone(), l, u, ok: false, work_x, work_c, } } /// The lower-triangular matrix of the cholesky decomposition. #[must_use] pub fn l(&self) -> Option<&CsMatrix> { if self.ok { Some(&self.l) } else { None } } /// Extracts the lower-triangular matrix of the cholesky decomposition. pub fn unwrap_l(self) -> Option> { if self.ok { Some(self.l) } else { None } } /// Perform a numerical left-looking cholesky decomposition of a matrix with the same structure as the /// one used to initialize `self`, but with different non-zero values provided by `values`. pub fn decompose_left_looking(&mut self, values: &[T]) -> bool { assert!( values.len() >= self.original_i.len(), "The set of values is too small." ); let n = self.l.nrows(); // Reset `work_c` to the column pointers of `l`. self.work_c.copy_from(&self.l.data.p); unsafe { for k in 0..n { // Scatter the k-th column of the original matrix with the values provided. let range_k = *self.original_p.get_unchecked(k)..*self.original_p.get_unchecked(k + 1); *self.work_x.vget_unchecked_mut(k) = T::zero(); for p in range_k.clone() { let irow = *self.original_i.get_unchecked(p); if irow >= k { *self.work_x.vget_unchecked_mut(irow) = values.get_unchecked(p).clone(); } } for j in self.u.data.column_row_indices(k) { let factor = -self .l .data .vals .get_unchecked(*self.work_c.vget_unchecked(j)) .clone(); *self.work_c.vget_unchecked_mut(j) += 1; if j < k { for (z, val) in self.l.data.column_entries(j) { if z >= k { *self.work_x.vget_unchecked_mut(z) += val * factor.clone(); } } } } let diag = self.work_x.vget_unchecked(k).clone(); if diag > T::zero() { let denom = diag.sqrt(); *self .l .data .vals .get_unchecked_mut(*self.l.data.p.vget_unchecked(k)) = denom.clone(); for (p, val) in self.l.data.column_entries_mut(k) { *val = self.work_x.vget_unchecked(p).clone() / denom.clone(); *self.work_x.vget_unchecked_mut(p) = T::zero(); } } else { self.ok = false; return false; } } } self.ok = true; true } /// Perform a numerical up-looking cholesky decomposition of a matrix with the same structure as the /// one used to initialize `self`, but with different non-zero values provided by `values`. pub fn decompose_up_looking(&mut self, values: &[T]) -> bool { assert!( values.len() >= self.original_i.len(), "The set of values is too small." ); // Reset `work_c` to the column pointers of `l`. self.work_c.copy_from(&self.l.data.p); // Perform the decomposition. for k in 0..self.l.nrows() { unsafe { // Scatter the k-th column of the original matrix with the values provided. let column_range = *self.original_p.get_unchecked(k)..*self.original_p.get_unchecked(k + 1); *self.work_x.vget_unchecked_mut(k) = T::zero(); for p in column_range.clone() { let irow = *self.original_i.get_unchecked(p); if irow <= k { *self.work_x.vget_unchecked_mut(irow) = values.get_unchecked(p).clone(); } } let mut diag = self.work_x.vget_unchecked(k).clone(); *self.work_x.vget_unchecked_mut(k) = T::zero(); // Triangular solve. for irow in self.u.data.column_row_indices(k) { if irow >= k { continue; } let lki = self.work_x.vget_unchecked(irow).clone() / self .l .data .vals .get_unchecked(*self.l.data.p.vget_unchecked(irow)) .clone(); *self.work_x.vget_unchecked_mut(irow) = T::zero(); for p in *self.l.data.p.vget_unchecked(irow) + 1..*self.work_c.vget_unchecked(irow) { *self .work_x .vget_unchecked_mut(*self.l.data.i.get_unchecked(p)) -= self.l.data.vals.get_unchecked(p).clone() * lki.clone(); } diag -= lki.clone() * lki.clone(); let p = *self.work_c.vget_unchecked(irow); *self.work_c.vget_unchecked_mut(irow) += 1; *self.l.data.i.get_unchecked_mut(p) = k; *self.l.data.vals.get_unchecked_mut(p) = lki; } if diag <= T::zero() { self.ok = false; return false; } // Deal with the diagonal element. let p = *self.work_c.vget_unchecked(k); *self.work_c.vget_unchecked_mut(k) += 1; *self.l.data.i.get_unchecked_mut(p) = k; *self.l.data.vals.get_unchecked_mut(p) = diag.sqrt(); } } self.ok = true; true } fn elimination_tree>(m: &CsMatrix) -> Vec { let nrows = m.nrows(); let mut forest: Vec<_> = iter::repeat(usize::max_value()).take(nrows).collect(); let mut ancestor: Vec<_> = iter::repeat(usize::max_value()).take(nrows).collect(); for k in 0..nrows { for irow in m.data.column_row_indices(k) { let mut i = irow; while i < k { let i_ancestor = ancestor[i]; ancestor[i] = k; if i_ancestor == usize::max_value() { forest[i] = k; break; } i = i_ancestor; } } } forest } fn reach>( m: &CsMatrix, j: usize, max_j: usize, tree: &[usize], marks: &mut Vec, out: &mut Vec, ) { marks.clear(); marks.resize(tree.len(), false); // TODO: avoid all those allocations. let mut tmp = Vec::new(); let mut res = Vec::new(); for irow in m.data.column_row_indices(j) { let mut curr = irow; while curr != usize::max_value() && curr <= max_j && !marks[curr] { marks[curr] = true; tmp.push(curr); curr = tree[curr]; } tmp.append(&mut res); mem::swap(&mut tmp, &mut res); } out.append(&mut res); } fn nonzero_pattern>( m: &CsMatrix, ) -> (CsMatrix, CsMatrix) { let etree = Self::elimination_tree(m); let (nrows, ncols) = m.data.shape(); let mut rows = Vec::with_capacity(m.len()); let mut cols = Matrix::zeros_generic(m.data.shape().0, Const::<1>); let mut marks = Vec::new(); // NOTE: the following will actually compute the non-zero pattern of // the transpose of l. for i in 0..nrows.value() { cols[i] = rows.len(); Self::reach(m, i, i, &etree, &mut marks, &mut rows); } let mut vals = Vec::with_capacity(rows.len()); unsafe { vals.set_len(rows.len()); } vals.shrink_to_fit(); let data = CsVecStorage { shape: (nrows, ncols), p: cols, i: rows, vals, }; let u = CsMatrix::from_data(data); // XXX: avoid this transpose. let l = u.transpose(); (l, u) } /* * * NOTE: All the following methods are untested and currently unused. * * fn column_counts>( m: &CsMatrix, tree: &[usize], ) -> Vec { let len = m.data.shape().0.value(); let mut counts: Vec<_> = iter::repeat(0).take(len).collect(); let mut reach = Vec::new(); let mut marks = Vec::new(); for i in 0..len { Self::reach(m, i, i, tree, &mut marks, &mut reach); for j in reach.drain(..) { counts[j] += 1; } } counts } fn tree_postorder(tree: &[usize]) -> Vec { // TODO: avoid all those allocations? let mut first_child: Vec<_> = iter::repeat(usize::max_value()).take(tree.len()).collect(); let mut other_children: Vec<_> = iter::repeat(usize::max_value()).take(tree.len()).collect(); // Build the children list from the parent list. // The set of children of the node `i` is given by the linked list // starting at `first_child[i]`. The nodes of this list are then: // { first_child[i], other_children[first_child[i]], other_children[other_children[first_child[i]], ... } for (i, parent) in tree.iter().enumerate() { if *parent != usize::max_value() { let brother = first_child[*parent]; first_child[*parent] = i; other_children[i] = brother; } } let mut stack = Vec::with_capacity(tree.len()); let mut postorder = Vec::with_capacity(tree.len()); for (i, node) in tree.iter().enumerate() { if *node == usize::max_value() { Self::dfs( i, &mut first_child, &other_children, &mut stack, &mut postorder, ) } } postorder } fn dfs( i: usize, first_child: &mut [usize], other_children: &[usize], stack: &mut Vec, result: &mut Vec, ) { stack.clear(); stack.push(i); while let Some(n) = stack.pop() { let child = first_child[n]; if child == usize::max_value() { // No children left. result.push(n); } else { stack.push(n); stack.push(child); first_child[n] = other_children[child]; } } } */ } nalgebra-0.33.0/src/sparse/cs_matrix_conversion.rs000064400000000000000000000063340072674642500204100ustar 00000000000000use num::Zero; use simba::scalar::ClosedAddAssign; use crate::allocator::Allocator; use crate::sparse::cs_utils; use crate::sparse::{CsMatrix, CsStorage}; use crate::storage::Storage; use crate::{DefaultAllocator, Dim, Dyn, Matrix, OMatrix, Scalar}; impl<'a, T: Scalar + Zero + ClosedAddAssign> CsMatrix { /// Creates a column-compressed sparse matrix from a sparse matrix in triplet form. pub fn from_triplet( nrows: usize, ncols: usize, irows: &[usize], icols: &[usize], vals: &[T], ) -> Self { Self::from_triplet_generic(Dyn(nrows), Dyn(ncols), irows, icols, vals) } } impl<'a, T: Scalar + Zero + ClosedAddAssign, R: Dim, C: Dim> CsMatrix where DefaultAllocator: Allocator + Allocator, { /// Creates a column-compressed sparse matrix from a sparse matrix in triplet form. pub fn from_triplet_generic( nrows: R, ncols: C, irows: &[usize], icols: &[usize], vals: &[T], ) -> Self { assert!(vals.len() == irows.len()); assert!(vals.len() == icols.len()); let mut res = CsMatrix::new_uninitialized_generic(nrows, ncols, vals.len()); let mut workspace = res.data.p.clone(); // Column count. for j in icols.iter().cloned() { workspace[j] += 1; } let _ = cs_utils::cumsum(&mut workspace, &mut res.data.p); // Fill i and vals. for ((i, j), val) in irows .iter() .cloned() .zip(icols.iter().cloned()) .zip(vals.iter().cloned()) { let offset = workspace[j]; res.data.i[offset] = i; res.data.vals[offset] = val; workspace[j] = offset + 1; } // Sort the result. res.sort(); res.dedup(); res } } impl<'a, T: Scalar + Zero, R: Dim, C: Dim, S> From> for OMatrix where S: CsStorage, DefaultAllocator: Allocator, { fn from(m: CsMatrix) -> Self { let (nrows, ncols) = m.data.shape(); let mut res = OMatrix::zeros_generic(nrows, ncols); for j in 0..ncols.value() { for (i, val) in m.data.column_entries(j) { res[(i, j)] = val; } } res } } impl<'a, T: Scalar + Zero, R: Dim, C: Dim, S> From> for CsMatrix where S: Storage, DefaultAllocator: Allocator + Allocator, { fn from(m: Matrix) -> Self { let (nrows, ncols) = m.data.shape(); let len = m.iter().filter(|e| !e.is_zero()).count(); let mut res = CsMatrix::new_uninitialized_generic(nrows, ncols, len); let mut nz = 0; for j in 0..ncols.value() { let column = m.column(j); res.data.p[j] = nz; for i in 0..nrows.value() { if !column[i].is_zero() { res.data.i[nz] = i; res.data.vals[nz] = column[i].clone(); nz += 1; } } } res } } nalgebra-0.33.0/src/sparse/cs_matrix_ops.rs000064400000000000000000000232730072674642500170250ustar 00000000000000use num::{One, Zero}; use simba::scalar::{ClosedAddAssign, ClosedMulAssign}; use std::ops::{Add, Mul}; use crate::allocator::Allocator; use crate::constraint::{AreMultipliable, DimEq, ShapeConstraint}; use crate::sparse::{CsMatrix, CsStorage, CsStorageMut, CsVector}; use crate::storage::StorageMut; use crate::{Const, DefaultAllocator, Dim, Matrix, OVector, Scalar, Vector}; impl> CsMatrix { fn scatter( &self, j: usize, beta: T, timestamps: &mut [usize], timestamp: usize, workspace: &mut [T], mut nz: usize, res: &mut CsMatrix, ) -> usize where T: ClosedAddAssign + ClosedMulAssign, DefaultAllocator: Allocator, { for (i, val) in self.data.column_entries(j) { if timestamps[i] < timestamp { timestamps[i] = timestamp; res.data.i[nz] = i; nz += 1; workspace[i] = val * beta.clone(); } else { workspace[i] += val * beta.clone(); } } nz } } /* impl CsVector { pub fn axpy(&mut self, alpha: T, x: CsVector, beta: T) { // First, compute the number of non-zero entries. let mut nnzero = 0; // Allocate a size large enough. self.data.set_column_len(0, nnzero); // Fill with the axpy. let mut i = self.len(); let mut j = x.len(); let mut k = nnzero - 1; let mut rid1 = self.data.row_index(0, i - 1); let mut rid2 = x.data.row_index(0, j - 1); while k > 0 { if rid1 == rid2 { self.data.set_row_index(0, k, rid1); self[k] = alpha * x[j] + beta * self[k]; i -= 1; j -= 1; } else if rid1 < rid2 { self.data.set_row_index(0, k, rid1); self[k] = beta * self[i]; i -= 1; } else { self.data.set_row_index(0, k, rid2); self[k] = alpha * x[j]; j -= 1; } k -= 1; } } } */ impl> Vector { /// Perform a sparse axpy operation: `self = alpha * x + beta * self` operation. pub fn axpy_cs(&mut self, alpha: T, x: &CsVector, beta: T) where S2: CsStorage, ShapeConstraint: DimEq, { if beta.is_zero() { for i in 0..x.len() { unsafe { let k = x.data.row_index_unchecked(i); let y = self.vget_unchecked_mut(k); *y = alpha.clone() * x.data.get_value_unchecked(i).clone(); } } } else { // Needed to be sure even components not present on `x` are multiplied. *self *= beta.clone(); for i in 0..x.len() { unsafe { let k = x.data.row_index_unchecked(i); let y = self.vget_unchecked_mut(k); *y += alpha.clone() * x.data.get_value_unchecked(i).clone(); } } } } /* pub fn gemv_sparse(&mut self, alpha: T, a: &CsMatrix, x: &DVector, beta: T) where S2: CsStorage { let col2 = a.column(0); let val = unsafe { *x.vget_unchecked(0) }; self.axpy_sparse(alpha * val, &col2, beta); for j in 1..ncols2 { let col2 = a.column(j); let val = unsafe { *x.vget_unchecked(j) }; self.axpy_sparse(alpha * val, &col2, T::one()); } } */ } impl<'a, 'b, T, R1, R2, C1, C2, S1, S2> Mul<&'b CsMatrix> for &'a CsMatrix where T: Scalar + ClosedAddAssign + ClosedMulAssign + Zero, R1: Dim, C1: Dim, R2: Dim, C2: Dim, S1: CsStorage, S2: CsStorage, ShapeConstraint: AreMultipliable, DefaultAllocator: Allocator + Allocator + Allocator, { type Output = CsMatrix; fn mul(self, rhs: &'b CsMatrix) -> Self::Output { let (nrows1, ncols1) = self.data.shape(); let (nrows2, ncols2) = rhs.data.shape(); assert_eq!( ncols1.value(), nrows2.value(), "Mismatched dimensions for matrix multiplication." ); let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); let mut workspace = OVector::::zeros_generic(nrows1, Const::<1>); let mut nz = 0; for j in 0..ncols2.value() { res.data.p[j] = nz; let new_size_bound = nz + nrows1.value(); res.data.i.resize(new_size_bound, 0); res.data.vals.resize(new_size_bound, T::zero()); for (i, beta) in rhs.data.column_entries(j) { for (k, val) in self.data.column_entries(i) { workspace[k] += val.clone() * beta.clone(); } } for (i, val) in workspace.as_mut_slice().iter_mut().enumerate() { if !val.is_zero() { res.data.i[nz] = i; res.data.vals[nz] = val.clone(); *val = T::zero(); nz += 1; } } } // NOTE: the following has a lower complexity, but is slower in many cases, likely because // of branching inside of the inner loop. // // let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); // let mut timestamps = OVector::zeros_generic(nrows1, Const::<)>; // let mut workspace = unsafe { OVector::new_uninitialized_generic(nrows1, Const::<)> }; // let mut nz = 0; // // for j in 0..ncols2.value() { // res.data.p[j] = nz; // let new_size_bound = nz + nrows1.value(); // res.data.i.resize(new_size_bound, 0); // res.data.vals.resize(new_size_bound, T::zero()); // // for (i, val) in rhs.data.column_entries(j) { // nz = self.scatter( // i, // val, // timestamps.as_mut_slice(), // j + 1, // workspace.as_mut_slice(), // nz, // &mut res, // ); // } // // // Keep the output sorted. // let range = res.data.p[j]..nz; // res.data.i[range.clone()].sort(); // // for p in range { // res.data.vals[p] = workspace[res.data.i[p]] // } // } res.data.i.truncate(nz); res.data.i.shrink_to_fit(); res.data.vals.truncate(nz); res.data.vals.shrink_to_fit(); res } } impl<'a, 'b, T, R1, R2, C1, C2, S1, S2> Add<&'b CsMatrix> for &'a CsMatrix where T: Scalar + ClosedAddAssign + ClosedMulAssign + Zero + One, R1: Dim, C1: Dim, R2: Dim, C2: Dim, S1: CsStorage, S2: CsStorage, ShapeConstraint: DimEq + DimEq, DefaultAllocator: Allocator + Allocator + Allocator, { type Output = CsMatrix; fn add(self, rhs: &'b CsMatrix) -> Self::Output { let (nrows1, ncols1) = self.data.shape(); let (nrows2, ncols2) = rhs.data.shape(); assert_eq!( (nrows1.value(), ncols1.value()), (nrows2.value(), ncols2.value()), "Mismatched dimensions for matrix sum." ); let mut res = CsMatrix::new_uninitialized_generic(nrows1, ncols2, self.len() + rhs.len()); let mut timestamps = OVector::zeros_generic(nrows1, Const::<1>); let mut workspace = Matrix::zeros_generic(nrows1, Const::<1>); let mut nz = 0; for j in 0..ncols2.value() { res.data.p[j] = nz; nz = self.scatter( j, T::one(), timestamps.as_mut_slice(), j + 1, workspace.as_mut_slice(), nz, &mut res, ); nz = rhs.scatter( j, T::one(), timestamps.as_mut_slice(), j + 1, workspace.as_mut_slice(), nz, &mut res, ); // Keep the output sorted. let range = res.data.p[j]..nz; res.data.i[range.clone()].sort_unstable(); for p in range { res.data.vals[p] = workspace[res.data.i[p]].clone() } } res.data.i.truncate(nz); res.data.i.shrink_to_fit(); res.data.vals.truncate(nz); res.data.vals.shrink_to_fit(); res } } impl<'a, 'b, T, R, C, S> Mul for CsMatrix where T: Scalar + ClosedAddAssign + ClosedMulAssign + Zero, R: Dim, C: Dim, S: CsStorageMut, { type Output = Self; fn mul(mut self, rhs: T) -> Self::Output { for e in self.values_mut() { *e *= rhs.clone() } self } } nalgebra-0.33.0/src/sparse/cs_matrix_solve.rs000064400000000000000000000216760072674642500173610ustar 00000000000000use crate::allocator::Allocator; use crate::constraint::{SameNumberOfRows, ShapeConstraint}; use crate::sparse::{CsMatrix, CsStorage, CsVector}; use crate::storage::{Storage, StorageMut}; use crate::{Const, DefaultAllocator, Dim, Matrix, OMatrix, OVector, RealField}; impl> CsMatrix { /// Solve a lower-triangular system with a dense right-hand-side. #[must_use = "Did you mean to use solve_lower_triangular_mut()?"] pub fn solve_lower_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut b = b.clone_owned(); if self.solve_lower_triangular_mut(&mut b) { Some(b) } else { None } } /// Solve a lower-triangular system with `self` transposed and a dense right-hand-side. #[must_use = "Did you mean to use tr_solve_lower_triangular_mut()?"] pub fn tr_solve_lower_triangular( &self, b: &Matrix, ) -> Option> where S2: Storage, DefaultAllocator: Allocator, ShapeConstraint: SameNumberOfRows, { let mut b = b.clone_owned(); if self.tr_solve_lower_triangular_mut(&mut b) { Some(b) } else { None } } /// Solve in-place a lower-triangular system with a dense right-hand-side. pub fn solve_lower_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let (nrows, ncols) = self.data.shape(); assert_eq!(nrows.value(), ncols.value(), "The matrix must be square."); assert_eq!(nrows.value(), b.len(), "Mismatched matrix dimensions."); for j2 in 0..b.ncols() { let mut b = b.column_mut(j2); for j in 0..ncols.value() { let mut column = self.data.column_entries(j); let mut diag_found = false; for (i, val) in &mut column { if i == j { if val.is_zero() { return false; } b[j] /= val; diag_found = true; break; } } if !diag_found { return false; } for (i, val) in column { let bj = b[j].clone(); b[i] -= bj * val; } } } true } /// Solve a lower-triangular system with `self` transposed and a dense right-hand-side. pub fn tr_solve_lower_triangular_mut( &self, b: &mut Matrix, ) -> bool where S2: StorageMut, ShapeConstraint: SameNumberOfRows, { let (nrows, ncols) = self.data.shape(); assert_eq!(nrows.value(), ncols.value(), "The matrix must be square."); assert_eq!(nrows.value(), b.len(), "Mismatched matrix dimensions."); for j2 in 0..b.ncols() { let mut b = b.column_mut(j2); for j in (0..ncols.value()).rev() { let mut column = self.data.column_entries(j); let mut diag = None; for (i, val) in &mut column { if i == j { if val.is_zero() { return false; } diag = Some(val); break; } } if let Some(diag) = diag { for (i, val) in column { let bi = b[i].clone(); b[j] -= val * bi; } b[j] /= diag; } else { return false; } } } true } /// Solve a lower-triangular system with a sparse right-hand-side. #[must_use] pub fn solve_lower_triangular_cs( &self, b: &CsVector, ) -> Option> where S2: CsStorage, DefaultAllocator: Allocator + Allocator, ShapeConstraint: SameNumberOfRows, { let mut reach = Vec::new(); // We don't compute a postordered reach here because it will be sorted after anyway. self.lower_triangular_reach(b, &mut reach); // We sort the reach so the result matrix has sorted indices. reach.sort_unstable(); let mut workspace = Matrix::zeros_generic(b.data.shape().0, Const::<1>); for i in reach.iter().cloned() { workspace[i] = T::zero(); } for (i, val) in b.data.column_entries(0) { workspace[i] = val; } for j in reach.iter().cloned() { let mut column = self.data.column_entries(j); let mut diag_found = false; for (i, val) in &mut column { if i == j { if val.is_zero() { break; } workspace[j] /= val; diag_found = true; break; } } if !diag_found { return None; } for (i, val) in column { let wj = workspace[j].clone(); workspace[i] -= wj * val; } } // Copy the result into a sparse vector. let mut result = CsVector::new_uninitialized_generic(b.data.shape().0, Const::<1>, reach.len()); for (i, val) in reach.iter().zip(result.data.vals.iter_mut()) { *val = workspace[*i].clone(); } result.data.i = reach; Some(result) } /* // Computes the reachable, post-ordered, nodes from `b`. fn lower_triangular_reach_postordered( &self, b: &CsVector, xi: &mut Vec, ) where S2: CsStorage, DefaultAllocator: Allocator, { let mut visited = OVector::repeat_generic(self.data.shape().1, U1, false); let mut stack = Vec::new(); for i in b.data.column_range(0) { let row_index = b.data.row_index(i); if !visited[row_index] { let rng = self.data.column_range(row_index); stack.push((row_index, rng)); self.lower_triangular_dfs(visited.as_mut_slice(), &mut stack, xi); } } } fn lower_triangular_dfs( &self, visited: &mut [bool], stack: &mut Vec<(usize, Range)>, xi: &mut Vec, ) { 'recursion: while let Some((j, rng)) = stack.pop() { visited[j] = true; for i in rng.clone() { let row_id = self.data.row_index(i); if row_id > j && !visited[row_id] { stack.push((j, (i + 1)..rng.end)); stack.push((row_id, self.data.column_range(row_id))); continue 'recursion; } } xi.push(j) } } */ // Computes the nodes reachable from `b` in an arbitrary order. fn lower_triangular_reach(&self, b: &CsVector, xi: &mut Vec) where S2: CsStorage, DefaultAllocator: Allocator, { let mut visited = OVector::repeat_generic(self.data.shape().1, Const::<1>, false); let mut stack = Vec::new(); for irow in b.data.column_row_indices(0) { self.lower_triangular_bfs(irow, visited.as_mut_slice(), &mut stack, xi); } } fn lower_triangular_bfs( &self, start: usize, visited: &mut [bool], stack: &mut Vec, xi: &mut Vec, ) { if !visited[start] { stack.clear(); stack.push(start); xi.push(start); visited[start] = true; while let Some(j) = stack.pop() { for irow in self.data.column_row_indices(j) { if irow > j && !visited[irow] { stack.push(irow); xi.push(irow); visited[irow] = true; } } } } } } nalgebra-0.33.0/src/sparse/cs_utils.rs000064400000000000000000000006020072674642500157670ustar 00000000000000use crate::allocator::Allocator; use crate::{DefaultAllocator, Dim, OVector}; pub fn cumsum(a: &mut OVector, b: &mut OVector) -> usize where DefaultAllocator: Allocator, { assert!(a.len() == b.len()); let mut sum = 0; for i in 0..a.len() { b[i] = sum; sum += a[i]; a[i] = b[i]; } sum } nalgebra-0.33.0/src/sparse/mod.rs000064400000000000000000000005220072674642500147220ustar 00000000000000//! Sparse matrices. pub use self::cs_matrix::{ CsMatrix, CsStorage, CsStorageIter, CsStorageIterMut, CsStorageMut, CsVecStorage, CsVector, }; pub use self::cs_matrix_cholesky::CsCholesky; mod cs_matrix; mod cs_matrix_cholesky; mod cs_matrix_conversion; mod cs_matrix_ops; mod cs_matrix_solve; pub(crate) mod cs_utils; nalgebra-0.33.0/src/third_party/alga/alga_dual_quaternion.rs000064400000000000000000000201310072674642500222570ustar 00000000000000use num::Zero; use alga::general::{ AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, Id, Identity, Module, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::{ AffineTransformation, DirectIsometry, FiniteDimVectorSpace, Isometry, NormedSpace, ProjectiveTransformation, Similarity, Transformation, VectorSpace, }; use crate::base::Vector3; use crate::geometry::{ DualQuaternion, Point3, Quaternion, Translation3, UnitDualQuaternion, UnitQuaternion, }; impl Identity for DualQuaternion { #[inline] fn identity() -> Self { Self::identity() } } impl Identity for DualQuaternion { #[inline] fn identity() -> Self { Self::zero() } } impl AbstractMagma for DualQuaternion { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } impl AbstractMagma for DualQuaternion { #[inline] fn operate(&self, rhs: &Self) -> Self { self + rhs } } impl TwoSidedInverse for DualQuaternion { #[inline] fn two_sided_inverse(&self) -> Self { -self } } macro_rules! impl_structures( ($DualQuaternion: ident; $($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for $DualQuaternion { } )*} ); impl_structures!( DualQuaternion; AbstractSemigroup, AbstractMonoid, AbstractSemigroup, AbstractQuasigroup, AbstractMonoid, AbstractLoop, AbstractGroup, AbstractGroupAbelian ); /* * * Vector space. * */ impl AbstractModule for DualQuaternion { type AbstractRing = T; #[inline] fn multiply_by(&self, n: T) -> Self { self * n } } impl Module for DualQuaternion { type Ring = T; } impl VectorSpace for DualQuaternion { type Field = T; } impl FiniteDimVectorSpace for DualQuaternion { #[inline] fn dimension() -> usize { 8 } #[inline] fn canonical_basis_element(i: usize) -> Self { if i < 4 { DualQuaternion::from_real_and_dual( Quaternion::canonical_basis_element(i), Quaternion::zero(), ) } else { DualQuaternion::from_real_and_dual( Quaternion::zero(), Quaternion::canonical_basis_element(i - 4), ) } } #[inline] fn dot(&self, other: &Self) -> T { self.real.dot(&other.real) + self.dual.dot(&other.dual) } #[inline] unsafe fn component_unchecked(&self, i: usize) -> &T { self.as_ref().get_unchecked(i) } #[inline] unsafe fn component_unchecked_mut(&mut self, i: usize) -> &mut T { self.as_mut().get_unchecked_mut(i) } } impl NormedSpace for DualQuaternion { type RealField = T; type ComplexField = T; #[inline] fn norm_squared(&self) -> T { self.real.norm_squared() } #[inline] fn norm(&self) -> T { self.real.norm() } #[inline] fn normalize(&self) -> Self { self.normalize() } #[inline] fn normalize_mut(&mut self) -> T { self.normalize_mut() } #[inline] fn try_normalize(&self, min_norm: T) -> Option { let real_norm = self.real.norm(); if real_norm > min_norm { Some(Self::from_real_and_dual( self.real / real_norm, self.dual / real_norm, )) } else { None } } #[inline] fn try_normalize_mut(&mut self, min_norm: T) -> Option { let real_norm = self.real.norm(); if real_norm > min_norm { self.real /= real_norm; self.dual /= real_norm; Some(real_norm) } else { None } } } /* * * Implementations for UnitDualQuaternion. * */ impl Identity for UnitDualQuaternion { #[inline] fn identity() -> Self { Self::identity() } } impl AbstractMagma for UnitDualQuaternion { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } impl TwoSidedInverse for UnitDualQuaternion { #[inline] fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } impl_structures!( UnitDualQuaternion; AbstractSemigroup, AbstractQuasigroup, AbstractMonoid, AbstractLoop, AbstractGroup ); impl Transformation> for UnitDualQuaternion { #[inline] fn transform_point(&self, pt: &Point3) -> Point3 { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &Vector3) -> Vector3 { self.transform_vector(v) } } impl ProjectiveTransformation> for UnitDualQuaternion { #[inline] fn inverse_transform_point(&self, pt: &Point3) -> Point3 { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 { self.inverse_transform_vector(v) } } impl AffineTransformation> for UnitDualQuaternion { type Rotation = UnitQuaternion; type NonUniformScaling = Id; type Translation = Translation3; #[inline] fn decompose(&self) -> (Self::Translation, Self::Rotation, Id, Self::Rotation) { ( self.translation(), self.rotation(), Id::new(), UnitQuaternion::identity(), ) } #[inline] fn append_translation(&self, translation: &Self::Translation) -> Self { self * Self::from_parts(*translation, UnitQuaternion::identity()) } #[inline] fn prepend_translation(&self, translation: &Self::Translation) -> Self { Self::from_parts(*translation, UnitQuaternion::identity()) * self } #[inline] fn append_rotation(&self, r: &Self::Rotation) -> Self { r * self } #[inline] fn prepend_rotation(&self, r: &Self::Rotation) -> Self { self * r } #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } } impl Similarity> for UnitDualQuaternion { type Scaling = Id; #[inline] fn translation(&self) -> Translation3 { self.translation() } #[inline] fn rotation(&self) -> UnitQuaternion { self.rotation() } #[inline] fn scaling(&self) -> Id { Id::new() } } macro_rules! marker_impl( ($($Trait: ident),*) => {$( impl $Trait> for UnitDualQuaternion { } )*} ); marker_impl!(Isometry, DirectIsometry); nalgebra-0.33.0/src/third_party/alga/alga_isometry.rs000064400000000000000000000125170072674642500207510ustar 00000000000000use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::Isometry as AlgaIsometry; use alga::linear::{ AffineTransformation, DirectIsometry, ProjectiveTransformation, Rotation, Similarity, Transformation, }; use crate::base::SVector; use crate::geometry::{AbstractRotation, Isometry, Point, Translation}; /* * * Algebraic structures. * */ impl Identity for Isometry where R: Rotation> + AbstractRotation, { #[inline] fn identity() -> Self { Self::identity() } } impl TwoSidedInverse for Isometry where R: Rotation> + AbstractRotation, { #[inline] #[must_use = "Did you mean to use two_sided_inverse_mut()?"] fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } impl AbstractMagma for Isometry where R: Rotation> + AbstractRotation, { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } macro_rules! impl_multiplicative_structures( ($($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for Isometry where R: Rotation> + AbstractRotation { } )*} ); impl_multiplicative_structures!( AbstractSemigroup, AbstractMonoid, AbstractQuasigroup, AbstractLoop, AbstractGroup ); /* * * Transformation groups. * */ impl Transformation> for Isometry where R: Rotation> + AbstractRotation, { #[inline] fn transform_point(&self, pt: &Point) -> Point { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { self.transform_vector(v) } } impl ProjectiveTransformation> for Isometry where R: Rotation> + AbstractRotation, { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.inverse_transform_vector(v) } } impl AffineTransformation> for Isometry where R: Rotation> + AbstractRotation, { type Rotation = R; type NonUniformScaling = Id; type Translation = Translation; #[inline] fn decompose(&self) -> (Self::Translation, R, Id, R) { ( self.translation, self.rotation.clone(), Id::new(), >::identity(), ) } #[inline] fn append_translation(&self, t: &Self::Translation) -> Self { t * self } #[inline] fn prepend_translation(&self, t: &Self::Translation) -> Self { self * t } #[inline] fn append_rotation(&self, r: &Self::Rotation) -> Self { let shift = Transformation::transform_vector(r, &self.translation.vector); Isometry::from_parts(Translation::from(shift), r.clone() * self.rotation.clone()) } #[inline] fn prepend_rotation(&self, r: &Self::Rotation) -> Self { Isometry::from_parts(self.translation, self.rotation.prepend_rotation(r)) } #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { self.clone() } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { self.clone() } #[inline] fn append_rotation_wrt_point(&self, r: &Self::Rotation, p: &Point) -> Option { let mut res = self.clone(); res.append_rotation_wrt_point_mut(r, p); Some(res) } } impl Similarity> for Isometry where R: Rotation> + AbstractRotation, { type Scaling = Id; #[inline] fn translation(&self) -> Translation { self.translation } #[inline] fn rotation(&self) -> R { self.rotation.clone() } #[inline] fn scaling(&self) -> Id { Id::new() } } macro_rules! marker_impl( ($($Trait: ident),*) => {$( impl $Trait> for Isometry where R: Rotation> + AbstractRotation { } )*} ); marker_impl!(AlgaIsometry, DirectIsometry); nalgebra-0.33.0/src/third_party/alga/alga_matrix.rs000064400000000000000000000320610072674642500203760ustar 00000000000000#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; use num::{One, Zero}; use alga::general::{ AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, ClosedAdd, ClosedMul, ClosedNeg, ComplexField, Field, Identity, JoinSemilattice, Lattice, MeetSemilattice, Module, Multiplicative, RingCommutative, TwoSidedInverse, }; use alga::linear::{ FiniteDimInnerSpace, FiniteDimVectorSpace, InnerSpace, NormedSpace, VectorSpace, }; use crate::base::allocator::Allocator; use crate::base::dimension::{Dim, DimName}; use crate::base::storage::{RawStorage, RawStorageMut}; use crate::base::{DefaultAllocator, Matrix, OMatrix, Scalar}; use std::mem::MaybeUninit; /* * * Additive structures. * */ impl Identity for OMatrix where T: Scalar + Zero, DefaultAllocator: Allocator, { #[inline] fn identity() -> Self { Self::from_element(T::zero()) } } impl AbstractMagma for OMatrix where T: Scalar + ClosedAdd, DefaultAllocator: Allocator, { #[inline] fn operate(&self, other: &Self) -> Self { self + other } } impl TwoSidedInverse for OMatrix where T: Scalar + ClosedNeg, DefaultAllocator: Allocator, { #[inline] #[must_use = "Did you mean to use two_sided_inverse_mut()?"] fn two_sided_inverse(&self) -> Self { -self } #[inline] fn two_sided_inverse_mut(&mut self) { *self = -self.clone() } } macro_rules! inherit_additive_structure( ($($marker: ident<$operator: ident> $(+ $bounds: ident)*),* $(,)*) => {$( impl $marker<$operator> for OMatrix where T: Scalar + $marker<$operator> $(+ $bounds)*, DefaultAllocator: Allocator { } )*} ); inherit_additive_structure!( AbstractSemigroup + ClosedAdd, AbstractMonoid + Zero + ClosedAdd, AbstractQuasigroup + ClosedAdd + ClosedNeg, AbstractLoop + Zero + ClosedAdd + ClosedNeg, AbstractGroup + Zero + ClosedAdd + ClosedNeg, AbstractGroupAbelian + Zero + ClosedAdd + ClosedNeg ); impl AbstractModule for OMatrix where T: Scalar + RingCommutative, DefaultAllocator: Allocator, { type AbstractRing = T; #[inline] fn multiply_by(&self, n: T) -> Self { self * n } } impl Module for OMatrix where T: Scalar + RingCommutative, DefaultAllocator: Allocator, { type Ring = T; } impl VectorSpace for OMatrix where T: Scalar + Field, DefaultAllocator: Allocator, { type Field = T; } impl FiniteDimVectorSpace for OMatrix where T: Scalar + Field, DefaultAllocator: Allocator, { #[inline] fn dimension() -> usize { R::dim() * C::dim() } #[inline] fn canonical_basis_element(i: usize) -> Self { assert!(i < Self::dimension(), "Index out of bound."); let mut res = Self::zero(); unsafe { *res.data.get_unchecked_linear_mut(i) = T::one(); } res } #[inline] fn dot(&self, other: &Self) -> T { self.dot(other) } #[inline] unsafe fn component_unchecked(&self, i: usize) -> &T { self.data.get_unchecked_linear(i) } #[inline] unsafe fn component_unchecked_mut(&mut self, i: usize) -> &mut T { self.data.get_unchecked_linear_mut(i) } } impl< T: ComplexField + simba::scalar::ComplexField::RealField>, R: DimName, C: DimName, > NormedSpace for OMatrix where ::RealField: simba::scalar::RealField, DefaultAllocator: Allocator, { type RealField = ::RealField; type ComplexField = T; #[inline] fn norm_squared(&self) -> ::RealField { self.norm_squared() } #[inline] fn norm(&self) -> ::RealField { self.norm() } #[inline] #[must_use = "Did you mean to use normalize_mut()?"] fn normalize(&self) -> Self { self.normalize() } #[inline] fn normalize_mut(&mut self) -> ::RealField { self.normalize_mut() } #[inline] #[must_use = "Did you mean to use try_normalize_mut()?"] fn try_normalize(&self, min_norm: ::RealField) -> Option { self.try_normalize(min_norm) } #[inline] fn try_normalize_mut( &mut self, min_norm: ::RealField, ) -> Option<::RealField> { self.try_normalize_mut(min_norm) } } impl< T: ComplexField + simba::scalar::ComplexField::RealField>, R: DimName, C: DimName, > InnerSpace for OMatrix where ::RealField: simba::scalar::RealField, DefaultAllocator: Allocator, { #[inline] fn angle(&self, other: &Self) -> ::RealField { self.angle(other) } #[inline] fn inner_product(&self, other: &Self) -> T { self.dotc(other) } } // TODO: specialization will greatly simplify this implementation in the future. // In particular: // − use `x()` instead of `::canonical_basis_element` // − use `::new(x, y, z)` instead of `::from_slice` impl< T: ComplexField + simba::scalar::ComplexField::RealField>, R: DimName, C: DimName, > FiniteDimInnerSpace for OMatrix where ::RealField: simba::scalar::RealField, DefaultAllocator: Allocator, { #[inline] fn orthonormalize(vs: &mut [Self]) -> usize { let mut nbasis_elements = 0; for i in 0..vs.len() { { let (elt, basis) = vs[..i + 1].split_last_mut().unwrap(); for basis_element in &basis[..nbasis_elements] { *elt -= &*basis_element * elt.dot(basis_element) } } if vs[i] .try_normalize_mut(::RealField::zero()) .is_some() { // TODO: this will be efficient on dynamically-allocated vectors but for // statically-allocated ones, `.clone_from` would be better. vs.swap(nbasis_elements, i); nbasis_elements += 1; // All the other vectors will be dependent. if nbasis_elements == Self::dimension() { break; } } } nbasis_elements } #[inline] fn orthonormal_subspace_basis(vs: &[Self], mut f: F) where F: FnMut(&Self) -> bool, { // TODO: is this necessary? assert!( vs.len() <= Self::dimension(), "The given set of vectors has no chance of being a free family." ); match Self::dimension() { 1 => { if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)); } } 2 => { if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)) && f(&Self::canonical_basis_element(1)); } else if vs.len() == 1 { let v = &vs[0]; let res = Self::from_column_slice(&[-v[1], v[0]]); let _ = f(&res.normalize()); } // Otherwise, nothing. } 3 => { if vs.is_empty() { let _ = f(&Self::canonical_basis_element(0)) && f(&Self::canonical_basis_element(1)) && f(&Self::canonical_basis_element(2)); } else if vs.len() == 1 { let v = &vs[0]; let mut a; if ComplexField::norm1(v[0]) > ComplexField::norm1(v[1]) { a = Self::from_column_slice(&[v[2], T::zero(), -v[0]]); } else { a = Self::from_column_slice(&[T::zero(), -v[2], v[1]]); }; let _ = a.normalize_mut(); if f(&a.cross(v)) { let _ = f(&a); } } else if vs.len() == 2 { let _ = f(&vs[0].cross(&vs[1]).normalize()); } } _ => { #[cfg(any(feature = "std", feature = "alloc"))] { // XXX: use a GenericArray instead. let mut known_basis = Vec::new(); for v in vs.iter() { known_basis.push(v.normalize()) } for i in 0..Self::dimension() - vs.len() { let mut elt = Self::canonical_basis_element(i); for v in &known_basis { elt -= v * elt.dot(v) } if let Some(subsp_elt) = elt.try_normalize(::RealField::zero()) { if !f(&subsp_elt) { return; }; known_basis.push(subsp_elt); } } } #[cfg(all(not(feature = "std"), not(feature = "alloc")))] { panic!("Cannot compute the orthogonal subspace basis of a vector with a dimension greater than 3 \ if #![no_std] is enabled and the 'alloc' feature is not enabled.") } } } } } /* * * * Multiplicative structures. * * */ impl Identity for OMatrix where T: Scalar + Zero + One, DefaultAllocator: Allocator, { #[inline] fn identity() -> Self { Self::identity() } } impl AbstractMagma for OMatrix where T: Scalar + Zero + One + ClosedAdd + ClosedMul, DefaultAllocator: Allocator, { #[inline] fn operate(&self, other: &Self) -> Self { self * other } } macro_rules! impl_multiplicative_structure( ($($marker: ident<$operator: ident> $(+ $bounds: ident)*),* $(,)*) => {$( impl $marker<$operator> for OMatrix where T: Scalar + Zero + One + ClosedAdd + ClosedMul + $marker<$operator> $(+ $bounds)*, DefaultAllocator: Allocator { } )*} ); impl_multiplicative_structure!( AbstractSemigroup, AbstractMonoid + One ); /* * * Ordering * */ impl MeetSemilattice for OMatrix where T: Scalar + MeetSemilattice, DefaultAllocator: Allocator, { #[inline] fn meet(&self, other: &Self) -> Self { self.zip_map(other, |a, b| a.meet(&b)) } } impl JoinSemilattice for OMatrix where T: Scalar + JoinSemilattice, DefaultAllocator: Allocator, { #[inline] fn join(&self, other: &Self) -> Self { self.zip_map(other, |a, b| a.join(&b)) } } impl Lattice for OMatrix where T: Scalar + Lattice, DefaultAllocator: Allocator, { #[inline] fn meet_join(&self, other: &Self) -> (Self, Self) { let shape = self.shape_generic(); assert!( shape == other.shape_generic(), "Matrix meet/join error: mismatched dimensions." ); let mut mres = Matrix::uninit(shape.0, shape.1); let mut jres = Matrix::uninit(shape.0, shape.1); for i in 0..shape.0.value() * shape.1.value() { unsafe { let mj = self .data .get_unchecked_linear(i) .meet_join(other.data.get_unchecked_linear(i)); *mres.data.get_unchecked_linear_mut(i) = MaybeUninit::new(mj.0); *jres.data.get_unchecked_linear_mut(i) = MaybeUninit::new(mj.1); } } // Safety: both mres and jres are now completely initialized. unsafe { (mres.assume_init(), jres.assume_init()) } } } nalgebra-0.33.0/src/third_party/alga/alga_point.rs000064400000000000000000000031540072674642500202240ustar 00000000000000use alga::general::{Field, JoinSemilattice, Lattice, MeetSemilattice, RealField}; use alga::linear::{AffineSpace, EuclideanSpace}; use crate::base::{SVector, Scalar}; use crate::geometry::Point; impl AffineSpace for Point where T: Scalar + Field, { type Translation = SVector; } impl EuclideanSpace for Point { type Coordinates = SVector; type RealField = T; #[inline] fn origin() -> Self { Self::origin() } #[inline] fn coordinates(&self) -> Self::Coordinates { self.coords } #[inline] fn from_coordinates(coords: Self::Coordinates) -> Self { Self::from(coords) } #[inline] fn scale_by(&self, n: T) -> Self { self * n } } /* * * Ordering * */ impl MeetSemilattice for Point where T: Scalar + MeetSemilattice, { #[inline] fn meet(&self, other: &Self) -> Self { Self::from(self.coords.meet(&other.coords)) } } impl JoinSemilattice for Point where T: Scalar + JoinSemilattice, { #[inline] fn join(&self, other: &Self) -> Self { Self::from(self.coords.join(&other.coords)) } } impl Lattice for Point where T: Scalar + Lattice, { #[inline] fn meet_join(&self, other: &Self) -> (Self, Self) { let (meet, join) = self.coords.meet_join(&other.coords); (Self::from(meet), Self::from(join)) } } nalgebra-0.33.0/src/third_party/alga/alga_quaternion.rs000064400000000000000000000170150072674642500212610ustar 00000000000000use num::Zero; use alga::general::{ AbstractGroup, AbstractGroupAbelian, AbstractLoop, AbstractMagma, AbstractModule, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Additive, Id, Identity, Module, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::{ AffineTransformation, DirectIsometry, FiniteDimVectorSpace, Isometry, NormedSpace, OrthogonalTransformation, ProjectiveTransformation, Rotation, Similarity, Transformation, VectorSpace, }; use crate::base::{Vector3, Vector4}; use crate::geometry::{Point3, Quaternion, UnitQuaternion}; impl Identity for Quaternion { #[inline] fn identity() -> Self { Self::identity() } } impl Identity for Quaternion { #[inline] fn identity() -> Self { Self::zero() } } impl AbstractMagma for Quaternion { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } impl AbstractMagma for Quaternion { #[inline] fn operate(&self, rhs: &Self) -> Self { self + rhs } } impl TwoSidedInverse for Quaternion { #[inline] fn two_sided_inverse(&self) -> Self { -self } } macro_rules! impl_structures( ($Quaternion: ident; $($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for $Quaternion { } )*} ); impl_structures!( Quaternion; AbstractSemigroup, AbstractMonoid, AbstractSemigroup, AbstractQuasigroup, AbstractMonoid, AbstractLoop, AbstractGroup, AbstractGroupAbelian ); /* * * Vector space. * */ impl AbstractModule for Quaternion { type AbstractRing = T; #[inline] fn multiply_by(&self, n: T) -> Self { self * n } } impl Module for Quaternion { type Ring = T; } impl VectorSpace for Quaternion { type Field = T; } impl FiniteDimVectorSpace for Quaternion { #[inline] fn dimension() -> usize { 4 } #[inline] fn canonical_basis_element(i: usize) -> Self { Self::from(Vector4::canonical_basis_element(i)) } #[inline] fn dot(&self, other: &Self) -> T { self.coords.dot(&other.coords) } #[inline] unsafe fn component_unchecked(&self, i: usize) -> &T { self.coords.component_unchecked(i) } #[inline] unsafe fn component_unchecked_mut(&mut self, i: usize) -> &mut T { self.coords.component_unchecked_mut(i) } } impl NormedSpace for Quaternion { type RealField = T; type ComplexField = T; #[inline] fn norm_squared(&self) -> T { self.coords.norm_squared() } #[inline] fn norm(&self) -> T { self.as_vector().norm() } #[inline] fn normalize(&self) -> Self { let v = self.coords.normalize(); Self::from(v) } #[inline] fn normalize_mut(&mut self) -> T { self.coords.normalize_mut() } #[inline] fn try_normalize(&self, min_norm: T) -> Option { self.coords.try_normalize(min_norm).map(Self::from) } #[inline] fn try_normalize_mut(&mut self, min_norm: T) -> Option { self.coords.try_normalize_mut(min_norm) } } /* * * Implementations for UnitQuaternion. * */ impl Identity for UnitQuaternion { #[inline] fn identity() -> Self { Self::identity() } } impl AbstractMagma for UnitQuaternion { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } impl TwoSidedInverse for UnitQuaternion { #[inline] fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } impl_structures!( UnitQuaternion; AbstractSemigroup, AbstractQuasigroup, AbstractMonoid, AbstractLoop, AbstractGroup ); impl Transformation> for UnitQuaternion { #[inline] fn transform_point(&self, pt: &Point3) -> Point3 { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &Vector3) -> Vector3 { self.transform_vector(v) } } impl ProjectiveTransformation> for UnitQuaternion { #[inline] fn inverse_transform_point(&self, pt: &Point3) -> Point3 { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &Vector3) -> Vector3 { self.inverse_transform_vector(v) } } impl AffineTransformation> for UnitQuaternion { type Rotation = Self; type NonUniformScaling = Id; type Translation = Id; #[inline] fn decompose(&self) -> (Id, Self, Id, Self) { (Id::new(), *self, Id::new(), Self::identity()) } #[inline] fn append_translation(&self, _: &Self::Translation) -> Self { *self } #[inline] fn prepend_translation(&self, _: &Self::Translation) -> Self { *self } #[inline] fn append_rotation(&self, r: &Self::Rotation) -> Self { r * self } #[inline] fn prepend_rotation(&self, r: &Self::Rotation) -> Self { self * r } #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } } impl Similarity> for UnitQuaternion { type Scaling = Id; #[inline] fn translation(&self) -> Id { Id::new() } #[inline] fn rotation(&self) -> Self { *self } #[inline] fn scaling(&self) -> Id { Id::new() } } macro_rules! marker_impl( ($($Trait: ident),*) => {$( impl $Trait> for UnitQuaternion { } )*} ); marker_impl!(Isometry, DirectIsometry, OrthogonalTransformation); impl Rotation> for UnitQuaternion { #[inline] fn powf(&self, n: T) -> Option { Some(self.powf(n)) } #[inline] fn rotation_between(a: &Vector3, b: &Vector3) -> Option { Self::rotation_between(a, b) } #[inline] fn scaled_rotation_between(a: &Vector3, b: &Vector3, s: T) -> Option { Self::scaled_rotation_between(a, b, s) } } nalgebra-0.33.0/src/third_party/alga/alga_rotation.rs000064400000000000000000000150650072674642500207360ustar 00000000000000use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::{ self, AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, ProjectiveTransformation, Similarity, Transformation, }; use crate::base::SVector; use crate::geometry::{Point, Rotation}; /* * * Algebraic structures. * */ impl Identity for Rotation { #[inline] fn identity() -> Self { Self::identity() } } impl TwoSidedInverse for Rotation { #[inline] #[must_use = "Did you mean to use two_sided_inverse_mut()?"] fn two_sided_inverse(&self) -> Self { self.transpose() } #[inline] fn two_sided_inverse_mut(&mut self) { self.transpose_mut() } } impl AbstractMagma for Rotation { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } macro_rules! impl_multiplicative_structures( ($($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for Rotation { } )*} ); impl_multiplicative_structures!( AbstractSemigroup, AbstractMonoid, AbstractQuasigroup, AbstractLoop, AbstractGroup ); /* * * Transformation groups. * */ impl Transformation> for Rotation { #[inline] fn transform_point(&self, pt: &Point) -> Point { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { self.transform_vector(v) } } impl ProjectiveTransformation> for Rotation { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.inverse_transform_vector(v) } } impl AffineTransformation> for Rotation { type Rotation = Self; type NonUniformScaling = Id; type Translation = Id; #[inline] fn decompose(&self) -> (Id, Self, Id, Self) { (Id::new(), *self, Id::new(), Self::identity()) } #[inline] fn append_translation(&self, _: &Self::Translation) -> Self { *self } #[inline] fn prepend_translation(&self, _: &Self::Translation) -> Self { *self } #[inline] fn append_rotation(&self, r: &Self::Rotation) -> Self { r * self } #[inline] fn prepend_rotation(&self, r: &Self::Rotation) -> Self { self * r } #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } } impl Similarity> for Rotation { type Scaling = Id; #[inline] fn translation(&self) -> Id { Id::new() } #[inline] fn rotation(&self) -> Self { *self } #[inline] fn scaling(&self) -> Id { Id::new() } } macro_rules! marker_impl( ($($Trait: ident),*) => {$( impl $Trait> for Rotation { } )*} ); marker_impl!(Isometry, DirectIsometry, OrthogonalTransformation); /// Subgroups of the n-dimensional rotation group `SO(n)`. impl linear::Rotation> for Rotation { #[inline] fn powf(&self, _: T) -> Option { // XXX: Add the general case. // XXX: Use specialization for 2D and 3D. unimplemented!() } #[inline] fn rotation_between(_: &SVector, _: &SVector) -> Option { // XXX: Add the general case. // XXX: Use specialization for 2D and 3D. unimplemented!() } #[inline] fn scaled_rotation_between(_: &SVector, _: &SVector, _: T) -> Option { // XXX: Add the general case. // XXX: Use specialization for 2D and 3D. unimplemented!() } } /* impl Matrix for Rotation { type Field = T; type Row = Matrix; type Column = Matrix; type Transpose = Self; #[inline] fn nrows(&self) -> usize { self.submatrix.nrows() } #[inline] fn ncolumns(&self) -> usize { self.submatrix.ncolumns() } #[inline] fn row(&self, i: usize) -> Self::Row { self.submatrix.row(i) } #[inline] fn column(&self, i: usize) -> Self::Column { self.submatrix.column(i) } #[inline] fn get(&self, i: usize, j: usize) -> Self::Field { self.submatrix[(i, j)] } #[inline] unsafe fn get_unchecked(&self, i: usize, j: usize) -> Self::Field { self.submatrix.at_fast(i, j) } #[inline] fn transpose(&self) -> Self::Transpose { Rotation::from_matrix_unchecked(self.submatrix.transpose()) } } impl SquareMatrix for Rotation { type Vector = Matrix; #[inline] fn diagonal(&self) -> Self::Coordinates { self.submatrix.diagonal() } #[inline] fn determinant(&self) -> Self::Field { crate::one() } #[inline] fn try_inverse(&self) -> Option { Some(::transpose(self)) } #[inline] fn try_inverse_mut(&mut self) -> bool { self.transpose_mut(); true } #[inline] fn transpose_mut(&mut self) { self.submatrix.transpose_mut() } } impl InversibleSquareMatrix for Rotation { } */ nalgebra-0.33.0/src/third_party/alga/alga_similarity.rs000064400000000000000000000120000072674642500212470ustar 00000000000000use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Identity, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::Similarity as AlgaSimilarity; use alga::linear::{AffineTransformation, ProjectiveTransformation, Rotation, Transformation}; use crate::base::SVector; use crate::geometry::{AbstractRotation, Point, Similarity, Translation}; /* * * Algebraic structures. * */ impl Identity for Similarity where R: Rotation> + AbstractRotation, { #[inline] fn identity() -> Self { Self::identity() } } impl TwoSidedInverse for Similarity where R: Rotation> + AbstractRotation, { #[inline] #[must_use = "Did you mean to use two_sided_inverse_mut()?"] fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } impl AbstractMagma for Similarity where R: Rotation> + AbstractRotation, { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } macro_rules! impl_multiplicative_structures( ($($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for Similarity where R: Rotation> + AbstractRotation, { } )*} ); impl_multiplicative_structures!( AbstractSemigroup, AbstractMonoid, AbstractQuasigroup, AbstractLoop, AbstractGroup ); /* * * Transformation groups. * */ impl Transformation> for Similarity where R: Rotation> + AbstractRotation, { #[inline] fn transform_point(&self, pt: &Point) -> Point { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { self.transform_vector(v) } } impl ProjectiveTransformation> for Similarity where R: Rotation> + AbstractRotation, { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.inverse_transform_vector(v) } } impl AffineTransformation> for Similarity where R: Rotation> + AbstractRotation, { type NonUniformScaling = T; type Rotation = R; type Translation = Translation; #[inline] fn decompose(&self) -> (Translation, R, T, R) { ( self.isometry.translation, self.isometry.rotation.clone(), self.scaling(), >::identity(), ) } #[inline] fn append_translation(&self, t: &Self::Translation) -> Self { t * self } #[inline] fn prepend_translation(&self, t: &Self::Translation) -> Self { self * t } #[inline] fn append_rotation(&self, r: &Self::Rotation) -> Self { Similarity::from_isometry(self.isometry.append_rotation(r), self.scaling()) } #[inline] fn prepend_rotation(&self, r: &Self::Rotation) -> Self { Similarity::from_isometry(self.isometry.prepend_rotation(r), self.scaling()) } #[inline] fn append_scaling(&self, s: &Self::NonUniformScaling) -> Self { self.append_scaling(*s) } #[inline] fn prepend_scaling(&self, s: &Self::NonUniformScaling) -> Self { self.prepend_scaling(*s) } #[inline] fn append_rotation_wrt_point(&self, r: &Self::Rotation, p: &Point) -> Option { let mut res = self.clone(); res.append_rotation_wrt_point_mut(r, p); Some(res) } } impl AlgaSimilarity> for Similarity where R: Rotation> + AbstractRotation, { type Scaling = T; #[inline] fn translation(&self) -> Translation { self.isometry.translation() } #[inline] fn rotation(&self) -> R { self.isometry.rotation() } #[inline] fn scaling(&self) -> T { self.scaling() } } nalgebra-0.33.0/src/third_party/alga/alga_transform.rs000064400000000000000000000114630072674642500211100ustar 00000000000000use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Identity, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::{ProjectiveTransformation, Transformation}; use crate::base::allocator::Allocator; use crate::base::dimension::{DimNameAdd, DimNameSum, U1}; use crate::base::{Const, DefaultAllocator, SVector}; use crate::geometry::{Point, SubTCategoryOf, TCategory, TProjective, Transform}; /* * * Algebraic structures. * */ impl Identity for Transform where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn identity() -> Self { Self::identity() } } impl TwoSidedInverse for Transform where Const: DimNameAdd, C: SubTCategoryOf, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] #[must_use = "Did you mean to use two_sided_inverse_mut()?"] fn two_sided_inverse(&self) -> Self { self.clone().inverse() } #[inline] fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } impl AbstractMagma for Transform where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>>, { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } macro_rules! impl_multiplicative_structures( ($($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for Transform where Const: DimNameAdd, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> { } )*} ); macro_rules! impl_inversible_multiplicative_structures( ($($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for Transform where Const: DimNameAdd, C: SubTCategoryOf, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> { } )*} ); impl_multiplicative_structures!( AbstractSemigroup, AbstractMonoid, ); impl_inversible_multiplicative_structures!( AbstractQuasigroup, AbstractLoop, AbstractGroup ); /* * * Transformation groups. * */ impl Transformation> for Transform where Const: DimNameAdd, T: RealField + simba::scalar::RealField, C: TCategory, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>>, { #[inline] fn transform_point(&self, pt: &Point) -> Point { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { self.transform_vector(v) } } impl ProjectiveTransformation> for Transform where Const: DimNameAdd, T: RealField + simba::scalar::RealField, C: SubTCategoryOf, DefaultAllocator: Allocator, U1>, DimNameSum, U1>> + Allocator, U1>>, { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { self.inverse_transform_vector(v) } } // TODO: we need to implement an SVD for this. // // impl AffineTransformation> for Transform // where T: RealField, // C: SubTCategoryOf, // DefaultAllocator: Allocator, U1>, DimNameSum, U1>> { // type PreRotation = Rotation; // type NonUniformScaling = OVector; // type PostRotation = Rotation; // type Translation = Translation; // // #[inline] // fn decompose(&self) -> (Self::Translation, Self::PostRotation, Self::NonUniformScaling, Self::PreRotation) { // unimplemented!() // } // } nalgebra-0.33.0/src/third_party/alga/alga_translation.rs000064400000000000000000000112630072674642500214310ustar 00000000000000use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::Translation as AlgaTranslation; use alga::linear::{ AffineTransformation, DirectIsometry, Isometry, ProjectiveTransformation, Similarity, Transformation, }; use crate::base::SVector; use crate::geometry::{Point, Translation}; /* * * Algebraic structures. * */ impl Identity for Translation { #[inline] fn identity() -> Self { Self::identity() } } impl TwoSidedInverse for Translation { #[inline] #[must_use = "Did you mean to use two_sided_inverse_mut()?"] fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } impl AbstractMagma for Translation { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } macro_rules! impl_multiplicative_structures( ($($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for Translation { } )*} ); impl_multiplicative_structures!( AbstractSemigroup, AbstractMonoid, AbstractQuasigroup, AbstractLoop, AbstractGroup ); /* * * Transformation groups. * */ impl Transformation> for Translation { #[inline] fn transform_point(&self, pt: &Point) -> Point { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &SVector) -> SVector { *v } } impl ProjectiveTransformation> for Translation { #[inline] fn inverse_transform_point(&self, pt: &Point) -> Point { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &SVector) -> SVector { *v } } impl AffineTransformation> for Translation { type Rotation = Id; type NonUniformScaling = Id; type Translation = Self; #[inline] fn decompose(&self) -> (Self, Id, Id, Id) { (*self, Id::new(), Id::new(), Id::new()) } #[inline] fn append_translation(&self, t: &Self::Translation) -> Self { t * self } #[inline] fn prepend_translation(&self, t: &Self::Translation) -> Self { self * t } #[inline] fn append_rotation(&self, _: &Self::Rotation) -> Self { *self } #[inline] fn prepend_rotation(&self, _: &Self::Rotation) -> Self { *self } #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } } impl Similarity> for Translation { type Scaling = Id; #[inline] fn translation(&self) -> Self { *self } #[inline] fn rotation(&self) -> Id { Id::new() } #[inline] fn scaling(&self) -> Id { Id::new() } } macro_rules! marker_impl( ($($Trait: ident),*) => {$( impl $Trait> for Translation { } )*} ); marker_impl!(Isometry, DirectIsometry); /// Subgroups of the n-dimensional translation group `T(n)`. impl AlgaTranslation> for Translation { #[inline] fn to_vector(&self) -> SVector { self.vector } #[inline] fn from_vector(v: SVector) -> Option { Some(Self::from(v)) } #[inline] fn powf(&self, n: T) -> Option { Some(Self::from(self.vector * n)) } #[inline] fn translation_between(a: &Point, b: &Point) -> Option { Some(Self::from(b - a)) } } nalgebra-0.33.0/src/third_party/alga/alga_unit_complex.rs000064400000000000000000000104770072674642500216070ustar 00000000000000use alga::general::{ AbstractGroup, AbstractLoop, AbstractMagma, AbstractMonoid, AbstractQuasigroup, AbstractSemigroup, Id, Identity, Multiplicative, RealField, TwoSidedInverse, }; use alga::linear::{ AffineTransformation, DirectIsometry, Isometry, OrthogonalTransformation, ProjectiveTransformation, Rotation, Similarity, Transformation, }; use crate::base::Vector2; use crate::geometry::{Point2, UnitComplex}; /* * * Implementations for UnitComplex. * */ impl Identity for UnitComplex { #[inline] fn identity() -> Self { Self::identity() } } impl AbstractMagma for UnitComplex { #[inline] fn operate(&self, rhs: &Self) -> Self { self * rhs } } impl TwoSidedInverse for UnitComplex { #[inline] #[must_use = "Did you mean to use two_sided_inverse_mut()?"] fn two_sided_inverse(&self) -> Self { self.inverse() } #[inline] fn two_sided_inverse_mut(&mut self) { self.inverse_mut() } } macro_rules! impl_structures( ($($marker: ident<$operator: ident>),* $(,)*) => {$( impl $marker<$operator> for UnitComplex { } )*} ); impl_structures!( AbstractSemigroup, AbstractQuasigroup, AbstractMonoid, AbstractLoop, AbstractGroup ); impl Transformation> for UnitComplex { #[inline] fn transform_point(&self, pt: &Point2) -> Point2 { self.transform_point(pt) } #[inline] fn transform_vector(&self, v: &Vector2) -> Vector2 { self.transform_vector(v) } } impl ProjectiveTransformation> for UnitComplex { #[inline] fn inverse_transform_point(&self, pt: &Point2) -> Point2 { self.inverse_transform_point(pt) } #[inline] fn inverse_transform_vector(&self, v: &Vector2) -> Vector2 { self.inverse_transform_vector(v) } } impl AffineTransformation> for UnitComplex { type Rotation = Self; type NonUniformScaling = Id; type Translation = Id; #[inline] fn decompose(&self) -> (Id, Self, Id, Self) { (Id::new(), *self, Id::new(), Self::identity()) } #[inline] fn append_translation(&self, _: &Self::Translation) -> Self { *self } #[inline] fn prepend_translation(&self, _: &Self::Translation) -> Self { *self } #[inline] fn append_rotation(&self, r: &Self::Rotation) -> Self { r * self } #[inline] fn prepend_rotation(&self, r: &Self::Rotation) -> Self { self * r } #[inline] fn append_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } #[inline] fn prepend_scaling(&self, _: &Self::NonUniformScaling) -> Self { *self } } impl Similarity> for UnitComplex { type Scaling = Id; #[inline] fn translation(&self) -> Id { Id::new() } #[inline] fn rotation(&self) -> Self { *self } #[inline] fn scaling(&self) -> Id { Id::new() } } macro_rules! marker_impl( ($($Trait: ident),*) => {$( impl $Trait> for UnitComplex { } )*} ); marker_impl!(Isometry, DirectIsometry, OrthogonalTransformation); impl Rotation> for UnitComplex { #[inline] fn powf(&self, n: T) -> Option { Some(self.powf(n)) } #[inline] fn rotation_between(a: &Vector2, b: &Vector2) -> Option { Some(Self::rotation_between(a, b)) } #[inline] fn scaled_rotation_between(a: &Vector2, b: &Vector2, s: T) -> Option { Some(Self::scaled_rotation_between(a, b, s)) } } nalgebra-0.33.0/src/third_party/alga/mod.rs000064400000000000000000000003260072674642500166640ustar 00000000000000mod alga_dual_quaternion; mod alga_isometry; mod alga_matrix; mod alga_point; mod alga_quaternion; mod alga_rotation; mod alga_similarity; mod alga_transform; mod alga_translation; mod alga_unit_complex; nalgebra-0.33.0/src/third_party/glam/common/glam_isometry.rs000064400000000000000000000074160072674642500222730ustar 00000000000000use super::glam::{DMat3, DMat4, DQuat, DVec2, DVec3, Mat3, Mat4, Quat, Vec2, Vec3}; use crate::{Isometry2, Isometry3, Matrix3, Matrix4, Translation3, UnitQuaternion}; use std::convert::TryFrom; impl From> for Mat3 { fn from(iso: Isometry2) -> Mat3 { iso.to_homogeneous().into() } } impl From> for Mat4 { fn from(iso: Isometry3) -> Mat4 { iso.to_homogeneous().into() } } impl From> for DMat3 { fn from(iso: Isometry2) -> DMat3 { iso.to_homogeneous().into() } } impl From> for DMat4 { fn from(iso: Isometry3) -> DMat4 { iso.to_homogeneous().into() } } impl From> for (Vec3, Quat) { fn from(iso: Isometry3) -> (Vec3, Quat) { (iso.translation.into(), iso.rotation.into()) } } impl From> for (DVec3, DQuat) { fn from(iso: Isometry3) -> (DVec3, DQuat) { (iso.translation.into(), iso.rotation.into()) } } impl From> for (Vec2, f32) { fn from(iso: Isometry2) -> (Vec2, f32) { let tra = Vec2::new(iso.translation.x, iso.translation.y); let rot = iso.rotation.angle(); (tra, rot) } } impl From> for (DVec2, f64) { fn from(iso: Isometry2) -> (DVec2, f64) { let tra = DVec2::new(iso.translation.x, iso.translation.y); let rot = iso.rotation.angle(); (tra, rot) } } impl From<(Vec3, Quat)> for Isometry3 { fn from((tra, rot): (Vec3, Quat)) -> Self { Isometry3::from_parts(tra.into(), rot.into()) } } impl From<(DVec3, DQuat)> for Isometry3 { fn from((tra, rot): (DVec3, DQuat)) -> Self { Isometry3::from_parts(tra.into(), rot.into()) } } impl From<(Vec2, f32)> for Isometry2 { fn from((tra, rot): (Vec2, f32)) -> Self { Isometry2::new(tra.into(), rot) } } impl From<(DVec2, f64)> for Isometry2 { fn from((tra, rot): (DVec2, f64)) -> Self { Isometry2::new(tra.into(), rot) } } impl From for Isometry3 { fn from(rot: Quat) -> Self { Isometry3::from_parts(Translation3::identity(), rot.into()) } } impl From for Isometry3 { fn from(rot: DQuat) -> Self { Isometry3::from_parts(Translation3::identity(), rot.into()) } } impl From for Isometry3 { fn from(tra: Vec3) -> Self { Isometry3::from_parts(tra.into(), UnitQuaternion::identity()) } } impl From for Isometry3 { fn from(tra: DVec3) -> Self { Isometry3::from_parts(tra.into(), UnitQuaternion::identity()) } } impl From for Isometry2 { fn from(tra: Vec2) -> Self { Isometry2::new(tra.into(), 0.0) } } impl From for Isometry2 { fn from(tra: DVec2) -> Self { Isometry2::new(tra.into(), 0.0) } } impl TryFrom for Isometry2 { type Error = (); fn try_from(mat3: Mat3) -> Result, Self::Error> { crate::try_convert(Matrix3::from(mat3)).ok_or(()) } } impl TryFrom for Isometry3 { type Error = (); fn try_from(mat4: Mat4) -> Result, Self::Error> { crate::try_convert(Matrix4::from(mat4)).ok_or(()) } } impl TryFrom for Isometry2 { type Error = (); fn try_from(mat3: DMat3) -> Result, Self::Error> { crate::try_convert(Matrix3::from(mat3)).ok_or(()) } } impl TryFrom for Isometry3 { type Error = (); fn try_from(mat4: DMat4) -> Result, Self::Error> { crate::try_convert(Matrix4::from(mat4)).ok_or(()) } } nalgebra-0.33.0/src/third_party/glam/common/glam_matrix.rs000064400000000000000000000166500072674642500217240ustar 00000000000000use super::glam::{ BVec2, BVec3, BVec4, DMat2, DMat3, DMat4, DVec2, DVec3, DVec4, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4, }; use crate::storage::RawStorage; use crate::{ Matrix, Matrix2, Matrix3, Matrix4, Unit, UnitVector2, UnitVector3, UnitVector4, Vector, Vector2, Vector3, Vector4, U2, U3, U4, }; use std::convert::TryFrom; macro_rules! impl_vec_conversion( ($N: ty, $Vec2: ty, $Vec3: ty, $Vec4: ty) => { impl From<$Vec2> for Vector2<$N> { #[inline] fn from(e: $Vec2) -> Vector2<$N> { <[$N;2]>::from(e).into() } } impl From> for $Vec2 where S: RawStorage<$N, U2>, { #[inline] fn from(e: Vector<$N, U2, S>) -> $Vec2 { <$Vec2>::new(e[0], e[1]) } } impl From<$Vec3> for Vector3<$N> { #[inline] fn from(e: $Vec3) -> Vector3<$N> { <[$N;3]>::from(e).into() } } impl From> for $Vec3 where S: RawStorage<$N, U3>, { #[inline] fn from(e: Vector<$N, U3, S>) -> $Vec3 { <$Vec3>::new(e[0], e[1], e[2]) } } impl From<$Vec4> for Vector4<$N> { #[inline] fn from(e: $Vec4) -> Vector4<$N> { <[$N;4]>::from(e).into() } } impl From> for $Vec4 where S: RawStorage<$N, U4>, { #[inline] fn from(e: Vector<$N, U4, S>) -> $Vec4 { <$Vec4>::new(e[0], e[1], e[2], e[3]) } } } ); impl_vec_conversion!(f32, Vec2, Vec3, Vec4); impl_vec_conversion!(f64, DVec2, DVec3, DVec4); impl_vec_conversion!(i32, IVec2, IVec3, IVec4); impl_vec_conversion!(u32, UVec2, UVec3, UVec4); impl_vec_conversion!(bool, BVec2, BVec3, BVec4); const ERR: &'static str = "Normalization failed."; macro_rules! impl_unit_vec_conversion( ($N: ty, $Vec2: ty, $Vec3: ty, $Vec4: ty) => { impl TryFrom<$Vec2> for UnitVector2<$N> { type Error = &'static str; #[inline] fn try_from(e: $Vec2) -> Result { Unit::try_new(e.into(), 0.0).ok_or(ERR) } } impl From> for $Vec2 { #[inline] fn from(e: UnitVector2<$N>) -> $Vec2 { e.into_inner().into() } } impl TryFrom<$Vec3> for UnitVector3<$N> { type Error = &'static str; #[inline] fn try_from(e: $Vec3) -> Result { Unit::try_new(e.into(), 0.0).ok_or(ERR) } } impl From> for $Vec3 { #[inline] fn from(e: UnitVector3<$N>) -> $Vec3 { e.into_inner().into() } } impl TryFrom<$Vec4> for UnitVector4<$N> { type Error = &'static str; #[inline] fn try_from(e: $Vec4) -> Result { Unit::try_new(e.into(), 0.0).ok_or(ERR) } } impl From> for $Vec4 { #[inline] fn from(e: UnitVector4<$N>) -> $Vec4 { e.into_inner().into() } } } ); impl_unit_vec_conversion!(f32, Vec2, Vec3, Vec4); impl_unit_vec_conversion!(f64, DVec2, DVec3, DVec4); impl From for Vector3 { #[inline] fn from(e: Vec3A) -> Vector3 { (*e.as_ref()).into() } } impl From> for Vec3A where S: RawStorage, { #[inline] fn from(e: Vector) -> Vec3A { Vec3A::new(e[0], e[1], e[2]) } } impl TryFrom for UnitVector3 { type Error = &'static str; #[inline] fn try_from(e: Vec3A) -> Result { Unit::try_new(e.into(), 0.0).ok_or(ERR) } } impl From> for Vec3A { #[inline] fn from(e: UnitVector3) -> Vec3A { e.into_inner().into() } } impl From for Matrix2 { #[inline] fn from(e: Mat2) -> Matrix2 { e.to_cols_array_2d().into() } } impl From> for Mat2 where S: RawStorage, { #[inline] fn from(e: Matrix) -> Mat2 { Mat2::from_cols( Vec2::new(e[(0, 0)], e[(1, 0)]), Vec2::new(e[(0, 1)], e[(1, 1)]), ) } } impl From for Matrix3 { #[inline] fn from(e: Mat3) -> Matrix3 { e.to_cols_array_2d().into() } } impl From> for Mat3 where S: RawStorage, { #[inline] fn from(e: Matrix) -> Mat3 { Mat3::from_cols( Vec3::new(e[(0, 0)], e[(1, 0)], e[(2, 0)]), Vec3::new(e[(0, 1)], e[(1, 1)], e[(2, 1)]), Vec3::new(e[(0, 2)], e[(1, 2)], e[(2, 2)]), ) } } impl From for Matrix4 { #[inline] fn from(e: Mat4) -> Matrix4 { e.to_cols_array_2d().into() } } impl From> for Mat4 where S: RawStorage, { #[inline] fn from(e: Matrix) -> Mat4 { Mat4::from_cols( Vec4::new(e[(0, 0)], e[(1, 0)], e[(2, 0)], e[(3, 0)]), Vec4::new(e[(0, 1)], e[(1, 1)], e[(2, 1)], e[(3, 1)]), Vec4::new(e[(0, 2)], e[(1, 2)], e[(2, 2)], e[(3, 2)]), Vec4::new(e[(0, 3)], e[(1, 3)], e[(2, 3)], e[(3, 3)]), ) } } impl From for Matrix2 { #[inline] fn from(e: DMat2) -> Matrix2 { e.to_cols_array_2d().into() } } impl From> for DMat2 where S: RawStorage, { #[inline] fn from(e: Matrix) -> DMat2 { DMat2::from_cols( DVec2::new(e[(0, 0)], e[(1, 0)]), DVec2::new(e[(0, 1)], e[(1, 1)]), ) } } impl From for Matrix3 { #[inline] fn from(e: DMat3) -> Matrix3 { e.to_cols_array_2d().into() } } impl From> for DMat3 where S: RawStorage, { #[inline] fn from(e: Matrix) -> DMat3 { DMat3::from_cols( DVec3::new(e[(0, 0)], e[(1, 0)], e[(2, 0)]), DVec3::new(e[(0, 1)], e[(1, 1)], e[(2, 1)]), DVec3::new(e[(0, 2)], e[(1, 2)], e[(2, 2)]), ) } } impl From for Matrix4 { #[inline] fn from(e: DMat4) -> Matrix4 { e.to_cols_array_2d().into() } } impl From> for DMat4 where S: RawStorage, { #[inline] fn from(e: Matrix) -> DMat4 { DMat4::from_cols( DVec4::new(e[(0, 0)], e[(1, 0)], e[(2, 0)], e[(3, 0)]), DVec4::new(e[(0, 1)], e[(1, 1)], e[(2, 1)], e[(3, 1)]), DVec4::new(e[(0, 2)], e[(1, 2)], e[(2, 2)], e[(3, 2)]), DVec4::new(e[(0, 3)], e[(1, 3)], e[(2, 3)], e[(3, 3)]), ) } } nalgebra-0.33.0/src/third_party/glam/common/glam_point.rs000064400000000000000000000036060072674642500215460ustar 00000000000000use super::glam::{ BVec2, BVec3, BVec4, DVec2, DVec3, DVec4, IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4, }; use crate::{Point2, Point3, Point4}; macro_rules! impl_point_conversion( ($N: ty, $Vec2: ty, $Vec3: ty, $Vec4: ty) => { impl From<$Vec2> for Point2<$N> { #[inline] fn from(e: $Vec2) -> Point2<$N> { <[$N;2]>::from(e).into() } } impl From> for $Vec2 { #[inline] fn from(e: Point2<$N>) -> $Vec2 { <$Vec2>::new(e[0], e[1]) } } impl From<$Vec3> for Point3<$N> { #[inline] fn from(e: $Vec3) -> Point3<$N> { <[$N;3]>::from(e).into() } } impl From> for $Vec3 { #[inline] fn from(e: Point3<$N>) -> $Vec3 { <$Vec3>::new(e[0], e[1], e[2]) } } impl From<$Vec4> for Point4<$N> { #[inline] fn from(e: $Vec4) -> Point4<$N> { <[$N;4]>::from(e).into() } } impl From> for $Vec4 { #[inline] fn from(e: Point4<$N>) -> $Vec4 { <$Vec4>::new(e[0], e[1], e[2], e[3]) } } } ); impl_point_conversion!(f32, Vec2, Vec3, Vec4); impl_point_conversion!(f64, DVec2, DVec3, DVec4); impl_point_conversion!(i32, IVec2, IVec3, IVec4); impl_point_conversion!(u32, UVec2, UVec3, UVec4); impl_point_conversion!(bool, BVec2, BVec3, BVec4); impl From for Point3 { #[inline] fn from(e: Vec3A) -> Point3 { (*e.as_ref()).into() } } impl From> for Vec3A { #[inline] fn from(e: Point3) -> Vec3A { Vec3A::new(e[0], e[1], e[2]) } } nalgebra-0.33.0/src/third_party/glam/common/glam_quaternion.rs000064400000000000000000000025430072674642500226010ustar 00000000000000use super::glam::{DQuat, Quat}; use crate::{Quaternion, UnitQuaternion}; impl From for Quaternion { #[inline] fn from(e: Quat) -> Quaternion { Quaternion::new(e.w, e.x, e.y, e.z) } } impl From> for Quat { #[inline] fn from(e: Quaternion) -> Quat { Quat::from_xyzw(e.i, e.j, e.k, e.w) } } impl From> for Quat { #[inline] fn from(e: UnitQuaternion) -> Quat { Quat::from_xyzw(e.i, e.j, e.k, e.w) } } impl From for Quaternion { #[inline] fn from(e: DQuat) -> Quaternion { Quaternion::new(e.w, e.x, e.y, e.z) } } impl From> for DQuat { #[inline] fn from(e: Quaternion) -> DQuat { DQuat::from_xyzw(e.i, e.j, e.k, e.w) } } impl From> for DQuat { #[inline] fn from(e: UnitQuaternion) -> DQuat { DQuat::from_xyzw(e.i, e.j, e.k, e.w) } } impl From for UnitQuaternion { #[inline] fn from(e: Quat) -> UnitQuaternion { UnitQuaternion::new_normalize(Quaternion::from(e)) } } impl From for UnitQuaternion { #[inline] fn from(e: DQuat) -> UnitQuaternion { UnitQuaternion::new_normalize(Quaternion::from(e)) } } nalgebra-0.33.0/src/third_party/glam/common/glam_rotation.rs000064400000000000000000000024470072674642500222560ustar 00000000000000use super::glam::{DMat2, DQuat, Mat2, Quat}; use crate::{Rotation2, Rotation3, UnitComplex, UnitQuaternion}; impl From> for Mat2 { #[inline] fn from(e: Rotation2) -> Mat2 { e.into_inner().into() } } impl From> for DMat2 { #[inline] fn from(e: Rotation2) -> DMat2 { e.into_inner().into() } } impl From> for Quat { #[inline] fn from(e: Rotation3) -> Quat { UnitQuaternion::from(e).into() } } impl From> for DQuat { #[inline] fn from(e: Rotation3) -> DQuat { UnitQuaternion::from(e).into() } } impl From for Rotation2 { #[inline] fn from(e: Mat2) -> Rotation2 { UnitComplex::from(e).to_rotation_matrix() } } impl From for Rotation2 { #[inline] fn from(e: DMat2) -> Rotation2 { UnitComplex::from(e).to_rotation_matrix() } } impl From for Rotation3 { #[inline] fn from(e: Quat) -> Rotation3 { Rotation3::from(UnitQuaternion::from(e)) } } impl From for Rotation3 { #[inline] fn from(e: DQuat) -> Rotation3 { Rotation3::from(UnitQuaternion::from(e)) } } nalgebra-0.33.0/src/third_party/glam/common/glam_similarity.rs000064400000000000000000000027000072674642500225750ustar 00000000000000use super::glam::{DMat3, DMat4, Mat3, Mat4}; use crate::{Matrix3, Matrix4, Similarity2, Similarity3}; use std::convert::TryFrom; impl From> for Mat3 { fn from(iso: Similarity2) -> Mat3 { iso.to_homogeneous().into() } } impl From> for Mat4 { fn from(iso: Similarity3) -> Mat4 { iso.to_homogeneous().into() } } impl From> for DMat3 { fn from(iso: Similarity2) -> DMat3 { iso.to_homogeneous().into() } } impl From> for DMat4 { fn from(iso: Similarity3) -> DMat4 { iso.to_homogeneous().into() } } impl TryFrom for Similarity2 { type Error = (); fn try_from(mat3: Mat3) -> Result, ()> { crate::try_convert(Matrix3::from(mat3)).ok_or(()) } } impl TryFrom for Similarity3 { type Error = (); fn try_from(mat4: Mat4) -> Result, ()> { crate::try_convert(Matrix4::from(mat4)).ok_or(()) } } impl TryFrom for Similarity2 { type Error = (); fn try_from(mat3: DMat3) -> Result, ()> { crate::try_convert(Matrix3::from(mat3)).ok_or(()) } } impl TryFrom for Similarity3 { type Error = (); fn try_from(mat4: DMat4) -> Result, ()> { crate::try_convert(Matrix4::from(mat4)).ok_or(()) } } nalgebra-0.33.0/src/third_party/glam/common/glam_translation.rs000064400000000000000000000033350072674642500227520ustar 00000000000000use super::glam::{DVec2, DVec3, DVec4, Vec2, Vec3, Vec3A, Vec4}; use crate::{Translation2, Translation3, Translation4}; macro_rules! impl_translation_conversion( ($N: ty, $Vec2: ty, $Vec3: ty, $Vec4: ty) => { impl From<$Vec2> for Translation2<$N> { #[inline] fn from(e: $Vec2) -> Translation2<$N> { (*e.as_ref()).into() } } impl From> for $Vec2 { #[inline] fn from(e: Translation2<$N>) -> $Vec2 { e.vector.into() } } impl From<$Vec3> for Translation3<$N> { #[inline] fn from(e: $Vec3) -> Translation3<$N> { (*e.as_ref()).into() } } impl From> for $Vec3 { #[inline] fn from(e: Translation3<$N>) -> $Vec3 { e.vector.into() } } impl From<$Vec4> for Translation4<$N> { #[inline] fn from(e: $Vec4) -> Translation4<$N> { (*e.as_ref()).into() } } impl From> for $Vec4 { #[inline] fn from(e: Translation4<$N>) -> $Vec4 { e.vector.into() } } } ); impl_translation_conversion!(f32, Vec2, Vec3, Vec4); impl_translation_conversion!(f64, DVec2, DVec3, DVec4); impl From for Translation3 { #[inline] fn from(e: Vec3A) -> Translation3 { (*e.as_ref()).into() } } impl From> for Vec3A { #[inline] fn from(e: Translation3) -> Vec3A { e.vector.into() } } nalgebra-0.33.0/src/third_party/glam/common/glam_unit_complex.rs000064400000000000000000000014010072674642500231120ustar 00000000000000use super::glam::{DMat2, Mat2}; use crate::{Complex, UnitComplex}; impl From> for Mat2 { #[inline] fn from(e: UnitComplex) -> Mat2 { e.to_rotation_matrix().into_inner().into() } } impl From> for DMat2 { #[inline] fn from(e: UnitComplex) -> DMat2 { e.to_rotation_matrix().into_inner().into() } } impl From for UnitComplex { #[inline] fn from(e: Mat2) -> UnitComplex { UnitComplex::new_normalize(Complex::new(e.x_axis.x, e.x_axis.y)) } } impl From for UnitComplex { #[inline] fn from(e: DMat2) -> UnitComplex { UnitComplex::new_normalize(Complex::new(e.x_axis.x, e.x_axis.y)) } } nalgebra-0.33.0/src/third_party/glam/mod.rs000064400000000000000000000010600072674642500166740ustar 00000000000000#[cfg(feature = "glam014")] mod v014; #[cfg(feature = "glam015")] mod v015; #[cfg(feature = "glam016")] mod v016; #[cfg(feature = "glam017")] mod v017; #[cfg(feature = "glam018")] mod v018; #[cfg(feature = "glam019")] mod v019; #[cfg(feature = "glam020")] mod v020; #[cfg(feature = "glam021")] mod v021; #[cfg(feature = "glam022")] mod v022; #[cfg(feature = "glam023")] mod v023; #[cfg(feature = "glam024")] mod v024; #[cfg(feature = "glam025")] mod v025; #[cfg(feature = "glam027")] mod v027; #[cfg(feature = "glam028")] mod v028; nalgebra-0.33.0/src/third_party/glam/v014/mod.rs000064400000000000000000000010160072674642500173670ustar 00000000000000#[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 glam014 as glam; nalgebra-0.33.0/src/third_party/glam/v015/mod.rs000064400000000000000000000010160072674642500173700ustar 00000000000000#[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 glam015 as glam; nalgebra-0.33.0/src/third_party/glam/v016/mod.rs000064400000000000000000000010160072674642500173710ustar 00000000000000#[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 glam016 as glam; nalgebra-0.33.0/src/third_party/glam/v017/mod.rs000064400000000000000000000010160072674642500173720ustar 00000000000000#[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 glam017 as glam; nalgebra-0.33.0/src/third_party/glam/v018/mod.rs000064400000000000000000000010160072674642500173730ustar 00000000000000#[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 glam018 as glam; nalgebra-0.33.0/src/third_party/glam/v019/mod.rs000064400000000000000000000010160072674642500173740ustar 00000000000000#[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 glam019 as glam; nalgebra-0.33.0/src/third_party/glam/v020/mod.rs000064400000000000000000000010160072674642500173640ustar 00000000000000#[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 glam020 as glam; nalgebra-0.33.0/src/third_party/glam/v021/mod.rs000064400000000000000000000010160072674642500173650ustar 00000000000000#[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 glam021 as glam; nalgebra-0.33.0/src/third_party/glam/v022/mod.rs000064400000000000000000000010160072674642500173660ustar 00000000000000#[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 glam022 as glam; nalgebra-0.33.0/src/third_party/glam/v023/mod.rs000064400000000000000000000010160072674642500173670ustar 00000000000000#[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 glam023 as glam; nalgebra-0.33.0/src/third_party/glam/v024/mod.rs000064400000000000000000000010160072674642500173700ustar 00000000000000#[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 glam024 as glam; nalgebra-0.33.0/src/third_party/glam/v025/mod.rs000064400000000000000000000010160072674642500173710ustar 00000000000000#[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; nalgebra-0.33.0/src/third_party/glam/v027/mod.rs000064400000000000000000000010160072674642500173730ustar 00000000000000#[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 glam027 as glam; nalgebra-0.33.0/src/third_party/glam/v028/mod.rs000064400000000000000000000010160072674642500173740ustar 00000000000000#[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 glam028 as glam; nalgebra-0.33.0/src/third_party/mint/mint_matrix.rs000064400000000000000000000120530072674642500205030ustar 00000000000000use std::convert::{AsMut, AsRef, From, Into}; use std::mem::{self, MaybeUninit}; use std::ptr; use crate::base::allocator::Allocator; use crate::base::dimension::{Const, DimName, U1, U2, U3, U4}; use crate::base::storage::{IsContiguous, RawStorage, RawStorageMut}; use crate::base::{DefaultAllocator, Matrix, OMatrix, Scalar}; macro_rules! impl_from_into_mint_1D( ($($NRows: ident => $VT:ident [$SZ: expr]);* $(;)*) => {$( impl From> for OMatrix where T: Scalar, DefaultAllocator: Allocator<$NRows, U1> { #[inline] fn from(v: mint::$VT) -> Self { unsafe { let mut res = Matrix::uninit(<$NRows>::name(), Const::<1>); // Copy the data. ptr::copy_nonoverlapping(&v.x, res.data.ptr_mut() as *mut T, $SZ); // Prevent from being dropped the originals we just copied. mem::forget(v); // The result is now fully initialized. res.assume_init() } } } impl Into> for Matrix where T: Scalar, S: RawStorage + IsContiguous { #[inline] fn into(self) -> mint::$VT { // SAFETY: this is OK thanks to the IsContiguous bound. unsafe { let mut res: MaybeUninit> = MaybeUninit::uninit(); // Copy the data. ptr::copy_nonoverlapping(self.data.ptr(), res.as_mut_ptr() as *mut T, $SZ); // Prevent from being dropped the originals we just copied. mem::forget(self); // The result is now fully initialized. res.assume_init() } } } impl AsRef> for Matrix where T: Scalar, S: RawStorage + IsContiguous { #[inline] fn as_ref(&self) -> &mint::$VT { // SAFETY: this is OK thanks to the IsContiguous bound. unsafe { mem::transmute(self.data.ptr()) } } } impl AsMut> for Matrix where T: Scalar, S: RawStorageMut + IsContiguous { #[inline] fn as_mut(&mut self) -> &mut mint::$VT { // SAFETY: this is OK thanks to the IsContiguous bound. unsafe { mem::transmute(self.data.ptr_mut()) } } } )*} ); // Implement for vectors of dimension 2 .. 4. impl_from_into_mint_1D!( U2 => Vector2[2]; U3 => Vector3[3]; U4 => Vector4[4]; ); macro_rules! impl_from_into_mint_2D( ($(($NRows: ty, $NCols: ty) => $MV:ident{ $($component:ident),* }[$SZRows: expr]);* $(;)*) => {$( impl From> for OMatrix where T: Scalar, DefaultAllocator: Allocator<$NRows, $NCols> { #[inline] fn from(m: mint::$MV) -> Self { unsafe { let mut res = Matrix::uninit(<$NRows>::name(), <$NCols>::name()); let mut ptr = res.data.ptr_mut(); $( ptr::copy_nonoverlapping(&m.$component.x, ptr as *mut T, $SZRows); ptr = ptr.offset($SZRows); )* let _ = ptr; // Just to avoid some unused assignment warnings. // Forget the original data to avoid double-free. mem::forget(m); res.assume_init() } } } impl Into> for OMatrix where T: Scalar, DefaultAllocator: Allocator<$NRows, $NCols> { #[inline] fn into(self) -> mint::$MV { unsafe { let mut res: MaybeUninit> = MaybeUninit::uninit(); let mut ptr = self.data.ptr(); $( ptr::copy_nonoverlapping(ptr, ptr::addr_of_mut!((*res.as_mut_ptr()).$component) as *mut T, $SZRows); ptr = ptr.offset($SZRows); )* let _ = ptr; // Forget the original data to avoid double-free. mem::forget(self); res.assume_init() } } } )*} ); // Implement for matrices with shape 2x2 .. 4x4. impl_from_into_mint_2D!( (U2, U2) => ColumnMatrix2{x, y}[2]; (U2, U3) => ColumnMatrix2x3{x, y, z}[2]; (U3, U3) => ColumnMatrix3{x, y, z}[3]; (U3, U4) => ColumnMatrix3x4{x, y, z, w}[3]; (U4, U4) => ColumnMatrix4{x, y, z, w}[4]; ); nalgebra-0.33.0/src/third_party/mint/mint_point.rs000064400000000000000000000030610072674642500203270ustar 00000000000000use crate::base::storage::{RawStorage, RawStorageMut}; use crate::{OVector, Point, Scalar}; use std::convert::{AsMut, AsRef}; macro_rules! impl_from_into_mint_1D( ($($NRows: expr => $PT:ident, $VT:ident [$SZ: expr]);* $(;)*) => {$( impl From> for Point where T: Scalar { #[inline] fn from(p: mint::$PT) -> Self { Self { coords: OVector::from(mint::$VT::from(p)), } } } impl Into> for Point where T: Scalar { #[inline] fn into(self) -> mint::$PT { let mint_vec: mint::$VT = self.coords.into(); mint::$PT::from(mint_vec) } } impl AsRef> for Point where T: Scalar { #[inline] fn as_ref(&self) -> &mint::$PT { unsafe { &*(self.coords.data.ptr() as *const mint::$PT) } } } impl AsMut> for Point where T: Scalar { #[inline] fn as_mut(&mut self) -> &mut mint::$PT { unsafe { &mut *(self.coords.data.ptr_mut() as *mut mint::$PT) } } } )*} ); // Implement for points of dimension 2, 3. impl_from_into_mint_1D!( 2 => Point2, Vector2[2]; 3 => Point3, Vector3[3]; ); nalgebra-0.33.0/src/third_party/mint/mint_quaternion.rs000064400000000000000000000016600072674642500213660ustar 00000000000000use crate::{Quaternion, Scalar, SimdValue, UnitQuaternion}; impl From> for Quaternion { fn from(q: mint::Quaternion) -> Self { Self::new(q.s, q.v.x, q.v.y, q.v.z) } } impl Into> for Quaternion { fn into(self) -> mint::Quaternion { mint::Quaternion { v: mint::Vector3 { x: self[0].clone(), y: self[1].clone(), z: self[2].clone(), }, s: self[3].clone(), } } } impl Into> for UnitQuaternion { fn into(self) -> mint::Quaternion { mint::Quaternion { v: mint::Vector3 { x: self[0].clone(), y: self[1].clone(), z: self[2].clone(), }, s: self[3].clone(), } } } nalgebra-0.33.0/src/third_party/mint/mint_rotation.rs000064400000000000000000000004020072674642500210310ustar 00000000000000use crate::{RealField, Rotation3}; impl From> for Rotation3 { fn from(euler: mint::EulerAngles) -> Self { Self::from_euler_angles(euler.a, euler.b, euler.c) } } nalgebra-0.33.0/src/third_party/mint/mod.rs000064400000000000000000000001150072674642500167230ustar 00000000000000mod mint_matrix; mod mint_point; mod mint_quaternion; mod mint_rotation; nalgebra-0.33.0/src/third_party/mod.rs000064400000000000000000000001250072674642500157550ustar 00000000000000#[cfg(feature = "alga")] mod alga; mod glam; #[cfg(feature = "mint")] mod mint; nalgebra-0.33.0/tests/core/blas.rs000064400000000000000000000100470072674642500150750ustar 00000000000000use na::{geometry::Quaternion, Matrix2, Vector3}; use num_traits::{One, Zero}; #[test] fn gemm_noncommutative() { type Qf64 = Quaternion; let i = Qf64::from_imag(Vector3::new(1.0, 0.0, 0.0)); let j = Qf64::from_imag(Vector3::new(0.0, 1.0, 0.0)); let k = Qf64::from_imag(Vector3::new(0.0, 0.0, 1.0)); let m1 = Matrix2::new(k, Qf64::zero(), j, i); // this is the inverse of m1 let m2 = Matrix2::new(-k, Qf64::zero(), Qf64::one(), -i); let mut res: Matrix2 = Matrix2::zero(); res.gemm(Qf64::one(), &m1, &m2, Qf64::zero()); assert_eq!(res, Matrix2::identity()); let mut res: Matrix2 = Matrix2::identity(); res.gemm(k, &m1, &m2, -k); assert_eq!(res, Matrix2::zero()); } #[cfg(feature = "proptest-support")] mod blas_proptest { use crate::proptest::{PROPTEST_F64, PROPTEST_MATRIX_DIM}; use na::{DMatrix, DVector}; use proptest::{prop_assert, proptest}; proptest! { /* * * Symmetric operators. * */ #[test] fn gemv_symm(n in PROPTEST_MATRIX_DIM, alpha in PROPTEST_F64, beta in PROPTEST_F64) { let a = DMatrix::::new_random(n, n); let a = &a * a.transpose(); let x = DVector::new_random(n); let mut y1 = DVector::new_random(n); let mut y2 = y1.clone(); y1.gemv(alpha, &a, &x, beta); y2.sygemv(alpha, &a.lower_triangle(), &x, beta); prop_assert!(relative_eq!(y1, y2, epsilon = 1.0e-10)); y1.gemv(alpha, &a, &x, 0.0); y2.sygemv(alpha, &a.lower_triangle(), &x, 0.0); prop_assert!(relative_eq!(y1, y2, epsilon = 1.0e-10)) } #[test] fn gemv_tr(n in PROPTEST_MATRIX_DIM, alpha in PROPTEST_F64, beta in PROPTEST_F64) { let a = DMatrix::::new_random(n, n); let x = DVector::new_random(n); let mut y1 = DVector::new_random(n); let mut y2 = y1.clone(); y1.gemv(alpha, &a, &x, beta); y2.gemv_tr(alpha, &a.transpose(), &x, beta); prop_assert!(relative_eq!(y1, y2, epsilon = 1.0e-10)); y1.gemv(alpha, &a, &x, 0.0); y2.gemv_tr(alpha, &a.transpose(), &x, 0.0); prop_assert!(relative_eq!(y1, y2, epsilon = 1.0e-10)) } #[test] fn ger_symm(n in PROPTEST_MATRIX_DIM, alpha in PROPTEST_F64, beta in PROPTEST_F64) { let a = DMatrix::::new_random(n, n); let mut a1 = &a * a.transpose(); let mut a2 = a1.lower_triangle(); let x = DVector::new_random(n); let y = DVector::new_random(n); a1.ger(alpha, &x, &y, beta); a2.syger(alpha, &x, &y, beta); prop_assert!(relative_eq!(a1.lower_triangle(), a2)); a1.ger(alpha, &x, &y, 0.0); a2.syger(alpha, &x, &y, 0.0); prop_assert!(relative_eq!(a1.lower_triangle(), a2)) } #[test] fn quadform(n in PROPTEST_MATRIX_DIM, alpha in PROPTEST_F64, beta in PROPTEST_F64) { let rhs = DMatrix::::new_random(6, n); let mid = DMatrix::::new_random(6, 6); let mut res = DMatrix::new_random(n, n); let expected = &res * beta + rhs.transpose() * &mid * &rhs * alpha; res.quadform(alpha, &mid, &rhs, beta); prop_assert!(relative_eq!(res, expected, epsilon = 1.0e-7)) } #[test] fn quadform_tr(n in PROPTEST_MATRIX_DIM, alpha in PROPTEST_F64, beta in PROPTEST_F64) { let lhs = DMatrix::::new_random(6, n); let mid = DMatrix::::new_random(n, n); let mut res = DMatrix::new_random(6, 6); let expected = &res * beta + &lhs * &mid * lhs.transpose() * alpha; res.quadform_tr(alpha, &lhs, &mid , beta); prop_assert!(relative_eq!(res, expected, epsilon = 1.0e-7)) } } } nalgebra-0.33.0/tests/core/cg.rs000064400000000000000000000040770072674642500145530ustar 00000000000000use na::{Matrix3, Matrix4, Point2, Point3, Vector2, Vector3}; /// See Example 3.4 of "Graphics and Visualization: Principles & Algorithms" /// by Theoharis, Papaioannou, Platis, Patrikalakis. #[test] fn test_scaling_wrt_point_1() { let a = Point2::new(0.0, 0.0); let b = Point2::new(1.0, 1.0); let c = Point2::new(5.0, 2.0); let scaling = Vector2::new(2.0, 2.0); let scale_about = Matrix3::new_nonuniform_scaling_wrt_point(&scaling, &c); let expected_a = Point2::new(-5.0, -2.0); let expected_b = Point2::new(-3.0, 0.0); let result_a = scale_about.transform_point(&a); let result_b = scale_about.transform_point(&b); let result_c = scale_about.transform_point(&c); assert!(expected_a == result_a); assert!(expected_b == result_b); assert!(c == result_c); } /// Based on the same example as the test above. #[test] fn test_scaling_wrt_point_2() { let a = Point3::new(0.0, 0.0, 1.0); let b = Point3::new(1.0, 1.0, 1.0); let c = Point3::new(5.0, 2.0, 1.0); let scaling = Vector3::new(2.0, 2.0, 1.0); let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(&scaling, &c); let expected_a = Point3::new(-5.0, -2.0, 1.0); let expected_b = Point3::new(-3.0, 0.0, 1.0); let result_a = scale_about.transform_point(&a); let result_b = scale_about.transform_point(&b); let result_c = scale_about.transform_point(&c); assert!(expected_a == result_a); assert!(expected_b == result_b); assert!(c == result_c); } /// Based on https://github.com/emlowry/AiE/blob/50bae4068edb686cf8ffacdf6fab8e7cb22e7eb1/Year%201%20Classwork/MathTest/Matrix4x4TestGroup.cpp#L145 #[test] fn test_scaling_wrt_point_3() { let about = Point3::new(2.0, 1.0, -2.0); let scale = Vector3::new(2.0, 0.5, -1.0); let pt = Point3::new(1.0, 2.0, 3.0); let scale_about = Matrix4::new_nonuniform_scaling_wrt_point(&scale, &about); let expected = Point3::new(0.0, 1.5, -7.0); let result = scale_about.transform_point(&pt); assert!(result == expected); } nalgebra-0.33.0/tests/core/conversion.rs000064400000000000000000000347630072674642500163540ustar 00000000000000#![cfg(all(feature = "proptest-support"))] use na::{ self, Affine3, Isometry3, Matrix2, Matrix2x3, Matrix2x4, Matrix2x5, Matrix2x6, Matrix3, Matrix3x2, Matrix3x4, Matrix3x5, Matrix3x6, Matrix4, Matrix4x2, Matrix4x3, Matrix4x5, Matrix4x6, Matrix5, Matrix5x2, Matrix5x3, Matrix5x4, Matrix5x6, Matrix6, Matrix6x2, Matrix6x3, Matrix6x4, Matrix6x5, Projective3, Rotation3, RowVector1, RowVector2, RowVector3, RowVector4, RowVector5, RowVector6, Similarity3, Transform3, UnitQuaternion, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6, }; use na::{DMatrix, DMatrixView, DMatrixViewMut, MatrixView, MatrixViewMut}; use na::{U1, U3, U4}; use crate::proptest::*; use proptest::{prop_assert, prop_assert_eq, proptest}; proptest! { #[test] fn translation_conversion(t in translation3(), p in point3()) { let iso: Isometry3 = na::convert(t); let sim: Similarity3 = na::convert(t); let aff: Affine3 = na::convert(t); let prj: Projective3 = na::convert(t); let tr: Transform3 = na::convert(t); prop_assert_eq!(t, na::try_convert(iso).unwrap()); prop_assert_eq!(t, na::try_convert(sim).unwrap()); prop_assert_eq!(t, na::try_convert(aff).unwrap()); prop_assert_eq!(t, na::try_convert(prj).unwrap()); prop_assert_eq!(t, na::try_convert(tr).unwrap() ); prop_assert_eq!(t * p, iso * p); prop_assert_eq!(t * p, sim * p); prop_assert_eq!(t * p, aff * p); prop_assert_eq!(t * p, prj * p); prop_assert_eq!(t * p, tr * p); } #[test] fn rotation_conversion(r in rotation3(), v in vector3(), p in point3()) { let uq: UnitQuaternion = na::convert(r); let iso: Isometry3 = na::convert(r); let sim: Similarity3 = na::convert(r); let aff: Affine3 = na::convert(r); let prj: Projective3 = na::convert(r); let tr: Transform3 = na::convert(r); prop_assert!(relative_eq!(r, na::try_convert(uq).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(r, na::try_convert(iso).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(r, na::try_convert(sim).unwrap(), epsilon = 1.0e-7)); prop_assert_eq!(r, na::try_convert(aff).unwrap()); prop_assert_eq!(r, na::try_convert(prj).unwrap()); prop_assert_eq!(r, na::try_convert(tr).unwrap() ); // NOTE: we need relative_eq because Isometry and Similarity use quaternions. prop_assert!(relative_eq!(r * v, uq * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(r * v, iso * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(r * v, sim * v, epsilon = 1.0e-7)); prop_assert_eq!(r * v, aff * v); prop_assert_eq!(r * v, prj * v); prop_assert_eq!(r * v, tr * v); prop_assert!(relative_eq!(r * p, uq * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(r * p, iso * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(r * p, sim * p, epsilon = 1.0e-7)); prop_assert_eq!(r * p, aff * p); prop_assert_eq!(r * p, prj * p); prop_assert_eq!(r * p, tr * p); } #[test] fn unit_quaternion_conversion(uq in unit_quaternion(), v in vector3(), p in point3()) { let rot: Rotation3 = na::convert(uq); let iso: Isometry3 = na::convert(uq); let sim: Similarity3 = na::convert(uq); let aff: Affine3 = na::convert(uq); let prj: Projective3 = na::convert(uq); let tr: Transform3 = na::convert(uq); prop_assert_eq!(uq, na::try_convert(iso).unwrap()); prop_assert_eq!(uq, na::try_convert(sim).unwrap()); prop_assert!(relative_eq!(uq, na::try_convert(rot).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq, na::try_convert(aff).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq, na::try_convert(prj).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq, na::try_convert(tr).unwrap(), epsilon = 1.0e-7) ); // NOTE: iso and sim use unit quaternions for the rotation so conversions to them are exact. prop_assert!(relative_eq!(uq * v, rot * v, epsilon = 1.0e-7)); prop_assert_eq!(uq * v, iso * v); prop_assert_eq!(uq * v, sim * v); prop_assert!(relative_eq!(uq * v, aff * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq * v, prj * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq * v, tr * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq * p, rot * p, epsilon = 1.0e-7)); prop_assert_eq!(uq * p, iso * p); prop_assert_eq!(uq * p, sim * p); prop_assert!(relative_eq!(uq * p, aff * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq * p, prj * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(uq * p, tr * p, epsilon = 1.0e-7)); } #[test] fn isometry_conversion(iso in isometry3(), v in vector3(), p in point3()) { let sim: Similarity3 = na::convert(iso); let aff: Affine3 = na::convert(iso); let prj: Projective3 = na::convert(iso); let tr: Transform3 = na::convert(iso); prop_assert_eq!(iso, na::try_convert(sim).unwrap()); prop_assert!(relative_eq!(iso, na::try_convert(aff).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso, na::try_convert(prj).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso, na::try_convert(tr).unwrap(), epsilon = 1.0e-7) ); prop_assert_eq!(iso * v, sim * v); prop_assert!(relative_eq!(iso * v, aff * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso * v, prj * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso * v, tr * v, epsilon = 1.0e-7)); prop_assert_eq!(iso * p, sim * p); prop_assert!(relative_eq!(iso * p, aff * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso * p, prj * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso * p, tr * p, epsilon = 1.0e-7)); } #[test] fn similarity_conversion(sim in similarity3(), v in vector3(), p in point3()) { let aff: Affine3 = na::convert(sim); let prj: Projective3 = na::convert(sim); let tr: Transform3 = na::convert(sim); prop_assert!(relative_eq!(sim, na::try_convert(aff).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim, na::try_convert(prj).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim, na::try_convert(tr).unwrap(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim * v, aff * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim * v, prj * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim * v, tr * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim * p, aff * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim * p, prj * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(sim * p, tr * p, epsilon = 1.0e-7)); } // XXX test Transform } macro_rules! array_vector_conversion( ($($array_vector_conversion_i: ident, $Vector: ident, $SZ: expr);* $(;)*) => {$( #[test] fn $array_vector_conversion_i() { let v = $Vector::from_fn(|i, _| i); let arr: [usize; $SZ] = v.into(); let arr_ref: &[usize; $SZ] = v.as_ref(); let v2 = $Vector::from(arr); for i in 0 .. $SZ { assert_eq!(arr[i], i); assert_eq!(arr_ref[i], i); } assert_eq!(v, v2); } )*} ); array_vector_conversion!( array_vector_conversion_1, Vector1, 1; array_vector_conversion_2, Vector2, 2; array_vector_conversion_3, Vector3, 3; array_vector_conversion_4, Vector4, 4; array_vector_conversion_5, Vector5, 5; array_vector_conversion_6, Vector6, 6; ); macro_rules! array_row_vector_conversion( ($($array_vector_conversion_i: ident, $Vector: ident, $SZ: expr);* $(;)*) => {$( #[test] fn $array_vector_conversion_i() { let v = $Vector::from_fn(|_, i| i); let arr: [usize; $SZ] = v.into(); let arr_ref = v.as_ref(); let v2 = $Vector::from(arr); for i in 0 .. $SZ { assert_eq!(arr[i], i); assert_eq!(arr_ref[i], i); } assert_eq!(v, v2); } )*} ); array_row_vector_conversion!( array_row_vector_conversion_1, RowVector1, 1; array_row_vector_conversion_2, RowVector2, 2; array_row_vector_conversion_3, RowVector3, 3; array_row_vector_conversion_4, RowVector4, 4; array_row_vector_conversion_5, RowVector5, 5; array_row_vector_conversion_6, RowVector6, 6; ); macro_rules! array_matrix_conversion( ($($array_matrix_conversion_i_j: ident, $Matrix: ident, ($NRows: expr, $NCols: expr));* $(;)*) => {$( #[test] fn $array_matrix_conversion_i_j() { let m = $Matrix::from_fn(|i, j| i * 10 + j); let arr: [[usize; $NRows]; $NCols] = m.into(); let arr_ref = m.as_ref(); let m2 = $Matrix::from(arr); for i in 0 .. $NRows { for j in 0 .. $NCols { assert_eq!(arr[j][i], i * 10 + j); assert_eq!(arr_ref[j][i], i * 10 + j); } } assert_eq!(m, m2); } )*} ); array_matrix_conversion!( array_matrix_conversion_2_2, Matrix2, (2, 2); array_matrix_conversion_2_3, Matrix2x3, (2, 3); array_matrix_conversion_2_4, Matrix2x4, (2, 4); array_matrix_conversion_2_5, Matrix2x5, (2, 5); array_matrix_conversion_2_6, Matrix2x6, (2, 6); array_matrix_conversion_3_2, Matrix3x2, (3, 2); array_matrix_conversion_3_3, Matrix3, (3, 3); array_matrix_conversion_3_4, Matrix3x4, (3, 4); array_matrix_conversion_3_5, Matrix3x5, (3, 5); array_matrix_conversion_3_6, Matrix3x6, (3, 6); array_matrix_conversion_4_2, Matrix4x2, (4, 2); array_matrix_conversion_4_3, Matrix4x3, (4, 3); array_matrix_conversion_4_4, Matrix4, (4, 4); array_matrix_conversion_4_5, Matrix4x5, (4, 5); array_matrix_conversion_4_6, Matrix4x6, (4, 6); array_matrix_conversion_5_2, Matrix5x2, (5, 2); array_matrix_conversion_5_3, Matrix5x3, (5, 3); array_matrix_conversion_5_4, Matrix5x4, (5, 4); array_matrix_conversion_5_5, Matrix5, (5, 5); array_matrix_conversion_5_6, Matrix5x6, (5, 6); array_matrix_conversion_6_2, Matrix6x2, (6, 2); array_matrix_conversion_6_3, Matrix6x3, (6, 3); array_matrix_conversion_6_4, Matrix6x4, (6, 4); array_matrix_conversion_6_5, Matrix6x5, (6, 5); array_matrix_conversion_6_6, Matrix6, (6, 6); ); #[test] fn matrix_slice_from_matrix_ref() { let a = Matrix3x4::new( 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, ); // TODO: What's a more idiomatic/better way to convert a static matrix to a dynamic one? let d = DMatrix::from(a.get((0..a.nrows(), 0..a.ncols())).unwrap()); // Note: these have to be macros, and not functions, because the input type is different // across the different tests. Moreover, the output type depends on the stride of the input, // which is different for static and dynamic matrices. macro_rules! dynamic_view { ($mref:expr) => { DMatrixView::<_>::from($mref) }; } macro_rules! dynamic_view_mut { ($mref:expr) => { DMatrixViewMut::<_>::from($mref) }; } macro_rules! fixed_view { ($mref:expr) => { MatrixView::<_, U3, U4, U1, U3>::from($mref) }; } macro_rules! fixed_view_mut { ($mref:expr) => { MatrixViewMut::<_, U3, U4, U1, U3>::from($mref) }; } // TODO: The `into_owned()` is a result of `PartialEq` not being implemented for different // Self and RHS. See issue #674. Once this is implemented, we can remove `into_owned` // from the below tests. // Construct views from reference to a { assert_eq!(a, fixed_view!(&a).into_owned()); assert_eq!(d, dynamic_view!(&a).into_owned()); } // Construct views from mutable reference to a { let mut a_clone = a.clone(); assert_eq!(a, fixed_view!(&mut a_clone).into_owned()); assert_eq!(d, dynamic_view!(&mut a_clone).into_owned()); } // Construct mutable slices from mutable reference to a { let mut a_clone = a.clone(); assert_eq!(a, fixed_view_mut!(&mut a_clone).into_owned()); assert_eq!(d, dynamic_view_mut!(&mut a_clone).into_owned()); } // Construct slices from reference to d { assert_eq!(a, fixed_view!(&d).into_owned()); assert_eq!(d, dynamic_view!(&d).into_owned()); } // Construct slices from mutable reference to d { let mut d_clone = a.clone(); assert_eq!(a, fixed_view!(&mut d_clone).into_owned()); assert_eq!(d, dynamic_view!(&mut d_clone).into_owned()); } // Construct mutable slices from mutable reference to d { let mut d_clone = d.clone(); assert_eq!(a, fixed_view_mut!(&mut d_clone).into_owned()); assert_eq!(d, dynamic_view_mut!(&mut d_clone).into_owned()); } // Construct slices from a slice of a { let mut a_slice = fixed_view!(&a); assert_eq!(a, fixed_view!(&a_slice).into_owned()); assert_eq!(a, fixed_view!(&mut a_slice).into_owned()); assert_eq!(d, dynamic_view!(&a_slice).into_owned()); assert_eq!(d, dynamic_view!(&mut a_slice).into_owned()); } // Construct slices from a slice mut of a { // Need a clone of a here, so that we can both have a mutable borrow and compare equality let mut a_clone = a.clone(); let mut a_slice = fixed_view_mut!(&mut a_clone); assert_eq!(a, fixed_view!(&a_slice).into_owned()); assert_eq!(a, fixed_view!(&mut a_slice).into_owned()); assert_eq!(d, dynamic_view!(&a_slice).into_owned()); assert_eq!(d, dynamic_view!(&mut a_slice).into_owned()); assert_eq!(a, fixed_view_mut!(&mut a_slice).into_owned()); assert_eq!(d, dynamic_view_mut!(&mut a_slice).into_owned()); } } nalgebra-0.33.0/tests/core/edition.rs000064400000000000000000000464510072674642500156170ustar 00000000000000use na::{ DMatrix, Matrix, Matrix3, Matrix3x4, Matrix3x5, Matrix4, Matrix4x3, Matrix4x5, Matrix5, Matrix5x3, Matrix5x4, }; use na::{Dyn, U3, U5}; #[test] #[rustfmt::skip] fn upper_lower_triangular() { let m = Matrix4::new( 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, 41.0, 42.0, 43.0, 44.0); let um = Matrix4::new( 11.0, 12.0, 13.0, 14.0, 0.0, 22.0, 23.0, 24.0, 0.0, 0.0, 33.0, 34.0, 0.0, 0.0, 0.0, 44.0); let lm = Matrix4::new( 11.0, 0.0, 0.0, 0.0, 21.0, 22.0, 0.0, 0.0, 31.0, 32.0, 33.0, 0.0, 41.0, 42.0, 43.0, 44.0); let computed_um = m.upper_triangle(); let computed_lm = m.lower_triangle(); assert_eq!(um, computed_um); assert_eq!(lm, computed_lm); let symm_um = Matrix4::new( 11.0, 12.0, 13.0, 14.0, 12.0, 22.0, 23.0, 24.0, 13.0, 23.0, 33.0, 34.0, 14.0, 24.0, 34.0, 44.0); let symm_lm = Matrix4::new( 11.0, 21.0, 31.0, 41.0, 21.0, 22.0, 32.0, 42.0, 31.0, 32.0, 33.0, 43.0, 41.0, 42.0, 43.0, 44.0); let mut computed_symm_um = m.clone(); let mut computed_symm_lm = m.clone(); computed_symm_um.fill_lower_triangle_with_upper_triangle(); computed_symm_lm.fill_upper_triangle_with_lower_triangle(); assert_eq!(symm_um, computed_symm_um); assert_eq!(symm_lm, computed_symm_lm); let m = Matrix5x3::new( 11.0, 12.0, 13.0, 21.0, 22.0, 23.0, 31.0, 32.0, 33.0, 41.0, 42.0, 43.0, 51.0, 52.0, 53.0); let um = Matrix5x3::new( 11.0, 12.0, 13.0, 0.0, 22.0, 23.0, 0.0, 0.0, 33.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); let lm = Matrix5x3::new( 11.0, 0.0, 0.0, 21.0, 22.0, 0.0, 31.0, 32.0, 33.0, 41.0, 42.0, 43.0, 51.0, 52.0, 53.0); let computed_um = m.upper_triangle(); let computed_lm = m.lower_triangle(); assert_eq!(um, computed_um); assert_eq!(lm, computed_lm); let m = Matrix3x5::new( 11.0, 12.0, 13.0, 14.0, 15.0, 21.0, 22.0, 23.0, 24.0, 25.0, 31.0, 32.0, 33.0, 34.0, 35.0); let um = Matrix3x5::new( 11.0, 12.0, 13.0, 14.0, 15.0, 0.0, 22.0, 23.0, 24.0, 25.0, 0.0, 0.0, 33.0, 34.0, 35.0); let lm = Matrix3x5::new( 11.0, 0.0, 0.0, 0.0, 0.0, 21.0, 22.0, 0.0, 0.0, 0.0, 31.0, 32.0, 33.0, 0.0, 0.0); let computed_um = m.upper_triangle(); let computed_lm = m.lower_triangle(); assert_eq!(um, computed_um); assert_eq!(lm, computed_lm); let mut m = Matrix4x5::new( 11.0, 12.0, 13.0, 14.0, 15.0, 21.0, 22.0, 23.0, 24.0, 25.0, 31.0, 32.0, 33.0, 34.0, 35.0, 41.0, 42.0, 43.0, 44.0, 45.0); let expected_m = Matrix4x5::new( 11.0, 12.0, 0.0, 0.0, 0.0, 21.0, 22.0, 23.0, 0.0, 0.0, 31.0, 32.0, 33.0, 34.0, 0.0, 41.0, 42.0, 43.0, 44.0, 45.0); m.fill_upper_triangle(0.0, 2); assert_eq!(m, expected_m); let mut m = Matrix4x5::new( 11.0, 12.0, 13.0, 14.0, 15.0, 21.0, 22.0, 23.0, 24.0, 25.0, 31.0, 32.0, 33.0, 34.0, 35.0, 41.0, 42.0, 43.0, 44.0, 45.0); let expected_m = Matrix4x5::new( 11.0, 12.0, 13.0, 14.0, 15.0, 21.0, 22.0, 23.0, 24.0, 25.0, 0.0, 32.0, 33.0, 34.0, 35.0, 0.0, 0.0, 43.0, 44.0, 45.0); m.fill_lower_triangle(0.0, 2); assert_eq!(m, expected_m); let mut m = Matrix5x4::new( 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, 41.0, 42.0, 43.0, 44.0, 51.0, 52.0, 53.0, 54.0); let expected_m = Matrix5x4::new( 11.0, 12.0, 0.0, 0.0, 21.0, 22.0, 23.0, 0.0, 31.0, 32.0, 33.0, 34.0, 41.0, 42.0, 43.0, 44.0, 51.0, 52.0, 53.0, 54.0); m.fill_upper_triangle(0.0, 2); assert_eq!(m, expected_m); let mut m = Matrix5x4::new( 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0, 41.0, 42.0, 43.0, 44.0, 51.0, 52.0, 53.0, 54.0); let expected_m = Matrix5x4::new( 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 0.0, 32.0, 33.0, 34.0, 0.0, 0.0, 43.0, 44.0, 0.0, 0.0, 0.0, 54.0); m.fill_lower_triangle(0.0, 2); assert_eq!(m, expected_m); } #[test] #[rustfmt::skip] fn swap_rows() { let mut m = Matrix5x3::new( 11.0, 12.0, 13.0, 21.0, 22.0, 23.0, 31.0, 32.0, 33.0, 41.0, 42.0, 43.0, 51.0, 52.0, 53.0); let expected = Matrix5x3::new( 11.0, 12.0, 13.0, 41.0, 42.0, 43.0, 31.0, 32.0, 33.0, 21.0, 22.0, 23.0, 51.0, 52.0, 53.0); m.swap_rows(1, 3); assert_eq!(m, expected); } #[test] #[rustfmt::skip] fn swap_columns() { let mut m = Matrix3x5::new( 11.0, 12.0, 13.0, 14.0, 15.0, 21.0, 22.0, 23.0, 24.0, 25.0, 31.0, 32.0, 33.0, 34.0, 35.0); let expected = Matrix3x5::new( 11.0, 14.0, 13.0, 12.0, 15.0, 21.0, 24.0, 23.0, 22.0, 25.0, 31.0, 34.0, 33.0, 32.0, 35.0); m.swap_columns(1, 3); assert_eq!(m, expected); } #[test] #[rustfmt::skip] fn remove_columns() { let m = Matrix3x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected_a1 = Matrix3x4::new( 12, 13, 14, 15, 22, 23, 24, 25, 32, 33, 34, 35); let expected_a2 = Matrix3x4::new( 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34); let expected_a3 = Matrix3x4::new( 11, 12, 14, 15, 21, 22, 24, 25, 31, 32, 34, 35); assert_eq!(m.remove_column(0), expected_a1); assert_eq!(m.remove_column(4), expected_a2); assert_eq!(m.remove_column(2), expected_a3); let expected_b1 = Matrix3::new( 13, 14, 15, 23, 24, 25, 33, 34, 35); let expected_b2 = Matrix3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33); let expected_b3 = Matrix3::new( 11, 12, 15, 21, 22, 25, 31, 32, 35); assert_eq!(m.remove_fixed_columns::<2>(0), expected_b1); assert_eq!(m.remove_fixed_columns::<2>(3), expected_b2); assert_eq!(m.remove_fixed_columns::<2>(2), expected_b3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, U3, Dyn, _> = m.remove_columns(3, 2); assert!(computed.eq(&expected_b2)); /* * Same thing but using a non-copy scalar type. */ let m = m.map(Box::new); let expected_a1 = expected_a1.map(Box::new); let expected_a2 = expected_a2.map(Box::new); let expected_a3 = expected_a3.map(Box::new); assert_eq!(m.clone().remove_column(0), expected_a1); assert_eq!(m.clone().remove_column(4), expected_a2); assert_eq!(m.clone().remove_column(2), expected_a3); let expected_b1 = expected_b1.map(Box::new); let expected_b2 = expected_b2.map(Box::new); let expected_b3 = expected_b3.map(Box::new); assert_eq!(m.clone().remove_fixed_columns::<2>(0), expected_b1); assert_eq!(m.clone().remove_fixed_columns::<2>(3), expected_b2); assert_eq!(m.remove_fixed_columns::<2>(2), expected_b3); } #[test] #[rustfmt::skip] fn remove_columns_at() { let m = DMatrix::from_row_slice(5, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, 51, 52, 53, 54, 55 ]); let expected1 = DMatrix::from_row_slice(5, 4, &[ 12, 13, 14, 15, 22, 23, 24, 25, 32, 33, 34, 35, 42, 43, 44, 45, 52, 53, 54, 55 ]); assert_eq!(m.remove_columns_at(&[0]), expected1); let m = DMatrix::from_row_slice(5, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, 51, 52, 53, 54, 55 ]); let expected2 = DMatrix::from_row_slice(5, 3, &[ 11, 13, 15, 21, 23, 25, 31, 33, 35, 41, 43, 45, 51, 53, 55 ]); assert_eq!(m.remove_columns_at(&[1,3]), expected2); let m = DMatrix::from_row_slice(5, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, 51, 52, 53, 54, 55 ]); let expected3 = DMatrix::from_row_slice(5, 2, &[ 12, 13, 22, 23, 32, 33, 42, 43, 52, 53, ]); assert_eq!(m.remove_columns_at(&[0,3,4]), expected3); } #[test] #[rustfmt::skip] fn remove_rows() { let m = Matrix5x3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected1 = Matrix4x3::new( 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected2 = Matrix4x3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43); let expected3 = Matrix4x3::new( 11, 12, 13, 21, 22, 23, 41, 42, 43, 51, 52, 53); assert_eq!(m.remove_row(0), expected1); assert_eq!(m.remove_row(4), expected2); assert_eq!(m.remove_row(2), expected3); let expected1 = Matrix3::new( 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected2 = Matrix3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33); let expected3 = Matrix3::new( 11, 12, 13, 21, 22, 23, 51, 52, 53); assert_eq!(m.remove_fixed_rows::<2>(0), expected1); assert_eq!(m.remove_fixed_rows::<2>(3), expected2); assert_eq!(m.remove_fixed_rows::<2>(2), expected3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, Dyn, U3, _> = m.remove_rows(3, 2); assert!(computed.eq(&expected2)); } #[test] #[rustfmt::skip] fn remove_rows_at() { let m = DMatrix::from_row_slice(5, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, 51, 52, 53, 54, 55 ]); let expected1 = DMatrix::from_row_slice(4, 5, &[ 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, 51, 52, 53, 54, 55 ]); assert_eq!(m.remove_rows_at(&[0]), expected1); let m = DMatrix::from_row_slice(5, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, 51, 52, 53, 54, 55 ]); let expected2 = DMatrix::from_row_slice(3, 5, &[ 11, 12, 13, 14, 15, 31, 32, 33, 34, 35, 51, 52, 53, 54, 55 ]); assert_eq!(m.remove_rows_at(&[1,3]), expected2); let m = DMatrix::from_row_slice(5, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, 51, 52, 53, 54, 55 ]); let expected3 = DMatrix::from_row_slice(2, 5, &[ 21, 22, 23, 24, 25, 31, 32, 33, 34, 35 ]); assert_eq!(m.remove_rows_at(&[0,3,4]), expected3); } #[test] #[rustfmt::skip] fn insert_columns() { let m = Matrix5x3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected1 = Matrix5x4::new( 0, 11, 12, 13, 0, 21, 22, 23, 0, 31, 32, 33, 0, 41, 42, 43, 0, 51, 52, 53); let expected2 = Matrix5x4::new( 11, 12, 13, 0, 21, 22, 23, 0, 31, 32, 33, 0, 41, 42, 43, 0, 51, 52, 53, 0); let expected3 = Matrix5x4::new( 11, 12, 0, 13, 21, 22, 0, 23, 31, 32, 0, 33, 41, 42, 0, 43, 51, 52, 0, 53); assert_eq!(m.insert_column(0, 0), expected1); assert_eq!(m.insert_column(3, 0), expected2); assert_eq!(m.insert_column(2, 0), expected3); let expected1 = Matrix5::new( 0, 0, 11, 12, 13, 0, 0, 21, 22, 23, 0, 0, 31, 32, 33, 0, 0, 41, 42, 43, 0, 0, 51, 52, 53); let expected2 = Matrix5::new( 11, 12, 13, 0, 0, 21, 22, 23, 0, 0, 31, 32, 33, 0, 0, 41, 42, 43, 0, 0, 51, 52, 53, 0, 0); let expected3 = Matrix5::new( 11, 12, 0, 0, 13, 21, 22, 0, 0, 23, 31, 32, 0, 0, 33, 41, 42, 0, 0, 43, 51, 52, 0, 0, 53); assert_eq!(m.insert_fixed_columns::<2>(0, 0), expected1); assert_eq!(m.insert_fixed_columns::<2>(3, 0), expected2); assert_eq!(m.insert_fixed_columns::<2>(2, 0), expected3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, U5, Dyn, _> = m.insert_columns(3, 2, 0); assert!(computed.eq(&expected2)); } #[test] #[rustfmt::skip] fn insert_columns_to_empty_matrix() { let m1 = DMatrix::repeat(0, 0, 0); let m2 = DMatrix::repeat(3, 0, 0); let expected1 = DMatrix::repeat(0, 5, 42); let expected2 = DMatrix::repeat(3, 5, 42); assert_eq!(expected1, m1.insert_columns(0, 5, 42)); assert_eq!(expected2, m2.insert_columns(0, 5, 42)); } #[test] #[rustfmt::skip] fn insert_rows() { let m = Matrix3x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected1 = Matrix4x5::new( 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected2 = Matrix4x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0); let expected3 = Matrix4x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 31, 32, 33, 34, 35); assert_eq!(m.insert_row(0, 0), expected1); assert_eq!(m.insert_row(3, 0), expected2); assert_eq!(m.insert_row(2, 0), expected3); let expected1 = Matrix5::new( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected2 = Matrix5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); let expected3 = Matrix5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 32, 33, 34, 35); assert_eq!(m.insert_fixed_rows::<2>(0, 0), expected1); assert_eq!(m.insert_fixed_rows::<2>(3, 0), expected2); assert_eq!(m.insert_fixed_rows::<2>(2, 0), expected3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, Dyn, U5, _> = m.insert_rows(3, 2, 0); assert!(computed.eq(&expected2)); } #[test] fn insert_rows_to_empty_matrix() { let m1 = DMatrix::repeat(0, 0, 0); let m2 = DMatrix::repeat(0, 5, 0); let expected1 = DMatrix::repeat(3, 0, 42); let expected2 = DMatrix::repeat(3, 5, 42); assert_eq!(expected1, m1.insert_rows(0, 3, 42)); assert_eq!(expected2, m2.insert_rows(0, 3, 42)); } #[test] #[rustfmt::skip] fn resize() { let m = Matrix3x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let add_colls = DMatrix::from_row_slice(3, 6, &[ 11, 12, 13, 14, 15, 42, 21, 22, 23, 24, 25, 42, 31, 32, 33, 34, 35, 42]); let del_colls = DMatrix::from_row_slice(3, 4, &[ 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34]); let add_rows = DMatrix::from_row_slice(4, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 42, 42, 42, 42, 42]); let del_rows = DMatrix::from_row_slice(2, 5, &[ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25]); let add_add = DMatrix::from_row_slice(5, 6, &[ 11, 12, 13, 14, 15, 42, 21, 22, 23, 24, 25, 42, 31, 32, 33, 34, 35, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]); let del_del = DMatrix::from_row_slice(1, 2, &[11, 12]); let add_del = DMatrix::from_row_slice(5, 2, &[ 11, 12, 21, 22, 31, 32, 42, 42, 42, 42]); let del_add = DMatrix::from_row_slice(1, 8, &[ 11, 12, 13, 14, 15, 42, 42, 42]); assert_eq!(add_colls, m.resize(3, 6, 42)); assert_eq!(del_colls, m.resize(3, 4, 42)); assert_eq!(add_rows, m.resize(4, 5, 42)); assert_eq!(del_rows, m.resize(2, 5, 42)); assert_eq!(del_del, m.resize(1, 2, 42)); assert_eq!(add_add, m.resize(5, 6, 42)); assert_eq!(add_del, m.resize(5, 2, 42)); assert_eq!(del_add, m.resize(1, 8, 42)); } #[test] fn resize_empty_matrix() { let m1 = DMatrix::repeat(0, 0, 0); let m2 = DMatrix::repeat(1, 0, 0); // Less rows than target size. let m3 = DMatrix::repeat(3, 0, 0); // Same rows as target size. let m4 = DMatrix::repeat(9, 0, 0); // More rows than target size. let m5 = DMatrix::repeat(0, 1, 0); // Less columns than target size. let m6 = DMatrix::repeat(0, 5, 0); // Same columns as target size. let m7 = DMatrix::repeat(0, 9, 0); // More columns than target size. let resized = DMatrix::repeat(3, 5, 42); let resized_wo_rows = DMatrix::repeat(0, 5, 42); let resized_wo_cols = DMatrix::repeat(3, 0, 42); assert_eq!(resized, m1.clone().resize(3, 5, 42)); assert_eq!(resized, m2.clone().resize(3, 5, 42)); assert_eq!(resized, m3.clone().resize(3, 5, 42)); assert_eq!(resized, m4.clone().resize(3, 5, 42)); assert_eq!(resized, m5.clone().resize(3, 5, 42)); assert_eq!(resized, m6.clone().resize(3, 5, 42)); assert_eq!(resized, m7.clone().resize(3, 5, 42)); assert_eq!(resized_wo_rows, m1.clone().resize(0, 5, 42)); assert_eq!(resized_wo_rows, m2.clone().resize(0, 5, 42)); assert_eq!(resized_wo_rows, m3.clone().resize(0, 5, 42)); assert_eq!(resized_wo_rows, m4.clone().resize(0, 5, 42)); assert_eq!(resized_wo_rows, m5.clone().resize(0, 5, 42)); assert_eq!(resized_wo_rows, m6.clone().resize(0, 5, 42)); assert_eq!(resized_wo_rows, m7.clone().resize(0, 5, 42)); assert_eq!(resized_wo_cols, m1.clone().resize(3, 0, 42)); assert_eq!(resized_wo_cols, m2.clone().resize(3, 0, 42)); assert_eq!(resized_wo_cols, m3.clone().resize(3, 0, 42)); assert_eq!(resized_wo_cols, m4.clone().resize(3, 0, 42)); assert_eq!(resized_wo_cols, m5.clone().resize(3, 0, 42)); assert_eq!(resized_wo_cols, m6.clone().resize(3, 0, 42)); assert_eq!(resized_wo_cols, m7.clone().resize(3, 0, 42)); assert_eq!(m1, m1.clone().resize(0, 0, 42)); assert_eq!(m1, m2.resize(0, 0, 42)); assert_eq!(m1, m3.resize(0, 0, 42)); assert_eq!(m1, m4.resize(0, 0, 42)); assert_eq!(m1, m5.resize(0, 0, 42)); assert_eq!(m1, m6.resize(0, 0, 42)); assert_eq!(m1, m7.resize(0, 0, 42)); } nalgebra-0.33.0/tests/core/empty.rs000064400000000000000000000033130072674642500153100ustar 00000000000000use na::{DMatrix, DVector}; #[test] fn empty_matrix_mul_vector() { // Issue #644 let m = DMatrix::::zeros(8, 0); let v = DVector::::zeros(0); assert_eq!(m * v, DVector::zeros(8)); } #[test] fn empty_matrix_mul_matrix() { let m1 = DMatrix::::zeros(3, 0); let m2 = DMatrix::::zeros(0, 4); assert_eq!(m1 * m2, DMatrix::zeros(3, 4)); // Still works with larger matrices. let m1 = DMatrix::::zeros(13, 0); let m2 = DMatrix::::zeros(0, 14); assert_eq!(m1 * m2, DMatrix::zeros(13, 14)); } #[test] fn empty_matrix_tr_mul_vector() { let m = DMatrix::::zeros(0, 5); let v = DVector::::zeros(0); assert_eq!(m.tr_mul(&v), DVector::zeros(5)); } #[test] fn empty_matrix_tr_mul_matrix() { let m1 = DMatrix::::zeros(0, 3); let m2 = DMatrix::::zeros(0, 4); assert_eq!(m1.tr_mul(&m2), DMatrix::zeros(3, 4)); } #[test] fn empty_matrix_gemm() { let mut res = DMatrix::repeat(3, 4, 1.0); let m1 = DMatrix::::zeros(3, 0); let m2 = DMatrix::::zeros(0, 4); res.gemm(1.0, &m1, &m2, 0.5); assert_eq!(res, DMatrix::repeat(3, 4, 0.5)); // Still works with lager matrices. let mut res = DMatrix::repeat(13, 14, 1.0); let m1 = DMatrix::::zeros(13, 0); let m2 = DMatrix::::zeros(0, 14); res.gemm(1.0, &m1, &m2, 0.5); assert_eq!(res, DMatrix::repeat(13, 14, 0.5)); } #[test] fn empty_matrix_gemm_tr() { let mut res = DMatrix::repeat(3, 4, 1.0); let m1 = DMatrix::::zeros(0, 3); let m2 = DMatrix::::zeros(0, 4); res.gemm_tr(1.0, &m1, &m2, 0.5); assert_eq!(res, DMatrix::repeat(3, 4, 0.5)); } nalgebra-0.33.0/tests/core/helper.rs000064400000000000000000000031130072674642500154270ustar 00000000000000// This module implement several methods to fill some // missing features of num-complex when it comes to randomness. use na::RealField; use num_complex::Complex; use quickcheck::{Arbitrary, Gen}; use rand::distributions::{Distribution, Standard}; use rand::Rng; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct RandComplex(pub Complex); impl Arbitrary for RandComplex { #[inline] fn arbitrary(rng: &mut Gen) -> Self { let im = Arbitrary::arbitrary(rng); let re = Arbitrary::arbitrary(rng); RandComplex(Complex::new(re, im)) } } impl Distribution> for Standard where Standard: Distribution, { #[inline] fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> RandComplex { let re = rng.gen(); let im = rng.gen(); RandComplex(Complex::new(re, im)) } } // This is a wrapper similar to RandComplex, but for non-complex. // This exists only to make generic tests easier to write. // // Generates variates in the range [0, 1). #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct RandScalar(pub T); impl Arbitrary for RandScalar { #[inline] fn arbitrary(rng: &mut Gen) -> Self { RandScalar(Arbitrary::arbitrary(rng)) } } impl Distribution> for Standard where Standard: Distribution, { #[inline] fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> RandScalar { RandScalar(self.sample(rng)) } } nalgebra-0.33.0/tests/core/macros.rs000064400000000000000000000006150072674642500154400ustar 00000000000000use nalgebra::{dmatrix, dvector, matrix, point, vector}; #[test] fn sanity_test() { // The macros are already tested in `nalgebra-macros`. Here we just test that they compile fine. let _ = matrix![1, 2, 3; 4, 5, 6]; let _ = dmatrix![1, 2, 3; 4, 5, 6]; let _ = point![1, 2, 3, 4, 5, 6]; let _ = vector![1, 2, 3, 4, 5, 6]; let _ = dvector![1, 2, 3, 4, 5, 6]; } nalgebra-0.33.0/tests/core/matrix.rs000064400000000000000000001202550072674642500154630ustar 00000000000000use na::iter::MatrixIter; use num::{One, Zero}; use std::cmp::Ordering; use na::dimension::{U15, U8}; use na::{ self, Const, DMatrix, DVector, Matrix2, Matrix2x3, Matrix2x4, Matrix3, Matrix3x2, Matrix3x4, Matrix4, Matrix4x3, Matrix4x5, Matrix5, Matrix6, MatrixView2x3, MatrixViewMut2x3, OMatrix, RowVector3, RowVector4, RowVector5, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6, }; #[test] fn iter() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let view: MatrixView2x3<_> = (&a).into(); fn test<'a, F: Fn() -> I, I: Iterator + DoubleEndedIterator>(it: F) { { let mut it = it(); assert_eq!(*it.next().unwrap(), 1.0); assert_eq!(*it.next().unwrap(), 4.0); assert_eq!(*it.next().unwrap(), 2.0); assert_eq!(*it.next().unwrap(), 5.0); assert_eq!(*it.next().unwrap(), 3.0); assert_eq!(*it.next().unwrap(), 6.0); assert!(it.next().is_none()); } { let mut it = it(); assert_eq!(*it.next().unwrap(), 1.0); assert_eq!(*it.next_back().unwrap(), 6.0); assert_eq!(*it.next_back().unwrap(), 3.0); assert_eq!(*it.next_back().unwrap(), 5.0); assert_eq!(*it.next().unwrap(), 4.0); assert_eq!(*it.next().unwrap(), 2.0); assert!(it.next().is_none()); } { let mut it = it().rev(); assert_eq!(*it.next().unwrap(), 6.0); assert_eq!(*it.next().unwrap(), 3.0); assert_eq!(*it.next().unwrap(), 5.0); assert_eq!(*it.next().unwrap(), 2.0); assert_eq!(*it.next().unwrap(), 4.0); assert_eq!(*it.next().unwrap(), 1.0); assert!(it.next().is_none()); } } test(|| a.iter()); test(|| view.into_iter()); let row = a.row(0); let row_test = |mut it: MatrixIter<_, _, _, _>| { assert_eq!(*it.next().unwrap(), 1.0); assert_eq!(*it.next().unwrap(), 2.0); assert_eq!(*it.next().unwrap(), 3.0); assert!(it.next().is_none()); }; row_test(row.iter()); row_test(row.into_iter()); let row = a.row(1); let row_test = |mut it: MatrixIter<_, _, _, _>| { assert_eq!(*it.next().unwrap(), 4.0); assert_eq!(*it.next().unwrap(), 5.0); assert_eq!(*it.next().unwrap(), 6.0); assert!(it.next().is_none()); }; row_test(row.iter()); row_test(row.into_iter()); let m22 = row.column(1); let m22_test = |mut it: MatrixIter<_, _, _, _>| { assert_eq!(*it.next().unwrap(), 5.0); assert!(it.next().is_none()); }; m22_test(m22.iter()); m22_test(m22.into_iter()); let col = a.column(0); let col_test = |mut it: MatrixIter<_, _, _, _>| { assert_eq!(*it.next().unwrap(), 1.0); assert_eq!(*it.next().unwrap(), 4.0); assert!(it.next().is_none()); }; col_test(col.iter()); col_test(col.into_iter()); let col = a.column(1); let col_test = |mut it: MatrixIter<_, _, _, _>| { assert_eq!(*it.next().unwrap(), 2.0); assert_eq!(*it.next().unwrap(), 5.0); assert!(it.next().is_none()); }; col_test(col.iter()); col_test(col.into_iter()); let col = a.column(2); let col_test = |mut it: MatrixIter<_, _, _, _>| { assert_eq!(*it.next().unwrap(), 3.0); assert_eq!(*it.next().unwrap(), 6.0); assert!(it.next().is_none()); }; col_test(col.iter()); col_test(col.into_iter()); } #[test] fn iter_mut() { let mut a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); for v in a.iter_mut() { *v *= 2.0; } assert_eq!(a, Matrix2x3::new(2.0, 4.0, 6.0, 8.0, 10.0, 12.0)); let view: MatrixViewMut2x3<_> = MatrixViewMut2x3::from(&mut a); for v in view.into_iter() { *v *= 2.0; } assert_eq!(a, Matrix2x3::new(4.0, 8.0, 12.0, 16.0, 20.0, 24.0)); } #[test] fn debug_output_corresponds_to_data_container() { let m = Matrix2::new(1.0, 2.0, 3.0, 4.0); let output_stable = "[[1, 3], [2, 4]]"; // Current output on the stable channel. let output_nightly = "[[1.0, 3.0], [2.0, 4.0]]"; // Current output on the nightly channel. let current_output = format!("{:?}", m); dbg!(output_stable); dbg!(output_nightly); dbg!(¤t_output); assert!(current_output == output_stable || current_output == output_nightly); } #[test] fn is_column_major() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let expected = &[1.0, 4.0, 2.0, 5.0, 3.0, 6.0]; assert_eq!(a.as_slice(), expected); let a = Matrix2x3::from_row_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); assert_eq!(a.as_slice(), expected); let a = Matrix2x3::from_column_slice(&[1.0, 4.0, 2.0, 5.0, 3.0, 6.0]); assert_eq!(a.as_slice(), expected); } #[test] fn linear_index() { let a = Matrix2x3::new(1, 2, 3, 4, 5, 6); assert_eq!(a[0], 1); assert_eq!(a[1], 4); assert_eq!(a[2], 2); assert_eq!(a[3], 5); assert_eq!(a[4], 3); assert_eq!(a[5], 6); let b = Vector4::new(1, 2, 3, 4); assert_eq!(b[0], 1); assert_eq!(b[1], 2); assert_eq!(b[2], 3); assert_eq!(b[3], 4); let c = RowVector4::new(1, 2, 3, 4); assert_eq!(c[0], 1); assert_eq!(c[1], 2); assert_eq!(c[2], 3); assert_eq!(c[3], 4); } #[test] fn identity() { let id1 = Matrix3::::identity(); let id2 = Matrix3x4::new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); let id2bis = Matrix3x4::identity(); let id3 = Matrix4x3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0); let id3bis = Matrix4x3::identity(); let not_id1 = Matrix3::identity() * 2.0; let not_id2 = Matrix3x4::new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0); let not_id3 = Matrix4x3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0); assert_eq!(id2, id2bis); assert_eq!(id3, id3bis); assert!(id1.is_identity(0.0)); assert!(id2.is_identity(0.0)); assert!(id3.is_identity(0.0)); assert!(!not_id1.is_identity(0.0)); assert!(!not_id2.is_identity(0.0)); assert!(!not_id3.is_identity(0.0)); } #[test] fn coordinates() { let a = Matrix3x4::new(11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34); assert_eq!(a.m11, 11); assert_eq!(a.m12, 12); assert_eq!(a.m13, 13); assert_eq!(a.m14, 14); assert_eq!(a.m21, 21); assert_eq!(a.m22, 22); assert_eq!(a.m23, 23); assert_eq!(a.m24, 24); assert_eq!(a.m31, 31); assert_eq!(a.m32, 32); assert_eq!(a.m33, 33); assert_eq!(a.m34, 34); } #[test] fn from_diagonal() { let diag = Vector3::new(1, 2, 3); let expected = Matrix3::new(1, 0, 0, 0, 2, 0, 0, 0, 3); let a = Matrix3::from_diagonal(&diag); assert_eq!(a, expected); } #[test] fn from_rows() { let rows = &[ RowVector4::new(11, 12, 13, 14), RowVector4::new(21, 22, 23, 24), RowVector4::new(31, 32, 33, 34), ]; let expected = Matrix3x4::new(11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34); let a = Matrix3x4::from_rows(rows); assert_eq!(a, expected); } #[test] fn from_columns() { let columns = &[ Vector3::new(11, 21, 31), Vector3::new(12, 22, 32), Vector3::new(13, 23, 33), Vector3::new(14, 24, 34), ]; let expected = Matrix3x4::new(11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34); let a = Matrix3x4::from_columns(columns); assert_eq!(a, expected); } #[test] fn from_columns_dynamic() { let columns = &[ DVector::from_row_slice(&[11, 21, 31]), DVector::from_row_slice(&[12, 22, 32]), DVector::from_row_slice(&[13, 23, 33]), DVector::from_row_slice(&[14, 24, 34]), ]; let expected = DMatrix::from_row_slice(3, 4, &[11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34]); let a = DMatrix::from_columns(columns); assert_eq!(a, expected); } #[test] #[should_panic] fn from_too_many_rows() { let rows = &[ RowVector4::new(11, 12, 13, 14), RowVector4::new(21, 22, 23, 24), RowVector4::new(31, 32, 33, 34), RowVector4::new(31, 32, 33, 34), ]; let _ = Matrix3x4::from_rows(rows); } #[test] #[should_panic] fn from_not_enough_columns() { let columns = &[Vector3::new(11, 21, 31), Vector3::new(14, 24, 34)]; let _ = Matrix3x4::from_columns(columns); } #[test] #[should_panic] fn from_rows_with_different_dimensions() { let columns = &[ DVector::from_row_slice(&[11, 21, 31]), DVector::from_row_slice(&[12, 22, 32, 33]), ]; let _ = DMatrix::from_columns(columns); } #[test] fn copy_from_slice() { let mut a = Matrix3::zeros(); let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]; let expected_a = Matrix3::new(1.0, 4.0, 7.0, 2.0, 5.0, 8.0, 3.0, 6.0, 9.0); a.copy_from_slice(&data); assert_eq!(a, expected_a); } #[should_panic] #[test] fn copy_from_slice_too_small() { let mut a = Matrix3::zeros(); let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; a.copy_from_slice(&data); } #[should_panic] #[test] fn copy_from_slice_too_large() { let mut a = Matrix3::zeros(); let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]; a.copy_from_slice(&data); } #[test] fn to_homogeneous() { let a = Vector3::new(1.0, 2.0, 3.0); let expected_a = Vector4::new(1.0, 2.0, 3.0, 0.0); let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 0.0]); let c = Matrix2::new(1.0, 2.0, 3.0, 4.0); let expected_c = Matrix3::new(1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0); let d = DMatrix::from_row_slice(2, 2, &[1.0, 2.0, 3.0, 4.0]); let expected_d = DMatrix::from_row_slice(3, 3, &[1.0, 2.0, 0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 1.0]); assert_eq!(a.to_homogeneous(), expected_a); assert_eq!(b.to_homogeneous(), expected_b); assert_eq!(c.to_homogeneous(), expected_c); assert_eq!(d.to_homogeneous(), expected_d); } #[test] fn push() { let a = Vector3::new(1.0, 2.0, 3.0); let expected_a = Vector4::new(1.0, 2.0, 3.0, 4.0); let b = DVector::from_row_slice(&[1.0, 2.0, 3.0]); let expected_b = DVector::from_row_slice(&[1.0, 2.0, 3.0, 4.0]); assert_eq!(a.push(4.0), expected_a); assert_eq!(b.push(4.0), expected_b); } #[test] fn simple_add() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let b = Matrix2x3::new(10.0, 20.0, 30.0, 40.0, 50.0, 60.0); let c = DMatrix::from_row_slice(2, 3, &[10.0, 20.0, 30.0, 40.0, 50.0, 60.0]); let expected = Matrix2x3::new(11.0, 22.0, 33.0, 44.0, 55.0, 66.0); assert_eq!(expected, &a + &b); assert_eq!(expected, &a + b); assert_eq!(expected, a + &b); assert_eq!(expected, a + b); // Sum of a static matrix with a dynamic one. assert_eq!(expected, &a + &c); assert_eq!(expected, a + &c); assert_eq!(expected, &c + &a); assert_eq!(expected, &c + a); } #[test] fn simple_sum() { type M = Matrix2x3; let a = M::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let b = M::new(10.0, 20.0, 30.0, 40.0, 50.0, 60.0); let c = M::new(100.0, 200.0, 300.0, 400.0, 500.0, 600.0); assert_eq!(M::zero(), Vec::::new().iter().sum()); assert_eq!(M::zero(), Vec::::new().into_iter().sum()); assert_eq!(a + b, vec![a, b].iter().sum()); assert_eq!(a + b, vec![a, b].into_iter().sum()); assert_eq!(a + b + c, vec![a, b, c].iter().sum()); assert_eq!(a + b + c, vec![a, b, c].into_iter().sum()); } #[test] fn simple_scalar_mul() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let expected = Matrix2x3::new(10.0, 20.0, 30.0, 40.0, 50.0, 60.0); assert_eq!(expected, a * 10.0); assert_eq!(expected, &a * 10.0); assert_eq!(expected, 10.0 * a); assert_eq!(expected, 10.0 * &a); } #[test] fn simple_mul() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let b = Matrix3x4::new( 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0, 120.0, ); let expected = Matrix2x4::new(380.0, 440.0, 500.0, 560.0, 830.0, 980.0, 1130.0, 1280.0); assert_eq!(expected, &a * &b); assert_eq!(expected, a * &b); assert_eq!(expected, &a * b); assert_eq!(expected, a * b); } #[test] fn simple_product() { type M = Matrix3; let a = M::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); let b = M::new(10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0); let c = M::new( 100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, ); assert_eq!(M::one(), Vec::::new().iter().product()); assert_eq!(M::one(), Vec::::new().into_iter().product()); assert_eq!(a * b, vec![a, b].iter().product()); assert_eq!(a * b, vec![a, b].into_iter().product()); assert_eq!(a * b * c, vec![a, b, c].iter().product()); assert_eq!(a * b * c, vec![a, b, c].into_iter().product()); } #[test] fn cross_product_vector_and_row_vector() { let v1 = Vector3::new(1.0, 2.0, 3.0); let v2 = Vector3::new(1.0, 5.0, 7.0); let column_cross = v1.cross(&v2); assert_eq!(column_cross, Vector3::new(-1.0, -4.0, 3.0)); let v1 = RowVector3::new(1.0, 2.0, 3.0); let v2 = RowVector3::new(1.0, 5.0, 7.0); let row_cross = v1.cross(&v2); assert_eq!(row_cross, RowVector3::new(-1.0, -4.0, 3.0)); assert_eq!( Vector3::new(1.0, 1.0, 0.0) .cross(&Vector3::new(-0.5, 17.0, 0.0)) .transpose(), RowVector3::new(1.0, 1.0, 0.0).cross(&RowVector3::new(-0.5, 17.0, 0.0)) ); } #[test] fn simple_scalar_conversion() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let expected = Matrix2x3::new(1, 2, 3, 4, 5, 6); let a_u32: Matrix2x3 = na::try_convert(a).unwrap(); // f32 -> u32 let a_f32: Matrix2x3 = na::convert(a_u32); // u32 -> f32 assert_eq!(a, a_f32); assert_eq!(expected, a_u32); } #[test] fn apply() { let mut a = Matrix4::new( 1.1f32, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, ); let expected = Matrix4::new( 1.0, 2.0, 3.0, 4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 9.0, 8.0, 7.0, 6.0, 4.0, 3.0, 2.0, ); a.apply(|e| *e = e.round()); assert_eq!(a, expected); } #[test] fn map() { let a = Matrix4::new( 1.1f64, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, ); let expected = Matrix4::new(1, 2, 3, 4, 6, 7, 8, 9, 10, 9, 8, 7, 6, 4, 3, 2); let computed = a.map(|e| e.round() as i64); assert_eq!(computed, expected); } #[test] fn map_with_location() { let a = Matrix4::new(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4); let expected = Matrix4::new(1, 2, 3, 4, 3, 4, 5, 6, 5, 6, 7, 8, 7, 8, 9, 10); let computed = a.map_with_location(|i, j, e| e + i + j); assert_eq!(computed, expected); } #[test] fn zip_map() { let a = Matrix3::new(11i32, 12, 13, 21, 22, 23, 31, 32, 33); let b = Matrix3::new(11u32, 12, 13, 21, 22, 23, 31, 32, 33); let expected = Matrix3::new(22.0f32, 24.0, 26.0, 42.0, 44.0, 46.0, 62.0, 64.0, 66.0); let computed = a.zip_map(&b, |ea, eb| ea as f32 + eb as f32); assert_eq!(computed, expected); } #[test] #[should_panic] fn trace_panic() { let m = DMatrix::::new_random(2, 3); let _ = m.trace(); } #[test] fn trace() { let m = Matrix2::new(1.0, 20.0, 30.0, 4.0); assert_eq!(m.trace(), 5.0); } #[test] fn simple_transpose() { let a = Matrix2x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); let expected = Matrix3x2::new(1.0, 4.0, 2.0, 5.0, 3.0, 6.0); assert_eq!(a.transpose(), expected); } #[test] fn simple_transpose_mut() { let mut a = Matrix3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); let expected = Matrix3::new(1.0, 4.0, 7.0, 2.0, 5.0, 8.0, 3.0, 6.0, 9.0); a.transpose_mut(); assert_eq!(a, expected); } #[test] fn vector_index_mut() { let mut v = Vector3::new(1, 2, 3); assert_eq!(v[0], 1); assert_eq!(v[1], 2); assert_eq!(v[2], 3); v[0] = 10; v[1] = 20; v[2] = 30; assert_eq!(v, Vector3::new(10, 20, 30)); } #[test] fn components_mut() { let mut m2 = Matrix2::from_element(1.0); let mut m3 = Matrix3::from_element(1.0); let mut m4 = Matrix4::from_element(1.0); let mut m5 = Matrix5::from_element(1.0); let mut m6 = Matrix6::from_element(1.0); m2.m11 = 0.0; m2.m12 = 0.0; m2.m21 = 0.0; m2.m22 = 0.0; m3.m11 = 0.0; m3.m12 = 0.0; m3.m13 = 0.0; m3.m21 = 0.0; m3.m22 = 0.0; m3.m23 = 0.0; m3.m31 = 0.0; m3.m32 = 0.0; m3.m33 = 0.0; m4.m11 = 0.0; m4.m12 = 0.0; m4.m13 = 0.0; m4.m14 = 0.0; m4.m21 = 0.0; m4.m22 = 0.0; m4.m23 = 0.0; m4.m24 = 0.0; m4.m31 = 0.0; m4.m32 = 0.0; m4.m33 = 0.0; m4.m34 = 0.0; m4.m41 = 0.0; m4.m42 = 0.0; m4.m43 = 0.0; m4.m44 = 0.0; m5.m11 = 0.0; m5.m12 = 0.0; m5.m13 = 0.0; m5.m14 = 0.0; m5.m15 = 0.0; m5.m21 = 0.0; m5.m22 = 0.0; m5.m23 = 0.0; m5.m24 = 0.0; m5.m25 = 0.0; m5.m31 = 0.0; m5.m32 = 0.0; m5.m33 = 0.0; m5.m34 = 0.0; m5.m35 = 0.0; m5.m41 = 0.0; m5.m42 = 0.0; m5.m43 = 0.0; m5.m44 = 0.0; m5.m45 = 0.0; m5.m51 = 0.0; m5.m52 = 0.0; m5.m53 = 0.0; m5.m54 = 0.0; m5.m55 = 0.0; m6.m11 = 0.0; m6.m12 = 0.0; m6.m13 = 0.0; m6.m14 = 0.0; m6.m15 = 0.0; m6.m16 = 0.0; m6.m21 = 0.0; m6.m22 = 0.0; m6.m23 = 0.0; m6.m24 = 0.0; m6.m25 = 0.0; m6.m26 = 0.0; m6.m31 = 0.0; m6.m32 = 0.0; m6.m33 = 0.0; m6.m34 = 0.0; m6.m35 = 0.0; m6.m36 = 0.0; m6.m41 = 0.0; m6.m42 = 0.0; m6.m43 = 0.0; m6.m44 = 0.0; m6.m45 = 0.0; m6.m46 = 0.0; m6.m51 = 0.0; m6.m52 = 0.0; m6.m53 = 0.0; m6.m54 = 0.0; m6.m55 = 0.0; m6.m56 = 0.0; m6.m61 = 0.0; m6.m62 = 0.0; m6.m63 = 0.0; m6.m64 = 0.0; m6.m65 = 0.0; m6.m66 = 0.0; assert!(m2.is_zero()); assert!(m3.is_zero()); assert!(m4.is_zero()); assert!(m5.is_zero()); assert!(m6.is_zero()); let mut v1 = Vector1::from_element(1.0); let mut v2 = Vector2::from_element(1.0); let mut v3 = Vector3::from_element(1.0); let mut v4 = Vector4::from_element(1.0); let mut v5 = Vector5::from_element(1.0); let mut v6 = Vector6::from_element(1.0); v1.x = 0.0; v2.x = 0.0; v2.y = 0.0; v3.x = 0.0; v3.y = 0.0; v3.z = 0.0; v4.x = 0.0; v4.y = 0.0; v4.z = 0.0; v4.w = 0.0; v5.x = 0.0; v5.y = 0.0; v5.z = 0.0; v5.w = 0.0; v5.a = 0.0; v6.x = 0.0; v6.y = 0.0; v6.z = 0.0; v6.w = 0.0; v6.a = 0.0; v6.b = 0.0; assert!(v1.is_zero()); assert!(v2.is_zero()); assert!(v3.is_zero()); assert!(v4.is_zero()); assert!(v5.is_zero()); assert!(v6.is_zero()); // Check that the components order is correct. m3.m11 = 11.0; m3.m12 = 12.0; m3.m13 = 13.0; m3.m21 = 21.0; m3.m22 = 22.0; m3.m23 = 23.0; m3.m31 = 31.0; m3.m32 = 32.0; m3.m33 = 33.0; let expected_m3 = Matrix3::new(11.0, 12.0, 13.0, 21.0, 22.0, 23.0, 31.0, 32.0, 33.0); assert_eq!(expected_m3, m3); } #[test] fn kronecker() { let a = Matrix2x3::new(11, 12, 13, 21, 22, 23); let b = Matrix4x5::new( 110, 120, 130, 140, 150, 210, 220, 230, 240, 250, 310, 320, 330, 340, 350, 410, 420, 430, 440, 450, ); let expected = OMatrix::<_, U8, U15>::from_row_slice(&[ 1210, 1320, 1430, 1540, 1650, 1320, 1440, 1560, 1680, 1800, 1430, 1560, 1690, 1820, 1950, 2310, 2420, 2530, 2640, 2750, 2520, 2640, 2760, 2880, 3000, 2730, 2860, 2990, 3120, 3250, 3410, 3520, 3630, 3740, 3850, 3720, 3840, 3960, 4080, 4200, 4030, 4160, 4290, 4420, 4550, 4510, 4620, 4730, 4840, 4950, 4920, 5040, 5160, 5280, 5400, 5330, 5460, 5590, 5720, 5850, 2310, 2520, 2730, 2940, 3150, 2420, 2640, 2860, 3080, 3300, 2530, 2760, 2990, 3220, 3450, 4410, 4620, 4830, 5040, 5250, 4620, 4840, 5060, 5280, 5500, 4830, 5060, 5290, 5520, 5750, 6510, 6720, 6930, 7140, 7350, 6820, 7040, 7260, 7480, 7700, 7130, 7360, 7590, 7820, 8050, 8610, 8820, 9030, 9240, 9450, 9020, 9240, 9460, 9680, 9900, 9430, 9660, 9890, 10120, 10350, ]); let computed = a.kronecker(&b); assert_eq!(computed, expected); let a = Vector2::new(1, 2); let b = Vector3::new(10, 20, 30); let expected = Vector6::new(10, 20, 30, 20, 40, 60); assert_eq!(a.kronecker(&b), expected); let a = Vector2::new(1, 2); let b = RowVector4::new(10, 20, 30, 40); let expected = Matrix2x4::new(10, 20, 30, 40, 20, 40, 60, 80); assert_eq!(a.kronecker(&b), expected); } #[test] fn set_row_column() { let a = Matrix4x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, ); let expected1 = Matrix4x5::new( 11, 12, 13, 14, 15, 42, 43, 44, 45, 46, 31, 32, 33, 34, 35, 41, 42, 43, 44, 45, ); let expected2 = Matrix4x5::new( 11, 12, 100, 14, 15, 42, 43, 101, 45, 46, 31, 32, 102, 34, 35, 41, 42, 103, 44, 45, ); let row = RowVector5::new(42, 43, 44, 45, 46); let col = Vector4::new(100, 101, 102, 103); let mut computed = a; computed.set_row(1, &row); assert_eq!(expected1, computed); computed.set_column(2, &col); assert_eq!(expected2, computed); } #[test] fn partial_clamp() { // NOTE: from #401. let n = Vector2::new(1.5, 0.0); let min = Vector2::new(-75.0, -0.0); let max = Vector2::new(75.0, 0.0); let inter = na::partial_clamp(&n, &min, &max); assert_eq!(*inter.unwrap(), n); } #[test] fn partial_cmp() { let a = Vector2::new(1.0, 6.0); let b = Vector2::new(1.0, 3.0); let c = Vector2::new(2.0, 7.0); let d = Vector2::new(0.0, 7.0); assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal)); assert_eq!(a.partial_cmp(&b), Some(Ordering::Greater)); assert_eq!(a.partial_cmp(&c), Some(Ordering::Less)); assert_eq!(a.partial_cmp(&d), None); } #[test] fn swizzle() { let a = Vector2::new(1.0f32, 2.0); let b = Vector3::new(1.0f32, 2.0, 3.0); let c = Vector4::new(1.0f32, 2.0, 3.0, 4.0); assert_eq!(a.xy(), Vector2::new(1.0, 2.0)); assert_eq!(a.yx(), Vector2::new(2.0, 1.0)); assert_eq!(a.xx(), Vector2::new(1.0, 1.0)); assert_eq!(a.yy(), Vector2::new(2.0, 2.0)); assert_eq!(a.xxx(), Vector3::new(1.0, 1.0, 1.0)); assert_eq!(a.yyy(), Vector3::new(2.0, 2.0, 2.0)); assert_eq!(a.xyx(), Vector3::new(1.0, 2.0, 1.0)); assert_eq!(a.yxy(), Vector3::new(2.0, 1.0, 2.0)); assert_eq!(b.xy(), Vector2::new(1.0, 2.0)); assert_eq!(b.yx(), Vector2::new(2.0, 1.0)); assert_eq!(b.xx(), Vector2::new(1.0, 1.0)); assert_eq!(b.yy(), Vector2::new(2.0, 2.0)); assert_eq!(b.xz(), Vector2::new(1.0, 3.0)); assert_eq!(b.zx(), Vector2::new(3.0, 1.0)); assert_eq!(b.yz(), Vector2::new(2.0, 3.0)); assert_eq!(b.zy(), Vector2::new(3.0, 2.0)); assert_eq!(b.zz(), Vector2::new(3.0, 3.0)); assert_eq!(b.xyz(), Vector3::new(1.0, 2.0, 3.0)); assert_eq!(b.xxx(), Vector3::new(1.0, 1.0, 1.0)); assert_eq!(b.yyy(), Vector3::new(2.0, 2.0, 2.0)); assert_eq!(b.zzz(), Vector3::new(3.0, 3.0, 3.0)); assert_eq!(b.zxy(), Vector3::new(3.0, 1.0, 2.0)); assert_eq!(b.zxz(), Vector3::new(3.0, 1.0, 3.0)); assert_eq!(b.zyz(), Vector3::new(3.0, 2.0, 3.0)); assert_eq!(c.xy(), Vector2::new(1.0, 2.0)); assert_eq!(c.yx(), Vector2::new(2.0, 1.0)); assert_eq!(c.xx(), Vector2::new(1.0, 1.0)); assert_eq!(c.yy(), Vector2::new(2.0, 2.0)); assert_eq!(c.xz(), Vector2::new(1.0, 3.0)); assert_eq!(c.zx(), Vector2::new(3.0, 1.0)); assert_eq!(c.yz(), Vector2::new(2.0, 3.0)); assert_eq!(c.zy(), Vector2::new(3.0, 2.0)); assert_eq!(c.zz(), Vector2::new(3.0, 3.0)); assert_eq!(c.xyz(), Vector3::new(1.0, 2.0, 3.0)); assert_eq!(c.xxx(), Vector3::new(1.0, 1.0, 1.0)); assert_eq!(c.yyy(), Vector3::new(2.0, 2.0, 2.0)); assert_eq!(c.zzz(), Vector3::new(3.0, 3.0, 3.0)); assert_eq!(c.zxy(), Vector3::new(3.0, 1.0, 2.0)); assert_eq!(c.zxz(), Vector3::new(3.0, 1.0, 3.0)); assert_eq!(c.zyz(), Vector3::new(3.0, 2.0, 3.0)); } #[cfg(feature = "proptest-support")] mod transposition_tests { use super::*; use crate::proptest::{dmatrix, matrix, vector4, PROPTEST_F64}; use proptest::{prop_assert, prop_assert_eq, proptest}; proptest! { #[test] fn transpose_transpose_is_self(m in matrix(PROPTEST_F64, Const::<2>, Const::<3>)) { prop_assert_eq!(m.transpose().transpose(), m) } #[test] fn transpose_mut_transpose_mut_is_self(m in matrix(PROPTEST_F64, Const::<3>, Const::<3>)) { let mut mm = m; mm.transpose_mut(); mm.transpose_mut(); prop_assert_eq!(m, mm) } #[test] fn transpose_transpose_is_id_dyn(m in dmatrix()) { prop_assert_eq!(m.transpose().transpose(), m) } #[test] fn check_transpose_components_dyn(m in dmatrix()) { let tr = m.transpose(); let (nrows, ncols) = m.shape(); prop_assert!(nrows == tr.shape().1 && ncols == tr.shape().0); for i in 0 .. nrows { for j in 0 .. ncols { prop_assert_eq!(m[(i, j)], tr[(j, i)]); } } } #[test] fn tr_mul_is_transpose_then_mul(m in matrix(PROPTEST_F64, Const::<4>, Const::<6>), v in vector4()) { prop_assert!(relative_eq!(m.transpose() * v, m.tr_mul(&v), epsilon = 1.0e-7)) } } } #[cfg(feature = "proptest-support")] mod inversion_tests { use super::*; use crate::proptest::*; use na::Matrix1; use proptest::{prop_assert, proptest}; proptest! { #[test] fn self_mul_inv_is_id_dim1(m in matrix1()) { if let Some(im) = m.try_inverse() { let id = Matrix1::one(); prop_assert!(relative_eq!(im * m, id, epsilon = 1.0e-7)); prop_assert!(relative_eq!(m * im, id, epsilon = 1.0e-7)); } } #[test] fn self_mul_inv_is_id_dim2(m in matrix2()) { if let Some(im) = m.try_inverse() { let id = Matrix2::one(); prop_assert!(relative_eq!(im * m, id, epsilon = 1.0e-7)); prop_assert!(relative_eq!(m * im, id, epsilon = 1.0e-7)); } } #[test] fn self_mul_inv_is_id_dim3(m in matrix3()) { if let Some(im) = m.try_inverse() { let id = Matrix3::one(); prop_assert!(relative_eq!(im * m, id, epsilon = 1.0e-7)); prop_assert!(relative_eq!(m * im, id, epsilon = 1.0e-7)); } } #[test] fn self_mul_inv_is_id_dim4(m in matrix4()) { if let Some(im) = m.try_inverse() { let id = Matrix4::one(); prop_assert!(relative_eq!(im * m, id, epsilon = 1.0e-7)); prop_assert!(relative_eq!(m * im, id, epsilon = 1.0e-7)); } } #[test] fn self_mul_inv_is_id_dim6(m in matrix6()) { if let Some(im) = m.try_inverse() { let id = Matrix6::one(); prop_assert!(relative_eq!(im * m, id, epsilon = 1.0e-7)); prop_assert!(relative_eq!(m * im, id, epsilon = 1.0e-7)); } } } } #[cfg(feature = "proptest-support")] mod normalization_tests { use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn normalized_vec_norm_is_one(v in vector3()) { if let Some(nv) = v.try_normalize(1.0e-10) { prop_assert!(relative_eq!(nv.norm(), 1.0, epsilon = 1.0e-7)); } } #[test] fn normalized_vec_norm_is_one_dyn(v in dvector()) { if let Some(nv) = v.try_normalize(1.0e-10) { prop_assert!(relative_eq!(nv.norm(), 1.0, epsilon = 1.0e-7)); } } } } #[cfg(all(feature = "proptest-support", feature = "alga"))] // TODO: move this to alga ? mod finite_dim_inner_space_tests { use super::*; use crate::proptest::*; use alga::linear::FiniteDimInnerSpace; use proptest::collection::vec; use proptest::{prop_assert, proptest}; use std::fmt::Display; macro_rules! finite_dim_inner_space_test( ($($Vector: ident, $vstrategy: ident, $orthonormal_subspace: ident, $orthonormalization: ident);* $(;)*) => {$( proptest! { #[test] fn $orthonormal_subspace(vs in vec($vstrategy(), 0..10)) { let mut given_basis = vs.clone(); let given_basis_dim = $Vector::orthonormalize(&mut given_basis[..]); let mut ortho_basis = Vec::new(); $Vector::orthonormal_subspace_basis( &given_basis[.. given_basis_dim], |e| { ortho_basis.push(*e); true } ); prop_assert!(is_subspace_basis(&ortho_basis[..])); for v in vs { for b in &ortho_basis { prop_assert!(relative_eq!(v.dot(b), 0.0, epsilon = 1.0e-7)); } } } #[test] fn $orthonormalization(vs in vec($vstrategy(), 0..10)) { let mut basis = vs.clone(); let subdim = $Vector::orthonormalize(&mut basis[..]); prop_assert!(is_subspace_basis(&basis[.. subdim])); for mut e in vs { for b in &basis[.. subdim] { e -= e.dot(b) * b } // Any element of `e` must be a linear combination of the basis elements. prop_assert!(relative_eq!(e.norm(), 0.0, epsilon = 1.0e-7)); } } } )*} ); finite_dim_inner_space_test!( Vector1, vector1, orthonormal_subspace_basis1, orthonormalize1; Vector2, vector2, orthonormal_subspace_basis2, orthonormalize2; Vector3, vector3, orthonormal_subspace_basis3, orthonormalize3; Vector4, vector4, orthonormal_subspace_basis4, orthonormalize4; Vector5, vector5, orthonormal_subspace_basis5, orthonormalize5; Vector6, vector6, orthonormal_subspace_basis6, orthonormalize6; ); /* * * Helper functions. * */ fn is_subspace_basis + Display>( vs: &[T], ) -> bool { for i in 0..vs.len() { // Basis elements must be normalized. if !relative_eq!(vs[i].norm(), 1.0, epsilon = 1.0e-7) { println!("Non-zero basis element norm: {}", vs[i].norm()); return false; } for j in 0..i { // Basis elements must be orthogonal. if !relative_eq!(vs[i].dot(&vs[j]), 0.0, epsilon = 1.0e-7) { println!( "Non-orthogonal basis elements: {} · {} = {}", vs[i], vs[j], vs[i].dot(&vs[j]) ); return false; } } } true } } #[test] fn partial_eq_shape_mismatch() { let a = Matrix2::new(1, 2, 3, 4); let b = Matrix2x3::new(1, 2, 3, 4, 5, 6); assert_ne!(a, b); assert_ne!(b, a); } #[test] fn partial_eq_different_types() { // Ensure comparability of several types of Matrices let dynamic_mat = DMatrix::from_row_slice(2, 4, &[1, 2, 3, 4, 5, 6, 7, 8]); let static_mat = Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8); let mut typenum_static_mat = OMatrix::, Const<4>>::zeros(); let mut view = typenum_static_mat.view_mut((0, 0), (2, 4)); view += static_mat; let fview_of_dmat = dynamic_mat.fixed_view::<2, 2>(0, 0); let dview_of_dmat = dynamic_mat.view((0, 0), (2, 2)); let fview_of_smat = static_mat.fixed_view::<2, 2>(0, 0); let dview_of_smat = static_mat.view((0, 0), (2, 2)); assert_eq!(dynamic_mat, static_mat); assert_eq!(static_mat, dynamic_mat); assert_eq!(dynamic_mat, view); assert_eq!(view, dynamic_mat); assert_eq!(static_mat, view); assert_eq!(view, static_mat); assert_eq!(fview_of_dmat, dview_of_dmat); assert_eq!(dview_of_dmat, fview_of_dmat); assert_eq!(fview_of_dmat, fview_of_smat); assert_eq!(fview_of_smat, fview_of_dmat); assert_eq!(fview_of_dmat, dview_of_smat); assert_eq!(dview_of_smat, fview_of_dmat); assert_eq!(dview_of_dmat, fview_of_smat); assert_eq!(fview_of_smat, dview_of_dmat); assert_eq!(dview_of_dmat, dview_of_smat); assert_eq!(dview_of_smat, dview_of_dmat); assert_eq!(fview_of_smat, dview_of_smat); assert_eq!(dview_of_smat, fview_of_smat); assert_ne!(dynamic_mat, dview_of_smat); assert_ne!(dview_of_smat, dynamic_mat); // TODO - implement those comparisons // assert_ne!(static_mat, typenum_static_mat); //assert_ne!(typenum_static_mat, static_mat); } fn generic_omatrix_to_string( vector: &nalgebra::OVector, matrix: &nalgebra::OMatrix, ) -> (String, String) where D: nalgebra::Dim, nalgebra::DefaultAllocator: nalgebra::base::allocator::Allocator, nalgebra::DefaultAllocator: nalgebra::base::allocator::Allocator, { (vector.to_string(), matrix.to_string()) } #[test] fn omatrix_to_string() { let dvec: nalgebra::DVector = nalgebra::dvector![1.0, 2.0]; let dmatr: nalgebra::DMatrix = nalgebra::dmatrix![1.0, 2.0; 3.0, 4.0]; let svec: nalgebra::SVector = nalgebra::vector![1.0, 2.0]; let smatr: nalgebra::SMatrix = nalgebra::matrix![1.0, 2.0; 3.0, 4.0]; assert_eq!( generic_omatrix_to_string(&dvec, &dmatr), (dvec.to_string(), dmatr.to_string()) ); assert_eq!( generic_omatrix_to_string(&svec, &smatr), (svec.to_string(), smatr.to_string()) ); } #[test] fn column_iteration() { // dynamic matrix let dmat = nalgebra::dmatrix![ 13,14,15; 23,24,25; 33,34,35; ]; let mut col_iter = dmat.column_iter(); assert_eq!(col_iter.next(), Some(dmat.column(0))); assert_eq!(col_iter.next(), Some(dmat.column(1))); assert_eq!(col_iter.next(), Some(dmat.column(2))); assert_eq!(col_iter.next(), None); // statically sized matrix let smat: nalgebra::SMatrix = nalgebra::matrix![1.0, 2.0; 3.0, 4.0]; let mut col_iter = smat.column_iter(); assert_eq!(col_iter.next(), Some(smat.column(0))); assert_eq!(col_iter.next(), Some(smat.column(1))); assert_eq!(col_iter.next(), None); } #[test] fn column_iteration_mut() { let mut dmat = nalgebra::dmatrix![ 13,14,15; 23,24,25; 33,34,35; ]; let mut cloned = dmat.clone(); let mut col_iter = dmat.column_iter_mut(); assert_eq!(col_iter.next(), Some(cloned.column_mut(0))); assert_eq!(col_iter.next(), Some(cloned.column_mut(1))); assert_eq!(col_iter.next(), Some(cloned.column_mut(2))); assert_eq!(col_iter.next(), None); // statically sized matrix let mut smat: nalgebra::SMatrix = nalgebra::matrix![1.0, 2.0; 3.0, 4.0]; let mut cloned = smat.clone(); let mut col_iter = smat.column_iter_mut(); assert_eq!(col_iter.next(), Some(cloned.column_mut(0))); assert_eq!(col_iter.next(), Some(cloned.column_mut(1))); assert_eq!(col_iter.next(), None); } #[test] fn column_iteration_double_ended() { let dmat = nalgebra::dmatrix![ 13,14,15,16,17; 23,24,25,26,27; 33,34,35,36,37; ]; let mut col_iter = dmat.column_iter(); assert_eq!(col_iter.next(), Some(dmat.column(0))); assert_eq!(col_iter.next(), Some(dmat.column(1))); assert_eq!(col_iter.next_back(), Some(dmat.column(4))); assert_eq!(col_iter.next_back(), Some(dmat.column(3))); assert_eq!(col_iter.next(), Some(dmat.column(2))); assert_eq!(col_iter.next_back(), None); assert_eq!(col_iter.next(), None); } #[test] fn column_iterator_double_ended_mut() { let mut dmat = nalgebra::dmatrix![ 13,14,15,16,17; 23,24,25,26,27; 33,34,35,36,37; ]; let mut cloned = dmat.clone(); let mut col_iter_mut = dmat.column_iter_mut(); assert_eq!(col_iter_mut.next(), Some(cloned.column_mut(0))); assert_eq!(col_iter_mut.next(), Some(cloned.column_mut(1))); assert_eq!(col_iter_mut.next_back(), Some(cloned.column_mut(4))); assert_eq!(col_iter_mut.next_back(), Some(cloned.column_mut(3))); assert_eq!(col_iter_mut.next(), Some(cloned.column_mut(2))); assert_eq!(col_iter_mut.next_back(), None); assert_eq!(col_iter_mut.next(), None); } #[test] fn test_inversion_failure_leaves_matrix4_unchanged() { let mut mat = na::Matrix4::new( 1.0, 2.0, 3.0, 4.0, 2.0, 4.0, 6.0, 8.0, 3.0, 6.0, 9.0, 12.0, 4.0, 8.0, 12.0, 16.0, ); let expected = mat.clone(); assert!(!mat.try_inverse_mut()); assert_eq!(mat, expected); } #[test] #[cfg(feature = "rayon")] fn parallel_column_iteration() { use nalgebra::dmatrix; use rayon::prelude::*; let dmat: DMatrix = dmatrix![ 13.,14.; 23.,24.; 33.,34.; ]; let cloned = dmat.clone(); // test that correct columns are iterated over dmat.par_column_iter().enumerate().for_each(|(idx, col)| { assert_eq!(col, cloned.column(idx)); }); // test that a more complex expression produces the same // result as the serial equivalent let par_result: f64 = dmat.par_column_iter().map(|col| col.norm()).sum(); let ser_result: f64 = dmat.column_iter().map(|col| col.norm()).sum(); assert_eq!(par_result, ser_result); // repeat this test using mutable iterators let mut dmat = dmat; dmat.par_column_iter_mut() .enumerate() .for_each(|(idx, col)| { assert_eq!(col, cloned.column(idx)); }); let par_mut_result: f64 = dmat.par_column_iter_mut().map(|col| col.norm()).sum(); assert_eq!(par_mut_result, ser_result); } #[test] #[cfg(feature = "rayon")] fn column_iteration_mut_double_ended() { let dmat = nalgebra::dmatrix![ 13,14,15,16,17; 23,24,25,26,27; 33,34,35,36,37; ]; let cloned = dmat.clone(); let mut col_iter = dmat.column_iter(); assert_eq!(col_iter.next(), Some(cloned.column(0))); assert_eq!(col_iter.next(), Some(cloned.column(1))); assert_eq!(col_iter.next_back(), Some(cloned.column(4))); assert_eq!(col_iter.next_back(), Some(cloned.column(3))); assert_eq!(col_iter.next(), Some(cloned.column(2))); assert_eq!(col_iter.next_back(), None); assert_eq!(col_iter.next(), None); } #[test] #[cfg(feature = "rayon")] fn parallel_column_iteration_mut() { use rayon::prelude::*; let mut first = DMatrix::::zeros(400, 300); let mut second = DMatrix::::zeros(400, 300); first .column_iter_mut() .enumerate() .for_each(|(idx, mut col)| col[idx] = 1.); second .par_column_iter_mut() .enumerate() .for_each(|(idx, mut col)| col[idx] = 1.); assert_eq!(first, second); assert_eq!(second, DMatrix::identity(400, 300)); } nalgebra-0.33.0/tests/core/matrix_view.rs000064400000000000000000000253440072674642500165200ustar 00000000000000#![allow(non_snake_case)] use na::{ DMatrix, DMatrixView, DMatrixViewMut, Matrix2, Matrix2x3, Matrix2x4, Matrix2x6, Matrix3, Matrix3x2, Matrix3x4, Matrix4x2, Matrix6x2, MatrixView2, MatrixView2x3, MatrixView2xX, MatrixView3, MatrixView3x2, MatrixViewMut2, MatrixViewMut2x3, MatrixViewMut2xX, MatrixViewMut3, MatrixViewMut3x2, MatrixViewMutXx3, MatrixViewXx3, RowVector4, Vector3, }; #[test] #[rustfmt::skip] fn nested_fixed_views() { let a = Matrix3x4::new(11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0); let s1 = a.fixed_view::<3, 3>(0, 1); // Simple view. let s2 = s1.fixed_view::<2, 2>(1, 1); // View of view. let s3 = s1.fixed_view_with_steps::<2, 2>((0, 0), (1, 1)); // View of view with steps. let expected_owned_s1 = Matrix3::new(12.0, 13.0, 14.0, 22.0, 23.0, 24.0, 32.0, 33.0, 34.0); let expected_owned_s2 = Matrix2::new(23.0, 24.0, 33.0, 34.0); let expected_owned_s3 = Matrix2::new(12.0, 14.0, 32.0, 34.0); assert_eq!(expected_owned_s1, s1.clone_owned()); assert_eq!(expected_owned_s2, s2.clone_owned()); assert_eq!(expected_owned_s3, s3.clone_owned()); } #[test] #[rustfmt::skip] fn nested_views() { let a = Matrix3x4::new(11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0); let s1 = a.view((0, 1), (3, 3)); let s2 = s1.view((1, 1), (2, 2)); let s3 = s1.view_with_steps((0, 0), (2, 2), (1, 1)); let expected_owned_s1 = DMatrix::from_row_slice(3, 3, &[ 12.0, 13.0, 14.0, 22.0, 23.0, 24.0, 32.0, 33.0, 34.0 ]); let expected_owned_s2 = DMatrix::from_row_slice(2, 2, &[ 23.0, 24.0, 33.0, 34.0 ]); let expected_owned_s3 = DMatrix::from_row_slice(2, 2, &[ 12.0, 14.0, 32.0, 34.0 ]); assert_eq!(expected_owned_s1, s1.clone_owned()); assert_eq!(expected_owned_s2, s2.clone_owned()); assert_eq!(expected_owned_s3, s3.clone_owned()); } #[test] #[rustfmt::skip] fn view_mut() { let mut a = Matrix3x4::new(11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0); { // We modify `a` through the mutable view. let mut s1 = a.view_with_steps_mut((0, 1), (2, 2), (1, 1)); s1.fill(0.0); } let expected_a = Matrix3x4::new(11.0, 0.0, 13.0, 0.0, 21.0, 22.0, 23.0, 24.0, 31.0, 0.0, 33.0, 0.0); assert_eq!(expected_a, a); } #[test] #[rustfmt::skip] fn nested_row_views() { let a = Matrix6x2::new(11.0, 12.0, 21.0, 22.0, 31.0, 32.0, 41.0, 42.0, 51.0, 52.0, 61.0, 62.0); let s1 = a.fixed_rows::<4>(1); let s2 = s1.fixed_rows_with_step::<2>(1, 1); let expected_owned_s1 = Matrix4x2::new(21.0, 22.0, 31.0, 32.0, 41.0, 42.0, 51.0, 52.0); let expected_owned_s2 = Matrix2::new(31.0, 32.0, 51.0, 52.0); assert_eq!(expected_owned_s1, s1.clone_owned()); assert_eq!(expected_owned_s2, s2.clone_owned()); } #[test] #[rustfmt::skip] fn row_view_mut() { let mut a = Matrix6x2::new(11.0, 12.0, 21.0, 22.0, 31.0, 32.0, 41.0, 42.0, 51.0, 52.0, 61.0, 62.0); { // We modify `a` through the mutable view. let mut s1 = a.rows_with_step_mut(1, 3, 1); s1.fill(0.0); } let expected_a = Matrix6x2::new(11.0, 12.0, 0.0, 0.0, 31.0, 32.0, 0.0, 0.0, 51.0, 52.0, 0.0, 0.0); assert_eq!(expected_a, a); } #[test] #[rustfmt::skip] fn nested_col_views() { let a = Matrix2x6::new(11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); let s1 = a.fixed_columns::<4>(1); let s2 = s1.fixed_columns_with_step::<2>(1, 1); let expected_owned_s1 = Matrix2x4::new(12.0, 13.0, 14.0, 15.0, 22.0, 23.0, 24.0, 25.0); let expected_owned_s2 = Matrix2::new(13.0, 15.0, 23.0, 25.0); assert_eq!(expected_owned_s1, s1.clone_owned()); assert_eq!(expected_owned_s2, s2.clone_owned()); } #[test] #[rustfmt::skip] fn col_view_mut() { let mut a = Matrix2x6::new(11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); { // We modify `a` through the mutable view. let mut s1 = a.columns_with_step_mut(1, 3, 1); s1.fill(0.0); } let expected_a = Matrix2x6::new(11.0, 0.0, 13.0, 0.0, 15.0, 0.0, 21.0, 0.0, 23.0, 0.0, 25.0, 0.0); assert_eq!(expected_a, a.clone_owned()); } #[test] #[rustfmt::skip] fn rows_range_pair() { let a = Matrix3x4::new(11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0); let (l, r) = a.rows_range_pair(.. 3, 3 ..); assert!(r.len() == 0 && l.eq(&a)); let (l, r) = a.rows_range_pair(0, 1 ..); let expected_l = RowVector4::new(11.0, 12.0, 13.0, 14.0); let expected_r = Matrix2x4::new(21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0); assert!(l.eq(&expected_l) && r.eq(&expected_r)); } #[test] #[rustfmt::skip] fn columns_range_pair() { let a = Matrix3x4::new(11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0, 24.0, 31.0, 32.0, 33.0, 34.0); let (l, r) = a.columns_range_pair(.. 4, 4 ..); assert!(r.len() == 0 && l.eq(&a)); let (l, r) = a.columns_range_pair(0, 1 ..); let expected_l = Vector3::new(11.0, 21.0, 31.0); let expected_r = Matrix3::new(12.0, 13.0, 14.0, 22.0, 23.0, 24.0, 32.0, 33.0, 34.0); assert!(l.eq(&expected_l) && r.eq(&expected_r)); } #[test] #[rustfmt::skip] fn new_from_slice() { let data = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 ]; let expected2 = Matrix2::from_column_slice(&data); let expected3 = Matrix3::from_column_slice(&data); let expected2x3 = Matrix2x3::from_column_slice(&data); let expected3x2 = Matrix3x2::from_column_slice(&data); { let m2 = MatrixView2::from_slice(&data); let m3 = MatrixView3::from_slice(&data); let m2x3 = MatrixView2x3::from_slice(&data); let m3x2 = MatrixView3x2::from_slice(&data); let m2xX = MatrixView2xX::from_slice(&data, 3); let mXx3 = MatrixViewXx3::from_slice(&data, 2); let mXxX = DMatrixView::from_slice(&data, 2, 3); assert!(m2.eq(&expected2)); assert!(m3.eq(&expected3)); assert!(m2x3.eq(&expected2x3)); assert!(m3x2.eq(&expected3x2)); assert!(m2xX.eq(&expected2x3)); assert!(mXx3.eq(&expected2x3)); assert!(mXxX.eq(&expected2x3)); } } #[test] #[rustfmt::skip] fn new_from_slice_mut() { let data = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 ]; let expected2 = [ 0.0, 0.0, 0.0, 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 ]; let expected3 = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 11.0, 12.0 ]; let expected2x3 = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 ]; let expected3x2 = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 ]; let mut data_mut = data.clone(); MatrixViewMut2::from_slice(&mut data_mut).fill(0.0); assert!(data_mut == expected2); let mut data_mut = data.clone(); MatrixViewMut3::from_slice(&mut data_mut).fill(0.0); assert!(data_mut == expected3); let mut data_mut = data.clone(); MatrixViewMut2x3::from_slice(&mut data_mut).fill(0.0); assert!(data_mut == expected2x3); let mut data_mut = data.clone(); MatrixViewMut3x2::from_slice(&mut data_mut).fill(0.0); assert!(data_mut == expected3x2); let mut data_mut = data.clone(); MatrixViewMut2xX::from_slice(&mut data_mut, 3).fill(0.0); assert!(data_mut == expected2x3); let mut data_mut = data.clone(); MatrixViewMutXx3::from_slice(&mut data_mut, 2).fill(0.0); assert!(data_mut == expected2x3); let mut data_mut = data.clone(); DMatrixViewMut::from_slice(&mut data_mut, 2, 3).fill(0.0); assert!(data_mut == expected2x3); } #[test] #[should_panic] fn row_out_of_bounds() { let a = Matrix3x4::::zeros(); a.row(3); } #[test] #[should_panic] fn rows_out_of_bounds() { let a = Matrix3x4::::zeros(); a.rows(1, 3); } #[test] #[should_panic] fn rows_with_step_out_of_bounds() { let a = Matrix3x4::::zeros(); a.rows_with_step(1, 2, 1); } #[test] #[should_panic] fn column_out_of_bounds() { let a = Matrix3x4::::zeros(); a.column(4); } #[test] #[should_panic] fn columns_out_of_bounds() { let a = Matrix3x4::::zeros(); a.columns(2, 3); } #[test] #[should_panic] fn columns_with_step_out_of_bounds() { let a = Matrix3x4::::zeros(); a.columns_with_step(2, 2, 1); } #[test] #[should_panic] fn view_out_of_bounds() { let a = Matrix3x4::::zeros(); a.view((1, 2), (3, 1)); } #[test] #[should_panic] fn view_with_steps_out_of_bounds() { let a = Matrix3x4::::zeros(); a.view_with_steps((1, 2), (2, 2), (0, 1)); } nalgebra-0.33.0/tests/core/matrixcompare.rs000064400000000000000000000053170072674642500170330ustar 00000000000000//! Tests for `matrixcompare` integration. //! //! The `matrixcompare` crate itself is responsible for testing the actual comparison. //! The tests here only check that the necessary trait implementations are correctly implemented, //! in addition to some sanity checks with example input. use matrixcompare::assert_matrix_eq; use nalgebra::{OMatrix, U4, U5}; #[cfg(feature = "proptest-support")] use { crate::proptest::*, matrixcompare::DenseAccess, nalgebra::DMatrix, proptest::{prop_assert_eq, proptest}, }; #[cfg(feature = "proptest-support")] proptest! { #[test] fn fetch_single_is_equivalent_to_index_f64(matrix in dmatrix()) { for i in 0 .. matrix.nrows() { for j in 0 .. matrix.ncols() { prop_assert_eq!(matrix.fetch_single(i, j), *matrix.index((i, j))); } } } #[test] fn matrixcompare_shape_agrees_with_matrix(matrix in dmatrix()) { prop_assert_eq!(matrix.nrows(), as matrixcompare::Matrix>::rows(&matrix)); prop_assert_eq!(matrix.ncols(), as matrixcompare::Matrix>::cols(&matrix)); } } #[test] fn assert_matrix_eq_dense_positive_comparison() { #[rustfmt::skip] let a = OMatrix::<_, U4, U5>::from_row_slice(&[ 1210, 1320, 1430, 1540, 1650, 2310, 2420, 2530, 2640, 2750, 3410, 3520, 3630, 3740, 3850, 4510, 4620, 4730, 4840, 4950, ]); #[rustfmt::skip] let b = OMatrix::<_, U4, U5>::from_row_slice(&[ 1210, 1320, 1430, 1540, 1650, 2310, 2420, 2530, 2640, 2750, 3410, 3520, 3630, 3740, 3850, 4510, 4620, 4730, 4840, 4950, ]); // Test matrices of static size assert_matrix_eq!(a, b); assert_matrix_eq!(&a, b); assert_matrix_eq!(a, &b); assert_matrix_eq!(&a, &b); // Test matrices of dynamic size let a_dyn = a.index((0..4, 0..5)); let b_dyn = b.index((0..4, 0..5)); assert_matrix_eq!(a_dyn, b_dyn); assert_matrix_eq!(a_dyn, &b_dyn); assert_matrix_eq!(&a_dyn, b_dyn); assert_matrix_eq!(&a_dyn, &b_dyn); } #[test] #[should_panic] fn assert_matrix_eq_dense_negative_comparison() { #[rustfmt::skip] let a = OMatrix::<_, U4, U5>::from_row_slice(&[ 1210, 1320, 1430, 1540, 1650, 2310, 2420, 2530, 2640, 2750, 3410, 3520, 3630, 3740, 3850, 4510, 4620, -4730, 4840, 4950, ]); #[rustfmt::skip] let b = OMatrix::<_, U4, U5>::from_row_slice(&[ 1210, 1320, 1430, 1540, 1650, 2310, 2420, 2530, 2640, 2750, 3410, 3520, 3630, 3740, 3850, 4510, 4620, 4730, 4840, 4950, ]); assert_matrix_eq!(a, b); } nalgebra-0.33.0/tests/core/mint.rs000064400000000000000000000043660072674642500151320ustar 00000000000000use mint; use na::{Matrix2, Matrix2x3, Matrix3, Matrix3x4, Matrix4, Quaternion, Vector2, Vector3, Vector4}; macro_rules! mint_vector_conversion( ($($mint_vector_conversion_i: ident, $Vector: ident, $SZ: expr);* $(;)*) => {$( #[test] fn $mint_vector_conversion_i() { let v = $Vector::from_fn(|i, _| i); let mv: mint::$Vector = v.into(); let mv_ref: &mint::$Vector = v.as_ref(); let v2 = $Vector::from(mv); let arr: [usize; $SZ] = mv.into(); for i in 0 .. $SZ { assert_eq!(arr[i], i); } assert_eq!(&mv, mv_ref); assert_eq!(v, v2); } )*} ); mint_vector_conversion!( mint_vector_conversion_2, Vector2, 2; mint_vector_conversion_3, Vector3, 3; mint_vector_conversion_4, Vector4, 4; ); #[test] fn mint_quaternion_conversions() { let q = Quaternion::new(0.1f64, 0.2, 0.3, 0.4); let mq: mint::Quaternion = q.into(); let q2 = Quaternion::from(mq); assert_eq!(mq.v.x, q[0]); assert_eq!(mq.v.y, q[1]); assert_eq!(mq.v.z, q[2]); assert_eq!(mq.s, q[3]); assert_eq!(q, q2); } macro_rules! mint_matrix_conversion( ($($mint_matrix_conversion_i_j: ident, $Matrix: ident, $Mint: ident, ($NRows: expr, $NCols: expr));* $(;)*) => {$( #[test] fn $mint_matrix_conversion_i_j() { let m = $Matrix::from_fn(|i, j| i * 10 + j); let mm: mint::$Mint = m.into(); let m2 = $Matrix::from(mm); let arr: [[usize; $NRows]; $NCols] = mm.into(); for i in 0 .. $NRows { for j in 0 .. $NCols { assert_eq!(arr[j][i], i * 10 + j); } } assert_eq!(m, m2); } )*} ); mint_matrix_conversion!( mint_matrix_conversion_2_2, Matrix2, ColumnMatrix2, (2, 2); mint_matrix_conversion_2_3, Matrix2x3, ColumnMatrix2x3, (2, 3); mint_matrix_conversion_3_3, Matrix3, ColumnMatrix3, (3, 3); mint_matrix_conversion_3_4, Matrix3x4, ColumnMatrix3x4, (3, 4); mint_matrix_conversion_4_4, Matrix4, ColumnMatrix4, (4, 4); ); nalgebra-0.33.0/tests/core/mod.rs000064400000000000000000000005620072674642500147340ustar 00000000000000mod blas; mod cg; mod conversion; mod edition; mod empty; mod matrix; mod matrix_view; #[cfg(feature = "mint")] mod mint; mod reshape; #[cfg(feature = "rkyv-serialize-no-std")] mod rkyv; mod serde; mod variance; #[cfg(feature = "compare")] mod matrixcompare; #[cfg(feature = "arbitrary")] pub mod helper; #[cfg(feature = "macros")] mod macros; nalgebra-0.33.0/tests/core/reshape.rs000064400000000000000000000071530072674642500156070ustar 00000000000000use na::{ Const, DMatrix, DMatrixView, DMatrixViewMut, Dyn, Matrix, MatrixView, MatrixViewMut, SMatrix, SMatrixView, SMatrixViewMut, VecStorage, U3, U4, }; use nalgebra_macros::matrix; use simba::scalar::SupersetOf; const MATRIX: SMatrix = matrix![ 1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12 ]; const RESHAPED_MATRIX: SMatrix = matrix![ 1, 10, 8, 6; 4, 2, 11, 9; 7, 5, 3, 12 ]; // Helper alias for making it easier to specify dynamically allocated matrices with // different dimension types (unlike DMatrix) type GenericDMatrix = Matrix>; #[test] fn reshape_owned() { macro_rules! test_reshape { ($in_matrix:ty => $out_matrix:ty, $rows:expr, $cols:expr) => {{ // This is a pretty weird way to convert, but Matrix implements SubsetOf let matrix: $in_matrix = MATRIX.to_subset().unwrap(); let reshaped: $out_matrix = matrix.reshape_generic($rows, $cols); assert_eq!(reshaped, RESHAPED_MATRIX); }}; } test_reshape!(SMatrix<_, 4, 3> => SMatrix<_, 3, 4>, U3, U4); test_reshape!(GenericDMatrix<_, U4, Dyn> => GenericDMatrix<_, Dyn, Dyn>, Dyn(3), Dyn(4)); test_reshape!(GenericDMatrix<_, U4, Dyn> => GenericDMatrix<_, U3, Dyn>, U3, Dyn(4)); test_reshape!(GenericDMatrix<_, U4, Dyn> => GenericDMatrix<_, Dyn, U4>, Dyn(3), U4); test_reshape!(DMatrix<_> => DMatrix<_>, Dyn(3), Dyn(4)); } #[test] fn reshape_slice() { macro_rules! test_reshape { ($in_slice:ty => $out_slice:ty, $rows:expr, $cols:expr) => { // We test both that types check out by being explicit about types // and the actual contents of the matrix { // By constructing the slice from a mutable reference we can obtain *either* // an immutable slice or a mutable slice, which simplifies the testing of both // types of mutability let mut source_matrix = MATRIX.clone(); let slice: $in_slice = Matrix::from(&mut source_matrix); let reshaped: $out_slice = slice.reshape_generic($rows, $cols); assert_eq!(reshaped, RESHAPED_MATRIX); } }; } // Static "source slice" test_reshape!(SMatrixView<_, 4, 3> => SMatrixView<_, 3, 4>, U3, U4); test_reshape!(SMatrixView<_, 4, 3> => DMatrixView<_>, Dyn(3), Dyn(4)); test_reshape!(SMatrixView<_, 4, 3> => MatrixView<_, Const<3>, Dyn>, U3, Dyn(4)); test_reshape!(SMatrixView<_, 4, 3> => MatrixView<_, Dyn, Const<4>>, Dyn(3), U4); test_reshape!(SMatrixViewMut<_, 4, 3> => SMatrixViewMut<_, 3, 4>, U3, U4); test_reshape!(SMatrixViewMut<_, 4, 3> => DMatrixViewMut<_>, Dyn(3), Dyn(4)); test_reshape!(SMatrixViewMut<_, 4, 3> => MatrixViewMut<_, Const<3>, Dyn>, U3, Dyn(4)); test_reshape!(SMatrixViewMut<_, 4, 3> => MatrixViewMut<_, Dyn, Const<4>>, Dyn(3), U4); // Dyn "source slice" test_reshape!(DMatrixView<_> => SMatrixView<_, 3, 4>, U3, U4); test_reshape!(DMatrixView<_> => DMatrixView<_>, Dyn(3), Dyn(4)); test_reshape!(DMatrixView<_> => MatrixView<_, Const<3>, Dyn>, U3, Dyn(4)); test_reshape!(DMatrixView<_> => MatrixView<_, Dyn, Const<4>>, Dyn(3), U4); test_reshape!(DMatrixViewMut<_> => SMatrixViewMut<_, 3, 4>, U3, U4); test_reshape!(DMatrixViewMut<_> => DMatrixViewMut<_>, Dyn(3), Dyn(4)); test_reshape!(DMatrixViewMut<_> => MatrixViewMut<_, Const<3>, Dyn>, U3, Dyn(4)); test_reshape!(DMatrixViewMut<_> => MatrixViewMut<_, Dyn, Const<4>>, Dyn(3), U4); } nalgebra-0.33.0/tests/core/rkyv.rs000064400000000000000000000044670072674642500151600ustar 00000000000000#![cfg(feature = "rkyv-serialize")] use na::{ Isometry3, IsometryMatrix2, IsometryMatrix3, Matrix3x4, Point2, Point3, Quaternion, Rotation2, Rotation3, Similarity3, SimilarityMatrix2, SimilarityMatrix3, Translation2, Translation3, }; use rand; use rkyv::{Deserialize, Infallible}; macro_rules! test_rkyv_same_type( ($($test: ident, $ty: ident);* $(;)*) => {$( #[test] fn $test() { let value: $ty = rand::random(); let bytes = rkyv::to_bytes::<_, 256>(&value).unwrap(); let archived = rkyv::check_archived_root::<$ty>(&bytes[..]).unwrap(); // Compare archived and non-archived assert_eq!(archived, &value); // Make sure Debug implementations are the same for Archived and non-Archived versions. assert_eq!(format!("{:?}", value), format!("{:?}", archived)); } )*} ); macro_rules! test_rkyv_diff_type( ($($test: ident, $ty: ident);* $(;)*) => {$( #[test] fn $test() { let value: $ty = Default::default(); let bytes = rkyv::to_bytes::<_, 256>(&value).unwrap(); let archived = rkyv::check_archived_root::<$ty>(&bytes[..]).unwrap(); let deserialized: $ty = archived.deserialize(&mut Infallible).unwrap(); assert_eq!(deserialized, value); } )*} ); // Tests to make sure test_rkyv_same_type!( rkyv_same_type_matrix3x4, Matrix3x4; rkyv_same_type_point3, Point3; rkyv_same_type_translation3, Translation3; rkyv_same_type_rotation3, Rotation3; rkyv_same_type_isometry3, Isometry3; rkyv_same_type_isometry_matrix3, IsometryMatrix3; rkyv_same_type_similarity3, Similarity3; rkyv_same_type_similarity_matrix3, SimilarityMatrix3; rkyv_same_type_quaternion, Quaternion; rkyv_same_type_point2, Point2; rkyv_same_type_translation2, Translation2; rkyv_same_type_rotation2, Rotation2; // rkyv_same_type_isometry2, Isometry2; rkyv_same_type_isometry_matrix2, IsometryMatrix2; // rkyv_same_type_similarity2, Similarity2; rkyv_same_type_similarity_matrix2, SimilarityMatrix2; ); test_rkyv_diff_type! { rkyv_diff_type_matrix3x4, Matrix3x4; } nalgebra-0.33.0/tests/core/serde.rs000064400000000000000000000065700072674642500152640ustar 00000000000000#![cfg(feature = "serde-serialize")] use na::{ DMatrix, Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, Matrix2x3, Matrix3x4, Point2, Point3, Quaternion, Rotation2, Rotation3, Similarity2, Similarity3, SimilarityMatrix2, SimilarityMatrix3, Translation2, Translation3, Unit, Vector2, }; use rand; use serde::{Deserialize, Serialize}; use serde_json; macro_rules! test_serde( ($($test: ident, $ty: ident);* $(;)*) => {$( #[test] fn $test() { let v: $ty = rand::random(); let serialized = serde_json::to_string(&v).unwrap(); let deserialized: $ty = serde_json::from_str(&serialized).unwrap(); assert_eq!(v, deserialized); } )*} ); #[test] fn serde_dmatrix() { let v: DMatrix = DMatrix::new_random(3, 4); let serialized = serde_json::to_string(&v).unwrap(); let deserialized: DMatrix = serde_json::from_str(&serialized).unwrap(); assert_eq!(v, deserialized); let m = DMatrix::from_column_slice(2, 3, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); let mat_str = "[[1.0, 2.0, 3.0, 4.0, 5.0, 6.0],2,3]"; let deserialized: DMatrix = serde_json::from_str(&mat_str).unwrap(); assert_eq!(m, deserialized); let m = Matrix2x3::from_column_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]); let mat_str = "[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]"; let deserialized: Matrix2x3 = serde_json::from_str(&mat_str).unwrap(); assert_eq!(m, deserialized); } #[test] #[should_panic] fn serde_dmatrix_invalid_len() { // This must fail: we attempt to deserialize a 2x3 with only 5 elements. let mat_str = "[[1.0, 2.0, 3.0, 4.0, 5.0],2,3]"; let _: DMatrix = serde_json::from_str(&mat_str).unwrap(); } #[test] #[should_panic] fn serde_smatrix_invalid_len() { // This must fail: we attempt to deserialize a 2x3 with only 5 elements. let mat_str = "[1.0, 2.0, 3.0, 4.0, 5.0]"; let _: Matrix2x3 = serde_json::from_str(&mat_str).unwrap(); } test_serde!( serde_matrix3x4, Matrix3x4; serde_point3, Point3; serde_translation3, Translation3; serde_rotation3, Rotation3; serde_isometry3, Isometry3; serde_isometry_matrix3, IsometryMatrix3; serde_similarity3, Similarity3; serde_similarity_matrix3, SimilarityMatrix3; serde_quaternion, Quaternion; serde_point2, Point2; serde_translation2, Translation2; serde_rotation2, Rotation2; serde_isometry2, Isometry2; serde_isometry_matrix2, IsometryMatrix2; serde_similarity2, Similarity2; serde_similarity_matrix2, SimilarityMatrix2; ); #[test] fn serde_flat() { // The actual storage is hidden behind three layers of wrapper types that shouldn't appear in serialized form. let v = Unit::new_normalize(Quaternion::new(0., 0., 0., 1.)); let serialized = serde_json::to_string(&v).unwrap(); assert_eq!(serialized, "[0.0,0.0,1.0,0.0]"); } #[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] enum Stuff { A(f64), B(f64), } #[test] fn deserialize_enum() { let json = r#"[{"letter":"A", "value":123.4}, {"letter":"B", "value":567.8}]"#; let parsed: Result, _> = serde_json::from_str(json); println!("parsed: {:?}", parsed); } nalgebra-0.33.0/tests/core/variance.rs000064400000000000000000000012110072674642500157350ustar 00000000000000use nalgebra::DVector; #[test] fn test_variance_catastrophic_cancellation() { let long_repeating_vector = DVector::repeat(10_000, 100000000.0); assert_eq!(long_repeating_vector.variance(), 0.0); let short_vec = DVector::from_vec(vec![1., 2., 3.]); assert_eq!(short_vec.variance(), 2.0 / 3.0); let short_vec = DVector::::from_vec(vec![1.0e8 + 4.0, 1.0e8 + 7.0, 1.0e8 + 13.0, 1.0e8 + 16.0]); assert_eq!(short_vec.variance(), 22.5); let short_vec = DVector::::from_vec(vec![1.0e9 + 4.0, 1.0e9 + 7.0, 1.0e9 + 13.0, 1.0e9 + 16.0]); assert_eq!(short_vec.variance(), 22.5); } nalgebra-0.33.0/tests/geometry/dual_quaternion.rs000064400000000000000000000256600072674642500202600ustar 00000000000000#![cfg(feature = "proptest-support")] #![allow(non_snake_case)] use na::{DualQuaternion, Point3, Unit, UnitDualQuaternion, UnitQuaternion, Vector3}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest!( #[test] fn isometry_equivalence(iso in isometry3(), p in point3(), v in vector3()) { let dq = UnitDualQuaternion::from_isometry(&iso); prop_assert!(relative_eq!(iso * p, dq * p, epsilon = 1.0e-7)); prop_assert!(relative_eq!(iso * v, dq * v, epsilon = 1.0e-7)); } #[test] fn inverse_is_identity(i in unit_dual_quaternion(), p in point3(), v in vector3()) { let ii = i.inverse(); prop_assert!(relative_eq!(i * ii, UnitDualQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(ii * i, UnitDualQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7)); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn multiply_equals_alga_transform( dq in unit_dual_quaternion(), v in vector3(), p in point3() ) { prop_assert!(dq * v == dq.transform_vector(&v) && dq * p == dq.transform_point(&p) && relative_eq!( dq.inverse() * v, dq.inverse_transform_vector(&v), epsilon = 1.0e-7 ) && relative_eq!( dq.inverse() * p, dq.inverse_transform_point(&p), epsilon = 1.0e-7 )); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn composition( dq in unit_dual_quaternion(), uq in unit_quaternion(), t in translation3(), v in vector3(), p in point3() ) { // (rotation × dual quaternion) * point = rotation × (dual quaternion * point) prop_assert!(relative_eq!((uq * dq) * v, uq * (dq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * dq) * p, uq * (dq * p), epsilon = 1.0e-7)); // (dual quaternion × rotation) * point = dual quaternion × (rotation * point) prop_assert!(relative_eq!((dq * uq) * v, dq * (uq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((dq * uq) * p, dq * (uq * p), epsilon = 1.0e-7)); // (translation × dual quaternion) * point = translation × (dual quaternion * point) prop_assert!(relative_eq!((t * dq) * v, (dq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * dq) * p, t * (dq * p), epsilon = 1.0e-7)); // (dual quaternion × translation) * point = dual quaternion × (translation * point) prop_assert!(relative_eq!((dq * t) * v, dq * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((dq * t) * p, dq * (t * p), epsilon = 1.0e-7)); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn sclerp_is_defined_for_identical_orientations( dq in unit_dual_quaternion(), s in -1.0f64..2.0f64, t in translation3(), ) { // Should not panic. prop_assert!(relative_eq!(dq.sclerp(&dq, 0.0), dq, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq, 0.5), dq, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq, 1.0), dq, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq, s), dq, epsilon = 1.0e-7)); let unit = UnitDualQuaternion::identity(); prop_assert!(relative_eq!(unit.sclerp(&unit, 0.0), unit, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit, 0.5), unit, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit, 1.0), unit, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit, s), unit, epsilon = 1.0e-7)); // ScLERPing two unit dual quaternions with nearly equal rotation // components should result in a unit dual quaternion with a rotation // component nearly equal to either input. let dq2 = t * dq; prop_assert!(relative_eq!(dq.sclerp(&dq2, 0.0).real, dq.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq2, 0.5).real, dq.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq2, 1.0).real, dq.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(dq.sclerp(&dq2, s).real, dq.real, epsilon = 1.0e-7)); // ScLERPing two unit dual quaternions with nearly equal rotation // components should result in a unit dual quaternion with a translation // component which is nearly equal to linearly interpolating the // translation components of the inputs. prop_assert!(relative_eq!( dq.sclerp(&dq2, s).translation().vector, dq.translation().vector.lerp(&dq2.translation().vector, s), epsilon = 1.0e-7 )); let unit2 = t * unit; prop_assert!(relative_eq!(unit.sclerp(&unit2, 0.0).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit2, 0.5).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit2, 1.0).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!(unit.sclerp(&unit2, s).real, unit.real, epsilon = 1.0e-7)); prop_assert!(relative_eq!( unit.sclerp(&unit2, s).translation().vector, unit.translation().vector.lerp(&unit2.translation().vector, s), epsilon = 1.0e-7 )); } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn sclerp_is_not_defined_for_opposite_orientations( dq in unit_dual_quaternion(), s in 0.1f64..0.9f64, t in translation3(), t2 in translation3(), v in vector3(), ) { let iso = dq.to_isometry(); let rot = iso.rotation; if let Some((axis, angle)) = rot.axis_angle() { let flipped = UnitQuaternion::from_axis_angle(&axis, angle + std::f64::consts::PI); let dqf = flipped * rot.inverse() * dq.clone(); prop_assert!(dq.try_sclerp(&dqf, 0.5, 1.0e-7).is_none()); prop_assert!(dq.try_sclerp(&dqf, s, 1.0e-7).is_none()); } let dq2 = t * dq; let iso2 = dq2.to_isometry(); let rot2 = iso2.rotation; if let Some((axis, angle)) = rot2.axis_angle() { let flipped = UnitQuaternion::from_axis_angle(&axis, angle + std::f64::consts::PI); let dq3f = t2 * flipped * rot.inverse() * dq.clone(); prop_assert!(dq2.try_sclerp(&dq3f, 0.5, 1.0e-7).is_none()); prop_assert!(dq2.try_sclerp(&dq3f, s, 1.0e-7).is_none()); } if let Some(axis) = Unit::try_new(v, 1.0e-7) { let unit = UnitDualQuaternion::identity(); let flip = UnitQuaternion::from_axis_angle(&axis, std::f64::consts::PI); let unitf = flip * unit; prop_assert!(unit.try_sclerp(&unitf, 0.5, 1.0e-7).is_none()); prop_assert!(unit.try_sclerp(&unitf, s, 1.0e-7).is_none()); let unit2f = t * unit * flip; prop_assert!(unit.try_sclerp(&unit2f, 0.5, 1.0e-7).is_none()); prop_assert!(unit.try_sclerp(&unit2f, s, 1.0e-7).is_none()); } } #[cfg_attr(rustfmt, rustfmt_skip)] #[test] fn all_op_exist( dq in dual_quaternion(), udq in unit_dual_quaternion(), uq in unit_quaternion(), s in PROPTEST_F64, t in translation3(), v in vector3(), p in point3() ) { let dqMs: DualQuaternion<_> = dq * s; let dqMdq: DualQuaternion<_> = dq * dq; let dqMudq: DualQuaternion<_> = dq * udq; let udqMdq: DualQuaternion<_> = udq * dq; let iMi: UnitDualQuaternion<_> = udq * udq; let iMuq: UnitDualQuaternion<_> = udq * uq; let iDi: UnitDualQuaternion<_> = udq / udq; let iDuq: UnitDualQuaternion<_> = udq / uq; let iMp: Point3<_> = udq * p; let iMv: Vector3<_> = udq * v; let iMt: UnitDualQuaternion<_> = udq * t; let tMi: UnitDualQuaternion<_> = t * udq; let uqMi: UnitDualQuaternion<_> = uq * udq; let uqDi: UnitDualQuaternion<_> = uq / udq; let mut dqMs1 = dq; let mut dqMdq1 = dq; let mut dqMdq2 = dq; let mut dqMudq1 = dq; let mut dqMudq2 = dq; let mut iMt1 = udq; let mut iMt2 = udq; let mut iMi1 = udq; let mut iMi2 = udq; let mut iMuq1 = udq; let mut iMuq2 = udq; let mut iDi1 = udq; let mut iDi2 = udq; let mut iDuq1 = udq; let mut iDuq2 = udq; dqMs1 *= s; dqMdq1 *= dq; dqMdq2 *= &dq; dqMudq1 *= udq; dqMudq2 *= &udq; iMt1 *= t; iMt2 *= &t; iMi1 *= udq; iMi2 *= &udq; iMuq1 *= uq; iMuq2 *= &uq; iDi1 /= udq; iDi2 /= &udq; iDuq1 /= uq; iDuq2 /= &uq; prop_assert!(dqMs == dqMs1 && dqMdq == dqMdq1 && dqMdq == dqMdq2 && dqMudq == dqMudq1 && dqMudq == dqMudq2 && iMt == iMt1 && iMt == iMt2 && iMi == iMi1 && iMi == iMi2 && iMuq == iMuq1 && iMuq == iMuq2 && iDi == iDi1 && iDi == iDi2 && iDuq == iDuq1 && iDuq == iDuq2 && dqMs == &dq * s && dqMdq == &dq * &dq && dqMdq == dq * &dq && dqMdq == &dq * dq && dqMudq == &dq * &udq && dqMudq == dq * &udq && dqMudq == &dq * udq && udqMdq == &udq * &dq && udqMdq == udq * &dq && udqMdq == &udq * dq && iMi == &udq * &udq && iMi == udq * &udq && iMi == &udq * udq && iMuq == &udq * &uq && iMuq == udq * &uq && iMuq == &udq * uq && iDi == &udq / &udq && iDi == udq / &udq && iDi == &udq / udq && iDuq == &udq / &uq && iDuq == udq / &uq && iDuq == &udq / uq && iMp == &udq * &p && iMp == udq * &p && iMp == &udq * p && iMv == &udq * &v && iMv == udq * &v && iMv == &udq * v && iMt == &udq * &t && iMt == udq * &t && iMt == &udq * t && tMi == &t * &udq && tMi == t * &udq && tMi == &t * udq && uqMi == &uq * &udq && uqMi == uq * &udq && uqMi == &uq * udq && uqDi == &uq / &udq && uqDi == uq / &udq && uqDi == &uq / udq) } ); nalgebra-0.33.0/tests/geometry/isometry.rs000064400000000000000000000224540072674642500167370ustar 00000000000000#![cfg(feature = "proptest-support")] #![allow(non_snake_case)] use na::{Isometry3, Point3, Vector3}; use crate::proptest::*; use proptest::{prop_assert, prop_assert_eq, proptest}; proptest!( #[test] fn append_rotation_wrt_point_to_id(r in unit_quaternion(), p in point3()) { let mut iso = Isometry3::identity(); iso.append_rotation_wrt_point_mut(&r, &p); prop_assert_eq!(iso, Isometry3::rotation_wrt_point(r, p)) } #[test] fn rotation_wrt_point_invariance(r in unit_quaternion(), p in point3()) { let iso = Isometry3::rotation_wrt_point(r, p); prop_assert!(relative_eq!(iso * p, p, epsilon = 1.0e-7)) } #[test] fn look_at_rh_3(eye in point3(), target in point3(), up in vector3()) { let viewmatrix = Isometry3::look_at_rh(&eye, &target, &up); let origin = Point3::origin(); prop_assert!(relative_eq!(viewmatrix * eye, origin, epsilon = 1.0e-7) && relative_eq!( (viewmatrix * (target - eye)).normalize(), -Vector3::z(), epsilon = 1.0e-7 )) } #[test] fn observer_frame_3(eye in point3(), target in point3(), up in vector3()) { let observer = Isometry3::face_towards(&eye, &target, &up); let origin = Point3::origin(); prop_assert!(relative_eq!(observer * origin, eye, epsilon = 1.0e-7) && relative_eq!( observer * Vector3::z(), (target - eye).normalize(), epsilon = 1.0e-7 )) } #[test] fn inverse_is_identity(i in isometry3(), p in point3(), v in vector3()) { let ii = i.inverse(); prop_assert!(relative_eq!(i * ii, Isometry3::identity(), epsilon = 1.0e-7) && relative_eq!(ii * i, Isometry3::identity(), epsilon = 1.0e-7) && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7)) } #[test] fn inverse_is_parts_inversion(t in translation3(), r in unit_quaternion()) { let i = t * r; prop_assert!(i.inverse() == r.inverse() * t.inverse()) } #[test] fn multiply_equals_alga_transform(i in isometry3(), v in vector3(), p in point3()) { prop_assert!(i * v == i.transform_vector(&v) && i * p == i.transform_point(&p) && relative_eq!( i.inverse() * v, i.inverse_transform_vector(&v), epsilon = 1.0e-7 ) && relative_eq!( i.inverse() * p, i.inverse_transform_point(&p), epsilon = 1.0e-7 )) } #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn composition2( i in isometry2(), uc in unit_complex(), r in rotation2(), t in translation2(), v in vector2(), p in point2() ) { // (rotation × translation) * point = rotation × (translation * point) prop_assert!(relative_eq!((uc * t) * v, uc * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((r * t) * v, r * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((uc * t) * p, uc * (t * p), epsilon = 1.0e-7)); prop_assert!(relative_eq!((r * t) * p, r * (t * p), epsilon = 1.0e-7)); // (translation × rotation) * point = translation × (rotation * point) prop_assert_eq!((t * uc) * v, uc * v); prop_assert_eq!((t * r) * v, r * v); prop_assert_eq!((t * uc) * p, t * (uc * p)); prop_assert_eq!((t * r) * p, t * (r * p)); // (rotation × isometry) * point = rotation × (isometry * point) prop_assert!(relative_eq!((uc * i) * v, uc * (i * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uc * i) * p, uc * (i * p), epsilon = 1.0e-7)); // (isometry × rotation) * point = isometry × (rotation * point) prop_assert!(relative_eq!((i * uc) * v, i * (uc * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * uc) * p, i * (uc * p), epsilon = 1.0e-7)); // (translation × isometry) * point = translation × (isometry * point) prop_assert!(relative_eq!((t * i) * v, (i * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * i) * p, t * (i * p), epsilon = 1.0e-7)); // (isometry × translation) * point = isometry × (translation * point) prop_assert!(relative_eq!((i * t) * v, i * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * t) * p, i * (t * p), epsilon = 1.0e-7)); } #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn composition3( i in isometry3(), uq in unit_quaternion(), r in rotation3(), t in translation3(), v in vector3(), p in point3() ) { // (rotation × translation) * point = rotation × (translation * point) prop_assert!(relative_eq!((uq * t) * v, uq * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((r * t) * v, r * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * t) * p, uq * (t * p), epsilon = 1.0e-7)); prop_assert!(relative_eq!((r * t) * p, r * (t * p), epsilon = 1.0e-7)); // (translation × rotation) * point = translation × (rotation * point) prop_assert_eq!((t * uq) * v, uq * v); prop_assert_eq!((t * r) * v, r * v); prop_assert_eq!((t * uq) * p, t * (uq * p)); prop_assert_eq!((t * r) * p, t * (r * p)); // (rotation × isometry) * point = rotation × (isometry * point) prop_assert!(relative_eq!((uq * i) * v, uq * (i * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * i) * p, uq * (i * p), epsilon = 1.0e-7)); // (isometry × rotation) * point = isometry × (rotation * point) prop_assert!(relative_eq!((i * uq) * v, i * (uq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * uq) * p, i * (uq * p), epsilon = 1.0e-7)); // (translation × isometry) * point = translation × (isometry * point) prop_assert!(relative_eq!((t * i) * v, (i * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * i) * p, t * (i * p), epsilon = 1.0e-7)); // (isometry × translation) * point = isometry × (translation * point) prop_assert!(relative_eq!((i * t) * v, i * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * t) * p, i * (t * p), epsilon = 1.0e-7)); } #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn all_op_exist( i in isometry3(), uq in unit_quaternion(), t in translation3(), v in vector3(), p in point3(), r in rotation3() ) { let iMi = i * i; let iMuq = i * uq; let iDi = i / i; let iDuq = i / uq; let iMp = i * p; let iMv = i * v; let iMt = i * t; let tMi = t * i; let tMr = t * r; let tMuq = t * uq; let uqMi = uq * i; let uqDi = uq / i; let rMt = r * t; let uqMt = uq * t; let mut iMt1 = i; let mut iMt2 = i; let mut iMi1 = i; let mut iMi2 = i; let mut iMuq1 = i; let mut iMuq2 = i; let mut iDi1 = i; let mut iDi2 = i; let mut iDuq1 = i; let mut iDuq2 = i; iMt1 *= t; iMt2 *= &t; iMi1 *= i; iMi2 *= &i; iMuq1 *= uq; iMuq2 *= &uq; iDi1 /= i; iDi2 /= &i; iDuq1 /= uq; iDuq2 /= &uq; prop_assert!(iMt == iMt1 && iMt == iMt2 && iMi == iMi1 && iMi == iMi2 && iMuq == iMuq1 && iMuq == iMuq2 && iDi == iDi1 && iDi == iDi2 && iDuq == iDuq1 && iDuq == iDuq2 && iMi == &i * &i && iMi == i * &i && iMi == &i * i && iMuq == &i * &uq && iMuq == i * &uq && iMuq == &i * uq && iDi == &i / &i && iDi == i / &i && iDi == &i / i && iDuq == &i / &uq && iDuq == i / &uq && iDuq == &i / uq && iMp == &i * &p && iMp == i * &p && iMp == &i * p && iMv == &i * &v && iMv == i * &v && iMv == &i * v && iMt == &i * &t && iMt == i * &t && iMt == &i * t && tMi == &t * &i && tMi == t * &i && tMi == &t * i && tMr == &t * &r && tMr == t * &r && tMr == &t * r && tMuq == &t * &uq && tMuq == t * &uq && tMuq == &t * uq && uqMi == &uq * &i && uqMi == uq * &i && uqMi == &uq * i && uqDi == &uq / &i && uqDi == uq / &i && uqDi == &uq / i && rMt == &r * &t && rMt == r * &t && rMt == &r * t && uqMt == &uq * &t && uqMt == uq * &t && uqMt == &uq * t) } ); nalgebra-0.33.0/tests/geometry/mod.rs000064400000000000000000000002060072674642500156320ustar 00000000000000mod dual_quaternion; mod isometry; mod point; mod projection; mod quaternion; mod rotation; mod similarity; mod unit_complex; nalgebra-0.33.0/tests/geometry/point.rs000064400000000000000000000045420072674642500162130ustar 00000000000000use na::{Point3, Vector3, Vector4}; use num::Zero; #[test] fn point_clone() { let p = Point3::new(1.0, 2.0, 3.0); let p2 = p.clone(); assert_eq!(p, p2); } #[test] fn point_ops() { let a = Point3::new(1.0, 2.0, 3.0); let b = Point3::new(1.0, 2.0, 3.0); let c = Vector3::new(1.0, 2.0, 3.0); assert_eq!(a - b, Vector3::zero()); assert_eq!(&a - &b, Vector3::zero()); assert_eq!(a - &b, Vector3::zero()); assert_eq!(&a - b, Vector3::zero()); assert_eq!(b - c, Point3::origin()); assert_eq!(&b - &c, Point3::origin()); assert_eq!(b - &c, Point3::origin()); assert_eq!(&b - c, Point3::origin()); assert_eq!(b + c, 2.0 * a); assert_eq!(&b + &c, 2.0 * a); assert_eq!(b + &c, 2.0 * a); assert_eq!(&b + c, 2.0 * a); let mut a1 = a; let mut a2 = a; let mut a3 = a; let mut a4 = a; a1 -= c; a2 -= &c; a3 += c; a4 += &c; assert_eq!(a1, Point3::origin()); assert_eq!(a2, Point3::origin()); assert_eq!(a3, 2.0 * a); assert_eq!(a4, 2.0 * a); } #[test] fn point_coordinates() { let mut pt = Point3::origin(); assert_eq!(pt.x, 0); assert_eq!(pt.y, 0); assert_eq!(pt.z, 0); pt.x = 1; pt.y = 2; pt.z = 3; assert_eq!(pt.x, 1); assert_eq!(pt.y, 2); assert_eq!(pt.z, 3); } #[test] fn point_scale() { let pt = Point3::new(1, 2, 3); let expected = Point3::new(10, 20, 30); assert_eq!(pt * 10, expected); assert_eq!(&pt * 10, expected); assert_eq!(10 * pt, expected); assert_eq!(10 * &pt, expected); } #[test] fn point_vector_sum() { let pt = Point3::new(1, 2, 3); let vec = Vector3::new(10, 20, 30); let expected = Point3::new(11, 22, 33); assert_eq!(&pt + &vec, expected); assert_eq!(pt + &vec, expected); assert_eq!(&pt + vec, expected); assert_eq!(pt + vec, expected); } #[test] fn to_homogeneous() { let a = Point3::new(1.0, 2.0, 3.0); let expected = Vector4::new(1.0, 2.0, 3.0, 1.0); 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}"); } nalgebra-0.33.0/tests/geometry/projection.rs000064400000000000000000000036060072674642500172360ustar 00000000000000use na::{Orthographic3, Perspective3, Point3}; #[test] fn perspective_inverse() { let proj = Perspective3::new(800.0 / 600.0, 3.14 / 2.0, 1.0, 1000.0); let inv = proj.inverse(); let id = inv * proj.into_inner(); assert!(id.is_identity(1.0e-7)); } #[test] fn orthographic_inverse() { let proj = Orthographic3::new(1.0, 2.0, -3.0, -2.5, 10.0, 900.0); let inv = proj.inverse(); let id = inv * proj.into_inner(); assert!(id.is_identity(1.0e-7)); } #[test] fn perspective_matrix_point_transformation() { // https://github.com/dimforge/nalgebra/issues/640 let proj = Perspective3::new(4.0 / 3.0, 90.0, 0.1, 100.0); let perspective_inv = proj.as_matrix().try_inverse().unwrap(); let some_point = Point3::new(1.0, 2.0, 0.0); assert_eq!( perspective_inv.transform_point(&some_point), Point3::from_homogeneous(perspective_inv * some_point.coords.push(1.0)).unwrap() ); } #[cfg(feature = "proptest-support")] mod proptest_tests { use na::{Orthographic3, Perspective3}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn perspective_project_unproject(pt in point3()) { let proj = Perspective3::new(800.0 / 600.0, 3.14 / 2.0, 1.0, 1000.0); let projected = proj.project_point(&pt); let unprojected = proj.unproject_point(&projected); prop_assert!(relative_eq!(pt, unprojected, epsilon = 1.0e-7)) } #[test] fn orthographic_project_unproject(pt in point3()) { let proj = Orthographic3::new(1.0, 2.0, -3.0, -2.5, 10.0, 900.0); let projected = proj.project_point(&pt); let unprojected = proj.unproject_point(&projected); prop_assert!(relative_eq!(pt, unprojected, epsilon = 1.0e-7)) } } } nalgebra-0.33.0/tests/geometry/quaternion.rs000064400000000000000000000175070072674642500172540ustar 00000000000000#![cfg(feature = "proptest-support")] #![allow(non_snake_case)] use na::{Unit, UnitQuaternion}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest!( /* * * Euler angles. * */ #[test] fn from_euler_angles(r in PROPTEST_F64, p in PROPTEST_F64, y in PROPTEST_F64) { let roll = UnitQuaternion::from_euler_angles(r, 0.0, 0.0); let pitch = UnitQuaternion::from_euler_angles(0.0, p, 0.0); let yaw = UnitQuaternion::from_euler_angles(0.0, 0.0, y); let rpy = UnitQuaternion::from_euler_angles(r, p, y); let rroll = roll.to_rotation_matrix(); let rpitch = pitch.to_rotation_matrix(); let ryaw = yaw.to_rotation_matrix(); prop_assert!(relative_eq!(rroll[(0, 0)], 1.0, epsilon = 1.0e-7)); // rotation wrt. x axis. prop_assert!(relative_eq!(rpitch[(1, 1)], 1.0, epsilon = 1.0e-7)); // rotation wrt. y axis. prop_assert!(relative_eq!(ryaw[(2, 2)], 1.0, epsilon = 1.0e-7)); // rotation wrt. z axis. prop_assert!(relative_eq!(yaw * pitch * roll, rpy, epsilon = 1.0e-7)); } #[test] fn euler_angles(r in PROPTEST_F64, p in PROPTEST_F64, y in PROPTEST_F64) { let rpy = UnitQuaternion::from_euler_angles(r, p, y); let (roll, pitch, yaw) = rpy.euler_angles(); prop_assert!(relative_eq!( UnitQuaternion::from_euler_angles(roll, pitch, yaw), rpy, epsilon = 1.0e-7 )) } /* * * From/to rotation matrix. * */ #[test] fn unit_quaternion_rotation_conversion(q in unit_quaternion()) { let r = q.to_rotation_matrix(); let qq = UnitQuaternion::from_rotation_matrix(&r); let rr = qq.to_rotation_matrix(); prop_assert!(relative_eq!(q, qq, epsilon = 1.0e-7) && relative_eq!(r, rr, epsilon = 1.0e-7)) } /* * * Point/Vector transformation. * */ #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn unit_quaternion_transformation( q in unit_quaternion(), v in vector3(), p in point3() ) { let r = q.to_rotation_matrix(); let rv = r * v; let rp = r * p; prop_assert!(relative_eq!(q * v, rv, epsilon = 1.0e-7) && relative_eq!(q * &v, rv, epsilon = 1.0e-7) && relative_eq!(&q * v, rv, epsilon = 1.0e-7) && relative_eq!(&q * &v, rv, epsilon = 1.0e-7) && relative_eq!(q * p, rp, epsilon = 1.0e-7) && relative_eq!(q * &p, rp, epsilon = 1.0e-7) && relative_eq!(&q * p, rp, epsilon = 1.0e-7) && relative_eq!(&q * &p, rp, epsilon = 1.0e-7)) } /* * * Inversion. * */ #[test] fn unit_quaternion_inv(q in unit_quaternion()) { let iq = q.inverse(); prop_assert!(relative_eq!(&iq * &q, UnitQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(iq * &q, UnitQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(&iq * q, UnitQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(iq * q, UnitQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(&q * &iq, UnitQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(q * &iq, UnitQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(&q * iq, UnitQuaternion::identity(), epsilon = 1.0e-7) && relative_eq!(q * iq, UnitQuaternion::identity(), epsilon = 1.0e-7)) } /* * * Quaterion * Vector == Rotation * Vector * */ #[test] fn unit_quaternion_mul_vector(q in unit_quaternion(), v in vector3(), p in point3()) { let r = q.to_rotation_matrix(); prop_assert!(relative_eq!(q * v, r * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(q * p, r * p, epsilon = 1.0e-7)); // Equivalence q = -q prop_assert!(relative_eq!(UnitQuaternion::new_unchecked(-q.into_inner()) * v, r * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(UnitQuaternion::new_unchecked(-q.into_inner()) * p, r * p, epsilon = 1.0e-7)); } /* * * Unit quaternion double-covering. * */ #[test] fn unit_quaternion_double_covering(q in unit_quaternion()) { let mq = UnitQuaternion::new_unchecked(-q.into_inner()); prop_assert!(mq == q && mq.angle() == q.angle() && mq.axis() == q.axis()) } // Test that all operators (incl. all combinations of references) work. // See the top comment on `geometry/quaternion_ops.rs` for details on which operations are // supported. #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn all_op_exist( q in quaternion(), uq in unit_quaternion(), v in vector3(), p in point3(), r in rotation3(), s in PROPTEST_F64 ) { let uv = Unit::new_normalize(v); let qpq = q + q; let qmq = q - q; let qMq = q * q; let mq = -q; let qMs = q * s; let qDs = q / s; let sMq = s * q; let uqMuq = uq * uq; let uqMr = uq * r; let rMuq = r * uq; let uqDuq = uq / uq; let uqDr = uq / r; let rDuq = r / uq; let uqMp = uq * p; let uqMv = uq * v; let uqMuv = uq * uv; let mut qMs1 = q; let mut qMq1 = q; let mut qMq2 = q; let mut qpq1 = q; let mut qpq2 = q; let mut qmq1 = q; let mut qmq2 = q; let mut uqMuq1 = uq; let mut uqMuq2 = uq; let mut uqMr1 = uq; let mut uqMr2 = uq; let mut uqDuq1 = uq; let mut uqDuq2 = uq; let mut uqDr1 = uq; let mut uqDr2 = uq; qMs1 *= s; qMq1 *= q; qMq2 *= &q; qpq1 += q; qpq2 += &q; qmq1 -= q; qmq2 -= &q; uqMuq1 *= uq; uqMuq2 *= &uq; uqMr1 *= r; uqMr2 *= &r; uqDuq1 /= uq; uqDuq2 /= &uq; uqDr1 /= r; uqDr2 /= &r; prop_assert!(qMs1 == qMs && qMq1 == qMq && qMq1 == qMq2 && qpq1 == qpq && qpq1 == qpq2 && qmq1 == qmq && qmq1 == qmq2 && uqMuq1 == uqMuq && uqMuq1 == uqMuq2 && uqMr1 == uqMr && uqMr1 == uqMr2 && uqDuq1 == uqDuq && uqDuq1 == uqDuq2 && uqDr1 == uqDr && uqDr1 == uqDr2 && qpq == &q + &q && qpq == q + &q && qpq == &q + q && qmq == &q - &q && qmq == q - &q && qmq == &q - q && qMq == &q * &q && qMq == q * &q && qMq == &q * q && mq == -&q && qMs == &q * s && qDs == &q / s && sMq == s * &q && uqMuq == &uq * &uq && uqMuq == uq * &uq && uqMuq == &uq * uq && uqMr == &uq * &r && uqMr == uq * &r && uqMr == &uq * r && rMuq == &r * &uq && rMuq == r * &uq && rMuq == &r * uq && uqDuq == &uq / &uq && uqDuq == uq / &uq && uqDuq == &uq / uq && uqDr == &uq / &r && uqDr == uq / &r && uqDr == &uq / r && rDuq == &r / &uq && rDuq == r / &uq && rDuq == &r / uq && uqMp == &uq * &p && uqMp == uq * &p && uqMp == &uq * p && uqMv == &uq * &v && uqMv == uq * &v && uqMv == &uq * v && uqMuv == &uq * &uv && uqMuv == uq * &uv && uqMuv == &uq * uv) } ); nalgebra-0.33.0/tests/geometry/rotation.rs000064400000000000000000000322710072674642500167210ustar 00000000000000use na::{ Matrix3, Quaternion, RealField, Rotation3, UnitQuaternion, UnitVector3, Vector2, Vector3, }; use std::f64::consts::PI; #[test] fn angle_2() { let a = Vector2::new(4.0, 0.0); let b = Vector2::new(9.0, 0.0); assert_eq!(a.angle(&b), 0.0); } #[test] fn angle_3() { let a = Vector3::new(4.0, 0.0, 0.5); let b = Vector3::new(8.0, 0.0, 1.0); assert_eq!(a.angle(&b), 0.0); } #[test] fn from_rotation_matrix() { // Test degenerate case when from_matrix gets stuck in Identity rotation let identity = Rotation3::from_matrix(&Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)); assert_relative_eq!(identity, &Rotation3::identity(), epsilon = 0.001); let rotated_z = Rotation3::from_matrix(&Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0)); assert_relative_eq!( rotated_z, &Rotation3::from_axis_angle(&UnitVector3::new_unchecked(Vector3::new(1.0, 0.0, 0.0)), PI), epsilon = 0.001 ); // Test that issue 627 is fixed let m_627 = Matrix3::::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0); assert_relative_ne!(identity, Rotation3::from_matrix(&m_627), epsilon = 0.01); assert_relative_eq!( Rotation3::from_matrix_unchecked(m_627.clone()), Rotation3::from_matrix(&m_627), epsilon = 0.001 ); // Test that issue 1078 is fixed let m_1078 = Matrix3::::new(0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, 0.0); assert_relative_ne!(identity, Rotation3::from_matrix(&m_1078), epsilon = 0.01); assert_relative_eq!( Rotation3::from_matrix_unchecked(m_1078.clone()), Rotation3::from_matrix(&m_1078), epsilon = 0.001 ); // Additional test cases for eps >= 1.0 assert_relative_ne!( identity, Rotation3::from_matrix_eps(&m_627, 1.2, 0, Rotation3::identity()), epsilon = 0.6 ); assert_relative_eq!( Rotation3::from_matrix_unchecked(m_627.clone()), Rotation3::from_matrix_eps(&m_627, 1.2, 0, Rotation3::identity()), epsilon = 0.6 ); assert_relative_ne!( identity, Rotation3::from_matrix_eps(&m_1078, 1.0, 0, Rotation3::identity()), epsilon = 0.1 ); assert_relative_eq!( Rotation3::from_matrix_unchecked(m_1078.clone()), Rotation3::from_matrix_eps(&m_1078, 1.0, 0, Rotation3::identity()), epsilon = 0.1 ); } #[test] fn quaternion_euler_angles_issue_494() { let quat = UnitQuaternion::from_quaternion(Quaternion::new( -0.10405792, -0.6993922f32, -0.10406871, 0.69942284, )); let angs = quat.euler_angles(); assert_eq!(angs.0, 2.8461843); assert_eq!(angs.1, f32::frac_pi_2()); assert_eq!(angs.2, 0.0); } #[cfg(feature = "proptest-support")] mod proptest_tests { use approx::AbsDiffEq; use na::{self, Rotation2, Rotation3, Unit}; use na::{UnitComplex, UnitQuaternion}; use simba::scalar::RealField; use std::f64; use crate::proptest::*; use proptest::{prop_assert, prop_assert_eq, proptest}; proptest! { /* * * Euler angles. * */ #[test] fn from_euler_angles(r in PROPTEST_F64, p in PROPTEST_F64, y in PROPTEST_F64) { let roll = Rotation3::from_euler_angles(r, 0.0, 0.0); let pitch = Rotation3::from_euler_angles(0.0, p, 0.0); let yaw = Rotation3::from_euler_angles(0.0, 0.0, y); let rpy = Rotation3::from_euler_angles(r, p, y); prop_assert_eq!(roll[(0, 0)], 1.0); // rotation wrt. x axis. prop_assert_eq!(pitch[(1, 1)], 1.0); // rotation wrt. y axis. prop_assert_eq!(yaw[(2, 2)], 1.0); // rotation wrt. z axis. prop_assert_eq!(yaw * pitch * roll, rpy); } #[test] fn euler_angles(r in PROPTEST_F64, p in PROPTEST_F64, y in PROPTEST_F64) { let rpy = Rotation3::from_euler_angles(r, p, y); let (roll, pitch, yaw) = rpy.euler_angles(); prop_assert!(relative_eq!(Rotation3::from_euler_angles(roll, pitch, yaw), rpy, epsilon = 1.0e-7)); } #[test] fn euler_angles_gimble_lock(r in PROPTEST_F64, y in PROPTEST_F64) { let pos = Rotation3::from_euler_angles(r, f64::frac_pi_2(), y); let neg = Rotation3::from_euler_angles(r, -f64::frac_pi_2(), y); let (pos_r, pos_p, pos_y) = pos.euler_angles(); let (neg_r, neg_p, neg_y) = neg.euler_angles(); prop_assert!(relative_eq!(Rotation3::from_euler_angles(pos_r, pos_p, pos_y), pos, epsilon = 1.0e-7)); prop_assert!(relative_eq!(Rotation3::from_euler_angles(neg_r, neg_p, neg_y), neg, epsilon = 1.0e-7)); } /* * * Inversion is transposition. * */ #[test] fn rotation_inv_3(a in rotation3()) { let ta = a.transpose(); let ia = a.inverse(); prop_assert_eq!(ta, ia); prop_assert!(relative_eq!(&ta * &a, Rotation3::identity(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(&ia * a, Rotation3::identity(), epsilon = 1.0e-7)); prop_assert!(relative_eq!( a * &ta, Rotation3::identity(), epsilon = 1.0e-7)); prop_assert!(relative_eq!( a * ia, Rotation3::identity(), epsilon = 1.0e-7)); } #[test] fn rotation_inv_2(a in rotation2()) { let ta = a.transpose(); let ia = a.inverse(); prop_assert_eq!(ta, ia); prop_assert!(relative_eq!(&ta * &a, Rotation2::identity(), epsilon = 1.0e-7)); prop_assert!(relative_eq!(&ia * a, Rotation2::identity(), epsilon = 1.0e-7)); prop_assert!(relative_eq!( a * &ta, Rotation2::identity(), epsilon = 1.0e-7)); prop_assert!(relative_eq!( a * ia, Rotation2::identity(), epsilon = 1.0e-7)); } /* * * Angle between vectors. * */ #[test] fn angle_is_commutative_2(a in vector2(), b in vector2()) { prop_assert_eq!(a.angle(&b), b.angle(&a)) } #[test] fn angle_is_commutative_3(a in vector3(), b in vector3()) { prop_assert_eq!(a.angle(&b), b.angle(&a)) } /* * * Rotation matrix between vectors. * */ #[test] fn rotation_between_is_anticommutative_2(a in vector2(), b in vector2()) { let rab = Rotation2::rotation_between(&a, &b); let rba = Rotation2::rotation_between(&b, &a); prop_assert!(relative_eq!(rab * rba, Rotation2::identity())); } #[test] fn rotation_between_is_anticommutative_3(a in vector3(), b in vector3()) { let rots = (Rotation3::rotation_between(&a, &b), Rotation3::rotation_between(&b, &a)); if let (Some(rab), Some(rba)) = rots { prop_assert!(relative_eq!(rab * rba, Rotation3::identity(), epsilon = 1.0e-7)); } } #[test] fn rotation_between_is_identity(v2 in vector2(), v3 in vector3()) { let vv2 = 3.42 * v2; let vv3 = 4.23 * v3; prop_assert!(relative_eq!(v2.angle(&vv2), 0.0, epsilon = 1.0e-7)); prop_assert!(relative_eq!(v3.angle(&vv3), 0.0, epsilon = 1.0e-7)); prop_assert!(relative_eq!(Rotation2::rotation_between(&v2, &vv2), Rotation2::identity())); prop_assert_eq!(Rotation3::rotation_between(&v3, &vv3).unwrap(), Rotation3::identity()); } #[test] fn rotation_between_2(a in vector2(), b in vector2()) { if !relative_eq!(a.angle(&b), 0.0, epsilon = 1.0e-7) { let r = Rotation2::rotation_between(&a, &b); prop_assert!(relative_eq!((r * a).angle(&b), 0.0, epsilon = 1.0e-7)) } } #[test] fn rotation_between_3(a in vector3(), b in vector3()) { if !relative_eq!(a.angle(&b), 0.0, epsilon = 1.0e-7) { let r = Rotation3::rotation_between(&a, &b).unwrap(); prop_assert!(relative_eq!((r * a).angle(&b), 0.0, epsilon = 1.0e-7)) } } /* * * Rotation construction. * */ #[test] fn new_rotation_2(angle in PROPTEST_F64) { let r = Rotation2::new(angle); let angle = na::wrap(angle, -f64::pi(), f64::pi()); prop_assert!(relative_eq!(r.angle(), angle, epsilon = 1.0e-7)) } #[test] fn new_rotation_3(axisangle in vector3()) { let r = Rotation3::new(axisangle); if let Some((axis, angle)) = Unit::try_new_and_get(axisangle, 0.0) { let angle = na::wrap(angle, -f64::pi(), f64::pi()); prop_assert!((relative_eq!(r.angle(), angle, epsilon = 1.0e-7) && relative_eq!(r.axis().unwrap(), axis, epsilon = 1.0e-7)) || (relative_eq!(r.angle(), -angle, epsilon = 1.0e-7) && relative_eq!(r.axis().unwrap(), -axis, epsilon = 1.0e-7))) } else { prop_assert_eq!(r, Rotation3::identity()) } } /* * * Rotation pow. * */ #[test] fn powf_rotation_2(angle in PROPTEST_F64, pow in PROPTEST_F64) { let r = Rotation2::new(angle).powf(pow); let angle = na::wrap(angle, -f64::pi(), f64::pi()); let pangle = na::wrap(angle * pow, -f64::pi(), f64::pi()); prop_assert!(relative_eq!(r.angle(), pangle, epsilon = 1.0e-7)); } #[test] fn powf_rotation_3(axisangle in vector3(), pow in PROPTEST_F64) { let r = Rotation3::new(axisangle).powf(pow); if let Some((axis, angle)) = Unit::try_new_and_get(axisangle, 0.0) { let angle = na::wrap(angle, -f64::pi(), f64::pi()); let pangle = na::wrap(angle * pow, -f64::pi(), f64::pi()); prop_assert!((relative_eq!(r.angle(), pangle, epsilon = 1.0e-7) && relative_eq!(r.axis().unwrap(), axis, epsilon = 1.0e-7)) || (relative_eq!(r.angle(), -pangle, epsilon = 1.0e-7) && relative_eq!(r.axis().unwrap(), -axis, epsilon = 1.0e-7))); } else { prop_assert_eq!(r, Rotation3::identity()) } } // //In general, `slerp(a,b,t)` should equal `(b/a)^t * a` even though in practice, //we may not use that formula directly for complex numbers or quaternions // #[test] fn slerp_powf_agree_2(a in unit_complex(), b in unit_complex(), t in PROPTEST_F64) { let z1 = a.slerp(&b, t); let z2 = (b/a).powf(t) * a; prop_assert!(relative_eq!(z1,z2,epsilon=1e-10)); } #[test] fn slerp_powf_agree_3(a in unit_quaternion(), b in unit_quaternion(), t in PROPTEST_F64) { if let Some(z1) = a.try_slerp(&b, t, f64::default_epsilon()) { let z2 = (b/a).powf(t) * a; prop_assert!(relative_eq!(z1,z2,epsilon=1e-10)); } } // //when not antipodal, slerp should always take the shortest path between two orientations // #[test] fn slerp_takes_shortest_path_2( z in unit_complex(), dtheta in -f64::pi()..f64::pi(), t in 0.0..1.0f64 ) { //ambiguous when at ends of angle range, so we don't really care here if dtheta.abs() != f64::pi() { //make two complex numbers separated by an angle between -pi and pi let (z1, z2) = (z, z * UnitComplex::new(dtheta)); let z3 = z1.slerp(&z2, t); //since the angle is no larger than a half-turn, and t is between 0 and 1, //the shortest path just corresponds to adding the scaled angle let a1 = z3.angle(); let a2 = na::wrap(z1.angle() + dtheta*t, -f64::pi(), f64::pi()); prop_assert!(relative_eq!(a1, a2, epsilon=1e-10)); } } #[test] fn slerp_takes_shortest_path_3( q in unit_quaternion(), dtheta in -f64::pi()..f64::pi(), t in 0.0..1.0f64 ) { //ambiguous when at ends of angle range, so we don't really care here if let Some(axis) = q.axis() { //make two quaternions separated by an angle between -pi and pi let (q1, q2) = (q, q * UnitQuaternion::from_axis_angle(&axis, dtheta)); let q3 = q1.slerp(&q2, t); //since the angle is no larger than a half-turn, and t is between 0 and 1, //the shortest path just corresponds to adding the scaled angle let q4 = q1 * UnitQuaternion::from_axis_angle(&axis, dtheta*t); prop_assert!(relative_eq!(q3, q4, epsilon=1e-10)); } } } } nalgebra-0.33.0/tests/geometry/similarity.rs000064400000000000000000000255120072674642500172500ustar 00000000000000#![cfg(feature = "proptest-support")] #![allow(non_snake_case)] use na::Similarity3; use crate::proptest::*; use proptest::{prop_assert, prop_assert_eq, proptest}; proptest!( #[test] fn inverse_is_identity(i in similarity3(), p in point3(), v in vector3()) { let ii = i.inverse(); prop_assert!(relative_eq!(i * ii, Similarity3::identity(), epsilon = 1.0e-7) && relative_eq!(ii * i, Similarity3::identity(), epsilon = 1.0e-7) && relative_eq!((i * ii) * p, p, epsilon = 1.0e-7) && relative_eq!((ii * i) * p, p, epsilon = 1.0e-7) && relative_eq!((i * ii) * v, v, epsilon = 1.0e-7) && relative_eq!((ii * i) * v, v, epsilon = 1.0e-7)) } #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn inverse_is_parts_inversion( t in translation3(), r in unit_quaternion(), scaling in PROPTEST_F64 ) { if !relative_eq!(scaling, 0.0) { let s = Similarity3::from_isometry(t * r, scaling); prop_assert_eq!(s.inverse(), Similarity3::from_scaling(1.0 / scaling) * r.inverse() * t.inverse()) } } #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn multiply_equals_alga_transform( s in similarity3(), v in vector3(), p in point3() ) { prop_assert!(s * v == s.transform_vector(&v) && s * p == s.transform_point(&p) && relative_eq!( s.inverse() * v, s.inverse_transform_vector(&v), epsilon = 1.0e-7 ) && relative_eq!( s.inverse() * p, s.inverse_transform_point(&p), epsilon = 1.0e-7 )) } #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn composition( i in isometry3(), uq in unit_quaternion(), t in translation3(), v in vector3(), p in point3(), scaling in PROPTEST_F64 ) { if !relative_eq!(scaling, 0.0) { let s = Similarity3::from_scaling(scaling); // (rotation × translation × scaling) × point = rotation × (translation × (scaling × point)) prop_assert!(relative_eq!((uq * t * s) * v, uq * (scaling * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * t * s) * p, uq * (t * (scaling * p)), epsilon = 1.0e-7)); // (translation × rotation × scaling) × point = translation × (rotation × (scaling × point)) prop_assert!(relative_eq!((t * uq * s) * v, uq * (scaling * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * uq * s) * p, t * (uq * (scaling * p)), epsilon = 1.0e-7)); // (rotation × isometry × scaling) × point = rotation × (isometry × (scaling × point)) prop_assert!(relative_eq!((uq * i * s) * v, uq * (i * (scaling * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * i * s) * p, uq * (i * (scaling * p)), epsilon = 1.0e-7)); // (isometry × rotation × scaling) × point = isometry × (rotation × (scaling × point)) prop_assert!(relative_eq!((i * uq * s) * v, i * (uq * (scaling * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * uq * s) * p, i * (uq * (scaling * p)), epsilon = 1.0e-7)); // (translation × isometry × scaling) × point = translation × (isometry × (scaling × point)) prop_assert!(relative_eq!((t * i * s) * v, (i * (scaling * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * i * s) * p, t * (i * (scaling * p)), epsilon = 1.0e-7)); // (isometry × translation × scaling) × point = isometry × (translation × (scaling × point)) prop_assert!(relative_eq!((i * t * s) * v, i * (scaling * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * t * s) * p, i * (t * (scaling * p)), epsilon = 1.0e-7)); /* * Same as before but with scaling on the middle. */ // (rotation × scaling × translation) × point = rotation × (scaling × (translation × point)) prop_assert!(relative_eq!((uq * s * t) * v, uq * (scaling * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * s * t) * p, uq * (scaling * (t * p)), epsilon = 1.0e-7)); // (translation × scaling × rotation) × point = translation × (scaling × (rotation × point)) prop_assert!(relative_eq!((t * s * uq) * v, scaling * (uq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * s * uq) * p, t * (scaling * (uq * p)), epsilon = 1.0e-7)); // (rotation × scaling × isometry) × point = rotation × (scaling × (isometry × point)) prop_assert!(relative_eq!((uq * s * i) * v, uq * (scaling * (i * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((uq * s * i) * p, uq * (scaling * (i * p)), epsilon = 1.0e-7)); // (isometry × scaling × rotation) × point = isometry × (scaling × (rotation × point)) prop_assert!(relative_eq!((i * s * uq) * v, i * (scaling * (uq * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * s * uq) * p, i * (scaling * (uq * p)), epsilon = 1.0e-7)); // (translation × scaling × isometry) × point = translation × (scaling × (isometry × point)) prop_assert!(relative_eq!((t * s * i) * v, (scaling * (i * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((t * s * i) * p, t * (scaling * (i * p)), epsilon = 1.0e-7)); // (isometry × scaling × translation) × point = isometry × (scaling × (translation × point)) prop_assert!(relative_eq!((i * s * t) * v, i * (scaling * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((i * s * t) * p, i * (scaling * (t * p)), epsilon = 1.0e-7)); /* * Same as before but with scaling on the left. */ // (scaling × rotation × translation) × point = scaling × (rotation × (translation × point)) prop_assert!(relative_eq!((s * uq * t) * v, scaling * (uq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((s * uq * t) * p, scaling * (uq * (t * p)), epsilon = 1.0e-7)); // (scaling × translation × rotation) × point = scaling × (translation × (rotation × point)) prop_assert!(relative_eq!((s * t * uq) * v, scaling * (uq * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((s * t * uq) * p, scaling * (t * (uq * p)), epsilon = 1.0e-7)); // (scaling × rotation × isometry) × point = scaling × (rotation × (isometry × point)) prop_assert!(relative_eq!((s * uq * i) * v, scaling * (uq * (i * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((s * uq * i) * p, scaling * (uq * (i * p)), epsilon = 1.0e-7)); // (scaling × isometry × rotation) × point = scaling × (isometry × (rotation × point)) prop_assert!(relative_eq!((s * i * uq) * v, scaling * (i * (uq * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((s * i * uq) * p, scaling * (i * (uq * p)), epsilon = 1.0e-7)); // (scaling × translation × isometry) × point = scaling × (translation × (isometry × point)) prop_assert!(relative_eq!((s * t * i) * v, (scaling * (i * v)), epsilon = 1.0e-7)); prop_assert!(relative_eq!((s * t * i) * p, scaling * (t * (i * p)), epsilon = 1.0e-7)); // (scaling × isometry × translation) × point = scaling × (isometry × (translation × point)) prop_assert!(relative_eq!((s * i * t) * v, scaling * (i * v), epsilon = 1.0e-7)); prop_assert!(relative_eq!((s * i * t) * p, scaling * (i * (t * p)), epsilon = 1.0e-7)); } } #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn all_op_exist( s in similarity3(), i in isometry3(), uq in unit_quaternion(), t in translation3(), v in vector3(), p in point3() ) { let sMs = s * s; let sMuq = s * uq; let sDs = s / s; let sDuq = s / uq; let sMp = s * p; let sMv = s * v; let sMt = s * t; let tMs = t * s; let uqMs = uq * s; let uqDs = uq / s; let sMi = s * i; let sDi = s / i; let iMs = i * s; let iDs = i / s; let mut sMt1 = s; let mut sMt2 = s; let mut sMs1 = s; let mut sMs2 = s; let mut sMuq1 = s; let mut sMuq2 = s; let mut sMi1 = s; let mut sMi2 = s; let mut sDs1 = s; let mut sDs2 = s; let mut sDuq1 = s; let mut sDuq2 = s; let mut sDi1 = s; let mut sDi2 = s; sMt1 *= t; sMt2 *= &t; sMs1 *= s; sMs2 *= &s; sMuq1 *= uq; sMuq2 *= &uq; sMi1 *= i; sMi2 *= &i; sDs1 /= s; sDs2 /= &s; sDuq1 /= uq; sDuq2 /= &uq; sDi1 /= i; sDi2 /= &i; prop_assert!(sMt == sMt1 && sMt == sMt2 && sMs == sMs1 && sMs == sMs2 && sMuq == sMuq1 && sMuq == sMuq2 && sMi == sMi1 && sMi == sMi2 && sDs == sDs1 && sDs == sDs2 && sDuq == sDuq1 && sDuq == sDuq2 && sDi == sDi1 && sDi == sDi2 && sMs == &s * &s && sMs == s * &s && sMs == &s * s && sMuq == &s * &uq && sMuq == s * &uq && sMuq == &s * uq && sDs == &s / &s && sDs == s / &s && sDs == &s / s && sDuq == &s / &uq && sDuq == s / &uq && sDuq == &s / uq && sMp == &s * &p && sMp == s * &p && sMp == &s * p && sMv == &s * &v && sMv == s * &v && sMv == &s * v && sMt == &s * &t && sMt == s * &t && sMt == &s * t && tMs == &t * &s && tMs == t * &s && tMs == &t * s && uqMs == &uq * &s && uqMs == uq * &s && uqMs == &uq * s && uqDs == &uq / &s && uqDs == uq / &s && uqDs == &uq / s && sMi == &s * &i && sMi == s * &i && sMi == &s * i && sDi == &s / &i && sDi == s / &i && sDi == &s / i && iMs == &i * &s && iMs == i * &s && iMs == &i * s && iDs == &i / &s && iDs == i / &s && iDs == &i / s) } ); nalgebra-0.33.0/tests/geometry/unit_complex.rs000064400000000000000000000113140072674642500175630ustar 00000000000000#![cfg(feature = "proptest-support")] #![allow(non_snake_case)] use na::{Unit, UnitComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest!( /* * * From/to rotation matrix. * */ #[test] fn unit_complex_rotation_conversion(c in unit_complex()) { let r = c.to_rotation_matrix(); let cc = UnitComplex::from_rotation_matrix(&r); let rr = cc.to_rotation_matrix(); prop_assert!(relative_eq!(c, cc, epsilon = 1.0e-7)); prop_assert!(relative_eq!(r, rr, epsilon = 1.0e-7)); } /* * * Point/Vector transformation. * */ #[test] fn unit_complex_transformation(c in unit_complex(), v in vector2(), p in point2()) { let r = c.to_rotation_matrix(); let rv = r * v; let rp = r * p; prop_assert!(relative_eq!(c * v, rv, epsilon = 1.0e-7) && relative_eq!(c * &v, rv, epsilon = 1.0e-7) && relative_eq!(&c * v, rv, epsilon = 1.0e-7) && relative_eq!(&c * &v, rv, epsilon = 1.0e-7) && relative_eq!(c * p, rp, epsilon = 1.0e-7) && relative_eq!(c * &p, rp, epsilon = 1.0e-7) && relative_eq!(&c * p, rp, epsilon = 1.0e-7) && relative_eq!(&c * &p, rp, epsilon = 1.0e-7)) } /* * * Inversion. * */ #[test] fn unit_complex_inv(c in unit_complex()) { let iq = c.inverse(); prop_assert!(relative_eq!(&iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) && relative_eq!(iq * &c, UnitComplex::identity(), epsilon = 1.0e-7) && relative_eq!(&iq * c, UnitComplex::identity(), epsilon = 1.0e-7) && relative_eq!(iq * c, UnitComplex::identity(), epsilon = 1.0e-7) && relative_eq!(&c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) && relative_eq!(c * &iq, UnitComplex::identity(), epsilon = 1.0e-7) && relative_eq!(&c * iq, UnitComplex::identity(), epsilon = 1.0e-7) && relative_eq!(c * iq, UnitComplex::identity(), epsilon = 1.0e-7)) } /* * * Quaternion * Vector == Rotation * Vector * */ #[test] fn unit_complex_mul_vector(c in unit_complex(), v in vector2(), p in point2()) { let r = c.to_rotation_matrix(); prop_assert!(relative_eq!(c * v, r * v, epsilon = 1.0e-7)); prop_assert!(relative_eq!(c * p, r * p, epsilon = 1.0e-7)); } // Test that all operators (incl. all combinations of references) work. // See the top comment on `geometry/quaternion_ops.rs` for details on which operations are // supported. #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn all_op_exist( uc in unit_complex(), v in vector2(), p in point2(), r in rotation2() ) { let uv = Unit::new_normalize(v); let ucMuc = uc * uc; let ucMr = uc * r; let rMuc = r * uc; let ucDuc = uc / uc; let ucDr = uc / r; let rDuc = r / uc; let ucMp = uc * p; let ucMv = uc * v; let ucMuv = uc * uv; let mut ucMuc1 = uc; let mut ucMuc2 = uc; let mut ucMr1 = uc; let mut ucMr2 = uc; let mut ucDuc1 = uc; let mut ucDuc2 = uc; let mut ucDr1 = uc; let mut ucDr2 = uc; ucMuc1 *= uc; ucMuc2 *= &uc; ucMr1 *= r; ucMr2 *= &r; ucDuc1 /= uc; ucDuc2 /= &uc; ucDr1 /= r; ucDr2 /= &r; prop_assert!(ucMuc1 == ucMuc && ucMuc1 == ucMuc2 && ucMr1 == ucMr && ucMr1 == ucMr2 && ucDuc1 == ucDuc && ucDuc1 == ucDuc2 && ucDr1 == ucDr && ucDr1 == ucDr2 && ucMuc == &uc * &uc && ucMuc == uc * &uc && ucMuc == &uc * uc && ucMr == &uc * &r && ucMr == uc * &r && ucMr == &uc * r && rMuc == &r * &uc && rMuc == r * &uc && rMuc == &r * uc && ucDuc == &uc / &uc && ucDuc == uc / &uc && ucDuc == &uc / uc && ucDr == &uc / &r && ucDr == uc / &r && ucDr == &uc / r && rDuc == &r / &uc && rDuc == r / &uc && rDuc == &r / uc && ucMp == &uc * &p && ucMp == uc * &p && ucMp == &uc * p && ucMv == &uc * &v && ucMv == uc * &v && ucMv == &uc * v && ucMuv == &uc * &uv && ucMuv == uc * &uv && ucMuv == &uc * uv) } ); nalgebra-0.33.0/tests/lib.rs000064400000000000000000000024320072674642500137710ustar 00000000000000#[cfg(not(all( feature = "debug", feature = "compare", feature = "rand", feature = "macros" )))] compile_error!( "Please enable the `debug`, `compare`, `rand` and `macros` features in order to compile and run the tests. Example: `cargo test --features debug,compare,rand,macros`" ); #[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] #[macro_use] extern crate approx; extern crate nalgebra as na; extern crate num_traits as num; #[cfg(feature = "rand")] extern crate rand_package as rand; #[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] mod core; #[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] mod geometry; #[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] mod linalg; #[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] #[cfg(feature = "proptest-support")] mod proptest; #[cfg(feature = "macros")] mod macros; //#[cfg(all(feature = "debug", feature = "compare", feature = "rand"))] //#[cfg(feature = "sparse")] //mod sparse; mod utils { /// Checks if a slice is sorted in descending order. pub fn is_sorted_descending(slice: &[T]) -> bool { slice.windows(2).all(|elts| elts[0] >= elts[1]) } } nalgebra-0.33.0/tests/linalg/balancing.rs000064400000000000000000000014010072674642500164020ustar 00000000000000#![cfg(feature = "proptest-support")] use na::balancing; use na::DMatrix; use crate::proptest::*; use proptest::{prop_assert_eq, proptest}; proptest! { #[test] fn balancing_parlett_reinsch(n in PROPTEST_MATRIX_DIM) { let m = DMatrix::::new_random(n, n); let mut balanced = m.clone(); let d = balancing::balance_parlett_reinsch(&mut balanced); balancing::unbalance(&mut balanced, &d); prop_assert_eq!(balanced, m); } #[test] fn balancing_parlett_reinsch_static(m in matrix4()) { let mut balanced = m; let d = balancing::balance_parlett_reinsch(&mut balanced); balancing::unbalance(&mut balanced, &d); prop_assert_eq!(balanced, m); } } nalgebra-0.33.0/tests/linalg/bidiagonal.rs000064400000000000000000000067130072674642500165700ustar 00000000000000#[cfg(feature = "proptest-support")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr) => { mod $module { #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn bidiagonal(m in dmatrix_($scalar)) { let bidiagonal = m.clone().bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); prop_assert!(relative_eq!(m, &u * d * &v_t, epsilon = 1.0e-7)) } #[test] fn bidiagonal_static_5_3(m in matrix5x3_($scalar)) { let bidiagonal = m.bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); prop_assert!(relative_eq!(m, &u * d * &v_t, epsilon = 1.0e-7)) } #[test] fn bidiagonal_static_3_5(m in matrix3x5_($scalar)) { let bidiagonal = m.bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); prop_assert!(relative_eq!(m, &u * d * &v_t, epsilon = 1.0e-7)) } #[test] fn bidiagonal_static_square(m in matrix4_($scalar)) { let bidiagonal = m.bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); prop_assert!(relative_eq!(m, &u * d * &v_t, epsilon = 1.0e-7)) } #[test] fn bidiagonal_static_square_2x2(m in matrix2_($scalar)) { let bidiagonal = m.bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); prop_assert!(relative_eq!(m, &u * d * &v_t, epsilon = 1.0e-7)) } } } } ); gen_tests!(complex, complex_f64()); gen_tests!(f64, PROPTEST_F64); } #[test] fn bidiagonal_identity() { let m = na::DMatrix::::identity(10, 10); let bidiagonal = m.clone().bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); assert_eq!(m, &u * d * &v_t); let m = na::DMatrix::::identity(10, 15); let bidiagonal = m.clone().bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); assert_eq!(m, &u * d * &v_t); let m = na::DMatrix::::identity(15, 10); let bidiagonal = m.clone().bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); assert_eq!(m, &u * d * &v_t); } #[test] fn bidiagonal_regression_issue_1313() { let s = 6.123234e-16_f32; let mut m = nalgebra::dmatrix![ 10.0, 0.0, 0.0, 0.0, -10.0, 0.0, 0.0, 0.0; s, 10.0, 0.0, 10.0, s, 0.0, 0.0, 0.0; 20.0, -20.0, 0.0, 20.0, 20.0, 0.0, 0.0, 0.0; ]; m.unscale_mut(m.camax()); let bidiagonal = m.clone().bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); let m2 = &u * d * &v_t; assert_relative_eq!(m, m2, epsilon = 1e-6); } #[test] fn bidiagonal_regression_issue_1313_minimal() { let s = 6.123234e-17_f32; let m = nalgebra::dmatrix![ 1.0, 0.0, -1.0; s, 1.0, s; ]; let bidiagonal = m.clone().bidiagonalize(); let (u, d, v_t) = bidiagonal.unpack(); let m2 = &u * &d * &v_t; assert_relative_eq!(m, m2, epsilon = 1e-6); } nalgebra-0.33.0/tests/linalg/cholesky.rs000064400000000000000000000174450072674642500163240ustar 00000000000000#![cfg(all(feature = "proptest-support", feature = "debug"))] #[test] // #[rustfmt::skip] fn cholesky_with_substitute() { // Make a tiny covariance matrix with a small covariance value. let m = na::Matrix2::new(1.0, f64::NAN, 1.0, 1e-32); // Show that the cholesky fails for our matrix. We then try again with a substitute. assert!(na::Cholesky::new(m).is_none()); // ...and show that we get some result this time around. assert!(na::Cholesky::new_with_substitute(m, 1e-8).is_some()); } macro_rules! gen_tests( ($module: ident, $scalar: ty) => { mod $module { use na::debug::RandomSDP; use na::dimension::{Const, Dyn}; use na::{DMatrix, DVector, Matrix4x3, Vector4}; use rand::random; use simba::scalar::ComplexField; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn cholesky(n in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Dyn(n), || random::<$scalar>().0).unwrap(); let l = m.clone().cholesky().unwrap().unpack(); prop_assert!(relative_eq!(m, &l * l.adjoint(), epsilon = 1.0e-7)); } #[test] fn cholesky_static(_n in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Const::<4>, || random::<$scalar>().0).unwrap(); let chol = m.cholesky().unwrap(); let l = chol.unpack(); prop_assert!(relative_eq!(m, &l * l.adjoint(), epsilon = 1.0e-7)); } #[test] fn cholesky_solve(n in PROPTEST_MATRIX_DIM, nb in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Dyn(n), || random::<$scalar>().0).unwrap(); let chol = m.clone().cholesky().unwrap(); let b1 = DVector::<$scalar>::new_random(n).map(|e| e.0); let b2 = DMatrix::<$scalar>::new_random(n, nb).map(|e| e.0); let sol1 = chol.solve(&b1); let sol2 = chol.solve(&b2); prop_assert!(relative_eq!(&m * &sol1, b1, epsilon = 1.0e-7)); prop_assert!(relative_eq!(&m * &sol2, b2, epsilon = 1.0e-7)); } #[test] fn cholesky_solve_static(_n in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Const::<4>, || random::<$scalar>().0).unwrap(); let chol = m.clone().cholesky().unwrap(); let b1 = Vector4::<$scalar>::new_random().map(|e| e.0); let b2 = Matrix4x3::<$scalar>::new_random().map(|e| e.0); let sol1 = chol.solve(&b1); let sol2 = chol.solve(&b2); prop_assert!(relative_eq!(m * sol1, b1, epsilon = 1.0e-7)); prop_assert!(relative_eq!(m * sol2, b2, epsilon = 1.0e-7)); } #[test] fn cholesky_inverse(n in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Dyn(n), || random::<$scalar>().0).unwrap(); let m1 = m.clone().cholesky().unwrap().inverse(); let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-7) && id2.is_identity(1.0e-7)); } #[test] fn cholesky_inverse_static(_n in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Const::<4>, || random::<$scalar>().0).unwrap(); let m1 = m.clone().cholesky().unwrap().inverse(); let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-7) && id2.is_identity(1.0e-7)); } #[test] fn cholesky_determinant(n in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Dyn(n), || random::<$scalar>().0).unwrap(); let lu_det = m.clone().lu().determinant(); assert_relative_eq!(lu_det.imaginary(), 0., epsilon = 1.0e-7); let chol_det = m.cholesky().unwrap().determinant(); prop_assert!(relative_eq!(lu_det.real(), chol_det, epsilon = 1.0e-7)); } #[test] fn cholesky_determinant_static(_n in PROPTEST_MATRIX_DIM) { let m = RandomSDP::new(Const::<4>, || random::<$scalar>().0).unwrap(); let lu_det = m.clone().lu().determinant(); assert_relative_eq!(lu_det.imaginary(), 0., epsilon = 1.0e-7); let chol_det = m.cholesky().unwrap().determinant(); prop_assert!(relative_eq!(lu_det.real(), chol_det, epsilon = 1.0e-7)); } #[test] fn cholesky_rank_one_update(_n in PROPTEST_MATRIX_DIM) { let mut m = RandomSDP::new(Const::<4>, || random::<$scalar>().0).unwrap(); let x = Vector4::<$scalar>::new_random().map(|e| e.0); // this is dirty but $scalar is not a scalar type (its a Rand) in this file let zero = random::<$scalar>().0 * 0.; let one = zero + 1.; let sigma = random::(); // needs to be a real let sigma_scalar = zero + sigma; // updates cholesky decomposition and reconstructs m updated let mut chol = m.clone().cholesky().unwrap(); chol.rank_one_update(&x, sigma); let m_chol_updated = chol.l() * chol.l().adjoint(); // updates m manually m.gerc(sigma_scalar, &x, &x, one); // m += sigma * x * x.adjoint() prop_assert!(relative_eq!(m, m_chol_updated, epsilon = 1.0e-7)); } #[test] fn cholesky_insert_column(n in PROPTEST_MATRIX_DIM) { let n = n.max(1).min(10); let j = random::() % n; let m_updated = RandomSDP::new(Dyn(n), || random::<$scalar>().0).unwrap(); // build m and col from m_updated let col = m_updated.column(j); let m = m_updated.clone().remove_column(j).remove_row(j); // remove column from cholesky decomposition and rebuild m let chol = m.clone().cholesky().unwrap().insert_column(j, col); let m_chol_updated = chol.l() * chol.l().adjoint(); prop_assert!(relative_eq!(m_updated, m_chol_updated, epsilon = 1.0e-7)); } #[test] fn cholesky_remove_column(n in PROPTEST_MATRIX_DIM) { let n = n.max(1).min(10); let j = random::() % n; let m = RandomSDP::new(Dyn(n), || random::<$scalar>().0).unwrap(); // remove column from cholesky decomposition and rebuild m let chol = m.clone().cholesky().unwrap().remove_column(j); let m_chol_updated = chol.l() * chol.l().adjoint(); // remove column from m let m_updated = m.remove_column(j).remove_row(j); prop_assert!(relative_eq!(m_updated, m_chol_updated, epsilon = 1.0e-7)); } } } } ); gen_tests!(complex, RandComplex); gen_tests!(f64, RandScalar); nalgebra-0.33.0/tests/linalg/col_piv_qr.rs000064400000000000000000000143520072674642500166320ustar 00000000000000#[cfg_attr(rustfmt, rustfmt_skip)] use na::Matrix4; #[test] fn col_piv_qr() { let m = Matrix4::new( 1.0, -1.0, 2.0, 1.0, -1.0, 3.0, -1.0, -1.0, 3.0, -5.0, 5.0, 3.0, 1.0, 2.0, 1.0, -2.0, ); let col_piv_qr = m.col_piv_qr(); assert!(relative_eq!( col_piv_qr.determinant(), 0.0, epsilon = 1.0e-7 )); let (q, r, p) = col_piv_qr.unpack(); let mut qr = q * r; p.inv_permute_columns(&mut qr); assert!(relative_eq!(m, qr, epsilon = 1.0e-7)); } #[cfg(feature = "proptest-support")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr ,$scalar_type: ty) => { mod $module { use na::{DMatrix, DVector, Matrix4x3, Vector4}; use std::cmp; #[allow(unused_imports)] use crate::core::helper::{RandComplex, RandScalar}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn col_piv_qr(m in dmatrix_($scalar)) { let col_piv_qr = m.clone().col_piv_qr(); let (q, r, p) = col_piv_qr.unpack(); let mut qr = &q * &r; p.inv_permute_columns(&mut qr); prop_assert!(relative_eq!(m, &qr, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn col_piv_qr_static_5_3(m in matrix5x3_($scalar)) { let col_piv_qr = m.col_piv_qr(); let (q, r, p) = col_piv_qr.unpack(); let mut qr = q * r; p.inv_permute_columns(&mut qr); prop_assert!(relative_eq!(m, qr, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn col_piv_qr_static_3_5(m in matrix3x5_($scalar)) { let col_piv_qr = m.col_piv_qr(); let (q, r, p) = col_piv_qr.unpack(); let mut qr = q * r; p.inv_permute_columns(&mut qr); prop_assert!(relative_eq!(m, qr, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn col_piv_qr_static_square(m in matrix4_($scalar)) { let col_piv_qr = m.col_piv_qr(); let (q, r, p) = col_piv_qr.unpack(); let mut qr = q * r; p.inv_permute_columns(&mut qr); prop_assert!(relative_eq!(m, qr, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn col_piv_qr_solve(n in PROPTEST_MATRIX_DIM, nb in PROPTEST_MATRIX_DIM) { if n != 0 && nb != 0 { let n = cmp::min(n, 50); // To avoid slowing down the test too much. let nb = cmp::min(nb, 50); // To avoid slowing down the test too much. let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let col_piv_qr = m.clone().col_piv_qr(); let b1 = DVector::<$scalar_type>::new_random(n).map(|e| e.0); let b2 = DMatrix::<$scalar_type>::new_random(n, nb).map(|e| e.0); if col_piv_qr.is_invertible() { let sol1 = col_piv_qr.solve(&b1).unwrap(); let sol2 = col_piv_qr.solve(&b2).unwrap(); prop_assert!(relative_eq!(&m * sol1, b1, epsilon = 1.0e-6)); prop_assert!(relative_eq!(&m * sol2, b2, epsilon = 1.0e-6)); } } } #[test] fn col_piv_qr_solve_static(m in matrix4_($scalar)) { let col_piv_qr = m.col_piv_qr(); let b1 = Vector4::<$scalar_type>::new_random().map(|e| e.0); let b2 = Matrix4x3::<$scalar_type>::new_random().map(|e| e.0); if col_piv_qr.is_invertible() { let sol1 = col_piv_qr.solve(&b1).unwrap(); let sol2 = col_piv_qr.solve(&b2).unwrap(); prop_assert!(relative_eq!(m * sol1, b1, epsilon = 1.0e-6)); prop_assert!(relative_eq!(m * sol2, b2, epsilon = 1.0e-6)); } } #[test] fn col_piv_qr_inverse(n in PROPTEST_MATRIX_DIM) { let n = cmp::max(1, cmp::min(n, 15)); // To avoid slowing down the test too much. let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); if let Some(m1) = m.clone().col_piv_qr().try_inverse() { let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } } #[test] fn col_piv_qr_inverse_static(m in matrix4_($scalar)) { let col_piv_qr = m.col_piv_qr(); if let Some(m1) = col_piv_qr.try_inverse() { let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); } nalgebra-0.33.0/tests/linalg/convolution.rs000064400000000000000000000075640072674642500170630ustar 00000000000000use na::{DVector, Vector2, Vector3, Vector4, Vector5}; use std::panic; // // Should mimic calculations in Python's scipy library // >>>from scipy.signal import convolve // // >>> convolve([1,2,3,4],[1,2],"same") // array([ 1, 4, 7, 10]) #[test] fn convolve_same_check() { // Static Tests let actual_s = Vector4::new(1.0, 4.0, 7.0, 10.0); let expected_s = Vector4::new(1.0, 2.0, 3.0, 4.0).convolve_same(Vector2::new(1.0, 2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); // Dyn Tests let actual_d = DVector::from_vec(vec![1.0, 4.0, 7.0, 10.0]); let expected_d = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]) .convolve_same(DVector::from_vec(vec![1.0, 2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); // Panic Tests // These really only apply to dynamic sized vectors assert!(panic::catch_unwind(|| { let _ = DVector::from_vec(vec![1.0, 2.0]) .convolve_same(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { let _ = DVector::::from_vec(vec![]) .convolve_same(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { let _ = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]) .convolve_same(DVector::::from_vec(vec![])); }) .is_err()); } // >>> convolve([1,2,3,4],[1,2],"full") // array([ 1, 4, 7, 10, 8]) #[test] fn convolve_full_check() { // Static Tests let actual_s = Vector5::new(1.0, 4.0, 7.0, 10.0, 8.0); let expected_s = Vector4::new(1.0, 2.0, 3.0, 4.0).convolve_full(Vector2::new(1.0, 2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); // Dyn Tests let actual_d = DVector::from_vec(vec![1.0, 4.0, 7.0, 10.0, 8.0]); let expected_d = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]) .convolve_full(DVector::from_vec(vec![1.0, 2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); // Panic Tests // These really only apply to dynamic sized vectors assert!(panic::catch_unwind(|| { DVector::from_vec(vec![1.0, 2.0]) .convolve_full(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { DVector::::from_vec(vec![]).convolve_full(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]).convolve_full(DVector::::from_vec(vec![])); }) .is_err()); } // >>> convolve([1, 2, 3, 4],[1, 2],"valid") // array([4, 7, 10]) #[test] fn convolve_valid_check() { // Static Tests let actual_s = Vector3::from_vec(vec![4.0, 7.0, 10.0]); let expected_s = Vector4::new(1.0, 2.0, 3.0, 4.0).convolve_valid(Vector2::new(1.0, 2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); // Dyn Tests let actual_d = DVector::from_vec(vec![4.0, 7.0, 10.0]); let expected_d = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]) .convolve_valid(DVector::from_vec(vec![1.0, 2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); // Panic Tests // These really only apply to dynamic sized vectors assert!(panic::catch_unwind(|| { DVector::from_vec(vec![1.0, 2.0]) .convolve_valid(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { DVector::::from_vec(vec![]) .convolve_valid(DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0])); }) .is_err()); assert!(panic::catch_unwind(|| { DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]) .convolve_valid(DVector::::from_vec(vec![])); }) .is_err()); } nalgebra-0.33.0/tests/linalg/eigen.rs000064400000000000000000000253040072674642500155630ustar 00000000000000use na::{DMatrix, Matrix3}; #[cfg(feature = "proptest-support")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use na::DMatrix; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use std::cmp; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn symmetric_eigen(n in PROPTEST_MATRIX_DIM) { let n = cmp::max(1, cmp::min(n, 10)); let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0).hermitian_part(); let eig = m.clone().symmetric_eigen(); let recomp = eig.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)) } #[test] fn symmetric_eigen_singular(n in PROPTEST_MATRIX_DIM) { let n = cmp::max(1, cmp::min(n, 10)); let mut m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0).hermitian_part(); m.row_mut(n / 2).fill(na::zero()); m.column_mut(n / 2).fill(na::zero()); let eig = m.clone().symmetric_eigen(); let recomp = eig.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)) } #[test] fn symmetric_eigen_static_square_4x4(m in matrix4_($scalar)) { let m = m.hermitian_part(); let eig = m.symmetric_eigen(); let recomp = eig.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)) } #[test] fn symmetric_eigen_static_square_3x3(m in matrix3_($scalar)) { let m = m.hermitian_part(); let eig = m.symmetric_eigen(); let recomp = eig.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)) } #[test] fn symmetric_eigen_static_square_2x2(m in matrix2_($scalar)) { let m = m.hermitian_part(); let eig = m.symmetric_eigen(); let recomp = eig.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5)) } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); } // Test proposed on the issue #176 of rulinalg. #[test] #[rustfmt::skip] fn symmetric_eigen_singular_24x24() { let m = DMatrix::from_row_slice( 24, 24, &[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0 ], ); let eig = m.clone().symmetric_eigen(); let recomp = eig.recompose(); assert_relative_eq!( m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-5 ); } // Regression test for #1368 #[test] fn very_small_deviation_from_identity_issue_1368() { let m = Matrix3::::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 // eigenvalues have multiplicity > 1. // // /* // * NOTE: for the following tests, we use only upper-triangular matrices. // * This ensures the schur decomposition will work, and allows use to test the eigenvector // * computation. // */ // fn eigen(n: usize) -> bool { // let n = cmp::max(1, cmp::min(n, 10)); // let m = DMatrix::::new_random(n, n).upper_triangle(); // // let eig = RealEigen::new(m.clone()).unwrap(); // verify_eigenvectors(m, eig) // } // // fn eigen_with_adjacent_duplicate_diagonals(n: usize) -> bool { // let n = cmp::max(1, cmp::min(n, 10)); // let mut m = DMatrix::::new_random(n, n).upper_triangle(); // // // Suplicate some adjacent diagonal elements. // for i in 0 .. n / 2 { // m[(i * 2 + 1, i * 2 + 1)] = m[(i * 2, i * 2)]; // } // // let eig = RealEigen::new(m.clone()).unwrap(); // verify_eigenvectors(m, eig) // } // // fn eigen_with_nonadjacent_duplicate_diagonals(n: usize) -> bool { // let n = cmp::max(3, cmp::min(n, 10)); // let mut m = DMatrix::::new_random(n, n).upper_triangle(); // // // Suplicate some diagonal elements. // for i in n / 2 .. n { // m[(i, i)] = m[(i - n / 2, i - n / 2)]; // } // // let eig = RealEigen::new(m.clone()).unwrap(); // verify_eigenvectors(m, eig) // } // // fn eigen_static_square_4x4(m: Matrix4) -> bool { // let m = m.upper_triangle(); // let eig = RealEigen::new(m.clone()).unwrap(); // verify_eigenvectors(m, eig) // } // // fn eigen_static_square_3x3(m: Matrix3) -> bool { // let m = m.upper_triangle(); // let eig = RealEigen::new(m.clone()).unwrap(); // verify_eigenvectors(m, eig) // } // // fn eigen_static_square_2x2(m: Matrix2) -> bool { // let m = m.upper_triangle(); // println!("{}", m); // let eig = RealEigen::new(m.clone()).unwrap(); // verify_eigenvectors(m, eig) // } // } // // fn verify_eigenvectors(m: OMatrix, mut eig: RealEigen) -> bool // where DefaultAllocator: Allocator + // Allocator + // Allocator + // Allocator, // OMatrix: Display, // OVector: Display { // let mv = &m * &eig.eigenvectors; // // println!("eigenvalues: {}eigenvectors: {}", eig.eigenvalues, eig.eigenvectors); // // let dim = m.nrows(); // for i in 0 .. dim { // let mut col = eig.eigenvectors.column_mut(i); // col *= eig.eigenvalues[i]; // } // // println!("{}{:.5}{:.5}", m, mv, eig.eigenvectors); // // relative_eq!(eig.eigenvectors, mv, epsilon = 1.0e-5) // } nalgebra-0.33.0/tests/linalg/exp.rs000064400000000000000000000130430072674642500152650ustar 00000000000000#[cfg(test)] mod tests { //https://github.com/scipy/scipy/blob/c1372d8aa90a73d8a52f135529293ff4edb98fc8/scipy/sparse/linalg/tests/test_matfuncs.py #[test] fn exp_static() { use nalgebra::{Matrix1, Matrix2, Matrix3}; { let m = Matrix1::new(1.0); let f = m.exp(); assert!(relative_eq!(f, Matrix1::new(1_f64.exp()), epsilon = 1.0e-7)); } { let m = Matrix2::new(0.0, 1.0, 0.0, 0.0); assert!(relative_eq!( m.exp(), Matrix2::new(1.0, 1.0, 0.0, 1.0), epsilon = 1.0e-7 )); } { let a: f64 = 1.0; let b: f64 = 2.0; let c: f64 = 3.0; let d: f64 = 4.0; let m = Matrix2::new(a, b, c, d); let delta = ((a - d).powf(2.0) + 4.0 * b * c).sqrt(); let delta_2 = delta / 2.0; let ad_2 = (a + d) / 2.0; let m11 = ad_2.exp() * (delta * delta_2.cosh() + (a - d) * delta_2.sinh()); let m12 = 2.0 * b * ad_2.exp() * delta_2.sinh(); let m21 = 2.0 * c * ad_2.exp() * delta_2.sinh(); let m22 = ad_2.exp() * (delta * delta_2.cosh() + (d - a) * delta_2.sinh()); let f = Matrix2::new(m11, m12, m21, m22) / delta; assert!(relative_eq!(f, m.exp(), epsilon = 1.0e-7)); } { // https://mathworld.wolfram.com/MatrixExponential.html use rand::{ distributions::{Distribution, Uniform}, thread_rng, }; let mut rng = thread_rng(); let dist = Uniform::new(-10.0, 10.0); loop { let a: f64 = dist.sample(&mut rng); let b: f64 = dist.sample(&mut rng); let c: f64 = dist.sample(&mut rng); let d: f64 = dist.sample(&mut rng); let m = Matrix2::new(a, b, c, d); let delta_sq = (a - d).powf(2.0) + 4.0 * b * c; if delta_sq < 0.0 { continue; } let delta = delta_sq.sqrt(); let delta_2 = delta / 2.0; let ad_2 = (a + d) / 2.0; let m11 = ad_2.exp() * (delta * delta_2.cosh() + (a - d) * delta_2.sinh()); let m12 = 2.0 * b * ad_2.exp() * delta_2.sinh(); let m21 = 2.0 * c * ad_2.exp() * delta_2.sinh(); let m22 = ad_2.exp() * (delta * delta_2.cosh() + (d - a) * delta_2.sinh()); let f = Matrix2::new(m11, m12, m21, m22) / delta; assert!(relative_eq!(f, m.exp(), epsilon = 1.0e-7)); break; } } { let m = Matrix3::new(1.0, 3.0, 0.0, 0.0, 1.0, 5.0, 0.0, 0.0, 2.0); let e1 = 1.0_f64.exp(); let e2 = 2.0_f64.exp(); let f = Matrix3::new( e1, 3.0 * e1, 15.0 * (e2 - 2.0 * e1), 0.0, e1, 5.0 * (e2 - e1), 0.0, 0.0, e2, ); assert!(relative_eq!(f, m.exp(), epsilon = 1.0e-7)); } } #[test] fn exp_dynamic() { use nalgebra::DMatrix; let m = DMatrix::from_row_slice(3, 3, &[1.0, 3.0, 0.0, 0.0, 1.0, 5.0, 0.0, 0.0, 2.0]); let e1 = 1.0_f64.exp(); let e2 = 2.0_f64.exp(); let f = DMatrix::from_row_slice( 3, 3, &[ e1, 3.0 * e1, 15.0 * (e2 - 2.0 * e1), 0.0, e1, 5.0 * (e2 - e1), 0.0, 0.0, e2, ], ); assert!(relative_eq!(f, m.exp(), epsilon = 1.0e-7)); } #[test] fn exp_complex() { use nalgebra::{Complex, DMatrix, DVector, Matrix2, RealField}; { let z = Matrix2::>::zeros(); let identity = Matrix2::>::identity(); assert!((z.exp() - identity).norm() < 1e-7); } { let a = Matrix2::>::new( Complex::::new(0.0, 1.0), Complex::::new(0.0, 2.0), Complex::::new(0.0, -1.0), Complex::::new(0.0, 3.0), ); let b = Matrix2::>::new( Complex::::new(0.42645929666726, 1.89217550966333), Complex::::new(-2.13721484276556, -0.97811251808259), Complex::::new(1.06860742138278, 0.48905625904129), Complex::::new(-1.7107555460983, 0.91406299158075), ); assert!((a.exp() - b).norm() < 1.0e-07); } { let d1 = Complex::::new(0.0, ::pi()); let d2 = Complex::::new(0.0, ::frac_pi_2()); let d3 = Complex::::new(0.0, ::frac_pi_4()); let m = DMatrix::>::from_diagonal(&DVector::from_row_slice(&[d1, d2, d3])); let res = DMatrix::>::from_diagonal(&DVector::from_row_slice(&[ d1.exp(), d2.exp(), d3.exp(), ])); assert!((m.exp() - res).norm() < 1e-07); } } } nalgebra-0.33.0/tests/linalg/full_piv_lu.rs000064400000000000000000000340120072674642500170100ustar 00000000000000use na::Matrix3; #[test] #[rustfmt::skip] fn full_piv_lu_simple() { let m = Matrix3::new( 2.0, -1.0, 0.0, -1.0, 2.0, -1.0, 0.0, -1.0, 2.0); let lu = m.full_piv_lu(); assert_eq!(lu.determinant(), 4.0); let (p, l, u, q) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); q.inv_permute_columns(&mut lu); assert!(relative_eq!(m, lu, epsilon = 1.0e-7)); } #[test] #[rustfmt::skip] fn full_piv_lu_simple_with_pivot() { let m = Matrix3::new(0.0, -1.0, 2.0, -1.0, 2.0, -1.0, 2.0, -1.0, 0.0); let lu = m.full_piv_lu(); assert_eq!(lu.determinant(), -4.0); let (p, l, u, q) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); q.inv_permute_columns(&mut lu); assert!(relative_eq!(m, lu, epsilon = 1.0e-7)); } #[cfg(feature = "arbitrary")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use std::cmp; use num::One; use na::{DMatrix, Matrix4x3, DVector, Vector4}; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn full_piv_lu(m in dmatrix_($scalar)) { let lu = m.clone().full_piv_lu(); let (p, l, u, q) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); q.inv_permute_columns(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)) } #[test] fn full_piv_lu_static_3_5(m in matrix3x5_($scalar)) { let lu = m.full_piv_lu(); let (p, l, u, q) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); q.inv_permute_columns(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)) } #[test] fn full_piv_lu_static_5_3(m in matrix5x3_($scalar)) { let lu = m.full_piv_lu(); let (p, l, u, q) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); q.inv_permute_columns(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)) } #[test] fn full_piv_lu_static_square(m in matrix4_($scalar)) { let lu = m.full_piv_lu(); let (p, l, u, q) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); q.inv_permute_columns(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)) } #[test] fn full_piv_lu_solve(n in PROPTEST_MATRIX_DIM, nb in PROPTEST_MATRIX_DIM) { let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let lu = m.clone().full_piv_lu(); let b1 = DVector::<$scalar_type>::new_random(n).map(|e| e.0); let b2 = DMatrix::<$scalar_type>::new_random(n, nb).map(|e| e.0); let sol1 = lu.solve(&b1); let sol2 = lu.solve(&b2); prop_assert!(sol1.is_none() || relative_eq!(&m * sol1.unwrap(), b1, epsilon = 1.0e-6)); prop_assert!(sol2.is_none() || relative_eq!(&m * sol2.unwrap(), b2, epsilon = 1.0e-6)); } #[test] fn full_piv_lu_solve_static(m in matrix4_($scalar)) { let lu = m.full_piv_lu(); let b1 = Vector4::<$scalar_type>::new_random().map(|e| e.0); let b2 = Matrix4x3::<$scalar_type>::new_random().map(|e| e.0); let sol1 = lu.solve(&b1); let sol2 = lu.solve(&b2); prop_assert!(sol1.is_none() || relative_eq!(&m * sol1.unwrap(), b1, epsilon = 1.0e-6)); prop_assert!(sol2.is_none() || relative_eq!(&m * sol2.unwrap(), b2, epsilon = 1.0e-6)); } #[test] fn full_piv_lu_inverse(n in PROPTEST_MATRIX_DIM) { let n = cmp::max(1, cmp::min(n, 15)); // To avoid slowing down the test too much. let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let mut l = m.lower_triangle(); let mut u = m.upper_triangle(); // Ensure the matrix is well conditioned for inversion. l.fill_diagonal(One::one()); u.fill_diagonal(One::one()); let m = l * u; let m1 = m.clone().full_piv_lu().try_inverse().unwrap(); let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } #[test] fn full_piv_lu_inverse_static(m in matrix4_($scalar)) { let lu = m.full_piv_lu(); if let Some(m1) = lu.try_inverse() { let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); } /* #[test] fn swap_rows() { let mut m = Matrix5x3::new( 11.0, 12.0, 13.0, 21.0, 22.0, 23.0, 31.0, 32.0, 33.0, 41.0, 42.0, 43.0, 51.0, 52.0, 53.0); let expected = Matrix5x3::new( 11.0, 12.0, 13.0, 41.0, 42.0, 43.0, 31.0, 32.0, 33.0, 21.0, 22.0, 23.0, 51.0, 52.0, 53.0); m.swap_rows(1, 3); assert_eq!(m, expected); } #[test] fn swap_columns() { let mut m = Matrix3x5::new( 11.0, 12.0, 13.0, 14.0, 15.0, 21.0, 22.0, 23.0, 24.0, 25.0, 31.0, 32.0, 33.0, 34.0, 35.0); let expected = Matrix3x5::new( 11.0, 14.0, 13.0, 12.0, 15.0, 21.0, 24.0, 23.0, 22.0, 25.0, 31.0, 34.0, 33.0, 32.0, 35.0); m.swap_columns(1, 3); assert_eq!(m, expected); } #[test] fn remove_columns() { let m = Matrix3x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected1 = Matrix3x4::new( 12, 13, 14, 15, 22, 23, 24, 25, 32, 33, 34, 35); let expected2 = Matrix3x4::new( 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34); let expected3 = Matrix3x4::new( 11, 12, 14, 15, 21, 22, 24, 25, 31, 32, 34, 35); assert_eq!(m.remove_column(0), expected1); assert_eq!(m.remove_column(4), expected2); assert_eq!(m.remove_column(2), expected3); let expected1 = Matrix3::new( 13, 14, 15, 23, 24, 25, 33, 34, 35); let expected2 = Matrix3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33); let expected3 = Matrix3::new( 11, 12, 15, 21, 22, 25, 31, 32, 35); assert_eq!(m.remove_fixed_columns::(0), expected1); assert_eq!(m.remove_fixed_columns::(3), expected2); assert_eq!(m.remove_fixed_columns::(2), expected3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, U3, Dyn, _> = m.remove_columns(3, 2); assert!(computed.eq(&expected2)); } #[test] fn remove_rows() { let m = Matrix5x3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected1 = Matrix4x3::new( 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected2 = Matrix4x3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43); let expected3 = Matrix4x3::new( 11, 12, 13, 21, 22, 23, 41, 42, 43, 51, 52, 53); assert_eq!(m.remove_row(0), expected1); assert_eq!(m.remove_row(4), expected2); assert_eq!(m.remove_row(2), expected3); let expected1 = Matrix3::new( 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected2 = Matrix3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33); let expected3 = Matrix3::new( 11, 12, 13, 21, 22, 23, 51, 52, 53); assert_eq!(m.remove_fixed_rows::(0), expected1); assert_eq!(m.remove_fixed_rows::(3), expected2); assert_eq!(m.remove_fixed_rows::(2), expected3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, Dyn, U3, _> = m.remove_rows(3, 2); assert!(computed.eq(&expected2)); } #[test] fn insert_columns() { let m = Matrix5x3::new( 11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53); let expected1 = Matrix5x4::new( 0, 11, 12, 13, 0, 21, 22, 23, 0, 31, 32, 33, 0, 41, 42, 43, 0, 51, 52, 53); let expected2 = Matrix5x4::new( 11, 12, 13, 0, 21, 22, 23, 0, 31, 32, 33, 0, 41, 42, 43, 0, 51, 52, 53, 0); let expected3 = Matrix5x4::new( 11, 12, 0, 13, 21, 22, 0, 23, 31, 32, 0, 33, 41, 42, 0, 43, 51, 52, 0, 53); assert_eq!(m.insert_column(0, 0), expected1); assert_eq!(m.insert_column(3, 0), expected2); assert_eq!(m.insert_column(2, 0), expected3); let expected1 = Matrix5::new( 0, 0, 11, 12, 13, 0, 0, 21, 22, 23, 0, 0, 31, 32, 33, 0, 0, 41, 42, 43, 0, 0, 51, 52, 53); let expected2 = Matrix5::new( 11, 12, 13, 0, 0, 21, 22, 23, 0, 0, 31, 32, 33, 0, 0, 41, 42, 43, 0, 0, 51, 52, 53, 0, 0); let expected3 = Matrix5::new( 11, 12, 0, 0, 13, 21, 22, 0, 0, 23, 31, 32, 0, 0, 33, 41, 42, 0, 0, 43, 51, 52, 0, 0, 53); assert_eq!(m.insert_fixed_columns::(0, 0), expected1); assert_eq!(m.insert_fixed_columns::(3, 0), expected2); assert_eq!(m.insert_fixed_columns::(2, 0), expected3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, U5, Dyn, _> = m.insert_columns(3, 2, 0); assert!(computed.eq(&expected2)); } #[test] fn insert_rows() { let m = Matrix3x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected1 = Matrix4x5::new( 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected2 = Matrix4x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0); let expected3 = Matrix4x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 31, 32, 33, 34, 35); assert_eq!(m.insert_row(0, 0), expected1); assert_eq!(m.insert_row(3, 0), expected2); assert_eq!(m.insert_row(2, 0), expected3); let expected1 = Matrix5::new( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let expected2 = Matrix5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); let expected3 = Matrix5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 32, 33, 34, 35); assert_eq!(m.insert_fixed_rows::<2>(0, 0), expected1); assert_eq!(m.insert_fixed_rows::<2>(3, 0), expected2); assert_eq!(m.insert_fixed_rows::<2>(2, 0), expected3); // The following is just to verify that the return type dimensions is correctly inferred. let computed: Matrix<_, Dyn, U5, _> = m.insert_rows(3, 2, 0); assert!(computed.eq(&expected2)); } #[test] fn resize() { let m = Matrix3x5::new( 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35); let add_add = DMatrix::from_row_slice(5, 6, &[ 11, 12, 13, 14, 15, 42, 21, 22, 23, 24, 25, 42, 31, 32, 33, 34, 35, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]); let del_del = DMatrix::from_row_slice(1, 2, &[11, 12]); let add_del = DMatrix::from_row_slice(5, 2, &[ 11, 12, 21, 22, 31, 32, 42, 42, 42, 42]); let del_add = DMatrix::from_row_slice(1, 8, &[ 11, 12, 13, 14, 15, 42, 42, 42]); assert_eq!(del_del, m.resize(1, 2, 42)); assert_eq!(add_add, m.resize(5, 6, 42)); assert_eq!(add_del, m.resize(5, 2, 42)); assert_eq!(del_add, m.resize(1, 8, 42)); } */ nalgebra-0.33.0/tests/linalg/hessenberg.rs000064400000000000000000000033540072674642500166220ustar 00000000000000#![cfg(feature = "proptest-support")] use na::Matrix2; #[test] fn hessenberg_simple() { let m = Matrix2::new(1.0, 0.0, 1.0, 3.0); let hess = m.hessenberg(); let (p, h) = hess.unpack(); assert!(relative_eq!(m, p * h * p.transpose(), epsilon = 1.0e-7)) } macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use na::DMatrix; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn hessenberg(n in PROPTEST_MATRIX_DIM) { let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let hess = m.clone().hessenberg(); let (p, h) = hess.unpack(); prop_assert!(relative_eq!(m, &p * h * p.adjoint(), epsilon = 1.0e-7)) } #[test] fn hessenberg_static_mat2(m in matrix2_($scalar)) { let hess = m.hessenberg(); let (p, h) = hess.unpack(); prop_assert!(relative_eq!(m, p * h * p.adjoint(), epsilon = 1.0e-7)) } #[test] fn hessenberg_static(m in matrix4_($scalar)) { let hess = m.hessenberg(); let (p, h) = hess.unpack(); prop_assert!(relative_eq!(m, p * h * p.adjoint(), epsilon = 1.0e-7)) } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); nalgebra-0.33.0/tests/linalg/inverse.rs000064400000000000000000000134770072674642500161570ustar 00000000000000use na::{Matrix1, Matrix2, Matrix3, Matrix4, Matrix5}; #[test] fn matrix1_try_inverse() { let a = Matrix1::new(3.0); let a_inv = a.try_inverse().expect("Matrix is invertible"); assert_relative_eq!(a_inv, Matrix1::new(1.0 / 3.0)); } #[test] #[rustfmt::skip] fn matrix2_try_inverse() { let a = Matrix2::new( 5.0, -2.0, -10.0, 1.0); let expected_inverse = Matrix2::new(-0.2 / 3.0, -2.0 / 15.0, -2.0 / 3.0, -1.0 / 3.0); let a_inv = a.try_inverse() .expect("Matrix is invertible"); assert_relative_eq!(a_inv, expected_inverse); } #[test] #[rustfmt::skip] fn matrix3_try_inverse() { let a = Matrix3::new(-3.0, 2.0, 0.0, -6.0, 9.0, -2.0, 9.0, -6.0, 4.0); let expected_inverse = Matrix3::new(-0.40, 0.4 / 3.0, 0.2 / 3.00, -0.10, 0.2, 0.10, 0.75, 0.0, 0.25); let a_inv = a.try_inverse() .expect("Matrix is invertible"); assert_relative_eq!(a_inv, expected_inverse); } #[test] #[rustfmt::skip] fn matrix4_try_inverse_issue_214() { let m1 = Matrix4::new( -0.34727043, 0.00000005397217, -0.000000000000003822135, -0.000000000000003821371, 0.0, -0.000000026986084, -1.0001999, -1.0, 0.000000030359345, 0.61736965, -0.000000043720128, -0.00000004371139, -0.0000000029144975, -0.05926739, 3.8007796, 4.0); let m2 = Matrix4::new( -0.34727043, 0.00000005397217, -0.000000000000003822135, -0.000000000000003821371, 0.0, -0.000000026986084, -1.0001999, -1.0, 0.000000030359345, 0.61736965, -0.000000043720128, -0.00000004371139, -0.0000000029448568, -0.05988476, 3.8007796, 4.0); assert!(m1.try_inverse().is_some()); assert!(m2.try_inverse().is_some()); assert!(m1.transpose().try_inverse().is_some()); assert!(m2.transpose().try_inverse().is_some()); } #[test] #[rustfmt::skip] fn matrix5_try_inverse() { // Dimension 5 is chosen so that the inversion happens by Gaussian elimination. // (at the time of writing dimensions <= 3 are implemented as analytic formulas, but we choose // 5 in the case that 4 also gets an analytic implementation) let a = Matrix5::new(-2.0, 0.0, 2.0, 5.0, -5.0, -6.0, 4.0, 4.0, 13.0, -15.0, 4.0, 16.0, -14.0, -19.0, 12.0, 12.0, 12.0, -22.0, -35.0, 34.0, -8.0, 4.0, 12.0, 27.0, -31.0); let expected_inverse = Matrix5::new( 3.9333e+00, -1.5667e+00, 2.6667e-01, 6.6667e-02, 3.0000e-01, -1.2033e+01, 3.9667e+00, -1.1167e+00, 2.8333e-01, -1.0000e-01, -1.8233e+01, 5.7667e+00, -1.5667e+00, 2.3333e-01, -2.0000e-01, -4.3333e+00, 1.6667e+00, -6.6667e-01, 3.3333e-01, -4.6950e-19, -1.3400e+01, 4.6000e+00, -1.4000e+00, 4.0000e-01, -2.0000e-01); let a_inv = a.try_inverse().expect("Matrix is not invertible"); assert_relative_eq!(a_inv, expected_inverse, max_relative=1e-4); } #[test] fn matrix1_try_inverse_scaled_identity() { // A perfectly invertible matrix with // very small coefficients let a = Matrix1::new(1.0e-20); let expected_inverse = Matrix1::new(1.0e20); let a_inv = a.try_inverse().expect("Matrix should be invertible"); assert_relative_eq!(a_inv, expected_inverse); } #[test] #[rustfmt::skip] fn matrix2_try_inverse_scaled_identity() { // A perfectly invertible matrix with // very small coefficients let a = Matrix2::new(1.0e-20, 0.0, 0.0, 1.0e-20); let expected_inverse = Matrix2::new(1.0e20, 0.0, 0.0, 1.0e20); let a_inv = a.try_inverse().expect("Matrix should be invertible"); assert_relative_eq!(a_inv, expected_inverse); } #[test] #[rustfmt::skip] fn matrix3_try_inverse_scaled_identity() { // A perfectly invertible matrix with // very small coefficients let a = Matrix3::new(1.0e-20, 0.0, 0.0, 0.0, 1.0e-20, 0.0, 0.0, 0.0, 1.0e-20); let expected_inverse = Matrix3::new(1.0e20, 0.0, 0.0, 0.0, 1.0e20, 0.0, 0.0, 0.0, 1.0e20); let a_inv = a.try_inverse().expect("Matrix should be invertible"); assert_relative_eq!(a_inv, expected_inverse); } #[test] #[rustfmt::skip] fn matrix5_try_inverse_scaled_identity() { // A perfectly invertible matrix with // very small coefficients let a = Matrix5::new(1.0e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e-20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e-20); let expected_inverse = Matrix5::new(1.0e+20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e+20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e+20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e+20, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0e+20); let a_inv = a.try_inverse().expect("Matrix should be invertible"); assert_relative_eq!(a_inv, expected_inverse); } nalgebra-0.33.0/tests/linalg/lu.rs000064400000000000000000000132260072674642500151140ustar 00000000000000use na::Matrix3; #[test] #[rustfmt::skip] fn lu_simple() { let m = Matrix3::new( 2.0, -1.0, 0.0, -1.0, 2.0, -1.0, 0.0, -1.0, 2.0); let lu = m.lu(); assert_eq!(lu.determinant(), 4.0); let (p, l, u) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); assert!(relative_eq!(m, lu, epsilon = 1.0e-7)); } #[test] #[rustfmt::skip] fn lu_simple_with_pivot() { let m = Matrix3::new( 0.0, -1.0, 2.0, -1.0, 2.0, -1.0, 2.0, -1.0, 0.0); let lu = m.lu(); assert_eq!(lu.determinant(), -4.0); let (p, l, u) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); assert!(relative_eq!(m, lu, epsilon = 1.0e-7)); } #[cfg(feature = "proptest-support")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use na::{DMatrix, Matrix4x3, DVector, Vector4}; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn lu(m in dmatrix_($scalar)) { let lu = m.clone().lu(); let (p, l, u) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)) } #[test] fn lu_static_3_5(m in matrix3x5_($scalar)) { let lu = m.lu(); let (p, l, u) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)) } fn lu_static_5_3(m in matrix5x3_($scalar)) { let lu = m.lu(); let (p, l, u) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)); } #[test] fn lu_static_square(m in matrix4_($scalar)) { let lu = m.lu(); let (p, l, u) = lu.unpack(); let mut lu = l * u; p.inv_permute_rows(&mut lu); prop_assert!(relative_eq!(m, lu, epsilon = 1.0e-7)); } #[test] fn lu_solve(n in PROPTEST_MATRIX_DIM, nb in PROPTEST_MATRIX_DIM) { let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let lu = m.clone().lu(); let b1 = DVector::<$scalar_type>::new_random(n).map(|e| e.0); let b2 = DMatrix::<$scalar_type>::new_random(n, nb).map(|e| e.0); let sol1 = lu.solve(&b1); let sol2 = lu.solve(&b2); prop_assert!(sol1.is_none() || relative_eq!(&m * sol1.unwrap(), b1, epsilon = 1.0e-6)); prop_assert!(sol2.is_none() || relative_eq!(&m * sol2.unwrap(), b2, epsilon = 1.0e-6)); } #[test] fn lu_solve_static(m in matrix4_($scalar)) { let lu = m.lu(); let b1 = Vector4::<$scalar_type>::new_random().map(|e| e.0); let b2 = Matrix4x3::<$scalar_type>::new_random().map(|e| e.0); let sol1 = lu.solve(&b1); let sol2 = lu.solve(&b2); prop_assert!(sol1.is_none() || relative_eq!(&m * sol1.unwrap(), b1, epsilon = 1.0e-6)); prop_assert!(sol2.is_none() || relative_eq!(&m * sol2.unwrap(), b2, epsilon = 1.0e-6)); } #[test] fn lu_inverse(n in PROPTEST_MATRIX_DIM) { let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let mut l = m.lower_triangle(); let mut u = m.upper_triangle(); // Ensure the matrix is well conditioned for inversion. l.fill_diagonal(na::one()); u.fill_diagonal(na::one()); let m = l * u; let m1 = m.clone().lu().try_inverse().unwrap(); let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } #[test] fn lu_inverse_static(m in matrix4_($scalar)) { let lu = m.lu(); if let Some(m1) = lu.try_inverse() { let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); } nalgebra-0.33.0/tests/linalg/mod.rs000064400000000000000000000003640072674642500152520ustar 00000000000000mod balancing; mod bidiagonal; mod cholesky; mod col_piv_qr; mod convolution; mod eigen; mod exp; mod full_piv_lu; mod hessenberg; mod inverse; mod lu; mod pow; mod qr; mod schur; mod solve; mod svd; mod tridiagonal; mod udu; nalgebra-0.33.0/tests/linalg/pow.rs000064400000000000000000000034320072674642500152770ustar 00000000000000#[cfg(feature = "proptest-support")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use na::DMatrix; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use std::cmp; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn pow(n in PROPTEST_MATRIX_DIM, p in 0u32..=4) { let n = cmp::max(1, cmp::min(n, 10)); let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let m_pow = m.pow(p); let mut expected = m.clone(); expected.fill_with_identity(); for _ in 0..p { expected = &m * &expected; } prop_assert!(relative_eq!(m_pow, expected, epsilon = 1.0e-5)) } #[test] fn pow_static_square_4x4(m in matrix4_($scalar), p in 0u32..=4) { let mut expected = m.clone(); let m_pow = m.pow(p); expected.fill_with_identity(); for _ in 0..p { expected = &m * &expected; } prop_assert!(relative_eq!(m_pow, expected, epsilon = 1.0e-5)) } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); } nalgebra-0.33.0/tests/linalg/qr.rs000064400000000000000000000107440072674642500151200ustar 00000000000000#![cfg(feature = "proptest-support")] macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use na::{DMatrix, DVector, Matrix4x3, Vector4}; use std::cmp; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn qr(m in dmatrix_($scalar)) { let qr = m.clone().qr(); let q = qr.q(); let r = qr.r(); prop_assert!(relative_eq!(m, &q * r, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn qr_static_5_3(m in matrix5x3_($scalar)) { let qr = m.qr(); let q = qr.q(); let r = qr.r(); prop_assert!(relative_eq!(m, q * r, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn qr_static_3_5(m in matrix3x5_($scalar)) { let qr = m.qr(); let q = qr.q(); let r = qr.r(); prop_assert!(relative_eq!(m, q * r, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn qr_static_square(m in matrix4_($scalar)) { let qr = m.qr(); let q = qr.q(); let r = qr.r(); prop_assert!(relative_eq!(m, q * r, epsilon = 1.0e-7)); prop_assert!(q.is_orthogonal(1.0e-7)); } #[test] fn qr_solve(n in PROPTEST_MATRIX_DIM, nb in PROPTEST_MATRIX_DIM) { let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let qr = m.clone().qr(); let b1 = DVector::<$scalar_type>::new_random(n).map(|e| e.0); let b2 = DMatrix::<$scalar_type>::new_random(n, nb).map(|e| e.0); if qr.is_invertible() { let sol1 = qr.solve(&b1).unwrap(); let sol2 = qr.solve(&b2).unwrap(); prop_assert!(relative_eq!(&m * sol1, b1, epsilon = 1.0e-6)); prop_assert!(relative_eq!(&m * sol2, b2, epsilon = 1.0e-6)); } } #[test] fn qr_solve_static(m in matrix4_($scalar)) { let qr = m.qr(); let b1 = Vector4::<$scalar_type>::new_random().map(|e| e.0); let b2 = Matrix4x3::<$scalar_type>::new_random().map(|e| e.0); if qr.is_invertible() { let sol1 = qr.solve(&b1).unwrap(); let sol2 = qr.solve(&b2).unwrap(); prop_assert!(relative_eq!(m * sol1, b1, epsilon = 1.0e-6)); prop_assert!(relative_eq!(m * sol2, b2, epsilon = 1.0e-6)); } } #[test] fn qr_inverse(n in PROPTEST_MATRIX_DIM) { let n = cmp::max(1, cmp::min(n, 15)); // To avoid slowing down the test too much. let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); if let Some(m1) = m.clone().qr().try_inverse() { let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } } #[test] fn qr_inverse_static(m in matrix4_($scalar)) { let qr = m.qr(); if let Some(m1) = qr.try_inverse() { let id1 = &m * &m1; let id2 = &m1 * &m; prop_assert!(id1.is_identity(1.0e-5)); prop_assert!(id2.is_identity(1.0e-5)); } } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); nalgebra-0.33.0/tests/linalg/schur.rs000064400000000000000000000172330072674642500156220ustar 00000000000000use na::{DMatrix, Matrix3, Matrix4}; #[test] #[rustfmt::skip] fn schur_simpl_mat3() { let m = Matrix3::new(-2.0, -4.0, 2.0, -2.0, 1.0, 2.0, 4.0, 2.0, 5.0); let schur = m.schur(); let (vecs, vals) = schur.unpack(); assert!(relative_eq!(vecs * vals * vecs.transpose(), m, epsilon = 1.0e-7)); } #[cfg(feature = "proptest-support")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use na::DMatrix; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn schur(n in PROPTEST_MATRIX_DIM) { let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let (vecs, vals) = m.clone().schur().unpack(); prop_assert!(relative_eq!(&vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7)); } #[test] fn schur_static_mat2(m in matrix2_($scalar)) { let (vecs, vals) = m.clone().schur().unpack(); prop_assert!(relative_eq!(vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7)); } #[test] fn schur_static_mat3(m in matrix3_($scalar)) { let (vecs, vals) = m.clone().schur().unpack(); prop_assert!(relative_eq!(vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7)); } #[test] fn schur_static_mat4(m in matrix4_($scalar)) { let (vecs, vals) = m.clone().schur().unpack(); prop_assert!(relative_eq!(vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7)); } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); } #[test] #[rustfmt::skip] fn schur_static_mat4_fail() { let m = Matrix4::new( 33.32699857679677, 46.794945978960044, -20.792148817005838, 84.73945485997737, -53.04896234480401, -4.031523330630989, 19.022858300892366, -93.2258351951158, -94.61793793643038, -18.64216213611094, 88.32376703241675, -99.30169870309795, 90.62661897246733, 96.74200696130146, 34.7421322611369, 84.86773307198098); let (vecs, vals) = m.clone().schur().unpack(); assert!(relative_eq!(vecs * vals * vecs.transpose(), m, epsilon = 1.0e-7)) } #[test] #[rustfmt::skip] fn schur_static_mat4_fail2() { let m = Matrix4::new( 14.623586538485966, 7.646156622760756, -52.11923331576265, -97.50030223503413, 53.829398131426785, -33.40560799661168, 70.31168286972388, -81.25248138434173, 27.932377940728202, 82.94220150938, -35.5898884705951, 67.56447552434219, 55.66754906908682, -42.14328890569226, -20.684709585152206, -87.9456949841046); let (vecs, vals) = m.clone().schur().unpack(); assert!(relative_eq!(vecs * vals * vecs.transpose(), m, epsilon = 1.0e-7)) } #[test] #[rustfmt::skip] fn schur_static_mat3_fail() { let m = Matrix3::new( -21.58457553143394, -67.3881542667948, -14.619829849784338, -7.525423104386547, -17.827350599642287, 11.297377444555849, 38.080736654870464, -84.27428302131528, -95.88198590331922); let (vecs, vals) = m.clone().schur().unpack(); assert!(relative_eq!(vecs * vals * vecs.transpose(), m, epsilon = 1.0e-7)) } // Test proposed on the issue #176 of rulinalg. #[test] #[rustfmt::skip] fn schur_singular() { let m = DMatrix::from_row_slice(24, 24, &[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0]); let (vecs, vals) = m.clone().schur().unpack(); assert!(relative_eq!(&vecs * vals * vecs.transpose(), m, epsilon = 1.0e-7)) } nalgebra-0.33.0/tests/linalg/solve.rs000064400000000000000000000047260072674642500156310ustar 00000000000000#![cfg(feature = "proptest-support")] macro_rules! gen_tests( ($module: ident, $scalar: expr) => { mod $module { use na::{Matrix4, ComplexField}; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; fn unzero_diagonal(a: &mut Matrix4) { for i in 0..4 { if a[(i, i)].clone().norm1() < na::convert(1.0e-7) { a[(i, i)] = T::one(); } } } proptest! { #[test] fn solve_lower_triangular(a in matrix4_($scalar), b in matrix4x5_($scalar)) { let mut a = a; unzero_diagonal(&mut a); let tri = a.lower_triangle(); let x = a.solve_lower_triangular(&b).unwrap(); prop_assert!(relative_eq!(tri * x, b, epsilon = 1.0e-7)) } #[test] fn solve_upper_triangular(a in matrix4_($scalar), b in matrix4x5_($scalar)) { let mut a = a; unzero_diagonal(&mut a); let tri = a.upper_triangle(); let x = a.solve_upper_triangular(&b).unwrap(); prop_assert!(relative_eq!(tri * x, b, epsilon = 1.0e-7)) } #[test] fn tr_solve_lower_triangular(a in matrix4_($scalar), b in matrix4x5_($scalar)) { let mut a = a; unzero_diagonal(&mut a); let tri = a.lower_triangle(); let x = a.tr_solve_lower_triangular(&b).unwrap(); prop_assert!(relative_eq!(tri.transpose() * x, b, epsilon = 1.0e-7)) } #[test] fn tr_solve_upper_triangular(a in matrix4_($scalar), b in matrix4x5_($scalar)) { let mut a = a; unzero_diagonal(&mut a); let tri = a.upper_triangle(); let x = a.tr_solve_upper_triangular(&b).unwrap(); prop_assert!(relative_eq!(tri.transpose() * x, b, epsilon = 1.0e-7)) } } } } ); gen_tests!(complex, complex_f64()); gen_tests!(f64, PROPTEST_F64); nalgebra-0.33.0/tests/linalg/svd.rs000064400000000000000000000717320072674642500152760ustar 00000000000000use crate::utils::is_sorted_descending; use na::{DMatrix, Matrix6}; #[cfg(feature = "proptest-support")] mod proptest_tests { macro_rules! gen_tests( ($module: ident, $scalar: expr, $scalar_type: ty) => { mod $module { use na::{ DMatrix, DVector, Matrix2, Matrix3, Matrix4, ComplexField }; use std::cmp; #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; use crate::utils::is_sorted_descending; proptest! { #[test] fn svd(m in dmatrix_($scalar)) { let svd = m.clone().svd(true, true); let recomp_m = svd.clone().recompose().unwrap(); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = DMatrix::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(&u * ds * &v_t, recomp_m, epsilon = 1.0e-5)); prop_assert!(relative_eq!(m, recomp_m, epsilon = 1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_static_5_3(m in matrix5x3_($scalar)) { let svd = m.svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = Matrix3::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(m, &u * ds * &v_t, epsilon = 1.0e-5)); prop_assert!(u.is_orthogonal(1.0e-5)); prop_assert!(v_t.is_orthogonal(1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_static_5_2(m in matrix5x2_($scalar)) { let svd = m.svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = Matrix2::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(m, &u * ds * &v_t, epsilon = 1.0e-5)); prop_assert!(u.is_orthogonal(1.0e-5)); prop_assert!(v_t.is_orthogonal(1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_static_3_5(m in matrix3x5_($scalar)) { let svd = m.svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = Matrix3::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_static_2_5(m in matrix2x5_($scalar)) { let svd = m.svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = Matrix2::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_static_square(m in matrix4_($scalar)) { let svd = m.svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = Matrix4::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5)); prop_assert!(u.is_orthogonal(1.0e-5)); prop_assert!(v_t.is_orthogonal(1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_static_square_2x2(m in matrix2_($scalar)) { let svd = m.svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = Matrix2::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5)); prop_assert!(u.is_orthogonal(1.0e-5)); prop_assert!(v_t.is_orthogonal(1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_static_square_3x3(m in matrix3_($scalar)) { let svd = m.svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = Matrix3::from_diagonal(&s.map(|e| ComplexField::from_real(e))); prop_assert!(s.iter().all(|e| *e >= 0.0)); prop_assert!(relative_eq!(m, u * ds * v_t, epsilon = 1.0e-5)); prop_assert!(u.is_orthogonal(1.0e-5)); prop_assert!(v_t.is_orthogonal(1.0e-5)); prop_assert!(is_sorted_descending(s.as_slice())); } #[test] fn svd_pseudo_inverse(m in dmatrix_($scalar)) { let svd = m.clone().svd(true, true); let pinv = svd.pseudo_inverse(1.0e-10).unwrap(); if m.nrows() > m.ncols() { prop_assert!((pinv * m).is_identity(1.0e-5)) } else { prop_assert!((m * pinv).is_identity(1.0e-5)) } } #[test] fn svd_solve(n in PROPTEST_MATRIX_DIM, nb in PROPTEST_MATRIX_DIM) { let n = cmp::max(1, cmp::min(n, 10)); let nb = cmp::min(nb, 10); let m = DMatrix::<$scalar_type>::new_random(n, n).map(|e| e.0); let svd = m.clone().svd(true, true); if svd.rank(1.0e-7) == n { let b1 = DVector::<$scalar_type>::new_random(n).map(|e| e.0); let b2 = DMatrix::<$scalar_type>::new_random(n, nb).map(|e| e.0); let sol1 = svd.solve(&b1, 1.0e-7).unwrap(); let sol2 = svd.solve(&b2, 1.0e-7).unwrap(); let recomp = svd.recompose().unwrap(); prop_assert!(relative_eq!(m, recomp, epsilon = 1.0e-6)); prop_assert!(relative_eq!(&m * &sol1, b1, epsilon = 1.0e-6)); prop_assert!(relative_eq!(&m * &sol2, b2, epsilon = 1.0e-6)); } } #[test] fn svd_polar_decomposition(m in dmatrix_($scalar)) { let svd = m.clone().svd_unordered(true, true); let (p, u) = svd.to_polar().unwrap(); assert_relative_eq!(m, &p* &u, epsilon = 1.0e-5); // semi-unitary check assert!(u.is_orthogonal(1.0e-5) || u.transpose().is_orthogonal(1.0e-5)); // hermitian check assert_relative_eq!(p, p.adjoint(), epsilon = 1.0e-5); /* * Same thing, but using the method instead of calling the SVD explicitly. */ let (p2, u2) = m.clone().polar(); assert_eq!(p, p2); assert_eq!(u, u2); } } } } ); gen_tests!(complex, complex_f64(), RandComplex); gen_tests!(f64, PROPTEST_F64, RandScalar); } // Test proposed on the issue #176 of rulinalg. #[test] #[rustfmt::skip] fn svd_singular() { let m = DMatrix::from_row_slice(24, 24, &[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0]); let svd = m.clone().svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = DMatrix::from_diagonal(&s); assert!(s.iter().all(|e| *e >= 0.0)); assert!(is_sorted_descending(s.as_slice())); assert!(u.is_orthogonal(1.0e-5)); assert!(v_t.is_orthogonal(1.0e-5)); assert_relative_eq!(m, &u * ds * &v_t, epsilon = 1.0e-5); } // Same as the previous test but with one additional row. #[test] #[rustfmt::skip] fn svd_singular_vertical() { let m = DMatrix::from_row_slice(25, 24, &[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0]); let svd = m.clone().svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = DMatrix::from_diagonal(&s); assert!(s.iter().all(|e| *e >= 0.0)); assert!(is_sorted_descending(s.as_slice())); assert_relative_eq!(m, &u * ds * &v_t, epsilon = 1.0e-5); } // Same as the previous test but with one additional column. #[test] #[rustfmt::skip] fn svd_singular_horizontal() { let m = DMatrix::from_row_slice(24, 25, &[ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); let svd = m.clone().svd(true, true); let (u, s, v_t) = (svd.u.unwrap(), svd.singular_values, svd.v_t.unwrap()); let ds = DMatrix::from_diagonal(&s); assert!(s.iter().all(|e| *e >= 0.0)); assert!(is_sorted_descending(s.as_slice())); assert_relative_eq!(m, &u * ds * &v_t, epsilon = 1.0e-5); } #[test] fn svd_zeros() { let m = DMatrix::from_element(10, 10, 0.0); let svd = m.clone().svd(true, true); assert_eq!(Ok(m), svd.recompose()); } #[test] fn svd_identity() { let m = DMatrix::::identity(10, 10); let svd = m.clone().svd(true, true); assert_eq!(Ok(m), svd.recompose()); let m = DMatrix::::identity(10, 15); let svd = m.clone().svd(true, true); assert_eq!(Ok(m), svd.recompose()); let m = DMatrix::::identity(15, 10); let svd = m.clone().svd(true, true); assert_eq!(Ok(m), svd.recompose()); } #[test] #[rustfmt::skip] fn svd_with_delimited_subproblem() { let mut m = DMatrix::::from_element(10, 10, 0.0); m[(0, 0)] = 1.0; m[(0, 1)] = 2.0; m[(1, 1)] = 0.0; m[(1, 2)] = 3.0; m[(2, 2)] = 4.0; m[(2, 3)] = 5.0; m[(3, 3)] = 6.0; m[(3, 4)] = 0.0; m[(4, 4)] = 8.0; m[(3, 5)] = 9.0; m[(5, 5)] = 10.0; m[(3, 6)] = 11.0; m[(6, 6)] = 12.0; m[(3, 7)] = 12.0; m[(7, 7)] = 14.0; m[(3, 8)] = 13.0; m[(8, 8)] = 16.0; m[(3, 9)] = 17.0; m[(9, 9)] = 18.0; let svd = m.clone().svd(true, true); assert_relative_eq!(m, svd.recompose().unwrap(), epsilon = 1.0e-7); // Rectangular versions. let mut m = DMatrix::::from_element(15, 10, 0.0); m[(0, 0)] = 1.0; m[(0, 1)] = 2.0; m[(1, 1)] = 0.0; m[(1, 2)] = 3.0; m[(2, 2)] = 4.0; m[(2, 3)] = 5.0; m[(3, 3)] = 6.0; m[(3, 4)] = 0.0; m[(4, 4)] = 8.0; m[(3, 5)] = 9.0; m[(5, 5)] = 10.0; m[(3, 6)] = 11.0; m[(6, 6)] = 12.0; m[(3, 7)] = 12.0; m[(7, 7)] = 14.0; m[(3, 8)] = 13.0; m[(8, 8)] = 16.0; m[(3, 9)] = 17.0; m[(9, 9)] = 18.0; let svd = m.clone().svd(true, true); assert_relative_eq!(m, svd.recompose().unwrap(), epsilon = 1.0e-7); let svd = m.transpose().svd(true, true); assert_relative_eq!(m.transpose(), svd.recompose().unwrap(), epsilon = 1.0e-7); } #[test] #[rustfmt::skip] fn svd_fail() { let m = Matrix6::new( 0.9299319121545955, 0.9955870335651049, 0.8824725266413644, 0.28966880207132295, 0.06102723649846409, 0.9311880746048009, 0.5938395242304351, 0.8398522876024204, 0.06672831951963198, 0.9941213119963099, 0.9431846038057834, 0.8159885168706427, 0.9121962883152357, 0.6471119669367571, 0.4823309702814407, 0.6420516076705516, 0.7731203925207113, 0.7424069470756647, 0.07311092531259344, 0.5579247949052946, 0.14518764691585773, 0.03502980663114896, 0.7991329455957719, 0.4929930019965745, 0.12293810556077789, 0.6617084679545999, 0.9002240700227326, 0.027153062135304884, 0.3630189466989524, 0.18207502727558866, 0.843196731466686, 0.08951878746549924, 0.7533450877576973, 0.009558876499740077, 0.9429679490873482, 0.9355764454129878); // Check unordered ... let svd = m.clone().svd_unordered(true, true); let recomp = svd.recompose().unwrap(); assert_relative_eq!(m, recomp, epsilon = 1.0e-5); // ... and ordered SVD. let svd = m.clone().svd(true, true); let recomp = svd.recompose().unwrap(); assert_relative_eq!(m, recomp, epsilon = 1.0e-5); } #[test] #[rustfmt::skip] fn svd3_fail() { // NOTE: this matrix fails the special case done for 3x3 SVDs. // It was found on an actual application using SVD as part of the minimization of a // quadratic error function. let m = nalgebra::matrix![ 0.0, 1.0, 0.0; 0.0, 1.7320508075688772, 0.0; 0.0, 0.0, 0.0 ]; // Check unordered ... let svd = m.svd_unordered(true, true); let recomp = svd.recompose().unwrap(); assert_relative_eq!(m, recomp, epsilon = 1.0e-5); // ... and ordered SVD. let svd = m.svd(true, true); let recomp = svd.recompose().unwrap(); assert_relative_eq!(m, recomp, epsilon = 1.0e-5); } #[test] fn svd_err() { let m = DMatrix::from_element(10, 10, 0.0); let svd = m.clone().svd(false, false); assert_eq!( Err("SVD recomposition: U and V^t have not been computed."), svd.clone().recompose() ); assert_eq!( Err("SVD pseudo inverse: the epsilon must be non-negative."), svd.clone().pseudo_inverse(-1.0) ); } #[test] #[rustfmt::skip] fn svd_sorted() { let reference = nalgebra::matrix![ 1.0, 2.0, 3.0, 4.0; 5.0, 6.0, 7.0, 8.0; 9.0, 10.0, 11.0, 12.0 ]; let mut svd = nalgebra::SVD { singular_values: nalgebra::matrix![1.72261225; 2.54368356e+01; 5.14037515e-16], u: Some(nalgebra::matrix![ -0.88915331, -0.20673589, 0.40824829; -0.25438183, -0.51828874, -0.81649658; 0.38038964, -0.82984158, 0.40824829 ]), v_t: Some(nalgebra::matrix![ 0.73286619, 0.28984978, -0.15316664, -0.59618305; -0.40361757, -0.46474413, -0.52587069, -0.58699725; 0.44527162, -0.83143156, 0.32704826, 0.05911168 ]), }; assert_relative_eq!( svd.recompose().expect("valid SVD"), reference, epsilon = 1.0e-5 ); svd.sort_by_singular_values(); // Ensure successful sorting assert_relative_eq!(svd.singular_values.x, 2.54368356e+01, epsilon = 1.0e-5); // Ensure that the sorted components represent the same decomposition assert_relative_eq!( svd.recompose().expect("valid SVD"), reference, epsilon = 1.0e-5 ); } #[test] // Exercises bug reported in issue #983 of nalgebra (https://github.com/dimforge/nalgebra/issues/983) fn svd_regression_issue_983() { let m = nalgebra::dmatrix![ 10.74785316637712f64, -5.994983325167452, -6.064492921857296; -4.149751381521569, 20.654504205822462, -4.470436210703133; -22.772715014220207, -1.4554372570788008, 18.108113992170573 ] .transpose(); let svd1 = m.clone().svd(true, true); let svd2 = m.clone().svd(false, true); let svd3 = m.clone().svd(true, false); let svd4 = m.svd(false, false); assert_relative_eq!(svd1.singular_values, svd2.singular_values, epsilon = 1e-9); assert_relative_eq!(svd1.singular_values, svd3.singular_values, epsilon = 1e-9); assert_relative_eq!(svd1.singular_values, svd4.singular_values, epsilon = 1e-9); assert_relative_eq!( svd1.singular_values, nalgebra::dvector![3.16188022e+01, 2.23811978e+01, 0.], epsilon = 1e-6 ); } #[test] // Exercises bug reported in issue #1072 of nalgebra (https://github.com/dimforge/nalgebra/issues/1072) fn svd_regression_issue_1072() { let x = nalgebra::dmatrix![-6.206610118536945f64, -3.67612186839874; -1.2755730783423473, 6.047238193479124]; let mut x_svd = x.svd(true, true); x_svd.singular_values = nalgebra::dvector![1.0, 0.0]; let y = x_svd.recompose().unwrap(); let y_svd = y.svd(true, true); assert_relative_eq!( y_svd.singular_values, nalgebra::dvector![1.0, 0.0], epsilon = 1e-9 ); } #[test] // Exercises bug reported in issue #1313 of nalgebra (https://github.com/dimforge/nalgebra/issues/1313) fn svd_regression_issue_1313() { let s = 6.123234e-16_f32; let m = nalgebra::dmatrix![ 10.0, 0.0, 0.0, 0.0, -10.0, 0.0, 0.0, 0.0; s, 10.0, 0.0, 10.0, s, 0.0, 0.0, 0.0; 20.0, -20.0, 0.0, 20.0, 20.0, 0.0, 0.0, 0.0; ]; let svd = m.clone().svd(true, true); let m2 = svd.recompose().unwrap(); assert_relative_eq!(&m, &m2, epsilon = 1e-5); } nalgebra-0.33.0/tests/linalg/tridiagonal.rs000064400000000000000000000042400072674642500167650ustar 00000000000000#![cfg(feature = "proptest-support")] macro_rules! gen_tests( ($module: ident, $scalar: expr) => { mod $module { #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn symm_tridiagonal(m in dmatrix_($scalar)) { let m = &m * m.adjoint(); let tri = m.clone().symmetric_tridiagonalize(); let recomp = tri.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-7)); } #[test] fn symm_tridiagonal_singular(m in dmatrix_($scalar)) { let mut m = &m * m.adjoint(); let n = m.nrows(); m.row_mut(n / 2).fill(na::zero()); m.column_mut(n / 2).fill(na::zero()); let tri = m.clone().symmetric_tridiagonalize(); let recomp = tri.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-7)); } #[test] fn symm_tridiagonal_static_square(m in matrix4_($scalar)) { let m = m.hermitian_part(); let tri = m.symmetric_tridiagonalize(); let recomp = tri.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-7)); } #[test] fn symm_tridiagonal_static_square_2x2(m in matrix2_($scalar)) { let m = m.hermitian_part(); let tri = m.symmetric_tridiagonalize(); let recomp = tri.recompose(); prop_assert!(relative_eq!(m.lower_triangle(), recomp.lower_triangle(), epsilon = 1.0e-7)); } } } } ); gen_tests!(complex, complex_f64()); gen_tests!(f64, PROPTEST_F64); nalgebra-0.33.0/tests/linalg/udu.rs000064400000000000000000000041630072674642500152710ustar 00000000000000use na::Matrix3; #[test] #[rustfmt::skip] fn udu_simple() { let m = Matrix3::new( 2.0, -1.0, 0.0, -1.0, 2.0, -1.0, 0.0, -1.0, 2.0); let udu = m.udu().unwrap(); // Rebuild let p = udu.u * udu.d_matrix() * udu.u.transpose(); assert!(relative_eq!(m, p, epsilon = 3.0e-16)); } #[test] #[should_panic] #[rustfmt::skip] fn udu_non_sym_panic() { let m = Matrix3::new( 2.0, -1.0, 0.0, 1.0, -2.0, 3.0, -2.0, 1.0, 0.3); let udu = m.udu().unwrap(); // Rebuild let p = udu.u * udu.d_matrix() * udu.u.transpose(); assert!(relative_eq!(m, p, epsilon = 3.0e-16)); } #[cfg(feature = "proptest-support")] mod proptest_tests { #[allow(unused_imports)] use crate::core::helper::{RandComplex, RandScalar}; macro_rules! gen_tests( ($module: ident, $scalar: expr) => { mod $module { #[allow(unused_imports)] use crate::core::helper::{RandScalar, RandComplex}; use crate::proptest::*; use proptest::{prop_assert, proptest}; proptest! { #[test] fn udu(m in dmatrix_($scalar)) { let m = &m * m.adjoint(); if let Some(udu) = m.clone().udu() { let p = &udu.u * &udu.d_matrix() * &udu.u.transpose(); println!("m: {}, p: {}", m, p); prop_assert!(relative_eq!(m, p, epsilon = 1.0e-7)); } } #[test] fn udu_static(m in matrix4_($scalar)) { let m = m.hermitian_part(); if let Some(udu) = m.udu() { let p = udu.u * udu.d_matrix() * udu.u.transpose(); prop_assert!(relative_eq!(m, p, epsilon = 1.0e-7)); } } } } } ); gen_tests!(f64, PROPTEST_F64); } nalgebra-0.33.0/tests/macros/matrix.rs000064400000000000000000000321750072674642500160220ustar 00000000000000use crate::macros::assert_eq_and_type; use nalgebra::{ DMatrix, DVector, Matrix1x2, Matrix1x3, Matrix1x4, Matrix2, Matrix2x1, Matrix2x3, Matrix2x4, Matrix3, Matrix3x1, Matrix3x2, Matrix3x4, Matrix4, Matrix4x1, Matrix4x2, Matrix4x3, Point, Point1, Point2, Point3, Point4, Point5, Point6, SMatrix, SVector, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6, }; use nalgebra_macros::{dmatrix, dvector, matrix, point, vector}; // Skip rustfmt because it just makes the test bloated without making it more readable #[rustfmt::skip] #[test] fn matrix_small_dims_exhaustive() { // 0x0 assert_eq_and_type!(matrix![], SMatrix::::zeros()); // 1xN assert_eq_and_type!(matrix![1], SMatrix::::new(1)); assert_eq_and_type!(matrix![1, 2], Matrix1x2::new(1, 2)); assert_eq_and_type!(matrix![1, 2, 3], Matrix1x3::new(1, 2, 3)); assert_eq_and_type!(matrix![1, 2, 3, 4], Matrix1x4::new(1, 2, 3, 4)); // 2xN assert_eq_and_type!(matrix![1; 2], Matrix2x1::new(1, 2)); assert_eq_and_type!(matrix![1, 2; 3, 4], Matrix2::new(1, 2, 3, 4)); assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6], Matrix2x3::new(1, 2, 3, 4, 5, 6)); assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8], Matrix2x4::new(1, 2, 3, 4, 5, 6, 7, 8)); // 3xN assert_eq_and_type!(matrix![1; 2; 3], Matrix3x1::new(1, 2, 3)); assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6], Matrix3x2::new(1, 2, 3, 4, 5, 6)); assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9], Matrix3::new(1, 2, 3, 4, 5, 6, 7, 8, 9)); assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12], Matrix3x4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); // 4xN assert_eq_and_type!(matrix![1; 2; 3; 4], Matrix4x1::new(1, 2, 3, 4)); assert_eq_and_type!(matrix![1, 2; 3, 4; 5, 6; 7, 8], Matrix4x2::new(1, 2, 3, 4, 5, 6, 7, 8)); assert_eq_and_type!(matrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12], Matrix4x3::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); assert_eq_and_type!(matrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16], Matrix4::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); } #[test] fn matrix_const_fn() { // Ensure that matrix! can be used in const contexts const _: SMatrix = matrix![]; const _: SMatrix = matrix![1, 2]; const _: SMatrix = matrix![1, 2, 3; 4, 5, 6]; } // Skip rustfmt because it just makes the test bloated without making it more readable #[rustfmt::skip] #[test] fn dmatrix_small_dims_exhaustive() { // 0x0 assert_eq_and_type!(dmatrix![], DMatrix::::zeros(0, 0)); // 1xN assert_eq_and_type!(dmatrix![1], DMatrix::from_row_slice(1, 1, &[1])); assert_eq_and_type!(dmatrix![1, 2], DMatrix::from_row_slice(1, 2, &[1, 2])); assert_eq_and_type!(dmatrix![1, 2, 3], DMatrix::from_row_slice(1, 3, &[1, 2, 3])); assert_eq_and_type!(dmatrix![1, 2, 3, 4], DMatrix::from_row_slice(1, 4, &[1, 2, 3, 4])); // 2xN assert_eq_and_type!(dmatrix![1; 2], DMatrix::from_row_slice(2, 1, &[1, 2])); assert_eq_and_type!(dmatrix![1, 2; 3, 4], DMatrix::from_row_slice(2, 2, &[1, 2, 3, 4])); assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6], DMatrix::from_row_slice(2, 3, &[1, 2, 3, 4, 5, 6])); assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8], DMatrix::from_row_slice(2, 4, &[1, 2, 3, 4, 5, 6, 7, 8])); // 3xN assert_eq_and_type!(dmatrix![1; 2; 3], DMatrix::from_row_slice(3, 1, &[1, 2, 3])); assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6], DMatrix::from_row_slice(3, 2, &[1, 2, 3, 4, 5, 6])); assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9], DMatrix::from_row_slice(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9])); assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12], DMatrix::from_row_slice(3, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])); // 4xN assert_eq_and_type!(dmatrix![1; 2; 3; 4], DMatrix::from_row_slice(4, 1, &[1, 2, 3, 4])); assert_eq_and_type!(dmatrix![1, 2; 3, 4; 5, 6; 7, 8], DMatrix::from_row_slice(4, 2, &[1, 2, 3, 4, 5, 6, 7, 8])); assert_eq_and_type!(dmatrix![1, 2, 3; 4, 5, 6; 7, 8, 9; 10, 11, 12], DMatrix::from_row_slice(4, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])); assert_eq_and_type!(dmatrix![1, 2, 3, 4; 5, 6, 7, 8; 9, 10, 11, 12; 13, 14, 15, 16], DMatrix::from_row_slice(4, 4, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])); } #[test] fn matrix_trailing_semi() { matrix![1, 2;]; dmatrix![1, 2;]; } // Skip rustfmt because it just makes the test bloated without making it more readable #[rustfmt::skip] #[test] fn vector_small_dims_exhaustive() { assert_eq_and_type!(vector![], SVector::::zeros()); assert_eq_and_type!(vector![1], Vector1::::new(1)); assert_eq_and_type!(vector![1, 2], Vector2::new(1, 2)); assert_eq_and_type!(vector![1, 2, 3], Vector3::new(1, 2, 3)); assert_eq_and_type!(vector![1, 2, 3, 4], Vector4::new(1, 2, 3, 4)); assert_eq_and_type!(vector![1, 2, 3, 4, 5], Vector5::new(1, 2, 3, 4, 5)); assert_eq_and_type!(vector![1, 2, 3, 4, 5, 6], Vector6::new(1, 2, 3, 4, 5, 6)); } // Skip rustfmt because it just makes the test bloated without making it more readable #[rustfmt::skip] #[test] fn point_small_dims_exhaustive() { assert_eq_and_type!(point![], Point::::origin()); assert_eq_and_type!(point![1], Point1::::new(1)); assert_eq_and_type!(point![1, 2], Point2::new(1, 2)); assert_eq_and_type!(point![1, 2, 3], Point3::new(1, 2, 3)); assert_eq_and_type!(point![1, 2, 3, 4], Point4::new(1, 2, 3, 4)); assert_eq_and_type!(point![1, 2, 3, 4, 5], Point5::new(1, 2, 3, 4, 5)); assert_eq_and_type!(point![1, 2, 3, 4, 5, 6], Point6::new(1, 2, 3, 4, 5, 6)); } #[test] fn vector_const_fn() { // Ensure that vector! can be used in const contexts const _: SVector = vector![]; const _: Vector1 = vector![1]; const _: Vector2 = vector![1, 2]; const _: Vector6 = vector![1, 2, 3, 4, 5, 6]; } #[test] fn point_const_fn() { // Ensure that vector! can be used in const contexts const _: Point = point![]; const _: Point1 = point![1]; const _: Point2 = point![1, 2]; const _: Point6 = point![1, 2, 3, 4, 5, 6]; } // Skip rustfmt because it just makes the test bloated without making it more readable #[rustfmt::skip] #[test] fn dvector_small_dims_exhaustive() { assert_eq_and_type!(dvector![], DVector::::zeros(0)); assert_eq_and_type!(dvector![1], DVector::from_column_slice(&[1])); assert_eq_and_type!(dvector![1, 2], DVector::from_column_slice(&[1, 2])); assert_eq_and_type!(dvector![1, 2, 3], DVector::from_column_slice(&[1, 2, 3])); assert_eq_and_type!(dvector![1, 2, 3, 4], DVector::from_column_slice(&[1, 2, 3, 4])); assert_eq_and_type!(dvector![1, 2, 3, 4, 5], DVector::from_column_slice(&[1, 2, 3, 4, 5])); assert_eq_and_type!(dvector![1, 2, 3, 4, 5, 6], DVector::from_column_slice(&[1, 2, 3, 4, 5, 6])); } #[test] fn vector_trailing_comma() { vector![1, 2,]; point![1, 2,]; dvector![1, 2,]; } #[test] fn matrix_trybuild_tests() { let t = trybuild::TestCases::new(); // Verify error message when we give a matrix with mismatched dimensions t.compile_fail("tests/macros/trybuild/matrix_mismatched_dimensions.rs"); } #[test] fn dmatrix_trybuild_tests() { let t = trybuild::TestCases::new(); // Verify error message when we give a matrix with mismatched dimensions t.compile_fail("tests/macros/trybuild/dmatrix_mismatched_dimensions.rs"); } #[test] fn matrix_builtin_types() { // Check that matrix! compiles for all built-in types const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0, 1; 2, 3]; const _: SMatrix = matrix![0.0, 1.0; 2.0, 3.0]; const _: SMatrix = matrix![0.0, 1.0; 2.0, 3.0]; } #[test] fn vector_builtin_types() { // Check that vector! compiles for all built-in types const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0, 1, 2, 3]; const _: SVector = vector![0.0, 1.0, 2.0, 3.0]; const _: SVector = vector![0.0, 1.0, 2.0, 3.0]; } #[test] fn dmatrix_builtin_types() { // Check that dmatrix! compiles for all built-in types let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0, 1; 2, 3]; let _: DMatrix = dmatrix![0.0, 1.0; 2.0, 3.0]; let _: DMatrix = dmatrix![0.0, 1.0; 2.0, 3.0]; } #[test] fn point_builtin_types() { // Check that point! compiles for all built-in types const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0, 1, 2, 3]; const _: Point = point![0.0, 1.0, 2.0, 3.0]; const _: Point = point![0.0, 1.0, 2.0, 3.0]; } #[test] fn dvector_builtin_types() { // Check that dvector! compiles for all built-in types let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0, 1, 2, 3]; let _: DVector = dvector![0.0, 1.0, 2.0, 3.0]; let _: DVector = dvector![0.0, 1.0, 2.0, 3.0]; } /// Black box function that's just used for testing macros with function call expressions. fn f(x: T) -> T { x } #[rustfmt::skip] #[test] fn matrix_arbitrary_expressions() { // Test that matrix! supports arbitrary expressions for its elements let a = matrix![1 + 2 , 2 * 3; 4 * f(5 + 6), 7 - 8 * 9]; let a_expected = Matrix2::new(1 + 2 , 2 * 3, 4 * f(5 + 6), 7 - 8 * 9); assert_eq_and_type!(a, a_expected); } #[rustfmt::skip] #[test] fn dmatrix_arbitrary_expressions() { // Test that dmatrix! supports arbitrary expressions for its elements let a = dmatrix![1 + 2 , 2 * 3; 4 * f(5 + 6), 7 - 8 * 9]; let a_expected = DMatrix::from_row_slice(2, 2, &[1 + 2 , 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]); assert_eq_and_type!(a, a_expected); } #[rustfmt::skip] #[test] fn vector_arbitrary_expressions() { // Test that vector! supports arbitrary expressions for its elements let a = vector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]; let a_expected = Vector4::new(1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9); assert_eq_and_type!(a, a_expected); } #[rustfmt::skip] #[test] fn point_arbitrary_expressions() { // Test that point! supports arbitrary expressions for its elements let a = point![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]; let a_expected = Point4::new(1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9); assert_eq_and_type!(a, a_expected); } #[rustfmt::skip] #[test] fn dvector_arbitrary_expressions() { // Test that dvector! supports arbitrary expressions for its elements let a = dvector![1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]; let a_expected = DVector::from_column_slice(&[1 + 2, 2 * 3, 4 * f(5 + 6), 7 - 8 * 9]); assert_eq_and_type!(a, a_expected); } nalgebra-0.33.0/tests/macros/mod.rs000064400000000000000000000011740072674642500152700ustar 00000000000000mod matrix; mod stack; /// Wrapper for `assert_eq` that also asserts that the types are the same // For some reason, rustfmt totally messes up the formatting of this macro. // For now we skip, but once https://github.com/rust-lang/rustfmt/issues/6131 // is fixed, we can perhaps remove the skip attribute #[rustfmt::skip] macro_rules! assert_eq_and_type { ($left:expr, $right:expr $(,)?) => { { fn check_statically_same_type(_: &T, _: &T) {} check_statically_same_type(&$left, &$right); } assert_eq!($left, $right); }; } pub(crate) use assert_eq_and_type; nalgebra-0.33.0/tests/macros/stack.rs000064400000000000000000000327710072674642500156250ustar 00000000000000use crate::macros::assert_eq_and_type; use cool_asserts::assert_panics; use na::VecStorage; use nalgebra::dimension::U1; use nalgebra::{dmatrix, matrix, stack}; use nalgebra::{ DMatrix, DMatrixView, Dyn, Matrix, Matrix2, Matrix4, OMatrix, SMatrix, SMatrixView, SMatrixViewMut, Scalar, U2, }; use nalgebra_macros::vector; use num_traits::Zero; /// Simple implementation that stacks dynamic matrices. /// /// Used for verifying results of the stack! macro. `None` entries are considered to represent /// a zero block. fn stack_dyn(blocks: DMatrix>>) -> DMatrix { let row_counts: Vec = blocks .row_iter() .map(|block_row| { block_row .iter() .map(|block_or_implicit_zero| { block_or_implicit_zero.as_ref().map(|block| block.nrows()) }) .reduce(|nrows1, nrows2| match (nrows1, nrows2) { (Some(_), None) => nrows1, (None, Some(_)) => nrows2, (None, None) => None, (Some(nrows1), Some(nrows2)) if nrows1 == nrows2 => Some(nrows1), _ => panic!("Number of rows must be consistent in each block row"), }) .unwrap_or(Some(0)) .expect("Each block row must have at least one entry which is not a zero literal") }) .collect(); let col_counts: Vec = blocks .column_iter() .map(|block_col| { block_col .iter() .map(|block_or_implicit_zero| { block_or_implicit_zero.as_ref().map(|block| block.ncols()) }) .reduce(|ncols1, ncols2| match (ncols1, ncols2) { (Some(_), None) => ncols1, (None, Some(_)) => ncols2, (None, None) => None, (Some(ncols1), Some(ncols2)) if ncols1 == ncols2 => Some(ncols1), _ => panic!("Number of columns must be consistent in each block column"), }) .unwrap_or(Some(0)) .expect( "Each block column must have at least one entry which is not a zero literal", ) }) .collect(); let nrows_total = row_counts.iter().sum(); let ncols_total = col_counts.iter().sum(); let mut output = DMatrix::zeros(nrows_total, ncols_total); let mut col_offset = 0; for j in 0..blocks.ncols() { let mut row_offset = 0; for i in 0..blocks.nrows() { if let Some(input_ij) = &blocks[(i, j)] { let (block_nrows, block_ncols) = input_ij.shape(); output .view_mut((row_offset, col_offset), (block_nrows, block_ncols)) .copy_from(&input_ij); } row_offset += row_counts[i]; } col_offset += col_counts[j]; } output } macro_rules! stack_dyn_convert_to_dmatrix_option { (0) => { None }; ($entry:expr) => { Some($entry.as_view::().clone_owned()) }; } /// Helper macro that compares the result of stack! with a simplified implementation that /// works only with heap-allocated data. /// /// This implementation is essentially radically different to the implementation in stack!, /// so if they both match, then it's a good sign that the stack! impl is correct. macro_rules! verify_stack { ($matrix_type:ty ; [$($($entry:expr),*);*]) => { { // Our input has the same syntax as the stack! macro (and matrix! macro, for that matter) let stack_result: $matrix_type = stack![$($($entry),*);*]; // Use the dmatrix! macro to nest matrices into each other let dyn_result = stack_dyn( dmatrix![$($(stack_dyn_convert_to_dmatrix_option!($entry)),*);*] ); // println!("{}", stack_result); // println!("{}", dyn_result); assert_eq!(stack_result, dyn_result); } } } #[test] fn stack_simple() { let m = stack![ Matrix2::::identity(), 0; 0, &Matrix2::identity(); ]; assert_eq_and_type!(m, Matrix4::identity()); } #[test] fn stack_diag() { let m = stack![ 0, matrix![1, 2; 3, 4;]; matrix![5, 6; 7, 8;], 0; ]; let res = matrix![ 0, 0, 1, 2; 0, 0, 3, 4; 5, 6, 0, 0; 7, 8, 0, 0; ]; assert_eq_and_type!(m, res); } #[test] fn stack_dynamic() { let m = stack![ matrix![ 1, 2; 3, 4; ], 0; 0, dmatrix![7, 8, 9; 10, 11, 12; ]; ]; let res = dmatrix![ 1, 2, 0, 0, 0; 3, 4, 0, 0, 0; 0, 0, 7, 8, 9; 0, 0, 10, 11, 12; ]; assert_eq_and_type!(m, res); } #[test] fn stack_nested() { let m = stack![ stack![ matrix![1, 2; 3, 4;]; matrix![5, 6;]], stack![ matrix![7;9;10;], matrix![11; 12; 13;] ]; ]; let res = matrix![ 1, 2, 7, 11; 3, 4, 9, 12; 5, 6, 10, 13; ]; assert_eq_and_type!(m, res); } #[test] fn stack_single() { let a = matrix![1, 2; 3, 4]; let b = stack![a]; assert_eq_and_type!(a, b); } #[test] fn stack_single_row() { let a = matrix![1, 2; 3, 4]; let m = stack![a, a]; let res = matrix![ 1, 2, 1, 2; 3, 4, 3, 4; ]; assert_eq_and_type!(m, res); } #[test] fn stack_single_col() { let a = matrix![1, 2; 3, 4]; let m = stack![a; a]; let res = matrix![ 1, 2; 3, 4; 1, 2; 3, 4; ]; assert_eq_and_type!(m, res); } #[test] #[rustfmt::skip] fn stack_expr() { let a = matrix![1, 2; 3, 4]; let b = matrix![5, 6; 7, 8]; let m = stack![a + b; 2i32 * b - a]; let res = matrix![ 6, 8; 10, 12; 9, 10; 11, 12; ]; assert_eq_and_type!(m, res); } #[test] fn stack_edge_cases() { { // Empty stack should return zero matrix with specified type let _: SMatrix = stack![]; let _: SMatrix = stack![]; } { // Case suggested by @tpdickso: https://github.com/dimforge/nalgebra/pull/1080#discussion_r1435871752 let a = matrix![1, 2; 3, 4]; let b = DMatrix::from_data(VecStorage::new(Dyn(2), Dyn(0), vec![])); assert_eq!( stack![a, 0; 0, b], matrix![1, 2; 3, 4; 0, 0; 0, 0] ); } } #[rustfmt::skip] #[test] fn stack_many_tests() { // s prefix means static, d prefix means dynamic // Static matrices let s_0x0: SMatrix = matrix![]; let s_0x1: SMatrix = Matrix::default(); let s_1x0: SMatrix = Matrix::default(); let s_1x1: SMatrix = matrix![1]; let s_2x2: SMatrix = matrix![6, 7; 8, 9]; let s_2x3: SMatrix = matrix![16, 17, 18; 19, 20, 21]; let s_3x3: SMatrix = matrix![28, 29, 30; 31, 32, 33; 34, 35, 36]; // Dynamic matrices let d_0x0: DMatrix = dmatrix![]; let d_1x2: DMatrix = dmatrix![9, 10]; let d_2x2: DMatrix = dmatrix![5, 6; 7, 8]; let d_4x4: DMatrix = dmatrix![10, 11, 12, 13; 14, 15, 16, 17; 18, 19, 20, 21; 22, 23, 24, 25]; // Check for weirdness with matrices that have zero row/cols verify_stack!(SMatrix<_, 0, 0>; [s_0x0]); verify_stack!(SMatrix<_, 0, 1>; [s_0x1]); verify_stack!(SMatrix<_, 1, 0>; [s_1x0]); verify_stack!(SMatrix<_, 0, 0>; [s_0x0; s_0x0]); verify_stack!(SMatrix<_, 0, 0>; [s_0x0, s_0x0; s_0x0, s_0x0]); verify_stack!(SMatrix<_, 0, 2>; [s_0x1, s_0x1]); verify_stack!(SMatrix<_, 2, 0>; [s_1x0; s_1x0]); verify_stack!(SMatrix<_, 1, 0>; [s_1x0, s_1x0]); verify_stack!(DMatrix<_>; [d_0x0]); // Horizontal stacking verify_stack!(SMatrix<_, 1, 2>; [s_1x1, s_1x1]); verify_stack!(SMatrix<_, 2, 4>; [s_2x2, s_2x2]); verify_stack!(DMatrix<_>; [d_1x2, d_1x2]); // Vertical stacking verify_stack!(SMatrix<_, 2, 1>; [s_1x1; s_1x1]); verify_stack!(SMatrix<_, 4, 2>; [s_2x2; s_2x2]); verify_stack!(DMatrix<_>; [d_2x2; d_2x2]); // Mix static and dynamic matrices verify_stack!(OMatrix<_, U2, Dyn>; [s_2x2, d_2x2]); verify_stack!(OMatrix<_, Dyn, U2>; [s_2x2; d_1x2]); // Stack more than two matrices verify_stack!(SMatrix<_, 1, 3>; [s_1x1, s_1x1, s_1x1]); verify_stack!(DMatrix<_>; [d_1x2, d_1x2, d_1x2]); // Slightly larger dims verify_stack!(SMatrix<_, 3, 6>; [s_3x3, s_3x3]); verify_stack!(DMatrix<_>; [d_4x4; d_4x4]); verify_stack!(SMatrix<_, 4, 7>; [s_2x2, s_2x3, d_2x2; d_2x2, s_2x3, s_2x2]); // Mix of references and owned verify_stack!(OMatrix<_, Dyn, U2>; [&s_2x2; &d_1x2]); verify_stack!(SMatrix<_, 4, 7>; [ s_2x2, &s_2x3, d_2x2; &d_2x2, s_2x3, &s_2x2]); // Views let s_2x2_v: SMatrixView<_, 2, 2> = s_2x2.as_view(); let s_2x3_v: SMatrixView<_, 2, 3> = s_2x3.as_view(); let d_2x2_v: DMatrixView<_> = d_2x2.as_view(); let mut s_2x2_vm = s_2x2.clone(); let s_2x2_vm: SMatrixViewMut<_, 2, 2> = s_2x2_vm.as_view_mut(); let mut s_2x3_vm = s_2x3.clone(); let s_2x3_vm: SMatrixViewMut<_, 2, 3> = s_2x3_vm.as_view_mut(); verify_stack!(SMatrix<_, 4, 7>; [ s_2x2_vm, &s_2x3_vm, d_2x2_v; &d_2x2_v, s_2x3_v, &s_2x2_v]); // Expressions let matrix_fn = |matrix: &DMatrix<_>| matrix.map(|x_ij| x_ij * 3); verify_stack!(SMatrix<_, 2, 5>; [ 2 * s_2x2 - 3 * &d_2x2, s_2x3 + 2 * s_2x3]); verify_stack!(DMatrix<_>; [ 2 * matrix_fn(&d_2x2) ]); verify_stack!(SMatrix<_, 2, 5>; [ (|matrix| 4 * matrix)(s_2x2), s_2x3 ]); } #[test] fn stack_trybuild_tests() { let t = trybuild::TestCases::new(); // Verify error message when a row or column only contains a zero entry t.compile_fail("tests/macros/trybuild/stack_empty_row.rs"); t.compile_fail("tests/macros/trybuild/stack_empty_col.rs"); t.compile_fail("tests/macros/trybuild/stack_incompatible_block_dimensions.rs"); t.compile_fail("tests/macros/trybuild/stack_incompatible_block_dimensions2.rs"); } #[test] fn stack_mismatched_dimensions_runtime_panics() { // s prefix denotes static, d dynamic let s_2x2 = matrix![1, 2; 3, 4]; let d_2x3 = dmatrix![5, 6, 7; 8, 9, 10]; let d_1x2 = dmatrix![11, 12]; let d_1x3 = dmatrix![13, 14, 15]; assert_panics!( stack![s_2x2, d_1x2], includes("All blocks in block row 0 must have the same number of rows") ); assert_panics!( stack![s_2x2; d_2x3], includes("All blocks in block column 0 must have the same number of columns") ); assert_panics!( stack![s_2x2, s_2x2; d_1x2, d_2x3], includes("All blocks in block row 1 must have the same number of rows") ); assert_panics!( stack![s_2x2, s_2x2; d_1x2, d_1x3], includes("All blocks in block column 1 must have the same number of columns") ); assert_panics!( { // Edge case suggested by @tpdickso: https://github.com/dimforge/nalgebra/pull/1080#discussion_r1435871752 let d_3x0 = DMatrix::from_data(VecStorage::new(Dyn(3), Dyn(0), Vec::::new())); stack![s_2x2, d_3x0] }, includes("All blocks in block row 0 must have the same number of rows") ); } #[test] fn stack_test_builtin_types() { // Other than T: Zero, there's nothing type-specific in the logic for stack! // These tests are just sanity tests, to make sure it works with the common built-in types let a = matrix![1, 2; 3, 4]; let b = vector![5, 6]; let c = matrix![7, 8]; let expected = matrix![ 1, 2, 5; 3, 4, 6; 7, 8, 0 ]; macro_rules! check_builtin { ($T:ty) => {{ // Cannot use .cast::<$T> because we cannot convert between unsigned and signed let stacked = stack![a.map(|a_ij| a_ij as $T), b.map(|b_ij| b_ij as $T); c.map(|c_ij| c_ij as $T), 0]; assert_eq!(stacked, expected.map(|e_ij| e_ij as $T)); }} } check_builtin!(i8); check_builtin!(i16); check_builtin!(i32); check_builtin!(i64); check_builtin!(i128); check_builtin!(u8); check_builtin!(u16); check_builtin!(u32); check_builtin!(u64); check_builtin!(u128); check_builtin!(f32); check_builtin!(f64); } #[test] fn stack_test_complex() { use num_complex::Complex as C; type C32 = C; let a = matrix![C::new(1.0, 1.0), C::new(2.0, 2.0); C::new(3.0, 3.0), C::new(4.0, 4.0)]; let b = vector![C::new(5.0, 5.0), C::new(6.0, 6.0)]; let c = matrix![C::new(7.0, 7.0), C::new(8.0, 8.0)]; let expected = matrix![ 1, 2, 5; 3, 4, 6; 7, 8, 0 ] .map(|x| C::new(x as f64, x as f64)); assert_eq!(stack![a, b; c, 0], expected); assert_eq!( stack![a.cast::(), b.cast::(); c.cast::(), 0], expected.cast::() ); } nalgebra-0.33.0/tests/macros/trybuild/dmatrix_mismatched_dimensions.rs000064400000000000000000000001230072674642500244360ustar 00000000000000use nalgebra::dmatrix; fn main() { dmatrix![1, 2, 3; 4, 5]; }nalgebra-0.33.0/tests/macros/trybuild/dmatrix_mismatched_dimensions.stderr000064400000000000000000000002760072674642500253260ustar 00000000000000error: Unexpected number of entries in row 1. Expected 3, found 2 entries. --> tests/macros/trybuild/dmatrix_mismatched_dimensions.rs:5:13 | 5 | 4, 5]; | ^ nalgebra-0.33.0/tests/macros/trybuild/matrix_mismatched_dimensions.rs000064400000000000000000000001210072674642500242700ustar 00000000000000use nalgebra::matrix; fn main() { matrix![1, 2, 3; 4, 5]; }nalgebra-0.33.0/tests/macros/trybuild/matrix_mismatched_dimensions.stderr000064400000000000000000000002750072674642500251610ustar 00000000000000error: Unexpected number of entries in row 1. Expected 3, found 2 entries. --> tests/macros/trybuild/matrix_mismatched_dimensions.rs:5:13 | 5 | 4, 5]; | ^ nalgebra-0.33.0/tests/macros/trybuild/stack_empty_col.rs000064400000000000000000000001470072674642500215260ustar 00000000000000use nalgebra::{matrix, stack}; fn main() { let m = matrix![1, 2; 3, 4]; stack![0, m]; } nalgebra-0.33.0/tests/macros/trybuild/stack_empty_col.stderr000064400000000000000000000004470072674642500224100ustar 00000000000000error: Block column 0 cannot consist entirely of implicit zero blocks. --> tests/macros/trybuild/stack_empty_col.rs:5:5 | 5 | stack![0, m]; | ^^^^^^^^^^^^ | = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) nalgebra-0.33.0/tests/macros/trybuild/stack_empty_row.rs000064400000000000000000000001470072674642500215600ustar 00000000000000use nalgebra::{matrix, stack}; fn main() { let m = matrix![1, 2; 3, 4]; stack![0; m]; } nalgebra-0.33.0/tests/macros/trybuild/stack_empty_row.stderr000064400000000000000000000004440072674642500224370ustar 00000000000000error: Block row 0 cannot consist entirely of implicit zero blocks. --> tests/macros/trybuild/stack_empty_row.rs:5:5 | 5 | stack![0; m]; | ^^^^^^^^^^^^ | = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) nalgebra-0.33.0/tests/macros/trybuild/stack_incompatible_block_dimensions.rs000064400000000000000000000005550072674642500256060ustar 00000000000000use nalgebra::{matrix, stack}; fn main() { // Use multi-letter names for checking that the reported span comes out correctly let a11 = matrix![1, 2; 3, 4]; let a12 = matrix![5, 6; 7, 8]; let a21 = matrix![9, 10, 11]; let a22 = matrix![12, 13]; stack![a11, a12; a21, a22]; }nalgebra-0.33.0/tests/macros/trybuild/stack_incompatible_block_dimensions.stderr000064400000000000000000000034410072674642500264620ustar 00000000000000error[E0277]: the trait bound `ShapeConstraint: SameNumberOfColumns, Const<3>>` is not satisfied --> tests/macros/trybuild/stack_incompatible_block_dimensions.rs:12:12 | 12 | a21, a22]; | ^^^ the trait `SameNumberOfColumns, Const<3>>` is not implemented for `ShapeConstraint` | = help: the following other types implement trait `SameNumberOfColumns`: > > > = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed --> tests/macros/trybuild/stack_incompatible_block_dimensions.rs:11:5 | 11 | / stack![a11, a12; 12 | | a21, a22]; | |____________________^ cannot infer type | = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: no method named `generic_view_mut` found for struct `Matrix<_, Const<3>, _, _>` in the current scope --> tests/macros/trybuild/stack_incompatible_block_dimensions.rs:11:5 | 11 | stack![a11, a12; | _____^ 12 | | a21, a22]; | |____________________^ method not found in `Matrix<_, Const<3>, _, _>` | ::: src/base/matrix_view.rs | | generic_slice_mut => generic_view_mut, | ---------------- the method is available for `Matrix<_, Const<3>, _, _>` here | = note: the method was found for - `Matrix` = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) nalgebra-0.33.0/tests/macros/trybuild/stack_incompatible_block_dimensions2.rs000064400000000000000000000006100072674642500256600ustar 00000000000000use nalgebra::{matrix, stack}; fn main() { // Use multi-letter names for checking that the reported span comes out correctly let a11 = matrix![1, 2; 3, 4]; let a12 = matrix![5, 6; 7, 8]; let a21 = matrix![9, 10]; let a22 = matrix![11, 12; 13, 14]; stack![a11, a12; a21, a22]; }nalgebra-0.33.0/tests/macros/trybuild/stack_incompatible_block_dimensions2.stderr000064400000000000000000000034270072674642500265500ustar 00000000000000error[E0277]: the trait bound `ShapeConstraint: SameNumberOfRows, Const<2>>` is not satisfied --> tests/macros/trybuild/stack_incompatible_block_dimensions2.rs:13:17 | 13 | a21, a22]; | ^^^ the trait `SameNumberOfRows, Const<2>>` is not implemented for `ShapeConstraint` | = help: the following other types implement trait `SameNumberOfRows`: > > > = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed --> tests/macros/trybuild/stack_incompatible_block_dimensions2.rs:12:5 | 12 | / stack![a11, a12; 13 | | a21, a22]; | |____________________^ cannot infer type | = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0599]: no method named `generic_view_mut` found for struct `Matrix<_, _, Const<4>, _>` in the current scope --> tests/macros/trybuild/stack_incompatible_block_dimensions2.rs:12:5 | 12 | stack![a11, a12; | _____^ 13 | | a21, a22]; | |____________________^ method not found in `Matrix<_, _, Const<4>, _>` | ::: src/base/matrix_view.rs | | generic_slice_mut => generic_view_mut, | ---------------- the method is available for `Matrix<_, _, Const<4>, _>` here | = note: the method was found for - `Matrix` = note: this error originates in the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info) nalgebra-0.33.0/tests/proptest/mod.rs000064400000000000000000000330660072674642500156710ustar 00000000000000//! Tests for proptest-related functionality. #![allow(dead_code)] use nalgebra::allocator::Allocator; use nalgebra::base::dimension::*; use nalgebra::proptest::{DimRange, MatrixStrategy}; use nalgebra::{ DMatrix, DVector, DefaultAllocator, Dim, DualQuaternion, Isometry2, Isometry3, Matrix3, OMatrix, Point2, Point3, Quaternion, Rotation2, Rotation3, Scalar, Similarity3, Translation2, Translation3, UnitComplex, UnitDualQuaternion, UnitQuaternion, Vector3, U3, U4, }; use num_complex::Complex; use proptest::prelude::*; use proptest::strategy::{Strategy, ValueTree}; use proptest::test_runner::TestRunner; use std::ops::RangeInclusive; pub const PROPTEST_MATRIX_DIM: RangeInclusive = 1..=20; pub const PROPTEST_F64: RangeInclusive = -100.0..=100.0; pub use nalgebra::proptest::{matrix, vector}; pub fn point2() -> impl Strategy> { vector2().prop_map(|v| Point2::from(v)) } pub fn point3() -> impl Strategy> { vector3().prop_map(|v| Point3::from(v)) } pub fn translation2() -> impl Strategy> { vector2().prop_map(|v| Translation2::from(v)) } pub fn translation3() -> impl Strategy> { vector3().prop_map(|v| Translation3::from(v)) } pub fn rotation2() -> impl Strategy> { PROPTEST_F64.prop_map(|v| Rotation2::new(v)) } pub fn rotation3() -> impl Strategy> { vector3().prop_map(|v| Rotation3::new(v)) } pub fn unit_complex() -> impl Strategy> { PROPTEST_F64.prop_map(|v| UnitComplex::new(v)) } pub fn isometry2() -> impl Strategy> { vector3().prop_map(|v| Isometry2::new(v.xy(), v.z)) } pub fn isometry3() -> impl Strategy> { vector6().prop_map(|v| Isometry3::new(v.xyz(), Vector3::new(v.w, v.a, v.b))) } // pub fn similarity2() -> impl Strategy> { // vector4().prop_map(|v| Similarity2::new(v.xy(), v.z, v.w)) // } pub fn similarity3() -> impl Strategy> { vector(PROPTEST_F64, Const::<7>) .prop_map(|v| Similarity3::new(v.xyz(), Vector3::new(v[3], v[4], v[5]), v[6])) } pub fn unit_dual_quaternion() -> impl Strategy> { isometry3().prop_map(|iso| UnitDualQuaternion::from_isometry(&iso)) } pub fn dual_quaternion() -> impl Strategy> { vector(PROPTEST_F64, Const::<8>).prop_map(|v| { DualQuaternion::from_real_and_dual( Quaternion::new(v[0], v[1], v[2], v[3]), Quaternion::new(v[4], v[5], v[6], v[7]), ) }) } pub fn quaternion() -> impl Strategy> { vector4().prop_map(|v| Quaternion::from(v)) } pub fn unit_quaternion() -> impl Strategy> { vector3().prop_map(|v| UnitQuaternion::new(v)) } pub fn complex_f64() -> impl Strategy> + Clone { vector(PROPTEST_F64, Const::<2>).prop_map(|v| Complex::new(v.x, v.y)) } pub fn dmatrix() -> impl Strategy> { matrix(PROPTEST_F64, PROPTEST_MATRIX_DIM, PROPTEST_MATRIX_DIM) } pub fn dvector() -> impl Strategy> { vector(PROPTEST_F64, PROPTEST_MATRIX_DIM) } pub fn dmatrix_( scalar_strategy: ScalarStrategy, ) -> impl Strategy> where ScalarStrategy: Strategy + Clone + 'static, ScalarStrategy::Value: Scalar, DefaultAllocator: Allocator, { matrix(scalar_strategy, PROPTEST_MATRIX_DIM, PROPTEST_MATRIX_DIM) } // pub fn dvector_(range: RangeInclusive) -> impl Strategy> // where // RangeInclusive: Strategy, // T: Scalar + PartialEq + Copy, // DefaultAllocator: Allocator, // { // vector(range, PROPTEST_MATRIX_DIM) // } macro_rules! define_strategies( ($($strategy_: ident $strategy: ident<$nrows: literal, $ncols: literal>),*) => {$( pub fn $strategy() -> impl Strategy, Const<$ncols>>> { matrix(PROPTEST_F64, Const::<$nrows>, Const::<$ncols>) } pub fn $strategy_(scalar_strategy: ScalarStrategy) -> impl Strategy, Const<$ncols>>> where ScalarStrategy: Strategy + Clone + 'static, ScalarStrategy::Value: Scalar, { matrix(scalar_strategy, Const::<$nrows>, Const::<$ncols>) } )*} ); define_strategies!( matrix1_ matrix1<1, 1>, matrix2_ matrix2<2, 2>, matrix3_ matrix3<3, 3>, matrix4_ matrix4<4, 4>, matrix5_ matrix5<5, 5>, matrix6_ matrix6<6, 6>, matrix5x2_ matrix5x2<5, 2>, matrix2x5_ matrix2x5<2, 5>, matrix5x3_ matrix5x3<5, 3>, matrix3x5_ matrix3x5<3, 5>, matrix5x4_ matrix5x4<5, 4>, matrix4x5_ matrix4x5<4, 5>, vector1_ vector1<1, 1>, vector2_ vector2<2, 1>, vector3_ vector3<3, 1>, vector4_ vector4<4, 1>, vector5_ vector5<5, 1>, vector6_ vector6<6, 1> ); /// Generate a proptest that tests that all matrices generated with the /// provided rows and columns conform to the constraints defined by the /// input. macro_rules! generate_matrix_sanity_test { ($test_name:ident, $rows:expr, $cols:expr) => { proptest! { #[test] fn $test_name(a in matrix(-5 ..= 5i32, $rows, $cols)) { // let a: OMatrix<_, $rows, $cols> = a; let rows_range = DimRange::from($rows); let cols_range = DimRange::from($cols); prop_assert!(a.nrows() >= rows_range.lower_bound().value() && a.nrows() <= rows_range.upper_bound().value()); prop_assert!(a.ncols() >= cols_range.lower_bound().value() && a.ncols() <= cols_range.upper_bound().value()); prop_assert!(a.iter().all(|x_ij| *x_ij >= -5 && *x_ij <= 5)); } } }; } // Test all fixed-size matrices with row/col dimensions up to 3 generate_matrix_sanity_test!(test_matrix_u0_u0, Const::<0>, Const::<0>); generate_matrix_sanity_test!(test_matrix_u1_u0, Const::<1>, Const::<0>); generate_matrix_sanity_test!(test_matrix_u0_u1, Const::<0>, Const::<1>); generate_matrix_sanity_test!(test_matrix_u1_u1, Const::<1>, Const::<1>); generate_matrix_sanity_test!(test_matrix_u2_u1, Const::<2>, Const::<1>); generate_matrix_sanity_test!(test_matrix_u1_u2, Const::<1>, Const::<2>); generate_matrix_sanity_test!(test_matrix_u2_u2, Const::<2>, Const::<2>); generate_matrix_sanity_test!(test_matrix_u3_u2, Const::<3>, Const::<2>); generate_matrix_sanity_test!(test_matrix_u2_u3, Const::<2>, Const::<3>); generate_matrix_sanity_test!(test_matrix_u3_u3, Const::<3>, Const::<3>); // Similarly test all heap-allocated but fixed dim ranges generate_matrix_sanity_test!(test_matrix_0_0, 0, 0); generate_matrix_sanity_test!(test_matrix_0_1, 0, 1); generate_matrix_sanity_test!(test_matrix_1_0, 1, 0); generate_matrix_sanity_test!(test_matrix_1_1, 1, 1); generate_matrix_sanity_test!(test_matrix_2_1, 2, 1); generate_matrix_sanity_test!(test_matrix_1_2, 1, 2); generate_matrix_sanity_test!(test_matrix_2_2, 2, 2); generate_matrix_sanity_test!(test_matrix_3_2, 3, 2); generate_matrix_sanity_test!(test_matrix_2_3, 2, 3); generate_matrix_sanity_test!(test_matrix_3_3, 3, 3); // Test arbitrary inputs generate_matrix_sanity_test!(test_matrix_input_1, Const::<5>, 1..=5); generate_matrix_sanity_test!(test_matrix_input_2, 3..=4, 1..=5); generate_matrix_sanity_test!(test_matrix_input_3, 1..=2, Const::<3>); generate_matrix_sanity_test!(test_matrix_input_4, 3, Const::<4>); #[test] fn test_matrix_output_types() { // Test that the dimension types are correct for the given inputs let _: MatrixStrategy<_, U3, U4> = matrix(-5..5, Const::<3>, Const::<4>); let _: MatrixStrategy<_, U3, U3> = matrix(-5..5, Const::<3>, Const::<3>); let _: MatrixStrategy<_, U3, Dyn> = matrix(-5..5, Const::<3>, 1..=5); let _: MatrixStrategy<_, Dyn, U3> = matrix(-5..5, 1..=5, Const::<3>); let _: MatrixStrategy<_, Dyn, Dyn> = matrix(-5..5, 1..=5, 1..=5); } // Below we have some tests to ensure that specific instances of OMatrix are usable // in a typical proptest scenario where we (implicitly) use the `Arbitrary` trait proptest! { #[test] fn ensure_arbitrary_test_compiles_matrix3(_: Matrix3) {} #[test] fn ensure_arbitrary_test_compiles_matrixmn_u3_dynamic(_: OMatrix) {} #[test] fn ensure_arbitrary_test_compiles_matrixmn_dynamic_u3(_: OMatrix) {} #[test] fn ensure_arbitrary_test_compiles_dmatrix(_: DMatrix) {} #[test] fn ensure_arbitrary_test_compiles_vector3(_: Vector3) {} #[test] fn ensure_arbitrary_test_compiles_dvector(_: DVector) {} } #[test] fn matrix_shrinking_satisfies_constraints() { // We use a deterministic test runner to make the test "stable". let mut runner = TestRunner::deterministic(); let strategy = matrix(-1..=2, 1..=3, 2..=4); let num_matrices = 25; macro_rules! maybeprintln { ($($arg:tt)*) => { // Uncomment the below line to enable printing of matrix sequences. This is handy // for manually inspecting the sequences of simplified matrices. // println!($($arg)*) }; } maybeprintln!("========================== (begin generation process)"); for _ in 0..num_matrices { let mut tree = strategy .new_tree(&mut runner) .expect("Tree generation should not fail."); let mut current = Some(tree.current()); maybeprintln!("------------------"); while let Some(matrix) = current { maybeprintln!("{}", matrix); assert!( matrix.iter().all(|&v| v >= -1 && v <= 2), "All matrix elements must satisfy constraints" ); assert!( matrix.nrows() >= 1 && matrix.nrows() <= 3, "Number of rows in matrix must satisfy constraints." ); assert!( matrix.ncols() >= 2 && matrix.ncols() <= 4, "Number of columns in matrix must satisfy constraints." ); current = if tree.simplify() { Some(tree.current()) } else { None } } } maybeprintln!("========================== (end of generation process)"); } #[cfg(feature = "slow-tests")] mod slow { use super::*; use itertools::Itertools; use std::collections::HashSet; use std::iter::repeat; #[cfg(feature = "slow-tests")] #[test] fn matrix_samples_all_possible_outputs() { // Test that the proptest generation covers all possible outputs for a small space of inputs // given enough samples. // We use a deterministic test runner to make the test "stable". let mut runner = TestRunner::deterministic(); // This number needs to be high enough so that we with high probability sample // all possible cases let num_generated_matrices = 200000; let values = -1..=1; let rows = 0..=2; let cols = 0..=3; let strategy = matrix(values.clone(), rows.clone(), cols.clone()); // Enumerate all possible combinations let mut all_combinations = HashSet::new(); for nrows in rows { for ncols in cols.clone() { // For the given number of rows and columns let n_values = nrows * ncols; if n_values == 0 { // If we have zero rows or columns, the set of matrices with the given // rows and columns is a single element: an empty matrix all_combinations.insert(DMatrix::from_row_slice(nrows, ncols, &[])); } else { // Otherwise, we need to sample all possible matrices. // To do this, we generate the values as the (multi) Cartesian product // of the value sets. For example, for a 2x2 matrices, we consider // all possible 4-element arrays that the matrices can take by // considering all elements in the cartesian product // V x V x V x V // where V is the set of eligible values, e.g. V := -1 ..= 1 for matrix_values in repeat(values.clone()) .take(n_values) .multi_cartesian_product() { all_combinations.insert(DMatrix::from_row_slice( nrows, ncols, &matrix_values, )); } } } } let mut visited_combinations = HashSet::new(); for _ in 0..num_generated_matrices { let tree = strategy .new_tree(&mut runner) .expect("Tree generation should not fail"); let matrix = tree.current(); visited_combinations.insert(matrix.clone()); } assert_eq!( visited_combinations, all_combinations, "Did not sample all possible values." ); } } nalgebra-0.33.0/tests/sparse/cs_cholesky.rs000064400000000000000000000041500072674642500170250ustar 00000000000000#![cfg_attr(rustfmt, rustfmt_skip)] use na::{CsMatrix, CsVector, CsCholesky, Cholesky, Matrix5, Vector5}; #[test] fn cs_cholesky() { let mut a = Matrix5::new( 40.0, 0.0, 0.0, 0.0, 0.0, 2.0, 60.0, 0.0, 0.0, 0.0, 1.0, 0.0, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.0, 0.0, 1.0, 0.0, 0.0, 4.0, 10.0 ); a.fill_upper_triangle_with_lower_triangle(); test_cholesky(a); let a = Matrix5::from_diagonal(&Vector5::new(40.0, 60.0, 11.0, 50.0, 10.0)); test_cholesky(a); let mut a = Matrix5::new( 40.0, 0.0, 0.0, 0.0, 0.0, 2.0, 60.0, 0.0, 0.0, 0.0, 1.0, 0.0, 11.0, 0.0, 0.0, 1.0, 0.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, 4.0, 10.0 ); a.fill_upper_triangle_with_lower_triangle(); test_cholesky(a); let mut a = Matrix5::new( 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 1.0, 1.0, 0.0, 0.0, 2.0 ); a.fill_upper_triangle_with_lower_triangle(); // Test crate::new, left_looking, and up_looking implementations. test_cholesky(a); } fn test_cholesky(a: Matrix5) { // Test crate::new test_cholesky_variant(a, 0); // Test up-looking test_cholesky_variant(a, 1); // Test left-looking test_cholesky_variant(a, 2); } fn test_cholesky_variant(a: Matrix5, option: usize) { let cs_a: CsMatrix<_, _, _> = a.into(); let chol_a = Cholesky::new(a).unwrap(); let mut chol_cs_a; match option { 0 => chol_cs_a = CsCholesky::new(&cs_a), 1 => { chol_cs_a = CsCholesky::new_symbolic(&cs_a); chol_cs_a.decompose_up_looking(cs_a.data.values()); } _ => { chol_cs_a = CsCholesky::new_symbolic(&cs_a); chol_cs_a.decompose_left_looking(cs_a.data.values()); } }; let l = chol_a.l(); let cs_l = chol_cs_a.unwrap_l().unwrap(); assert!(cs_l.is_sorted()); let cs_l_mat: Matrix5<_> = cs_l.into(); assert_relative_eq!(l, cs_l_mat); } nalgebra-0.33.0/tests/sparse/cs_construction.rs000064400000000000000000000000020072674642500177260ustar 00000000000000 nalgebra-0.33.0/tests/sparse/cs_conversion.rs000064400000000000000000000045250072674642500173770ustar 00000000000000use na::{CsMatrix, DMatrix, Matrix4x5}; #[test] fn cs_from_to_matrix() { #[cfg_attr(rustfmt, rustfmt_skip)] let m = Matrix4x5::new( 5.0, 6.0, 0.0, 8.0, 15.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, 0.0, 13.0, 0.0, 0.0, 0.0, 1.0, 4.0, 0.0, 14.0, ); let cs: CsMatrix<_, _, _> = m.into(); assert!(cs.is_sorted()); let m2: Matrix4x5<_> = cs.into(); assert_eq!(m2, m); } #[test] fn cs_matrix_from_triplet() { let mut irows = vec![0, 0, 0, 0, 1, 1, 1, 1, 2, 3, 3, 3]; let mut icols = vec![0, 1, 3, 4, 0, 1, 2, 3, 2, 1, 2, 4]; let mut vals = vec![ 5.0, 6.0, 8.0, 15.0, 9.0, 10.0, 11.0, 12.0, 13.0, 1.0, 4.0, 14.0, ]; #[cfg_attr(rustfmt, rustfmt_skip)] let expected = DMatrix::from_row_slice(4, 5, &[ 5.0, 6.0, 0.0, 8.0, 15.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, 0.0, 13.0, 0.0, 0.0, 0.0, 1.0, 4.0, 0.0, 14.0, ]); let cs_expected = CsMatrix::from_parts( 4, 5, vec![0, 2, 5, 8, 10], vec![0, 1, 0, 1, 3, 1, 2, 3, 0, 1, 0, 3], vec![ 5.0, 9.0, 6.0, 10.0, 1.0, 11.0, 13.0, 4.0, 8.0, 12.0, 15.0, 14.0, ], ); let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); assert!(cs_mat.is_sorted()); assert_eq!(cs_mat, cs_expected); let m: DMatrix<_> = cs_mat.into(); assert_eq!(m, expected); /* * Try again with some permutations. */ let permutations = [(2, 5), (0, 4), (8, 10), (1, 11)]; for (i, j) in &permutations { irows.swap(*i, *j); icols.swap(*i, *j); vals.swap(*i, *j); } let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); assert!(cs_mat.is_sorted()); assert_eq!(cs_mat, cs_expected); let m: DMatrix<_> = cs_mat.into(); assert_eq!(m, expected); /* * Try again, duplicating all entries. */ let mut ir = irows.clone(); let mut ic = icols.clone(); let mut va = vals.clone(); irows.append(&mut ir); icols.append(&mut ic); vals.append(&mut va); let cs_mat = CsMatrix::from_triplet(4, 5, &irows, &icols, &vals); assert!(cs_mat.is_sorted()); assert_eq!(cs_mat, cs_expected * 2.0); let m: DMatrix<_> = cs_mat.into(); assert_eq!(m, expected * 2.0); } nalgebra-0.33.0/tests/sparse/cs_matrix.rs000064400000000000000000000010720072674642500165100ustar 00000000000000#![cfg_attr(rustfmt, rustfmt_skip)] use na::{Matrix4x5, Matrix5x4, CsMatrix}; #[test] fn cs_transpose() { let m = Matrix4x5::new( 4.0, 1.0, 4.0, 0.0, 9.0, 5.0, 6.0, 0.0, 8.0, 10.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, 0.0, 1.0, 0.0, 10.0 ); let cs: CsMatrix<_, _, _> = m.into(); assert!(cs.is_sorted()); let cs_transposed = cs.transpose(); assert!(cs_transposed.is_sorted()); let cs_transposed_mat: Matrix5x4<_> = cs_transposed.into(); assert_eq!(cs_transposed_mat, m.transpose()) } nalgebra-0.33.0/tests/sparse/cs_matrix_market.rs000064400000000000000000000035720072674642500200620ustar 00000000000000#![cfg_attr(rustfmt, rustfmt_skip)] use na::io; use na::DMatrix; #[test] fn cs_matrix_market() { let file_str = r#" %%MatrixMarket matrix coordinate real general %================================================================================= % % This ASCII file represents a sparse MxN matrix with L % nonzeros in the following Matrix Market format: % % +----------------------------------------------+ % |%%MatrixMarket matrix coordinate real general | <--- header line % |% | <--+ % |% comments | |-- 0 or more comment lines % |% | <--+ % | M T L | <--- rows, columns, entries % | I1 J1 A(I1, J1) | <--+ % | I2 J2 A(I2, J2) | | % | I3 J3 A(I3, J3) | |-- L lines % | . . . | | % | IL JL A(IL, JL) | <--+ % +----------------------------------------------+ % % Indices are 1-based, i.e. A(1,1) is the first element. % %================================================================================= 5 5 8 1 1 1.000e+00 2 2 1.050e+01 3 3 1.500e-02 1 4 6.000e+00 4 2 2.505e+02 4 4 -2.800e+02 4 5 3.332e+01 5 5 1.200e+01 "#; let cs_mat = io::cs_matrix_from_matrix_market_str(file_str).unwrap(); let mat: DMatrix<_> = cs_mat.into(); let expected = DMatrix::from_row_slice(5, 5, &[ 1.0, 0.0, 0.0, 6.0, 0.0, 0.0, 10.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.015, 0.0, 0.0, 0.0, 250.5, 0.0, -280.0, 33.32, 0.0, 0.0, 0.0, 0.0, 12.0, ]); assert_eq!(mat, expected); } nalgebra-0.33.0/tests/sparse/cs_ops.rs000064400000000000000000000032520072674642500160070ustar 00000000000000#![cfg_attr(rustfmt, rustfmt_skip)] use na::{Matrix3x4, Matrix4x5, Matrix3x5, CsMatrix, Vector5, CsVector}; #[test] fn axpy_cs() { let mut v1 = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); let v2 = Vector5::new(10.0, 0.0, 30.0, 0.0, 50.0); let expected = 5.0 * v2 + 10.0 * v1; let cs: CsVector<_, _> = v2.into(); v1.axpy_cs(5.0, &cs, 10.0); assert!(cs.is_sorted()); assert_eq!(v1, expected) } #[test] fn cs_mat_mul() { let m1 = Matrix3x4::new( 0.0, 1.0, 4.0, 0.0, 5.0, 6.0, 0.0, 8.0, 9.0, 10.0, 11.0, 12.0, ); let m2 = Matrix4x5::new( 5.0, 6.0, 0.0, 8.0, 15.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, 0.0, 13.0, 0.0, 0.0, 0.0, 1.0, 4.0, 0.0, 14.0, ); let sm1: CsMatrix<_, _, _> = m1.into(); let sm2: CsMatrix<_, _, _> = m2.into(); let mul = &sm1 * &sm2; assert!(sm1.is_sorted()); assert!(sm2.is_sorted()); assert!(mul.is_sorted()); assert_eq!(Matrix3x5::from(mul), m1 * m2); } #[test] fn cs_mat_add() { let m1 = Matrix4x5::new( 4.0, 1.0, 4.0, 0.0, 0.0, 5.0, 6.0, 0.0, 8.0, 0.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, 0.0, 1.0, 0.0, 10.0 ); let m2 = Matrix4x5::new( 0.0, 1.0, 4.0, 0.0, 14.0, 5.0, 6.0, 0.0, 8.0, 15.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, 0.0, 13.0, 0.0, 0.0, ); let sm1: CsMatrix<_, _, _> = m1.into(); let sm2: CsMatrix<_, _, _> = m2.into(); let sum = &sm1 + &sm2; assert!(sm1.is_sorted()); assert!(sm2.is_sorted()); assert!(sum.is_sorted()); assert_eq!(Matrix4x5::from(sum), m1 + m2); } nalgebra-0.33.0/tests/sparse/cs_solve.rs000064400000000000000000000103370072674642500163400ustar 00000000000000#![cfg_attr(rustfmt, rustfmt_skip)] use na::{CsMatrix, CsVector, Matrix5, Vector5}; #[test] fn cs_lower_triangular_solve() { let a = Matrix5::new( 4.0, 1.0, 4.0, 0.0, 9.0, 5.0, 6.0, 0.0, 8.0, 10.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, -8.0, 3.0, 5.0, 9.0, 0.0, 0.0, 1.0, 0.0, -10.0 ); let b = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); let cs_a: CsMatrix<_, _, _> = a.into(); assert_eq!(cs_a.solve_lower_triangular(&b), a.solve_lower_triangular(&b)); } #[test] fn cs_tr_lower_triangular_solve() { let a = Matrix5::new( 4.0, 1.0, 4.0, 0.0, 9.0, 5.0, 6.0, 0.0, 8.0, 10.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, -8.0, 3.0, 5.0, 9.0, 0.0, 0.0, 1.0, 0.0, -10.0 ); let b = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); let cs_a: CsMatrix<_, _, _> = a.into(); assert!(cs_a.tr_solve_lower_triangular(&b).is_some()); assert_eq!(cs_a.tr_solve_lower_triangular(&b), a.tr_solve_lower_triangular(&b)); // Singular case. let a = Matrix5::new( 4.0, 1.0, 4.0, 0.0, 9.0, 5.0, 6.0, 0.0, 8.0, 10.0, 9.0, 10.0, 0.0, 12.0, 0.0, 0.0, -8.0, 3.0, 5.0, 9.0, 0.0, 0.0, 1.0, 0.0, -10.0 ); let cs_a: CsMatrix<_, _, _> = a.into(); assert!(cs_a.tr_solve_lower_triangular(&b).is_none()); } #[test] fn cs_lower_triangular_solve_cs() { let a = Matrix5::new( 4.0, 1.0, 4.0, 0.0, 9.0, 5.0, 6.0, 0.0, 8.0, 10.0, 9.0, 10.0, 11.0, 12.0, 0.0, 0.0, -8.0, 3.0, 5.0, 9.0, 0.0, 0.0, 1.0, 0.0, -10.0 ); let b1 = Vector5::zeros(); let b2 = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0); let b3 = Vector5::new(1.0, 0.0, 0.0, 4.0, 0.0); let b4 = Vector5::new(0.0, 1.0, 0.0, 4.0, 5.0); let b5 = Vector5::x(); let b6 = Vector5::y(); let b7 = Vector5::z(); let b8 = Vector5::w(); let b9 = Vector5::a(); let cs_a: CsMatrix<_, _, _> = a.into(); let cs_b1: CsVector<_, _> = Vector5::zeros().into(); let cs_b2: CsVector<_, _> = Vector5::new(1.0, 2.0, 3.0, 4.0, 5.0).into(); let cs_b3: CsVector<_, _> = Vector5::new(1.0, 0.0, 0.0, 4.0, 0.0).into(); let cs_b4: CsVector<_, _> = Vector5::new(0.0, 1.0, 0.0, 4.0, 5.0).into(); let cs_b5: CsVector<_, _> = Vector5::x().into(); let cs_b6: CsVector<_, _> = Vector5::y().into(); let cs_b7: CsVector<_, _> = Vector5::z().into(); let cs_b8: CsVector<_, _> = Vector5::w().into(); let cs_b9: CsVector<_, _> = Vector5::a().into(); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b1).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b1)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b5).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b5)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b6).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b6)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b7).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b7)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b8).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b8)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b9).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b9)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b2).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b2)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b3).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b3)); assert_eq!(cs_a.solve_lower_triangular_cs(&cs_b4).map(|v| { assert!(v.is_sorted()); v.into() }), a.solve_lower_triangular(&b4)); // Singular case. let a = Matrix5::new( 4.0, 1.0, 4.0, 0.0, 9.0, 5.0, 0.0, 0.0, 8.0, 10.0, 9.0, 10.0, 0.0, 12.0, 0.0, 0.0, -8.0, 3.0, 5.0, 9.0, 0.0, 0.0, 1.0, 0.0, -10.0 ); let cs_a: CsMatrix<_, _, _> = a.into(); assert!(cs_a.solve_lower_triangular_cs(&cs_b2).is_none()); assert!(cs_a.solve_lower_triangular_cs(&cs_b3).is_none()); assert!(cs_a.solve_lower_triangular_cs(&cs_b4).is_none()); } nalgebra-0.33.0/tests/sparse/mod.rs000064400000000000000000000002270072674642500152770ustar 00000000000000mod cs_cholesky; mod cs_construction; mod cs_conversion; mod cs_matrix; #[cfg(feature = "io")] mod cs_matrix_market; mod cs_ops; mod cs_solve;