├── .git-blame-ignore-revs ├── .github └── workflows │ ├── ci.yaml │ └── latest-deps.yaml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README-crates.io.md ├── README-quick-start.md ├── README.rst ├── RELEASES.md ├── benches ├── append.rs ├── bench1.rs ├── chunks.rs ├── construct.rs ├── gemv_gemm.rs ├── higher-order.rs ├── iter.rs ├── numeric.rs ├── par_rayon.rs ├── reserve.rs ├── to_shape.rs └── zip.rs ├── clippy.toml ├── crates ├── blas-mock-tests │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ └── use-blas.rs ├── blas-tests │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── dyn.rs │ │ └── oper.rs ├── ndarray-gen │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── array_builder.rs │ │ └── lib.rs ├── numeric-tests │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ └── accuracy.rs └── serialization-tests │ ├── Cargo.toml │ ├── src │ └── lib.rs │ └── tests │ └── serialize.rs ├── examples ├── axis_ops.rs ├── bounds_check_elim.rs ├── column_standardize.rs ├── convo.rs ├── functions_and_traits.rs ├── life.rs ├── life.txt ├── lifelite.txt ├── rollaxis.rs ├── sort-axis.rs ├── type_conversion.rs └── zip_many.rs ├── misc ├── axis_iter.svg └── split_at.svg ├── ndarray-rand ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── RELEASES.md ├── benches │ └── bench.rs ├── src │ └── lib.rs └── tests │ └── tests.rs ├── rustfmt.toml ├── scripts ├── all-tests.sh ├── blas-integ-tests.sh ├── cross-tests.sh ├── makechangelog.sh └── miri-tests.sh ├── src ├── alias_asref.rs ├── aliases.rs ├── argument_traits.rs ├── array_approx.rs ├── array_serde.rs ├── arrayformat.rs ├── arraytraits.rs ├── data_repr.rs ├── data_traits.rs ├── dimension │ ├── axes.rs │ ├── axis.rs │ ├── broadcast.rs │ ├── conversion.rs │ ├── dim.rs │ ├── dimension_trait.rs │ ├── dynindeximpl.rs │ ├── macros.rs │ ├── mod.rs │ ├── ndindex.rs │ ├── ops.rs │ ├── remove_axis.rs │ ├── reshape.rs │ └── sequence.rs ├── doc │ ├── crate_feature_flags.rs │ ├── mod.rs │ └── ndarray_for_numpy_users │ │ ├── coord_transform.rs │ │ ├── mod.rs │ │ ├── rk_step.rs │ │ └── simple_math.rs ├── error.rs ├── extension.rs ├── extension │ └── nonnull.rs ├── free_functions.rs ├── geomspace.rs ├── impl_1d.rs ├── impl_2d.rs ├── impl_arc_array.rs ├── impl_clone.rs ├── impl_constructors.rs ├── impl_cow.rs ├── impl_dyn.rs ├── impl_internal_constructors.rs ├── impl_methods.rs ├── impl_ops.rs ├── impl_owned_array.rs ├── impl_raw_views.rs ├── impl_ref_types.rs ├── impl_special_element_types.rs ├── impl_views │ ├── constructors.rs │ ├── conversions.rs │ ├── indexing.rs │ ├── mod.rs │ └── splitting.rs ├── indexes.rs ├── iterators │ ├── chunks.rs │ ├── into_iter.rs │ ├── iter.rs │ ├── lanes.rs │ ├── macros.rs │ ├── mod.rs │ └── windows.rs ├── itertools.rs ├── layout │ ├── layoutfmt.rs │ └── mod.rs ├── lib.rs ├── linalg │ ├── impl_linalg.rs │ └── mod.rs ├── linalg_traits.rs ├── linspace.rs ├── logspace.rs ├── low_level_util.rs ├── macro_utils.rs ├── math_cell.rs ├── numeric │ ├── impl_float_maths.rs │ ├── impl_numeric.rs │ └── mod.rs ├── numeric_util.rs ├── order.rs ├── parallel │ ├── impl_par_methods.rs │ ├── into_impls.rs │ ├── mod.rs │ ├── par.rs │ ├── send_producer.rs │ └── zipmacro.rs ├── partial.rs ├── prelude.rs ├── private.rs ├── shape_builder.rs ├── slice.rs ├── split_at.rs ├── stacking.rs ├── tri.rs └── zip │ ├── mod.rs │ ├── ndproducer.rs │ └── zipmacro.rs └── tests ├── append.rs ├── array-construct.rs ├── array.rs ├── assign.rs ├── azip.rs ├── broadcast.rs ├── clone.rs ├── complex.rs ├── dimension.rs ├── format.rs ├── higher_order_f.rs ├── indices.rs ├── into-ixdyn.rs ├── iterator_chunks.rs ├── iterators.rs ├── ix0.rs ├── ixdyn.rs ├── numeric.rs ├── oper.rs ├── par_azip.rs ├── par_rayon.rs ├── par_zip.rs ├── raw_views.rs ├── reserve.rs ├── reshape.rs ├── s.rs ├── stacking.rs ├── variance.rs ├── views.rs ├── windows.rs └── zst.rs /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # rustfmt codebase (gh-1375) 2 | d07f5f33800e5240e7edb02bdbc4815ab30ef37e 3 | -------------------------------------------------------------------------------- /.github/workflows/latest-deps.yaml: -------------------------------------------------------------------------------- 1 | name: Check Latest Dependencies 2 | on: 3 | schedule: 4 | # Chosen so that it runs right before the international date line experiences the weekend. 5 | # Since we're open source, that means globally we should be aware of it right when we have the most 6 | # time to fix it. 7 | # 8 | # Sorry if this ruins your weekend, future maintainer... 9 | - cron: '0 12 * * FRI' 10 | workflow_dispatch: # For running manually 11 | pull_request: 12 | paths: 13 | - '.github/workflows/latest-deps.yaml' 14 | 15 | env: 16 | CARGO_TERM_COLOR: always 17 | HOST: x86_64-unknown-linux-gnu 18 | FEATURES: "approx,serde,rayon" 19 | RUSTFLAGS: "-D warnings" 20 | MSRV: 1.64.0 21 | BLAS_MSRV: 1.71.0 22 | 23 | jobs: 24 | latest_deps_stable: 25 | runs-on: ubuntu-latest 26 | name: Check Latest Dependencies on Stable 27 | steps: 28 | - name: Check Out Repo 29 | uses: actions/checkout@v4 30 | - name: Install Rust 31 | uses: dtolnay/rust-toolchain@master 32 | with: 33 | toolchain: stable 34 | - name: Setup Mold Linker 35 | uses: rui314/setup-mold@v1 36 | - name: Setup Rust Cache 37 | uses: Swatinem/rust-cache@v2 38 | - name: Install openblas 39 | run: sudo apt-get install libopenblas-dev gfortran 40 | - name: Ensure latest dependencies 41 | run: cargo update 42 | - name: Run Tests 43 | run: ./scripts/all-tests.sh "$FEATURES" stable 44 | 45 | latest_deps_msrv: 46 | runs-on: ubuntu-latest 47 | name: Check Latest Dependencies on MSRV 48 | steps: 49 | - name: Check Out Repo 50 | uses: actions/checkout@v4 51 | - name: Install Stable Rust for Update 52 | uses: dtolnay/rust-toolchain@master 53 | with: 54 | toolchain: stable 55 | - name: Setup Mold Linker 56 | uses: rui314/setup-mold@v1 57 | - name: Setup Rust Cache 58 | uses: Swatinem/rust-cache@v2 59 | - name: Install openblas 60 | run: sudo apt-get install libopenblas-dev gfortran 61 | - name: Ensure latest dependencies 62 | # The difference is here between this and `latest_deps_stable` 63 | run: CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS="fallback" cargo update 64 | - name: Install MSRV Rust for Test 65 | uses: dtolnay/rust-toolchain@master 66 | with: 67 | toolchain: ${{ env.MSRV }} 68 | - name: Run Tests 69 | run: ./scripts/all-tests.sh "$FEATURES" $MSRV 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust items 2 | target/ 3 | 4 | # Editor settings 5 | .vscode 6 | .idea 7 | 8 | # Apple details 9 | **/.DS_Store 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "ndarray" 4 | version = "0.16.1" 5 | edition = "2021" 6 | rust-version = "1.64" 7 | authors = [ 8 | "Ulrik Sverdrup \"bluss\"", 9 | "Jim Turner" 10 | ] 11 | license = "MIT OR Apache-2.0" 12 | readme = "README-crates.io.md" 13 | 14 | repository = "https://github.com/rust-ndarray/ndarray" 15 | documentation = "https://docs.rs/ndarray/" 16 | 17 | description = "An n-dimensional array for general elements and for numerics. Lightweight array views and slicing; views support chunking and splitting." 18 | 19 | keywords = ["array", "data-structure", "multidimensional", "matrix", "blas"] 20 | categories = ["data-structures", "science"] 21 | 22 | exclude = ["docgen/images/*"] 23 | resolver = "2" 24 | 25 | [lib] 26 | name = "ndarray" 27 | bench = false 28 | test = true 29 | 30 | [dependencies] 31 | num-integer = { workspace = true } 32 | num-traits = { workspace = true } 33 | num-complex = { workspace = true } 34 | 35 | approx = { workspace = true, optional = true } 36 | rayon = { version = "1.10.0", optional = true } 37 | 38 | # Use via the `blas` crate feature 39 | cblas-sys = { workspace = true, optional = true } 40 | libc = { version = "0.2.82", optional = true } 41 | 42 | matrixmultiply = { version = "0.3.2", default-features = false, features=["cgemm"] } 43 | 44 | serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } 45 | rawpointer = { version = "0.2" } 46 | 47 | [dev-dependencies] 48 | defmac = "0.2" 49 | quickcheck = { workspace = true } 50 | approx = { workspace = true, default-features = true } 51 | itertools = { workspace = true } 52 | ndarray-gen = { workspace = true } 53 | 54 | [features] 55 | default = ["std"] 56 | 57 | # Enable blas usage 58 | # See README for more instructions 59 | blas = ["dep:cblas-sys", "dep:libc"] 60 | 61 | serde = ["dep:serde"] 62 | 63 | std = ["num-traits/std", "matrixmultiply/std"] 64 | rayon = ["dep:rayon", "std"] 65 | 66 | matrixmultiply-threading = ["matrixmultiply/threading"] 67 | 68 | portable-atomic-critical-section = ["portable-atomic/critical-section"] 69 | 70 | 71 | [target.'cfg(not(target_has_atomic = "ptr"))'.dependencies] 72 | portable-atomic = { version = "1.6.0" } 73 | portable-atomic-util = { version = "0.2.0", features = [ "alloc" ] } 74 | 75 | [workspace] 76 | members = [ 77 | "ndarray-rand", 78 | "crates/*", 79 | ] 80 | default-members = [ 81 | ".", 82 | "ndarray-rand", 83 | "crates/ndarray-gen", 84 | "crates/numeric-tests", 85 | "crates/serialization-tests", 86 | # exclude blas-tests and blas-mock-tests that activate "blas" feature 87 | ] 88 | 89 | [workspace.dependencies] 90 | ndarray = { version = "0.16", path = ".", default-features = false } 91 | ndarray-rand = { path = "ndarray-rand" } 92 | ndarray-gen = { path = "crates/ndarray-gen" } 93 | 94 | num-integer = { version = "0.1.39", default-features = false } 95 | num-traits = { version = "0.2", default-features = false } 96 | num-complex = { version = "0.4", default-features = false } 97 | approx = { version = "0.5", default-features = false } 98 | quickcheck = { version = "1.0", default-features = false } 99 | rand = { version = "0.9.0", features = ["small_rng"] } 100 | rand_distr = { version = "0.5.0" } 101 | itertools = { version = "0.13.0", default-features = false, features = ["use_std"] } 102 | cblas-sys = { version = "0.1.4", default-features = false } 103 | 104 | [profile.bench] 105 | debug = true 106 | 107 | [profile.test.package.numeric-tests] 108 | opt-level = 2 109 | [profile.test.package.blas-tests] 110 | opt-level = 2 111 | 112 | [package.metadata.release] 113 | no-dev-version = true 114 | tag-name = "{{version}}" 115 | 116 | # Config specific to docs.rs 117 | [package.metadata.docs.rs] 118 | features = ["approx", "serde", "rayon"] 119 | # Define the configuration attribute `docsrs` 120 | rustdoc-args = ["--cfg", "docsrs"] 121 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 - 2021 Ulrik Sverdrup "bluss", 2 | Jim Turner, 3 | and ndarray developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /README-crates.io.md: -------------------------------------------------------------------------------- 1 | 2 | `ndarray` implements an *n*-dimensional container for general elements and for 3 | numerics. 4 | 5 | In *n*-dimensional we include for example 1-dimensional rows or columns, 6 | 2-dimensional matrices, and higher dimensional arrays. If the array has *n* 7 | dimensions, then an element in the array is accessed by using that many indices. 8 | Each dimension is also called an *axis*. 9 | 10 | ## Highlights 11 | 12 | - Generic *n*-dimensional array 13 | - Slicing, also with arbitrary step size, and negative indices to mean 14 | elements from the end of the axis. 15 | - Views and subviews of arrays; iterators that yield subviews. 16 | - Higher order operations and arithmetic are performant 17 | - Array views can be used to slice and mutate any `[T]` data using 18 | `ArrayView::from` and `ArrayViewMut::from`. 19 | - `Zip` for lock step function application across two or more arrays or other 20 | item producers (`NdProducer` trait). 21 | -------------------------------------------------------------------------------- /benches/append.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | use test::Bencher; 5 | 6 | use ndarray::prelude::*; 7 | 8 | #[bench] 9 | fn select_axis0(bench: &mut Bencher) 10 | { 11 | let a = Array::::zeros((256, 256)); 12 | let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; 13 | bench.iter(|| a.select(Axis(0), &selectable)); 14 | } 15 | 16 | #[bench] 17 | fn select_axis1(bench: &mut Bencher) 18 | { 19 | let a = Array::::zeros((256, 256)); 20 | let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; 21 | bench.iter(|| a.select(Axis(1), &selectable)); 22 | } 23 | 24 | #[bench] 25 | fn select_1d(bench: &mut Bencher) 26 | { 27 | let a = Array::::zeros(1024); 28 | let mut selectable = (0..a.len()).step_by(17).collect::>(); 29 | selectable.extend(selectable.clone().iter().rev()); 30 | 31 | bench.iter(|| a.select(Axis(0), &selectable)); 32 | } 33 | -------------------------------------------------------------------------------- /benches/chunks.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | use test::Bencher; 5 | 6 | use ndarray::prelude::*; 7 | use ndarray::NdProducer; 8 | 9 | #[bench] 10 | fn chunk2x2_iter_sum(bench: &mut Bencher) 11 | { 12 | let a = Array::::zeros((256, 256)); 13 | let chunksz = (2, 2); 14 | let mut sum = Array::zeros(a.exact_chunks(chunksz).raw_dim()); 15 | bench.iter(|| { 16 | azip!((a in a.exact_chunks(chunksz), sum in &mut sum) { 17 | *sum = a.iter().sum::(); 18 | }); 19 | }); 20 | } 21 | 22 | #[bench] 23 | fn chunk2x2_sum(bench: &mut Bencher) 24 | { 25 | let a = Array::::zeros((256, 256)); 26 | let chunksz = (2, 2); 27 | let mut sum = Array::zeros(a.exact_chunks(chunksz).raw_dim()); 28 | bench.iter(|| { 29 | azip!((a in a.exact_chunks(chunksz), sum in &mut sum) { 30 | *sum = a.sum(); 31 | }); 32 | }); 33 | } 34 | 35 | #[bench] 36 | fn chunk2x2_sum_get1(bench: &mut Bencher) 37 | { 38 | let a = Array::::zeros((256, 256)); 39 | let chunksz = (2, 2); 40 | let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); 41 | bench.iter(|| { 42 | let (m, n) = a.dim(); 43 | for i in 0..m { 44 | for j in 0..n { 45 | sum[[i / 2, j / 2]] += a[[i, j]]; 46 | } 47 | } 48 | }); 49 | } 50 | 51 | #[bench] 52 | fn chunk2x2_sum_uget1(bench: &mut Bencher) 53 | { 54 | let a = Array::::zeros((256, 256)); 55 | let chunksz = (2, 2); 56 | let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); 57 | bench.iter(|| { 58 | let (m, n) = a.dim(); 59 | for i in 0..m { 60 | for j in 0..n { 61 | unsafe { 62 | *sum.uget_mut([i / 2, j / 2]) += *a.uget([i, j]); 63 | } 64 | } 65 | } 66 | }); 67 | } 68 | 69 | #[bench] 70 | #[allow(clippy::identity_op)] 71 | fn chunk2x2_sum_get2(bench: &mut Bencher) 72 | { 73 | let a = Array::::zeros((256, 256)); 74 | let chunksz = (2, 2); 75 | let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); 76 | bench.iter(|| { 77 | let (m, n) = sum.dim(); 78 | for i in 0..m { 79 | for j in 0..n { 80 | sum[[i, j]] += a[[i * 2 + 0, j * 2 + 0]]; 81 | sum[[i, j]] += a[[i * 2 + 0, j * 2 + 1]]; 82 | sum[[i, j]] += a[[i * 2 + 1, j * 2 + 1]]; 83 | sum[[i, j]] += a[[i * 2 + 1, j * 2 + 0]]; 84 | } 85 | } 86 | }); 87 | } 88 | -------------------------------------------------------------------------------- /benches/construct.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![allow( 3 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 4 | )] 5 | extern crate test; 6 | use test::Bencher; 7 | 8 | use ndarray::prelude::*; 9 | 10 | #[bench] 11 | fn default_f64(bench: &mut Bencher) 12 | { 13 | bench.iter(|| Array::::default((128, 128))) 14 | } 15 | 16 | #[bench] 17 | fn zeros_f64(bench: &mut Bencher) 18 | { 19 | bench.iter(|| Array::::zeros((128, 128))) 20 | } 21 | 22 | #[cfg(feature = "std")] 23 | #[bench] 24 | fn map_regular(bench: &mut test::Bencher) 25 | { 26 | let a = Array::linspace(0., 127., 128) 27 | .into_shape_with_order((8, 16)) 28 | .unwrap(); 29 | bench.iter(|| a.map(|&x| 2. * x)); 30 | } 31 | 32 | #[cfg(feature = "std")] 33 | #[bench] 34 | fn map_stride(bench: &mut test::Bencher) 35 | { 36 | let a = Array::linspace(0., 127., 256) 37 | .into_shape_with_order((8, 32)) 38 | .unwrap(); 39 | let av = a.slice(s![.., ..;2]); 40 | bench.iter(|| av.map(|&x| 2. * x)); 41 | } 42 | -------------------------------------------------------------------------------- /benches/gemv_gemm.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![allow( 3 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 4 | )] 5 | 6 | extern crate test; 7 | use test::Bencher; 8 | 9 | use num_complex::Complex; 10 | use num_traits::{Float, One, Zero}; 11 | 12 | use ndarray::prelude::*; 13 | 14 | use ndarray::linalg::general_mat_mul; 15 | use ndarray::linalg::general_mat_vec_mul; 16 | use ndarray::LinalgScalar; 17 | 18 | #[bench] 19 | fn gemv_64_64c(bench: &mut Bencher) 20 | { 21 | let a = Array::zeros((64, 64)); 22 | let (m, n) = a.dim(); 23 | let x = Array::zeros(n); 24 | let mut y = Array::zeros(m); 25 | bench.iter(|| { 26 | general_mat_vec_mul(1.0, &a, &x, 1.0, &mut y); 27 | }); 28 | } 29 | 30 | #[bench] 31 | fn gemv_64_64f(bench: &mut Bencher) 32 | { 33 | let a = Array::zeros((64, 64).f()); 34 | let (m, n) = a.dim(); 35 | let x = Array::zeros(n); 36 | let mut y = Array::zeros(m); 37 | bench.iter(|| { 38 | general_mat_vec_mul(1.0, &a, &x, 1.0, &mut y); 39 | }); 40 | } 41 | 42 | #[bench] 43 | fn gemv_64_32(bench: &mut Bencher) 44 | { 45 | let a = Array::zeros((64, 32)); 46 | let (m, n) = a.dim(); 47 | let x = Array::zeros(n); 48 | let mut y = Array::zeros(m); 49 | bench.iter(|| { 50 | general_mat_vec_mul(1.0, &a, &x, 1.0, &mut y); 51 | }); 52 | } 53 | 54 | #[bench] 55 | fn cgemm_100(bench: &mut Bencher) 56 | { 57 | cgemm_bench::(100, bench); 58 | } 59 | 60 | #[bench] 61 | fn zgemm_100(bench: &mut Bencher) 62 | { 63 | cgemm_bench::(100, bench); 64 | } 65 | 66 | fn cgemm_bench(size: usize, bench: &mut Bencher) 67 | where A: LinalgScalar + Float 68 | { 69 | let (m, k, n) = (size, size, size); 70 | let a = Array::, _>::zeros((m, k)); 71 | 72 | let x = Array::zeros((k, n)); 73 | let mut y = Array::zeros((m, n)); 74 | bench.iter(|| { 75 | general_mat_mul(Complex::one(), &a, &x, Complex::zero(), &mut y); 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /benches/higher-order.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | #![allow( 3 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 4 | )] 5 | extern crate test; 6 | use test::black_box; 7 | use test::Bencher; 8 | 9 | use ndarray::prelude::*; 10 | 11 | const N: usize = 1024; 12 | const X: usize = 64; 13 | const Y: usize = 16; 14 | 15 | #[cfg(feature = "std")] 16 | #[bench] 17 | fn map_regular(bench: &mut Bencher) 18 | { 19 | let a = Array::linspace(0., 127., N) 20 | .into_shape_with_order((X, Y)) 21 | .unwrap(); 22 | bench.iter(|| a.map(|&x| 2. * x)); 23 | } 24 | 25 | pub fn double_array(mut a: ArrayViewMut2<'_, f64>) 26 | { 27 | a *= 2.0; 28 | } 29 | 30 | #[cfg(feature = "std")] 31 | #[bench] 32 | fn map_stride_double_f64(bench: &mut Bencher) 33 | { 34 | let mut a = Array::linspace(0., 127., N * 2) 35 | .into_shape_with_order([X, Y * 2]) 36 | .unwrap(); 37 | let mut av = a.slice_mut(s![.., ..;2]); 38 | bench.iter(|| { 39 | double_array(av.view_mut()); 40 | }); 41 | } 42 | 43 | #[cfg(feature = "std")] 44 | #[bench] 45 | fn map_stride_f64(bench: &mut Bencher) 46 | { 47 | let a = Array::linspace(0., 127., N * 2) 48 | .into_shape_with_order([X, Y * 2]) 49 | .unwrap(); 50 | let av = a.slice(s![.., ..;2]); 51 | bench.iter(|| av.map(|&x| 2. * x)); 52 | } 53 | 54 | #[cfg(feature = "std")] 55 | #[bench] 56 | fn map_stride_u32(bench: &mut Bencher) 57 | { 58 | let a = Array::linspace(0., 127., N * 2) 59 | .into_shape_with_order([X, Y * 2]) 60 | .unwrap(); 61 | let b = a.mapv(|x| x as u32); 62 | let av = b.slice(s![.., ..;2]); 63 | bench.iter(|| av.map(|&x| 2 * x)); 64 | } 65 | 66 | #[cfg(feature = "std")] 67 | #[bench] 68 | fn fold_axis(bench: &mut Bencher) 69 | { 70 | let a = Array::linspace(0., 127., N * 2) 71 | .into_shape_with_order([X, Y * 2]) 72 | .unwrap(); 73 | bench.iter(|| a.fold_axis(Axis(0), 0., |&acc, &elt| acc + elt)); 74 | } 75 | 76 | const MA: usize = 64; 77 | const MASZ: usize = MA * MA; 78 | 79 | #[bench] 80 | fn map_axis_0(bench: &mut Bencher) 81 | { 82 | let a = Array::from_iter(0..MASZ as i32) 83 | .into_shape_with_order([MA, MA]) 84 | .unwrap(); 85 | bench.iter(|| a.map_axis(Axis(0), black_box)); 86 | } 87 | 88 | #[bench] 89 | fn map_axis_1(bench: &mut Bencher) 90 | { 91 | let a = Array::from_iter(0..MASZ as i32) 92 | .into_shape_with_order([MA, MA]) 93 | .unwrap(); 94 | bench.iter(|| a.map_axis(Axis(1), black_box)); 95 | } 96 | -------------------------------------------------------------------------------- /benches/numeric.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | use test::Bencher; 5 | 6 | use ndarray::prelude::*; 7 | 8 | const N: usize = 1024; 9 | const X: usize = 64; 10 | const Y: usize = 16; 11 | 12 | #[cfg(feature = "std")] 13 | #[bench] 14 | fn clip(bench: &mut Bencher) 15 | { 16 | let mut a = Array::linspace(0., 127., N * 2) 17 | .into_shape_with_order([X, Y * 2]) 18 | .unwrap(); 19 | let min = 2.; 20 | let max = 5.; 21 | bench.iter(|| { 22 | a.mapv_inplace(|mut x| { 23 | if x < min { 24 | x = min 25 | } 26 | if x > max { 27 | x = max 28 | } 29 | x 30 | }) 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /benches/par_rayon.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "rayon")] 2 | #![feature(test)] 3 | 4 | use ndarray::parallel::prelude::*; 5 | use ndarray::prelude::*; 6 | 7 | extern crate test; 8 | use test::Bencher; 9 | 10 | use ndarray::Zip; 11 | 12 | const EXP_N: usize = 256; 13 | const ADDN: usize = 512; 14 | 15 | fn set_threads() 16 | { 17 | // Consider setting a fixed number of threads here, for example to avoid 18 | // oversubscribing on hyperthreaded cores. 19 | // let n = 4; 20 | // let _ = rayon::ThreadPoolBuilder::new().num_threads(n).build_global(); 21 | } 22 | 23 | #[bench] 24 | fn map_exp_regular(bench: &mut Bencher) 25 | { 26 | let mut a = Array2::::zeros((EXP_N, EXP_N)); 27 | a.swap_axes(0, 1); 28 | bench.iter(|| { 29 | a.mapv_inplace(|x| x.exp()); 30 | }); 31 | } 32 | 33 | #[bench] 34 | fn rayon_exp_regular(bench: &mut Bencher) 35 | { 36 | set_threads(); 37 | let mut a = Array2::::zeros((EXP_N, EXP_N)); 38 | a.swap_axes(0, 1); 39 | bench.iter(|| { 40 | a.view_mut().into_par_iter().for_each(|x| *x = x.exp()); 41 | }); 42 | } 43 | 44 | const FASTEXP: usize = EXP_N; 45 | 46 | #[inline] 47 | fn fastexp(x: f64) -> f64 48 | { 49 | let x = 1. + x / 1024.; 50 | x.powi(1024) 51 | } 52 | 53 | #[bench] 54 | fn map_fastexp_regular(bench: &mut Bencher) 55 | { 56 | let mut a = Array2::::zeros((FASTEXP, FASTEXP)); 57 | bench.iter(|| a.mapv_inplace(|x| fastexp(x))); 58 | } 59 | 60 | #[bench] 61 | fn rayon_fastexp_regular(bench: &mut Bencher) 62 | { 63 | set_threads(); 64 | let mut a = Array2::::zeros((FASTEXP, FASTEXP)); 65 | bench.iter(|| { 66 | a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x)); 67 | }); 68 | } 69 | 70 | #[bench] 71 | fn map_fastexp_cut(bench: &mut Bencher) 72 | { 73 | let mut a = Array2::::zeros((FASTEXP, FASTEXP)); 74 | let mut a = a.slice_mut(s![.., ..-1]); 75 | bench.iter(|| a.mapv_inplace(|x| fastexp(x))); 76 | } 77 | 78 | #[bench] 79 | fn rayon_fastexp_cut(bench: &mut Bencher) 80 | { 81 | set_threads(); 82 | let mut a = Array2::::zeros((FASTEXP, FASTEXP)); 83 | let mut a = a.slice_mut(s![.., ..-1]); 84 | bench.iter(|| { 85 | a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x)); 86 | }); 87 | } 88 | 89 | #[bench] 90 | fn map_fastexp_by_axis(bench: &mut Bencher) 91 | { 92 | let mut a = Array2::::zeros((FASTEXP, FASTEXP)); 93 | bench.iter(|| { 94 | for mut sheet in a.axis_iter_mut(Axis(0)) { 95 | sheet.mapv_inplace(fastexp) 96 | } 97 | }); 98 | } 99 | 100 | #[bench] 101 | fn rayon_fastexp_by_axis(bench: &mut Bencher) 102 | { 103 | set_threads(); 104 | let mut a = Array2::::zeros((FASTEXP, FASTEXP)); 105 | bench.iter(|| { 106 | a.axis_iter_mut(Axis(0)) 107 | .into_par_iter() 108 | .for_each(|mut sheet| sheet.mapv_inplace(fastexp)); 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn rayon_fastexp_zip(bench: &mut Bencher) 114 | { 115 | set_threads(); 116 | let mut a = Array2::::zeros((FASTEXP, FASTEXP)); 117 | bench.iter(|| { 118 | Zip::from(&mut a) 119 | .into_par_iter() 120 | .for_each(|(elt,)| *elt = fastexp(*elt)); 121 | }); 122 | } 123 | 124 | #[bench] 125 | fn add(bench: &mut Bencher) 126 | { 127 | let mut a = Array2::::zeros((ADDN, ADDN)); 128 | let b = Array2::::zeros((ADDN, ADDN)); 129 | let c = Array2::::zeros((ADDN, ADDN)); 130 | let d = Array2::::zeros((ADDN, ADDN)); 131 | bench.iter(|| { 132 | azip!((a in &mut a, &b in &b, &c in &c, &d in &d) { 133 | *a += b.exp() + c.exp() + d.exp(); 134 | }); 135 | }); 136 | } 137 | 138 | #[bench] 139 | fn rayon_add(bench: &mut Bencher) 140 | { 141 | set_threads(); 142 | let mut a = Array2::::zeros((ADDN, ADDN)); 143 | let b = Array2::::zeros((ADDN, ADDN)); 144 | let c = Array2::::zeros((ADDN, ADDN)); 145 | let d = Array2::::zeros((ADDN, ADDN)); 146 | bench.iter(|| { 147 | par_azip!((a in &mut a, b in &b, c in &c, d in &d) { 148 | *a += b.exp() + c.exp() + d.exp(); 149 | }); 150 | }); 151 | } 152 | 153 | const COLL_STRING_N: usize = 64; 154 | const COLL_F64_N: usize = 128; 155 | 156 | #[bench] 157 | fn vec_string_collect(bench: &mut test::Bencher) 158 | { 159 | let v = vec![""; COLL_STRING_N * COLL_STRING_N]; 160 | bench.iter(|| v.iter().map(|s| s.to_owned()).collect::>()); 161 | } 162 | 163 | #[bench] 164 | fn array_string_collect(bench: &mut test::Bencher) 165 | { 166 | let v = Array::from_elem((COLL_STRING_N, COLL_STRING_N), ""); 167 | bench.iter(|| Zip::from(&v).par_map_collect(|s| s.to_owned())); 168 | } 169 | 170 | #[bench] 171 | fn vec_f64_collect(bench: &mut test::Bencher) 172 | { 173 | let v = vec![1.; COLL_F64_N * COLL_F64_N]; 174 | bench.iter(|| v.iter().map(|s| s + 1.).collect::>()); 175 | } 176 | 177 | #[bench] 178 | fn array_f64_collect(bench: &mut test::Bencher) 179 | { 180 | let v = Array::from_elem((COLL_F64_N, COLL_F64_N), 1.); 181 | bench.iter(|| Zip::from(&v).par_map_collect(|s| s + 1.)); 182 | } 183 | -------------------------------------------------------------------------------- /benches/reserve.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | use test::Bencher; 5 | 6 | use ndarray::prelude::*; 7 | 8 | #[bench] 9 | fn push_reserve(bench: &mut Bencher) 10 | { 11 | let ones: Array = array![1f32]; 12 | bench.iter(|| { 13 | let mut a: Array = array![]; 14 | a.reserve(Axis(0), 100).unwrap(); 15 | for _ in 0..100 { 16 | a.append(Axis(0), ones.view()).unwrap(); 17 | } 18 | }); 19 | } 20 | 21 | #[bench] 22 | fn push_no_reserve(bench: &mut Bencher) 23 | { 24 | let ones: Array = array![1f32]; 25 | bench.iter(|| { 26 | let mut a: Array = array![]; 27 | for _ in 0..100 { 28 | a.append(Axis(0), ones.view()).unwrap(); 29 | } 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /benches/to_shape.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | use test::Bencher; 5 | 6 | use ndarray::prelude::*; 7 | use ndarray::Order; 8 | 9 | #[bench] 10 | fn to_shape2_1(bench: &mut Bencher) 11 | { 12 | let a = Array::::zeros((4, 5)); 13 | let view = a.view(); 14 | bench.iter(|| view.to_shape(4 * 5).unwrap()); 15 | } 16 | 17 | #[bench] 18 | fn to_shape2_2_same(bench: &mut Bencher) 19 | { 20 | let a = Array::::zeros((4, 5)); 21 | let view = a.view(); 22 | bench.iter(|| view.to_shape((4, 5)).unwrap()); 23 | } 24 | 25 | #[bench] 26 | fn to_shape2_2_flip(bench: &mut Bencher) 27 | { 28 | let a = Array::::zeros((4, 5)); 29 | let view = a.view(); 30 | bench.iter(|| view.to_shape((5, 4)).unwrap()); 31 | } 32 | 33 | #[bench] 34 | fn to_shape2_3(bench: &mut Bencher) 35 | { 36 | let a = Array::::zeros((4, 5)); 37 | let view = a.view(); 38 | bench.iter(|| view.to_shape((2, 5, 2)).unwrap()); 39 | } 40 | 41 | #[bench] 42 | fn to_shape3_1(bench: &mut Bencher) 43 | { 44 | let a = Array::::zeros((3, 4, 5)); 45 | let view = a.view(); 46 | bench.iter(|| view.to_shape(3 * 4 * 5).unwrap()); 47 | } 48 | 49 | #[bench] 50 | fn to_shape3_2_order(bench: &mut Bencher) 51 | { 52 | let a = Array::::zeros((3, 4, 5)); 53 | let view = a.view(); 54 | bench.iter(|| view.to_shape((12, 5)).unwrap()); 55 | } 56 | 57 | #[bench] 58 | fn to_shape3_2_outoforder(bench: &mut Bencher) 59 | { 60 | let a = Array::::zeros((3, 4, 5)); 61 | let view = a.view(); 62 | bench.iter(|| view.to_shape((4, 15)).unwrap()); 63 | } 64 | 65 | #[bench] 66 | fn to_shape3_3c(bench: &mut Bencher) 67 | { 68 | let a = Array::::zeros((3, 4, 5)); 69 | let view = a.view(); 70 | bench.iter(|| view.to_shape((3, 4, 5)).unwrap()); 71 | } 72 | 73 | #[bench] 74 | fn to_shape3_3f(bench: &mut Bencher) 75 | { 76 | let a = Array::::zeros((3, 4, 5).f()); 77 | let view = a.view(); 78 | bench.iter(|| view.to_shape(((3, 4, 5), Order::F)).unwrap()); 79 | } 80 | 81 | #[bench] 82 | fn to_shape3_4c(bench: &mut Bencher) 83 | { 84 | let a = Array::::zeros((3, 4, 5)); 85 | let view = a.view(); 86 | bench.iter(|| view.to_shape(((2, 3, 2, 5), Order::C)).unwrap()); 87 | } 88 | 89 | #[bench] 90 | fn to_shape3_4f(bench: &mut Bencher) 91 | { 92 | let a = Array::::zeros((3, 4, 5).f()); 93 | let view = a.view(); 94 | bench.iter(|| view.to_shape(((2, 3, 2, 5), Order::F)).unwrap()); 95 | } 96 | -------------------------------------------------------------------------------- /benches/zip.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | use ndarray::s; 4 | use ndarray::IntoNdProducer; 5 | use ndarray::{Array3, ShapeBuilder, Zip}; 6 | use test::{black_box, Bencher}; 7 | 8 | pub fn zip_copy<'a, A, P, Q>(data: P, out: Q) 9 | where 10 | P: IntoNdProducer, 11 | Q: IntoNdProducer, 12 | A: Copy + 'a, 13 | { 14 | Zip::from(data).and(out).for_each(|&i, o| { 15 | *o = i; 16 | }); 17 | } 18 | 19 | pub fn zip_copy_split<'a, A, P, Q>(data: P, out: Q) 20 | where 21 | P: IntoNdProducer, 22 | Q: IntoNdProducer, 23 | A: Copy + 'a, 24 | { 25 | let z = Zip::from(data).and(out); 26 | let (z1, z2) = z.split(); 27 | let (z11, z12) = z1.split(); 28 | let (z21, z22) = z2.split(); 29 | let f = |&i: &A, o: &mut A| *o = i; 30 | z11.for_each(f); 31 | z12.for_each(f); 32 | z21.for_each(f); 33 | z22.for_each(f); 34 | } 35 | 36 | pub fn zip_indexed(data: &Array3, out: &mut Array3) 37 | { 38 | Zip::indexed(data).and(out).for_each(|idx, &i, o| { 39 | let _ = black_box(idx); 40 | *o = i; 41 | }); 42 | } 43 | 44 | // array size in benchmarks 45 | const SZ3: (usize, usize, usize) = (100, 110, 100); 46 | 47 | #[bench] 48 | fn zip_cc(b: &mut Bencher) 49 | { 50 | let data: Array3 = Array3::zeros(SZ3); 51 | let mut out = Array3::zeros(data.dim()); 52 | b.iter(|| zip_copy(&data, &mut out)); 53 | } 54 | 55 | #[bench] 56 | fn zip_cf(b: &mut Bencher) 57 | { 58 | let data: Array3 = Array3::zeros(SZ3); 59 | let mut out = Array3::zeros(data.dim().f()); 60 | b.iter(|| zip_copy(&data, &mut out)); 61 | } 62 | 63 | #[bench] 64 | fn zip_fc(b: &mut Bencher) 65 | { 66 | let data: Array3 = Array3::zeros(SZ3.f()); 67 | let mut out = Array3::zeros(data.dim()); 68 | b.iter(|| zip_copy(&data, &mut out)); 69 | } 70 | 71 | #[bench] 72 | fn zip_ff(b: &mut Bencher) 73 | { 74 | let data: Array3 = Array3::zeros(SZ3.f()); 75 | let mut out = Array3::zeros(data.dim().f()); 76 | b.iter(|| zip_copy(&data, &mut out)); 77 | } 78 | 79 | #[bench] 80 | fn zip_indexed_cc(b: &mut Bencher) 81 | { 82 | let data: Array3 = Array3::zeros(SZ3); 83 | let mut out = Array3::zeros(data.dim()); 84 | b.iter(|| zip_indexed(&data, &mut out)); 85 | } 86 | 87 | #[bench] 88 | fn zip_indexed_ff(b: &mut Bencher) 89 | { 90 | let data: Array3 = Array3::zeros(SZ3.f()); 91 | let mut out = Array3::zeros(data.dim().f()); 92 | b.iter(|| zip_indexed(&data, &mut out)); 93 | } 94 | 95 | #[bench] 96 | fn slice_zip_cc(b: &mut Bencher) 97 | { 98 | let data: Array3 = Array3::zeros(SZ3); 99 | let mut out = Array3::zeros(data.dim()); 100 | let data = data.slice(s![1.., 1.., 1..]); 101 | let mut out = out.slice_mut(s![1.., 1.., 1..]); 102 | b.iter(|| zip_copy(&data, &mut out)); 103 | } 104 | 105 | #[bench] 106 | fn slice_zip_ff(b: &mut Bencher) 107 | { 108 | let data: Array3 = Array3::zeros(SZ3.f()); 109 | let mut out = Array3::zeros(data.dim().f()); 110 | let data = data.slice(s![1.., 1.., 1..]); 111 | let mut out = out.slice_mut(s![1.., 1.., 1..]); 112 | b.iter(|| zip_copy(&data, &mut out)); 113 | } 114 | 115 | #[bench] 116 | fn slice_split_zip_cc(b: &mut Bencher) 117 | { 118 | let data: Array3 = Array3::zeros(SZ3); 119 | let mut out = Array3::zeros(data.dim()); 120 | let data = data.slice(s![1.., 1.., 1..]); 121 | let mut out = out.slice_mut(s![1.., 1.., 1..]); 122 | b.iter(|| zip_copy_split(&data, &mut out)); 123 | } 124 | 125 | #[bench] 126 | fn slice_split_zip_ff(b: &mut Bencher) 127 | { 128 | let data: Array3 = Array3::zeros(SZ3.f()); 129 | let mut out = Array3::zeros(data.dim().f()); 130 | let data = data.slice(s![1.., 1.., 1..]); 131 | let mut out = out.slice_mut(s![1.., 1.., 1..]); 132 | b.iter(|| zip_copy_split(&data, &mut out)); 133 | } 134 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | single-char-binding-names-threshold = 1000 2 | -------------------------------------------------------------------------------- /crates/blas-mock-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blas-mock-tests" 3 | version = "0.1.0" 4 | edition = "2018" 5 | publish = false 6 | 7 | [lib] 8 | test = false 9 | doc = false 10 | doctest = false 11 | 12 | [dependencies] 13 | cblas-sys = { workspace = true } 14 | 15 | [dev-dependencies] 16 | ndarray = { workspace = true, features = ["approx", "blas"] } 17 | ndarray-gen = { workspace = true } 18 | itertools = { workspace = true } 19 | -------------------------------------------------------------------------------- /crates/blas-mock-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Mock interfaces to BLAS 2 | 3 | use core::cell::RefCell; 4 | use core::ffi::{c_double, c_float, c_int}; 5 | use std::thread_local; 6 | 7 | use cblas_sys::{c_double_complex, c_float_complex, CBLAS_LAYOUT, CBLAS_TRANSPOSE}; 8 | 9 | thread_local! { 10 | /// This counter is incremented every time a gemm function is called 11 | pub static CALL_COUNT: RefCell = RefCell::new(0); 12 | } 13 | 14 | #[rustfmt::skip] 15 | #[no_mangle] 16 | #[allow(unused)] 17 | pub unsafe extern "C" fn cblas_sgemm( 18 | layout: CBLAS_LAYOUT, 19 | transa: CBLAS_TRANSPOSE, 20 | transb: CBLAS_TRANSPOSE, 21 | m: c_int, 22 | n: c_int, 23 | k: c_int, 24 | alpha: c_float, 25 | a: *const c_float, 26 | lda: c_int, 27 | b: *const c_float, 28 | ldb: c_int, 29 | beta: c_float, 30 | c: *mut c_float, 31 | ldc: c_int 32 | ) { 33 | CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); 34 | } 35 | 36 | #[rustfmt::skip] 37 | #[no_mangle] 38 | #[allow(unused)] 39 | pub unsafe extern "C" fn cblas_dgemm( 40 | layout: CBLAS_LAYOUT, 41 | transa: CBLAS_TRANSPOSE, 42 | transb: CBLAS_TRANSPOSE, 43 | m: c_int, 44 | n: c_int, 45 | k: c_int, 46 | alpha: c_double, 47 | a: *const c_double, 48 | lda: c_int, 49 | b: *const c_double, 50 | ldb: c_int, 51 | beta: c_double, 52 | c: *mut c_double, 53 | ldc: c_int 54 | ) { 55 | CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); 56 | } 57 | 58 | #[rustfmt::skip] 59 | #[no_mangle] 60 | #[allow(unused)] 61 | pub unsafe extern "C" fn cblas_cgemm( 62 | layout: CBLAS_LAYOUT, 63 | transa: CBLAS_TRANSPOSE, 64 | transb: CBLAS_TRANSPOSE, 65 | m: c_int, 66 | n: c_int, 67 | k: c_int, 68 | alpha: *const c_float_complex, 69 | a: *const c_float_complex, 70 | lda: c_int, 71 | b: *const c_float_complex, 72 | ldb: c_int, 73 | beta: *const c_float_complex, 74 | c: *mut c_float_complex, 75 | ldc: c_int 76 | ) { 77 | CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); 78 | } 79 | 80 | #[rustfmt::skip] 81 | #[no_mangle] 82 | #[allow(unused)] 83 | pub unsafe extern "C" fn cblas_zgemm( 84 | layout: CBLAS_LAYOUT, 85 | transa: CBLAS_TRANSPOSE, 86 | transb: CBLAS_TRANSPOSE, 87 | m: c_int, 88 | n: c_int, 89 | k: c_int, 90 | alpha: *const c_double_complex, 91 | a: *const c_double_complex, 92 | lda: c_int, 93 | b: *const c_double_complex, 94 | ldb: c_int, 95 | beta: *const c_double_complex, 96 | c: *mut c_double_complex, 97 | ldc: c_int 98 | ) { 99 | CALL_COUNT.with(|ctx| *ctx.borrow_mut() += 1); 100 | } 101 | -------------------------------------------------------------------------------- /crates/blas-mock-tests/tests/use-blas.rs: -------------------------------------------------------------------------------- 1 | extern crate ndarray; 2 | 3 | use ndarray::prelude::*; 4 | 5 | use blas_mock_tests::CALL_COUNT; 6 | use ndarray::linalg::general_mat_mul; 7 | use ndarray::Order; 8 | use ndarray_gen::array_builder::ArrayBuilder; 9 | 10 | use itertools::iproduct; 11 | 12 | #[test] 13 | fn test_gen_mat_mul_uses_blas() 14 | { 15 | let alpha = 1.0; 16 | let beta = 0.0; 17 | 18 | let sizes = vec![ 19 | (8, 8, 8), 20 | (10, 10, 10), 21 | (8, 8, 1), 22 | (1, 10, 10), 23 | (10, 1, 10), 24 | (10, 10, 1), 25 | (1, 10, 1), 26 | (10, 1, 1), 27 | (1, 1, 10), 28 | (4, 17, 3), 29 | (17, 3, 22), 30 | (19, 18, 2), 31 | (16, 17, 15), 32 | (15, 16, 17), 33 | (67, 63, 62), 34 | ]; 35 | let strides = &[1, 2, -1, -2]; 36 | let cf_order = [Order::C, Order::F]; 37 | 38 | // test different strides and memory orders 39 | for &(m, k, n) in &sizes { 40 | for (&s1, &s2) in iproduct!(strides, strides) { 41 | for (ord1, ord2, ord3) in iproduct!(cf_order, cf_order, cf_order) { 42 | println!("Case s1={}, s2={}, orders={:?}, {:?}, {:?}", s1, s2, ord1, ord2, ord3); 43 | 44 | let a = ArrayBuilder::new((m, k)).memory_order(ord1).build(); 45 | let b = ArrayBuilder::new((k, n)).memory_order(ord2).build(); 46 | let mut c = ArrayBuilder::new((m, n)).memory_order(ord3).build(); 47 | 48 | { 49 | let av; 50 | let bv; 51 | let mut cv; 52 | 53 | if s1 != 1 || s2 != 1 { 54 | av = a.slice(s![..;s1, ..;s2]); 55 | bv = b.slice(s![..;s2, ..;s2]); 56 | cv = c.slice_mut(s![..;s1, ..;s2]); 57 | } else { 58 | // different stride cases for slicing versus not sliced (for axes of 59 | // len=1); so test not sliced here. 60 | av = a.view(); 61 | bv = b.view(); 62 | cv = c.view_mut(); 63 | } 64 | 65 | let pre_count = CALL_COUNT.with(|ctx| *ctx.borrow()); 66 | general_mat_mul(alpha, &av, &bv, beta, &mut cv); 67 | let after_count = CALL_COUNT.with(|ctx| *ctx.borrow()); 68 | let ncalls = after_count - pre_count; 69 | debug_assert!(ncalls <= 1); 70 | 71 | let always_uses_blas = s1 == 1 && s2 == 1; 72 | 73 | if always_uses_blas { 74 | assert_eq!(ncalls, 1, "Contiguous arrays should use blas, orders={:?}", (ord1, ord2, ord3)); 75 | } 76 | 77 | let should_use_blas = av.strides().iter().all(|&s| s > 0) 78 | && bv.strides().iter().all(|&s| s > 0) 79 | && cv.strides().iter().all(|&s| s > 0) 80 | && av.strides().iter().any(|&s| s == 1) 81 | && bv.strides().iter().any(|&s| s == 1) 82 | && cv.strides().iter().any(|&s| s == 1); 83 | assert_eq!(should_use_blas, ncalls > 0); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /crates/blas-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blas-tests" 3 | version = "0.1.0" 4 | authors = ["bluss"] 5 | publish = false 6 | edition = "2018" 7 | 8 | [lib] 9 | test = false 10 | doc = false 11 | doctest = false 12 | 13 | [dependencies] 14 | ndarray = { workspace = true, features = ["approx", "blas"] } 15 | ndarray-gen = { workspace = true } 16 | 17 | blas-src = { version = "0.10", optional = true } 18 | openblas-src = { version = ">=0.10.11", optional = true } 19 | netlib-src = { version = "0.8", optional = true } 20 | blis-src = { version = "0.2", features = ["system"], optional = true } 21 | 22 | [dev-dependencies] 23 | defmac = "0.2" 24 | approx = { workspace = true } 25 | num-traits = { workspace = true } 26 | num-complex = { workspace = true } 27 | itertools = { workspace = true } 28 | 29 | [features] 30 | # Just for making an example and to help testing, , multiple different possible 31 | # configurations are selectable here. 32 | openblas-system = ["blas-src", "blas-src/openblas", "openblas-src/system"] 33 | openblas-cache = ["blas-src", "blas-src/openblas", "openblas-src/cache"] 34 | netlib = ["blas-src", "blas-src/netlib"] 35 | netlib-system = ["blas-src", "blas-src/netlib", "netlib-src/system"] 36 | blis-system = ["blas-src", "blas-src/blis", "blis-src/system"] 37 | accelerate = ["blas-src", "blas-src/accelerate"] 38 | -------------------------------------------------------------------------------- /crates/blas-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "blas-src"))] 2 | compile_error!("Missing backend: could not compile. 3 | Help: For this testing crate, select one of the blas backend features, for example \ 4 | openblas-system"); 5 | -------------------------------------------------------------------------------- /crates/blas-tests/tests/dyn.rs: -------------------------------------------------------------------------------- 1 | extern crate blas_src; 2 | use ndarray::{linalg::Dot, Array1, Array2, ArrayD, Ix1, Ix2}; 3 | 4 | #[test] 5 | fn test_arrayd_dot_2d() 6 | { 7 | let mat1 = ArrayD::from_shape_vec(vec![3, 2], vec![3.0; 6]).unwrap(); 8 | let mat2 = ArrayD::from_shape_vec(vec![2, 3], vec![1.0; 6]).unwrap(); 9 | 10 | let result = mat1.dot(&mat2); 11 | 12 | // Verify the result is correct 13 | assert_eq!(result.ndim(), 2); 14 | assert_eq!(result.shape(), &[3, 3]); 15 | 16 | // Compare with Array2 implementation 17 | let mat1_2d = Array2::from_shape_vec((3, 2), vec![3.0; 6]).unwrap(); 18 | let mat2_2d = Array2::from_shape_vec((2, 3), vec![1.0; 6]).unwrap(); 19 | let expected = mat1_2d.dot(&mat2_2d); 20 | 21 | assert_eq!(result.into_dimensionality::().unwrap(), expected); 22 | } 23 | 24 | #[test] 25 | fn test_arrayd_dot_1d() 26 | { 27 | // Test 1D array dot product 28 | let vec1 = ArrayD::from_shape_vec(vec![3], vec![1.0, 2.0, 3.0]).unwrap(); 29 | let vec2 = ArrayD::from_shape_vec(vec![3], vec![4.0, 5.0, 6.0]).unwrap(); 30 | 31 | let result = vec1.dot(&vec2); 32 | 33 | // Verify scalar result 34 | assert_eq!(result.ndim(), 0); 35 | assert_eq!(result.shape(), &[]); 36 | assert_eq!(result[[]], 32.0); // 1*4 + 2*5 + 3*6 37 | } 38 | 39 | #[test] 40 | #[should_panic(expected = "Dot product for ArrayD is only supported for 1D and 2D arrays")] 41 | fn test_arrayd_dot_3d() 42 | { 43 | // Test that 3D arrays are not supported 44 | let arr1 = ArrayD::from_shape_vec(vec![2, 2, 2], vec![1.0; 8]).unwrap(); 45 | let arr2 = ArrayD::from_shape_vec(vec![2, 2, 2], vec![1.0; 8]).unwrap(); 46 | 47 | let _result = arr1.dot(&arr2); // Should panic 48 | } 49 | 50 | #[test] 51 | #[should_panic(expected = "ndarray: inputs 2 × 3 and 4 × 5 are not compatible for matrix multiplication")] 52 | fn test_arrayd_dot_incompatible_dims() 53 | { 54 | // Test arrays with incompatible dimensions 55 | let arr1 = ArrayD::from_shape_vec(vec![2, 3], vec![1.0; 6]).unwrap(); 56 | let arr2 = ArrayD::from_shape_vec(vec![4, 5], vec![1.0; 20]).unwrap(); 57 | 58 | let _result = arr1.dot(&arr2); // Should panic 59 | } 60 | 61 | #[test] 62 | fn test_arrayd_dot_matrix_vector() 63 | { 64 | // Test matrix-vector multiplication 65 | let mat = ArrayD::from_shape_vec(vec![3, 2], vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap(); 66 | let vec = ArrayD::from_shape_vec(vec![2], vec![1.0, 2.0]).unwrap(); 67 | 68 | let result = mat.dot(&vec); 69 | 70 | // Verify result 71 | assert_eq!(result.ndim(), 1); 72 | assert_eq!(result.shape(), &[3]); 73 | 74 | // Compare with Array2 implementation 75 | let mat_2d = Array2::from_shape_vec((3, 2), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap(); 76 | let vec_1d = Array1::from_vec(vec![1.0, 2.0]); 77 | let expected = mat_2d.dot(&vec_1d); 78 | 79 | assert_eq!(result.into_dimensionality::().unwrap(), expected); 80 | } 81 | -------------------------------------------------------------------------------- /crates/ndarray-gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ndarray-gen" 3 | version = "0.1.0" 4 | edition = "2018" 5 | publish = false 6 | 7 | [dependencies] 8 | ndarray = { workspace = true, default-features = false } 9 | num-traits = { workspace = true } 10 | -------------------------------------------------------------------------------- /crates/ndarray-gen/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## ndarray-gen 3 | 4 | Array generation functions, used for testing. 5 | -------------------------------------------------------------------------------- /crates/ndarray-gen/src/array_builder.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use ndarray::Array; 10 | use ndarray::Dimension; 11 | use ndarray::IntoDimension; 12 | use ndarray::Order; 13 | 14 | use num_traits::Num; 15 | 16 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 17 | pub struct ArrayBuilder 18 | { 19 | dim: D, 20 | memory_order: Order, 21 | generator: ElementGenerator, 22 | } 23 | 24 | /// How to generate elements 25 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 26 | pub enum ElementGenerator 27 | { 28 | Sequential, 29 | Checkerboard, 30 | Zero, 31 | } 32 | 33 | impl Default for ArrayBuilder 34 | { 35 | fn default() -> Self 36 | { 37 | Self::new(D::zeros(D::NDIM.unwrap_or(1))) 38 | } 39 | } 40 | 41 | impl ArrayBuilder 42 | where D: Dimension 43 | { 44 | pub fn new(dim: impl IntoDimension) -> Self 45 | { 46 | ArrayBuilder { 47 | dim: dim.into_dimension(), 48 | memory_order: Order::C, 49 | generator: ElementGenerator::Sequential, 50 | } 51 | } 52 | 53 | pub fn memory_order(mut self, order: Order) -> Self 54 | { 55 | self.memory_order = order; 56 | self 57 | } 58 | 59 | pub fn generator(mut self, generator: ElementGenerator) -> Self 60 | { 61 | self.generator = generator; 62 | self 63 | } 64 | 65 | pub fn build(self) -> Array 66 | where T: Num + Clone 67 | { 68 | let zero = T::zero(); 69 | let size = self.dim.size(); 70 | (match self.generator { 71 | ElementGenerator::Sequential => 72 | Array::from_iter(core::iter::successors(Some(zero), |elt| Some(elt.clone() + T::one())).take(size)), 73 | ElementGenerator::Checkerboard => Array::from_iter([T::one(), zero].iter().cycle().take(size).cloned()), 74 | ElementGenerator::Zero => Array::zeros(size), 75 | }) 76 | .into_shape_with_order((self.dim, self.memory_order)) 77 | .unwrap() 78 | } 79 | } 80 | 81 | #[test] 82 | fn test_order() 83 | { 84 | let (m, n) = (12, 13); 85 | let c = ArrayBuilder::new((m, n)) 86 | .memory_order(Order::C) 87 | .build::(); 88 | let f = ArrayBuilder::new((m, n)) 89 | .memory_order(Order::F) 90 | .build::(); 91 | 92 | assert_eq!(c.shape(), &[m, n]); 93 | assert_eq!(f.shape(), &[m, n]); 94 | assert_eq!(c.strides(), &[n as isize, 1]); 95 | assert_eq!(f.strides(), &[1, m as isize]); 96 | } 97 | -------------------------------------------------------------------------------- /crates/ndarray-gen/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | // Copyright 2024 bluss and ndarray developers. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | /// Build ndarray arrays for test purposes 11 | pub mod array_builder; 12 | -------------------------------------------------------------------------------- /crates/numeric-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "numeric-tests" 3 | version = "0.1.0" 4 | authors = ["bluss"] 5 | publish = false 6 | edition = "2018" 7 | 8 | [lib] 9 | test = false 10 | doc = false 11 | doctest = false 12 | 13 | [dependencies] 14 | ndarray = { workspace = true, features = ["approx"] } 15 | ndarray-rand = { workspace = true } 16 | 17 | approx = { workspace = true } 18 | rand = { workspace = true } 19 | rand_distr = { workspace = true } 20 | 21 | blas-src = { optional = true, version = "0.10", default-features = false, features = ["openblas"] } 22 | openblas-src = { optional = true, version = ">=0.10.11", default-features = false, features = ["cblas", "system"] } 23 | 24 | [dev-dependencies] 25 | num-traits = { workspace = true } 26 | num-complex = { workspace = true } 27 | 28 | [features] 29 | test_blas = ["ndarray/blas", "blas-src", "openblas-src"] 30 | -------------------------------------------------------------------------------- /crates/numeric-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "test_blas")] 2 | extern crate blas_src; 3 | -------------------------------------------------------------------------------- /crates/serialization-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serialization-tests" 3 | version = "0.1.0" 4 | authors = ["bluss"] 5 | publish = false 6 | edition = "2018" 7 | 8 | [lib] 9 | test = false 10 | doc = false 11 | doctest = false 12 | 13 | [dependencies] 14 | ndarray = { workspace = true, features = ["serde"] } 15 | 16 | serde = { version = "1.0.100", default-features = false } 17 | ron = { version = "0.8.1" } 18 | 19 | [dev-dependencies] 20 | serde_json = { version = "1.0.40" } 21 | # >=0.8.11 to avoid rmp-serde security vulnerability 22 | # <0.8.14 to allows MSRV 1.64.0 23 | rmp = { version = ">=0.8.11,<0.8.14" } 24 | # Old version to work with Rust 1.64+ 25 | rmp-serde = { version = ">=1.1.1" } 26 | -------------------------------------------------------------------------------- /crates/serialization-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/axis_ops.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 3 | )] 4 | 5 | use ndarray::prelude::*; 6 | 7 | /// Reorder a's axes so that they are in "standard" axis order; 8 | /// make sure axes are in positive stride direction, and merge adjacent 9 | /// axes if possible. 10 | /// 11 | /// This changes the logical order of the elements in the 12 | /// array, so that if we read them in row-major order after regularization, 13 | /// it corresponds to their order in memory. 14 | /// 15 | /// Errors if array has a 0-stride axis 16 | fn regularize(a: &mut ArrayRef) -> Result<(), &'static str> 17 | where 18 | D: Dimension, 19 | A: ::std::fmt::Debug, 20 | { 21 | println!("Regularize:\n{:?}", a); 22 | // reverse all neg axes 23 | while let Some(ax) = a.axes().find(|ax| ax.stride <= 0) { 24 | if ax.stride == 0 { 25 | // no real reason to error on this case; other than 26 | // stride == 0 is incompatible with an owned array. 27 | return Err("Cannot regularize array with stride == 0 axis"); 28 | } 29 | // reverse ax 30 | println!("Reverse {:?}", ax.axis); 31 | a.invert_axis(ax.axis); 32 | } 33 | 34 | // sort by least stride 35 | let mut i = 0; 36 | let n = a.ndim(); 37 | while let Some(ax) = a.axes().rev().skip(i).min_by_key(|ax| ax.stride.abs()) { 38 | let cur_axis = Axis(n - 1 - i); 39 | if ax.axis != cur_axis { 40 | a.swap_axes(cur_axis.index(), ax.axis.index()); 41 | println!("Swap {:?} <=> {:?}", cur_axis, ax.axis); 42 | } 43 | i += 1; 44 | } 45 | 46 | // merge the lower axes if possible 47 | for j in (0..n).rev().skip(1) { 48 | if a.merge_axes(Axis(j), Axis(n - 1)) { 49 | println!("Merged {:?} into {:?}", Axis(j), Axis(n - 1)); 50 | } else { 51 | break; 52 | } 53 | } 54 | println!("Result:\n{:?}\n", a); 55 | Ok(()) 56 | } 57 | 58 | fn main() 59 | { 60 | let mut a = Array::::zeros((2, 3, 4)); 61 | for (i, elt) in (0..).zip(&mut a) { 62 | *elt = i; 63 | } 64 | a.swap_axes(0, 1); 65 | a.swap_axes(0, 2); 66 | a.slice_collapse(s![.., ..;-1, ..]); 67 | regularize(&mut a).unwrap(); 68 | 69 | let mut b = Array::::zeros((2, 3, 4)); 70 | for (i, elt) in (0..).zip(&mut b) { 71 | *elt = i; 72 | } 73 | regularize(&mut b).unwrap(); 74 | 75 | let mut b = b.into_shape_with_order(a.len()).unwrap(); 76 | regularize(&mut b).unwrap(); 77 | 78 | b.invert_axis(Axis(0)); 79 | regularize(&mut b).unwrap(); 80 | 81 | let mut a = Array::::zeros((2, 3, 4)); 82 | for (i, elt) in (0..).zip(&mut a) { 83 | *elt = i; 84 | } 85 | a.slice_collapse(s![..;-1, ..;2, ..]); 86 | regularize(&mut a).unwrap(); 87 | } 88 | -------------------------------------------------------------------------------- /examples/bounds_check_elim.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "lib"] 2 | #![allow( 3 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 4 | )] 5 | 6 | // Test cases for bounds check elimination 7 | 8 | use ndarray::prelude::*; 9 | 10 | /* 11 | pub fn testslice(a: &[f64]) -> f64 { 12 | let mut sum = 0.; 13 | for i in 0..a.len() { 14 | sum += a[i]; 15 | } 16 | sum 17 | } 18 | 19 | pub fn testvec(a: &Vec) -> f64 { 20 | let mut sum = 0.; 21 | for i in 0..a.len() { 22 | sum += a[i]; 23 | } 24 | sum 25 | } 26 | 27 | pub fn testvec_as_slice(a: &Vec) -> f64 { 28 | let a = a.as_slice(); 29 | let mut sum = 0.; 30 | for i in 0..a.len() { 31 | sum += a[i]; 32 | } 33 | sum 34 | } 35 | */ 36 | 37 | #[no_mangle] 38 | pub fn test1d_single(a: &Array1, i: usize) -> f64 39 | { 40 | if i < a.len() { 41 | a[i] 42 | } else { 43 | 0. 44 | } 45 | } 46 | 47 | #[no_mangle] 48 | pub fn test1d_single_mut(a: &mut Array1, i: usize) -> f64 49 | { 50 | if i < a.len() { 51 | *&mut a[i] 52 | } else { 53 | 0. 54 | } 55 | } 56 | 57 | #[no_mangle] 58 | pub fn test1d_len_of(a: &Array1) -> f64 59 | { 60 | let a = a; 61 | let mut sum = 0.; 62 | for i in 0..a.len_of(Axis(0)) { 63 | sum += a[i]; 64 | } 65 | sum 66 | } 67 | 68 | #[no_mangle] 69 | pub fn test1d_range(a: &Array1) -> f64 70 | { 71 | let mut sum = 0.; 72 | for i in 0..a.len() { 73 | sum += a[i]; 74 | } 75 | sum 76 | } 77 | 78 | #[no_mangle] 79 | pub fn test1d_while(a: &Array1) -> f64 80 | { 81 | let mut sum = 0.; 82 | let mut i = 0; 83 | while i < a.len() { 84 | sum += a[i]; 85 | i += 1; 86 | } 87 | sum 88 | } 89 | 90 | #[no_mangle] 91 | pub fn test2d_ranges(a: &Array2) -> f64 92 | { 93 | let mut sum = 0.; 94 | for i in 0..a.nrows() { 95 | for j in 0..a.ncols() { 96 | sum += a[[i, j]]; 97 | } 98 | } 99 | sum 100 | } 101 | 102 | #[no_mangle] 103 | pub fn test2d_whiles(a: &Array2) -> f64 104 | { 105 | let mut sum = 0.; 106 | let mut i = 0; 107 | while i < a.nrows() { 108 | let mut j = 0; 109 | while j < a.ncols() { 110 | sum += a[[i, j]]; 111 | j += 1; 112 | } 113 | i += 1; 114 | } 115 | sum 116 | } 117 | 118 | fn main() {} 119 | -------------------------------------------------------------------------------- /examples/column_standardize.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | use ndarray::prelude::*; 3 | 4 | #[cfg(feature = "std")] 5 | fn main() 6 | { 7 | // This example recreates the following from python/numpy 8 | // counts -= np.mean(counts, axis=0) 9 | // counts /= np.std(counts, axis=0) 10 | 11 | let mut data = array![[-1., -2., -3.], [1., -3., 5.], [2., 2., 2.]]; 12 | 13 | println!("{:8.4}", data); 14 | println!("Mean along axis=0 (along columns):\n{:8.4}", data.mean_axis(Axis(0)).unwrap()); 15 | 16 | data -= &data.mean_axis(Axis(0)).unwrap(); 17 | println!("Centered around mean:\n{:8.4}", data); 18 | 19 | data /= &data.std_axis(Axis(0), 0.); 20 | println!("Scaled to normalize std:\n{:8.4}", data); 21 | 22 | println!("New mean:\n{:8.4}", data.mean_axis(Axis(0)).unwrap()); 23 | println!("New std: \n{:8.4}", data.std_axis(Axis(0), 0.)); 24 | } 25 | 26 | #[cfg(not(feature = "std"))] 27 | fn main() {} 28 | -------------------------------------------------------------------------------- /examples/convo.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | extern crate ndarray; 3 | 4 | #[cfg(feature = "std")] 5 | use num_traits::Float; 6 | 7 | use ndarray::prelude::*; 8 | 9 | const SOBEL_X: [[f32; 3]; 3] = [[-1., 0., 1.], [-2., 0., 2.], [-1., 0., 1.]]; 10 | const SOBEL_Y: [[f32; 3]; 3] = [[1., 2., 1.], [0., 0., 0.], [-1., -2., -1.]]; 11 | const SHARPEN: [[f32; 3]; 3] = [[0., -1., 0.], [-1., 5., -1.], [0., -1., 0.]]; 12 | 13 | type Kernel3x3 = [[A; 3]; 3]; 14 | 15 | #[inline(never)] 16 | #[cfg(feature = "std")] 17 | fn conv_3x3(a: &ArrayRef2, out: &mut ArrayRef2, kernel: &Kernel3x3) 18 | where F: Float 19 | { 20 | let (n, m) = a.dim(); 21 | let (np, mp) = out.dim(); 22 | if n < 3 || m < 3 { 23 | return; 24 | } 25 | assert!(np >= n && mp >= m); 26 | // i, j offset by -1 so that we can use unsigned indices 27 | unsafe { 28 | for i in 0..n - 2 { 29 | for j in 0..m - 2 { 30 | let mut conv = F::zero(); 31 | #[allow(clippy::needless_range_loop)] 32 | for k in 0..3 { 33 | for l in 0..3 { 34 | conv = conv + *a.uget((i + k, j + l)) * kernel[k][l]; 35 | //conv += a[[i + k, j + l]] * x_kernel[k][l]; 36 | } 37 | } 38 | *out.uget_mut((i + 1, j + 1)) = conv; 39 | } 40 | } 41 | } 42 | } 43 | 44 | #[cfg(feature = "std")] 45 | fn main() 46 | { 47 | let n = 16; 48 | let mut a = Array::zeros((n, n)); 49 | // make a circle 50 | let c = (8., 8.); 51 | for ((i, j), elt) in a.indexed_iter_mut() { 52 | { 53 | let s = ((i as f32) - c.0).powi(2) + (j as f32 - c.1).powi(2); 54 | if s.sqrt() > 3. && s.sqrt() < 6. { 55 | *elt = 1.; 56 | } 57 | } 58 | } 59 | println!("{:2}", a); 60 | let mut res = Array::zeros(a.dim()); 61 | for _ in 0..1000 { 62 | conv_3x3(&a.view(), &mut res.view_mut(), &SOBEL_X); 63 | } 64 | println!("{:2}", res); 65 | } 66 | #[cfg(not(feature = "std"))] 67 | fn main() {} 68 | -------------------------------------------------------------------------------- /examples/life.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 3 | )] 4 | 5 | use ndarray::prelude::*; 6 | 7 | const INPUT: &[u8] = include_bytes!("life.txt"); 8 | 9 | const N: usize = 100; 10 | 11 | type Board = Array2; 12 | 13 | fn parse(x: &[u8]) -> Board 14 | { 15 | // make a border of 0 cells 16 | let mut map = Board::from_elem(((N + 2), (N + 2)), 0); 17 | let a = Array::from_iter(x.iter().filter_map(|&b| match b { 18 | b'#' => Some(1), 19 | b'.' => Some(0), 20 | _ => None, 21 | })); 22 | 23 | let a = a.into_shape_with_order((N, N)).unwrap(); 24 | map.slice_mut(s![1..-1, 1..-1]).assign(&a); 25 | map 26 | } 27 | 28 | // Rules 29 | // 30 | // 2 or 3 neighbors: stay alive 31 | // 3 neighbors: birth 32 | // otherwise: death 33 | 34 | fn iterate(z: &mut Board, scratch: &mut Board) 35 | { 36 | // compute number of neighbors 37 | let mut neigh = scratch.view_mut(); 38 | neigh.fill(0); 39 | neigh += &z.slice(s![0..-2, 0..-2]); 40 | neigh += &z.slice(s![0..-2, 1..-1]); 41 | neigh += &z.slice(s![0..-2, 2..]); 42 | 43 | neigh += &z.slice(s![1..-1, 0..-2]); 44 | neigh += &z.slice(s![1..-1, 2..]); 45 | 46 | neigh += &z.slice(s![2.., 0..-2]); 47 | neigh += &z.slice(s![2.., 1..-1]); 48 | neigh += &z.slice(s![2.., 2..]); 49 | 50 | // birth where n = 3 and z[i] = 0, 51 | // survive where n = 2 || n = 3 and z[i] = 1 52 | let mut zv = z.slice_mut(s![1..-1, 1..-1]); 53 | 54 | // this is autovectorized amazingly well! 55 | zv.zip_mut_with(&neigh, |y, &n| *y = ((n == 3) || (n == 2 && *y > 0)) as u8); 56 | } 57 | 58 | fn turn_on_corners(z: &mut Board) 59 | { 60 | let n = z.nrows(); 61 | let m = z.ncols(); 62 | z[[1, 1]] = 1; 63 | z[[1, m - 2]] = 1; 64 | z[[n - 2, 1]] = 1; 65 | z[[n - 2, m - 2]] = 1; 66 | } 67 | 68 | fn render(a: &Board) 69 | { 70 | for row in a.rows() { 71 | for &x in row { 72 | if x > 0 { 73 | print!("#"); 74 | } else { 75 | print!("."); 76 | } 77 | } 78 | println!(); 79 | } 80 | } 81 | 82 | fn main() 83 | { 84 | let mut a = parse(INPUT); 85 | let mut scratch = Board::zeros((N, N)); 86 | let steps = 100; 87 | turn_on_corners(&mut a); 88 | for _ in 0..steps { 89 | iterate(&mut a, &mut scratch); 90 | turn_on_corners(&mut a); 91 | //render(&a); 92 | } 93 | render(&a); 94 | let alive = a.iter().filter(|&&x| x > 0).count(); 95 | println!("After {} steps there are {} cells alive", steps, alive); 96 | } 97 | -------------------------------------------------------------------------------- /examples/lifelite.txt: -------------------------------------------------------------------------------- 1 | ####.#.# 2 | .##...## 3 | ###.##.. 4 | ....#..# 5 | ..###.## 6 | #.#...#. 7 | ....##.# 8 | #...##.. 9 | -------------------------------------------------------------------------------- /examples/rollaxis.rs: -------------------------------------------------------------------------------- 1 | use ndarray::prelude::*; 2 | use ndarray::Data; 3 | 4 | pub fn roll_axis(mut a: ArrayBase, to: Axis, from: Axis) -> ArrayBase 5 | where 6 | S: Data, 7 | D: Dimension, 8 | { 9 | let i = to.index(); 10 | let mut j = from.index(); 11 | if j > i { 12 | while i != j { 13 | a.swap_axes(i, j); 14 | j -= 1; 15 | } 16 | } else { 17 | while i != j { 18 | a.swap_axes(i, j); 19 | j += 1; 20 | } 21 | } 22 | a 23 | } 24 | 25 | fn main() 26 | { 27 | let mut data = array![ 28 | [[-1., 0., -2.], [1., 7., -3.]], 29 | [[1., 0., -3.], [1., 7., 5.]], 30 | [[1., 0., -3.], [1., 7., 5.]], 31 | [[2., 0., 2.], [1., 7., 2.]] 32 | ]; 33 | 34 | println!("{:8.4?}", data); 35 | 36 | data = roll_axis(data, Axis(2), Axis(0)); 37 | 38 | println!("{:8.4?}", data); 39 | } 40 | -------------------------------------------------------------------------------- /examples/zip_many.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 3 | )] 4 | 5 | use ndarray::prelude::*; 6 | use ndarray::Zip; 7 | 8 | fn main() 9 | { 10 | let n = 6; 11 | 12 | let mut a = Array::::zeros((n, n)); 13 | let mut b = Array::::zeros((n, n)); 14 | for ((i, j), elt) in b.indexed_iter_mut() { 15 | *elt = 1. / (1. + (i + 2 * j) as f32); 16 | } 17 | let c = Array::::from_elem((n, n + 1), 1.7); 18 | let c = c.slice(s![.., ..-1]); 19 | 20 | // Using Zip for arithmetic ops across a, b, c 21 | Zip::from(&mut a) 22 | .and(&b) 23 | .and(&c) 24 | .for_each(|a, &b, &c| *a = b + c); 25 | assert_eq!(a, &b + &c); 26 | 27 | // and this is how to do the *same thing* with azip!() 28 | azip!((a in &mut a, &b in &b, &c in c) *a = b + c); 29 | 30 | println!("{:8.4}", a); 31 | 32 | // sum of each row 33 | let mut sums = Array::zeros(a.nrows()); 34 | Zip::from(a.rows()) 35 | .and(&mut sums) 36 | .for_each(|row, sum| *sum = row.sum()); 37 | // show sums as a column matrix 38 | println!("{:8.4}", sums.insert_axis(Axis(1))); 39 | 40 | // sum of each 2x2 chunk 41 | let chunk_sz = (2, 2); 42 | let nchunks = (n / chunk_sz.0, n / chunk_sz.1); 43 | let mut sums = Array::zeros(nchunks); 44 | 45 | Zip::from(a.exact_chunks(chunk_sz)) 46 | .and(&mut sums) 47 | .for_each(|chunk, sum| *sum = chunk.sum()); 48 | println!("{:8.4}", sums); 49 | } 50 | -------------------------------------------------------------------------------- /ndarray-rand/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ndarray-rand" 3 | version = "0.15.0" 4 | edition = "2018" 5 | authors = ["bluss"] 6 | license = "MIT OR Apache-2.0" 7 | 8 | repository = "https://github.com/rust-ndarray/ndarray" 9 | documentation = "https://docs.rs/ndarray-rand/" 10 | readme = "README.md" 11 | 12 | description = "Constructors for randomized arrays. `rand` integration for `ndarray`." 13 | 14 | keywords = ["multidimensional", "matrix", "rand", "ndarray"] 15 | 16 | [dependencies] 17 | ndarray = { workspace = true } 18 | 19 | rand = { workspace = true } 20 | rand_distr = { workspace = true } 21 | quickcheck = { workspace = true, optional = true } 22 | 23 | [dev-dependencies] 24 | rand_isaac = "0.4.0" 25 | quickcheck = { workspace = true } 26 | 27 | [package.metadata.release] 28 | no-dev-version = true 29 | tag-name = "ndarray-rand-{{version}}" 30 | 31 | -------------------------------------------------------------------------------- /ndarray-rand/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 - 2018 Ulrik Sverdrup "bluss", 2 | Jim Turner, 3 | and ndarray developers 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /ndarray-rand/README.md: -------------------------------------------------------------------------------- 1 | ndarray-rand 2 | ============ 3 | 4 | Constructors for randomized arrays: `rand`'s integration with `ndarray`. 5 | 6 | Example 7 | ======= 8 | 9 | Generate a 2-dimensional array with shape `(2,5)` and elements drawn from a uniform distribution 10 | over the `(0., 10.)` interval: 11 | 12 | ```rust 13 | use ndarray::Array; 14 | use ndarray_rand::RandomExt; 15 | use ndarray_rand::rand_distr::Uniform; 16 | 17 | fn main() { 18 | let a = Array::random((2, 5), Uniform::new(0., 10.)); 19 | println!("{:8.4}", a); 20 | // Example Output: 21 | // [[ 8.6900, 6.9824, 3.8922, 6.5861, 2.4890], 22 | // [ 0.0914, 5.5186, 5.8135, 5.2361, 3.1879]] 23 | } 24 | ``` 25 | 26 | Dependencies 27 | ============ 28 | 29 | ``ndarray-rand`` depends on ``rand``. 30 | 31 | [`rand`](https://docs.rs/rand/) and [`rand-distr`](https://docs.rs/rand_distr/) are 32 | re-exported as sub-modules, `ndarray_rand::rand` and `ndarray_rand::rand_distr` respectively. 33 | Please rely on these submodules for guaranteed version compatibility. 34 | 35 | If you want to use a random number generator or distribution from another crate 36 | with `ndarray-rand`, you need to make sure that the other crate also depends on the 37 | same version of `rand`. Otherwise, the compiler may return errors saying 38 | that the items are not compatible (e.g. that a type doesn't implement a 39 | necessary trait). 40 | 41 | Recent changes 42 | ============== 43 | 44 | Check _[RELEASES.md](https://github.com/rust-ndarray/ndarray/blob/master/ndarray-rand/RELEASES.md)_ to see 45 | the changes introduced in previous releases. 46 | 47 | 48 | License 49 | ======= 50 | 51 | Dual-licensed to be compatible with the Rust project. 52 | 53 | Licensed under the Apache License, Version 2.0 54 | http://www.apache.org/licenses/LICENSE-2.0 or the MIT license 55 | http://opensource.org/licenses/MIT, at your 56 | option. This file may not be copied, modified, or distributed 57 | except according to those terms. 58 | -------------------------------------------------------------------------------- /ndarray-rand/RELEASES.md: -------------------------------------------------------------------------------- 1 | Recent Changes 2 | -------------- 3 | 4 | - 0.15.0 5 | 6 | - Require ndarray 0.16 7 | - Remove deprecated F32 by [@bluss](https://github.com/bluss) [#1409](https://github.com/rust-ndarray/ndarray/pull/1409) 8 | 9 | - 0.14.0 10 | 11 | - Require ndarray 0.15 12 | - Require rand 0.8 (unchanged from previous version) 13 | - The F32 wrapper is now deprecated, it's redundant 14 | 15 | - 0.13.0 16 | 17 | - Require ndarray 0.14 (unchanged from previous version) 18 | - Require rand 0.8 19 | - Require rand_distr 0.4 20 | - Fix methods `sample_axis` and `sample_axis_using` so that they can be used on array views too. 21 | 22 | - 0.12.0 23 | 24 | - Require ndarray 0.14 25 | - Require rand 0.7 (unchanged from previous version) 26 | - Require rand_distr 0.3 27 | 28 | - 0.11.0 29 | 30 | - Require ndarray 0.13 31 | - Require rand 0.7 (unchanged from previous version) 32 | 33 | - 0.10.0 34 | 35 | - Require `rand` 0.7 36 | - Require Rust 1.32 or later 37 | - Re-export `rand` as a submodule, `ndarray_rand::rand` 38 | - Re-export `rand-distr` as a submodule, `ndarray_rand::rand_distr` 39 | 40 | - 0.9.0 41 | 42 | - Require rand 0.6 43 | 44 | - 0.8.0 45 | 46 | - Require ndarray 0.12 47 | - Require rand 0.5 48 | 49 | - 0.7.0 50 | 51 | - Require ndarray 0.11 52 | - Require rand 0.4 53 | 54 | - 0.6.1 55 | 56 | - Clean up implementation of ``Array::random`` by @v-shmyhlo 57 | 58 | - 0.6.0 59 | 60 | - Require ndarray 0.10.0 61 | 62 | - 0.5.0 63 | 64 | - Require ndarray 0.9 65 | 66 | - 0.4.0 67 | 68 | - Require ndarray 0.8 69 | 70 | - 0.3.0 71 | 72 | - Require ndarray 0.7 73 | 74 | - 0.2.0 75 | 76 | - Require ndarray 0.6 77 | 78 | - 0.1.0 79 | 80 | - Initial release 81 | -------------------------------------------------------------------------------- /ndarray-rand/benches/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use ndarray::Array; 6 | use ndarray_rand::RandomExt; 7 | use rand_distr::Normal; 8 | use rand_distr::Uniform; 9 | 10 | use test::Bencher; 11 | 12 | #[bench] 13 | fn uniform_f32(b: &mut Bencher) 14 | { 15 | let m = 100; 16 | b.iter(|| Array::random((m, m), Uniform::new(-1f32, 1.).unwrap())); 17 | } 18 | 19 | #[bench] 20 | fn norm_f32(b: &mut Bencher) 21 | { 22 | let m = 100; 23 | b.iter(|| Array::random((m, m), Normal::new(0f32, 1.).unwrap())); 24 | } 25 | 26 | #[bench] 27 | fn norm_f64(b: &mut Bencher) 28 | { 29 | let m = 100; 30 | b.iter(|| Array::random((m, m), Normal::new(0f64, 1.).unwrap())); 31 | } 32 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | array_width = 100 3 | chain_width = 60 4 | fn_call_width = 100 5 | max_width = 120 6 | brace_style = "AlwaysNextLine" 7 | control_brace_style = "AlwaysSameLine" 8 | fn_params_layout = "Compressed" # ? 9 | format_macro_bodies = false 10 | imports_granularity = "Preserve" 11 | imports_indent = "Block" 12 | imports_layout = "HorizontalVertical" 13 | inline_attribute_width = 0 14 | indent_style = "Block" 15 | match_arm_blocks = false 16 | match_arm_leading_pipes = "Preserve" 17 | merge_derives = false 18 | overflow_delimited_expr = true 19 | reorder_modules = false # impacts rustdoc order 20 | short_array_element_width_threshold = 32 21 | skip_macro_invocations = ["*"] 22 | unstable_features = true 23 | where_single_line = true 24 | 25 | # ignored files 26 | ignore = [] 27 | -------------------------------------------------------------------------------- /scripts/all-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | 6 | FEATURES=$1 7 | CHANNEL=$2 8 | 9 | QC_FEAT=--features=ndarray-rand/quickcheck 10 | 11 | # build check with no features 12 | cargo build -v --no-default-features 13 | 14 | # ndarray with no features 15 | cargo test -p ndarray -v --no-default-features 16 | # ndarray with no_std-compatible features 17 | cargo test -p ndarray -v --no-default-features --features approx 18 | # all with features 19 | cargo test -v --features "$FEATURES" $QC_FEAT 20 | # all with features and release (ignore test crates which is already optimized) 21 | cargo test -v -p ndarray -p ndarray-rand --release --features "$FEATURES" $QC_FEAT --lib --tests 22 | 23 | # BLAS tests 24 | cargo test -p ndarray --lib -v --features blas 25 | cargo test -p blas-mock-tests -v 26 | if [[ -z "${MSRV}" ]] && [ "$CHANNEL" != "$MSRV" ]; then 27 | ./scripts/blas-integ-tests.sh "$FEATURES" $CHANNEL 28 | fi 29 | 30 | # Examples 31 | cargo test --examples 32 | 33 | # Benchmarks 34 | ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") 35 | -------------------------------------------------------------------------------- /scripts/blas-integ-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | 6 | # BLAS tests 7 | cargo test -p blas-tests -v --features blas-tests/openblas-system 8 | cargo test -p numeric-tests -v --features numeric-tests/test_blas 9 | -------------------------------------------------------------------------------- /scripts/cross-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | 6 | FEATURES=$1 7 | CHANNEL=$2 8 | TARGET=$3 9 | 10 | QC_FEAT=--features=ndarray-rand/quickcheck 11 | 12 | cross build -v --features="$FEATURES" $QC_FEAT --target=$TARGET 13 | cross test -v --no-fail-fast --features="$FEATURES" $QC_FEAT --target=$TARGET 14 | cross test -v -p blas-mock-tests 15 | -------------------------------------------------------------------------------- /scripts/makechangelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: makechangelog 4 | # 5 | # This script depends on and uses the github `gh` binary 6 | # which needs to be authenticated to use. 7 | # 8 | # Will produce some duplicates for PRs integrated using rebase, 9 | # but those will not occur with current merge queue. 10 | 11 | git log --first-parent --pretty="tformat:%H" "$@" | while IFS= read -r commit_sha 12 | do 13 | gh api "/repos/:owner/:repo/commits/${commit_sha}/pulls" \ 14 | -q ".[] | \"- \(.title) by [@\(.user.login)](\(.user.html_url)) [#\(.number)](\(.html_url))\"" 15 | done | uniq 16 | 17 | -------------------------------------------------------------------------------- /scripts/miri-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | 6 | # We rely on layout-dependent casts, which should be covered with #[repr(transparent)] 7 | # This should catch if we missed that 8 | RUSTFLAGS="-Zrandomize-layout" 9 | 10 | # Miri reports a stacked borrow violation deep within rayon, in a crate called crossbeam-epoch 11 | # The crate has a PR to fix this: https://github.com/crossbeam-rs/crossbeam/pull/871 12 | # but using Miri's tree borrow mode may resolve it for now. 13 | # Disabled until we can figure out a different rayon issue: https://github.com/rust-lang/miri/issues/1371 14 | # MIRIFLAGS="-Zmiri-tree-borrows" 15 | 16 | # General tests 17 | # Note that we exclude blas feature because Miri can't do cblas_gemm 18 | cargo miri test -v -p ndarray -p ndarray-rand --features approx,serde 19 | -------------------------------------------------------------------------------- /src/argument_traits.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::mem::MaybeUninit; 3 | 4 | use crate::math_cell::MathCell; 5 | 6 | /// A producer element that can be assigned to once 7 | pub trait AssignElem 8 | { 9 | /// Assign the value `input` to the element that self represents. 10 | fn assign_elem(self, input: T); 11 | } 12 | 13 | /// Assignable element, simply `*self = input`. 14 | impl AssignElem for &mut T 15 | { 16 | fn assign_elem(self, input: T) 17 | { 18 | *self = input; 19 | } 20 | } 21 | 22 | /// Assignable element, simply `self.set(input)`. 23 | impl AssignElem for &Cell 24 | { 25 | fn assign_elem(self, input: T) 26 | { 27 | self.set(input); 28 | } 29 | } 30 | 31 | /// Assignable element, simply `self.set(input)`. 32 | impl AssignElem for &MathCell 33 | { 34 | fn assign_elem(self, input: T) 35 | { 36 | self.set(input); 37 | } 38 | } 39 | 40 | /// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not 41 | /// read or dropped). 42 | impl AssignElem for &mut MaybeUninit 43 | { 44 | fn assign_elem(self, input: T) 45 | { 46 | *self = MaybeUninit::new(input); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/dimension/axes.rs: -------------------------------------------------------------------------------- 1 | use crate::{Axis, Dimension, Ixs}; 2 | 3 | /// Create a new Axes iterator 4 | pub(crate) fn axes_of<'a, D>(d: &'a D, strides: &'a D) -> Axes<'a, D> 5 | where D: Dimension 6 | { 7 | Axes { 8 | dim: d, 9 | strides, 10 | start: 0, 11 | end: d.ndim(), 12 | } 13 | } 14 | 15 | /// An iterator over the length and stride of each axis of an array. 16 | /// 17 | /// This iterator is created from the array method 18 | /// [`.axes()`](crate::ArrayBase::axes). 19 | /// 20 | /// Iterator element type is [`AxisDescription`]. 21 | /// 22 | /// # Examples 23 | /// 24 | /// ``` 25 | /// use ndarray::Array3; 26 | /// use ndarray::Axis; 27 | /// 28 | /// let a = Array3::::zeros((3, 5, 4)); 29 | /// 30 | /// // find the largest axis in the array 31 | /// // check the axis index and its length 32 | /// 33 | /// let largest_axis = a.axes() 34 | /// .max_by_key(|ax| ax.len) 35 | /// .unwrap(); 36 | /// assert_eq!(largest_axis.axis, Axis(1)); 37 | /// assert_eq!(largest_axis.len, 5); 38 | /// ``` 39 | #[derive(Debug)] 40 | pub struct Axes<'a, D> 41 | { 42 | dim: &'a D, 43 | strides: &'a D, 44 | start: usize, 45 | end: usize, 46 | } 47 | 48 | /// Description of the axis, its length and its stride. 49 | #[derive(Debug)] 50 | pub struct AxisDescription 51 | { 52 | /// Axis identifier (index) 53 | pub axis: Axis, 54 | /// Length in count of elements of the current axis 55 | pub len: usize, 56 | /// Stride in count of elements of the current axis 57 | pub stride: isize, 58 | } 59 | 60 | copy_and_clone!(AxisDescription); 61 | copy_and_clone!(['a, D] Axes<'a, D>); 62 | 63 | impl Iterator for Axes<'_, D> 64 | where D: Dimension 65 | { 66 | /// Description of the axis, its length and its stride. 67 | type Item = AxisDescription; 68 | 69 | fn next(&mut self) -> Option 70 | { 71 | if self.start < self.end { 72 | let i = self.start.post_inc(); 73 | Some(AxisDescription { 74 | axis: Axis(i), 75 | len: self.dim[i], 76 | stride: self.strides[i] as Ixs, 77 | }) 78 | } else { 79 | None 80 | } 81 | } 82 | 83 | fn fold(self, init: B, f: F) -> B 84 | where F: FnMut(B, AxisDescription) -> B 85 | { 86 | (self.start..self.end) 87 | .map(move |i| AxisDescription { 88 | axis: Axis(i), 89 | len: self.dim[i], 90 | stride: self.strides[i] as isize, 91 | }) 92 | .fold(init, f) 93 | } 94 | 95 | fn size_hint(&self) -> (usize, Option) 96 | { 97 | let len = self.end - self.start; 98 | (len, Some(len)) 99 | } 100 | } 101 | 102 | impl DoubleEndedIterator for Axes<'_, D> 103 | where D: Dimension 104 | { 105 | fn next_back(&mut self) -> Option 106 | { 107 | if self.start < self.end { 108 | let i = self.end.pre_dec(); 109 | Some(AxisDescription { 110 | axis: Axis(i), 111 | len: self.dim[i], 112 | stride: self.strides[i] as Ixs, 113 | }) 114 | } else { 115 | None 116 | } 117 | } 118 | } 119 | 120 | trait IncOps: Copy 121 | { 122 | fn post_inc(&mut self) -> Self; 123 | fn pre_dec(&mut self) -> Self; 124 | } 125 | 126 | impl IncOps for usize 127 | { 128 | #[inline(always)] 129 | fn post_inc(&mut self) -> Self 130 | { 131 | let x = *self; 132 | *self += 1; 133 | x 134 | } 135 | #[inline(always)] 136 | fn pre_dec(&mut self) -> Self 137 | { 138 | *self -= 1; 139 | *self 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/dimension/axis.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | /// An axis index. 10 | /// 11 | /// An axis one of an array’s “dimensions”; an *n*-dimensional array has *n* 12 | /// axes. Axis *0* is the array’s outermost axis and *n*-1 is the innermost. 13 | /// 14 | /// All array axis arguments use this type to make the code easier to write 15 | /// correctly and easier to understand. 16 | /// 17 | /// For example: in a method like `index_axis(axis, index)` the code becomes 18 | /// self-explanatory when it's called like `.index_axis(Axis(1), i)`; it's 19 | /// evident which integer is the axis number and which is the index. 20 | /// 21 | /// Note: This type does **not** implement From/Into usize and similar trait 22 | /// based conversions, because we want to preserve code readability and quality. 23 | /// 24 | /// `Axis(1)` in itself is a very clear code style and the style that should be 25 | /// avoided is code like `1.into()`. 26 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 27 | pub struct Axis(pub usize); 28 | 29 | impl Axis 30 | { 31 | /// Return the index of the axis. 32 | #[inline(always)] 33 | pub fn index(self) -> usize 34 | { 35 | self.0 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/dimension/broadcast.rs: -------------------------------------------------------------------------------- 1 | use crate::error::*; 2 | use crate::{Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; 3 | 4 | /// Calculate the common shape for a pair of array shapes, that they can be broadcasted 5 | /// to. Return an error if the shapes are not compatible. 6 | /// 7 | /// Uses the [NumPy broadcasting rules] 8 | // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). 9 | pub(crate) fn co_broadcast(shape1: &D1, shape2: &D2) -> Result 10 | where 11 | D1: Dimension, 12 | D2: Dimension, 13 | Output: Dimension, 14 | { 15 | let (k, overflow) = shape1.ndim().overflowing_sub(shape2.ndim()); 16 | // Swap the order if d2 is longer. 17 | if overflow { 18 | return co_broadcast::(shape2, shape1); 19 | } 20 | // The output should be the same length as shape1. 21 | let mut out = Output::zeros(shape1.ndim()); 22 | for (out, s) in izip!(out.slice_mut(), shape1.slice()) { 23 | *out = *s; 24 | } 25 | for (out, s2) in izip!(&mut out.slice_mut()[k..], shape2.slice()) { 26 | if *out != *s2 { 27 | if *out == 1 { 28 | *out = *s2 29 | } else if *s2 != 1 { 30 | return Err(from_kind(ErrorKind::IncompatibleShape)); 31 | } 32 | } 33 | } 34 | Ok(out) 35 | } 36 | 37 | pub trait DimMax 38 | { 39 | /// The resulting dimension type after broadcasting. 40 | type Output: Dimension; 41 | } 42 | 43 | /// Dimensions of the same type remain unchanged when co_broadcast. 44 | /// So you can directly use `D` as the resulting type. 45 | /// (Instead of `>::BroadcastOutput`) 46 | impl DimMax for D 47 | { 48 | type Output = D; 49 | } 50 | 51 | macro_rules! impl_broadcast_distinct_fixed { 52 | ($smaller:ty, $larger:ty) => { 53 | impl DimMax<$larger> for $smaller { 54 | type Output = $larger; 55 | } 56 | 57 | impl DimMax<$smaller> for $larger { 58 | type Output = $larger; 59 | } 60 | }; 61 | } 62 | 63 | impl_broadcast_distinct_fixed!(Ix0, Ix1); 64 | impl_broadcast_distinct_fixed!(Ix0, Ix2); 65 | impl_broadcast_distinct_fixed!(Ix0, Ix3); 66 | impl_broadcast_distinct_fixed!(Ix0, Ix4); 67 | impl_broadcast_distinct_fixed!(Ix0, Ix5); 68 | impl_broadcast_distinct_fixed!(Ix0, Ix6); 69 | impl_broadcast_distinct_fixed!(Ix1, Ix2); 70 | impl_broadcast_distinct_fixed!(Ix1, Ix3); 71 | impl_broadcast_distinct_fixed!(Ix1, Ix4); 72 | impl_broadcast_distinct_fixed!(Ix1, Ix5); 73 | impl_broadcast_distinct_fixed!(Ix1, Ix6); 74 | impl_broadcast_distinct_fixed!(Ix2, Ix3); 75 | impl_broadcast_distinct_fixed!(Ix2, Ix4); 76 | impl_broadcast_distinct_fixed!(Ix2, Ix5); 77 | impl_broadcast_distinct_fixed!(Ix2, Ix6); 78 | impl_broadcast_distinct_fixed!(Ix3, Ix4); 79 | impl_broadcast_distinct_fixed!(Ix3, Ix5); 80 | impl_broadcast_distinct_fixed!(Ix3, Ix6); 81 | impl_broadcast_distinct_fixed!(Ix4, Ix5); 82 | impl_broadcast_distinct_fixed!(Ix4, Ix6); 83 | impl_broadcast_distinct_fixed!(Ix5, Ix6); 84 | impl_broadcast_distinct_fixed!(Ix0, IxDyn); 85 | impl_broadcast_distinct_fixed!(Ix1, IxDyn); 86 | impl_broadcast_distinct_fixed!(Ix2, IxDyn); 87 | impl_broadcast_distinct_fixed!(Ix3, IxDyn); 88 | impl_broadcast_distinct_fixed!(Ix4, IxDyn); 89 | impl_broadcast_distinct_fixed!(Ix5, IxDyn); 90 | impl_broadcast_distinct_fixed!(Ix6, IxDyn); 91 | 92 | #[cfg(test)] 93 | #[cfg(feature = "std")] 94 | mod tests 95 | { 96 | use super::co_broadcast; 97 | use crate::{Dim, DimMax, Dimension, ErrorKind, Ix0, IxDynImpl, ShapeError}; 98 | 99 | #[test] 100 | fn test_broadcast_shape() 101 | { 102 | fn test_co(d1: &D1, d2: &D2, r: Result<>::Output, ShapeError>) 103 | where 104 | D1: Dimension + DimMax, 105 | D2: Dimension, 106 | { 107 | let d = co_broadcast::>::Output>(&d1, d2); 108 | assert_eq!(d, r); 109 | } 110 | test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); 111 | test_co(&Dim([1, 2, 2]), &Dim([1, 3, 4]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); 112 | test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); 113 | let v = vec![1, 2, 3, 4, 5, 6, 7]; 114 | test_co(&Dim(vec![1, 1, 3, 1, 5, 1, 7]), &Dim([2, 1, 4, 1, 6, 1]), Ok(Dim(IxDynImpl::from(v.as_slice())))); 115 | let d = Dim([1, 2, 1, 3]); 116 | test_co(&d, &d, Ok(d)); 117 | test_co(&Dim([2, 1, 2]).into_dyn(), &Dim(0), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); 118 | test_co(&Dim([2, 1, 1]), &Dim([0, 0, 1, 3, 4]), Ok(Dim([0, 0, 2, 3, 4]))); 119 | test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); 120 | test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); 121 | test_co(&Dim([1, 3, 0, 1, 1]), &Dim([1, 2, 3, 1]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/dimension/macros.rs: -------------------------------------------------------------------------------- 1 | /// Indexing macro for Dim<[usize; N]> this 2 | /// gets the index at `$i` in the underlying array 3 | macro_rules! get { 4 | ($dim:expr, $i:expr) => { 5 | (*$dim.ix())[$i] 6 | }; 7 | } 8 | macro_rules! getm { 9 | ($dim:expr, $i:expr) => { 10 | (*$dim.ixm())[$i] 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /src/dimension/ops.rs: -------------------------------------------------------------------------------- 1 | use crate::imp_prelude::*; 2 | 3 | /// Adds the two dimensions at compile time. 4 | pub trait DimAdd 5 | { 6 | /// The sum of the two dimensions. 7 | type Output: Dimension; 8 | } 9 | 10 | macro_rules! impl_dimadd_const_out_const { 11 | ($lhs:expr, $rhs:expr) => { 12 | impl DimAdd> for Dim<[usize; $lhs]> { 13 | type Output = Dim<[usize; $lhs + $rhs]>; 14 | } 15 | }; 16 | } 17 | 18 | macro_rules! impl_dimadd_const_out_dyn { 19 | ($lhs:expr, IxDyn) => { 20 | impl DimAdd for Dim<[usize; $lhs]> { 21 | type Output = IxDyn; 22 | } 23 | }; 24 | ($lhs:expr, $rhs:expr) => { 25 | impl DimAdd> for Dim<[usize; $lhs]> { 26 | type Output = IxDyn; 27 | } 28 | }; 29 | } 30 | 31 | impl DimAdd for Ix0 32 | { 33 | type Output = D; 34 | } 35 | 36 | impl_dimadd_const_out_const!(1, 0); 37 | impl_dimadd_const_out_const!(1, 1); 38 | impl_dimadd_const_out_const!(1, 2); 39 | impl_dimadd_const_out_const!(1, 3); 40 | impl_dimadd_const_out_const!(1, 4); 41 | impl_dimadd_const_out_const!(1, 5); 42 | impl_dimadd_const_out_dyn!(1, 6); 43 | impl_dimadd_const_out_dyn!(1, IxDyn); 44 | 45 | impl_dimadd_const_out_const!(2, 0); 46 | impl_dimadd_const_out_const!(2, 1); 47 | impl_dimadd_const_out_const!(2, 2); 48 | impl_dimadd_const_out_const!(2, 3); 49 | impl_dimadd_const_out_const!(2, 4); 50 | impl_dimadd_const_out_dyn!(2, 5); 51 | impl_dimadd_const_out_dyn!(2, 6); 52 | impl_dimadd_const_out_dyn!(2, IxDyn); 53 | 54 | impl_dimadd_const_out_const!(3, 0); 55 | impl_dimadd_const_out_const!(3, 1); 56 | impl_dimadd_const_out_const!(3, 2); 57 | impl_dimadd_const_out_const!(3, 3); 58 | impl_dimadd_const_out_dyn!(3, 4); 59 | impl_dimadd_const_out_dyn!(3, 5); 60 | impl_dimadd_const_out_dyn!(3, 6); 61 | impl_dimadd_const_out_dyn!(3, IxDyn); 62 | 63 | impl_dimadd_const_out_const!(4, 0); 64 | impl_dimadd_const_out_const!(4, 1); 65 | impl_dimadd_const_out_const!(4, 2); 66 | impl_dimadd_const_out_dyn!(4, 3); 67 | impl_dimadd_const_out_dyn!(4, 4); 68 | impl_dimadd_const_out_dyn!(4, 5); 69 | impl_dimadd_const_out_dyn!(4, 6); 70 | impl_dimadd_const_out_dyn!(4, IxDyn); 71 | 72 | impl_dimadd_const_out_const!(5, 0); 73 | impl_dimadd_const_out_const!(5, 1); 74 | impl_dimadd_const_out_dyn!(5, 2); 75 | impl_dimadd_const_out_dyn!(5, 3); 76 | impl_dimadd_const_out_dyn!(5, 4); 77 | impl_dimadd_const_out_dyn!(5, 5); 78 | impl_dimadd_const_out_dyn!(5, 6); 79 | impl_dimadd_const_out_dyn!(5, IxDyn); 80 | 81 | impl_dimadd_const_out_const!(6, 0); 82 | impl_dimadd_const_out_dyn!(6, 1); 83 | impl_dimadd_const_out_dyn!(6, 2); 84 | impl_dimadd_const_out_dyn!(6, 3); 85 | impl_dimadd_const_out_dyn!(6, 4); 86 | impl_dimadd_const_out_dyn!(6, 5); 87 | impl_dimadd_const_out_dyn!(6, 6); 88 | impl_dimadd_const_out_dyn!(6, IxDyn); 89 | 90 | impl DimAdd for IxDyn 91 | { 92 | type Output = IxDyn; 93 | } 94 | -------------------------------------------------------------------------------- /src/dimension/remove_axis.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::{Axis, Dim, Dimension, Ix, Ix0, Ix1}; 10 | 11 | /// Array shape with a next smaller dimension. 12 | /// 13 | /// `RemoveAxis` defines a larger-than relation for array shapes: 14 | /// removing one axis from *Self* gives smaller dimension *Smaller*. 15 | pub trait RemoveAxis: Dimension 16 | { 17 | fn remove_axis(&self, axis: Axis) -> Self::Smaller; 18 | } 19 | 20 | impl RemoveAxis for Dim<[Ix; 1]> 21 | { 22 | #[inline] 23 | fn remove_axis(&self, axis: Axis) -> Ix0 24 | { 25 | debug_assert!(axis.index() < self.ndim()); 26 | Ix0() 27 | } 28 | } 29 | 30 | impl RemoveAxis for Dim<[Ix; 2]> 31 | { 32 | #[inline] 33 | fn remove_axis(&self, axis: Axis) -> Ix1 34 | { 35 | let axis = axis.index(); 36 | debug_assert!(axis < self.ndim()); 37 | if axis == 0 { 38 | Ix1(get!(self, 1)) 39 | } else { 40 | Ix1(get!(self, 0)) 41 | } 42 | } 43 | } 44 | 45 | macro_rules! impl_remove_axis_array( 46 | ($($n:expr),*) => ( 47 | $( 48 | impl RemoveAxis for Dim<[Ix; $n]> 49 | { 50 | #[inline] 51 | fn remove_axis(&self, axis: Axis) -> Self::Smaller { 52 | debug_assert!(axis.index() < self.ndim()); 53 | let mut result = Dim([0; $n - 1]); 54 | { 55 | let src = self.slice(); 56 | let dst = result.slice_mut(); 57 | dst[..axis.index()].copy_from_slice(&src[..axis.index()]); 58 | dst[axis.index()..].copy_from_slice(&src[axis.index() + 1..]); 59 | } 60 | result 61 | } 62 | } 63 | )* 64 | ); 65 | ); 66 | 67 | impl_remove_axis_array!(3, 4, 5, 6); 68 | -------------------------------------------------------------------------------- /src/dimension/sequence.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Index; 2 | use std::ops::IndexMut; 3 | 4 | use crate::dimension::Dimension; 5 | 6 | pub(in crate::dimension) struct Forward(pub(crate) D); 7 | pub(in crate::dimension) struct Reverse(pub(crate) D); 8 | 9 | impl Index for Forward<&D> 10 | where D: Dimension 11 | { 12 | type Output = usize; 13 | 14 | #[inline] 15 | fn index(&self, index: usize) -> &usize 16 | { 17 | &self.0[index] 18 | } 19 | } 20 | 21 | impl Index for Forward<&mut D> 22 | where D: Dimension 23 | { 24 | type Output = usize; 25 | 26 | #[inline] 27 | fn index(&self, index: usize) -> &usize 28 | { 29 | &self.0[index] 30 | } 31 | } 32 | 33 | impl IndexMut for Forward<&mut D> 34 | where D: Dimension 35 | { 36 | #[inline] 37 | fn index_mut(&mut self, index: usize) -> &mut usize 38 | { 39 | &mut self.0[index] 40 | } 41 | } 42 | 43 | impl Index for Reverse<&D> 44 | where D: Dimension 45 | { 46 | type Output = usize; 47 | 48 | #[inline] 49 | fn index(&self, index: usize) -> &usize 50 | { 51 | &self.0[self.len() - index - 1] 52 | } 53 | } 54 | 55 | impl Index for Reverse<&mut D> 56 | where D: Dimension 57 | { 58 | type Output = usize; 59 | 60 | #[inline] 61 | fn index(&self, index: usize) -> &usize 62 | { 63 | &self.0[self.len() - index - 1] 64 | } 65 | } 66 | 67 | impl IndexMut for Reverse<&mut D> 68 | where D: Dimension 69 | { 70 | #[inline] 71 | fn index_mut(&mut self, index: usize) -> &mut usize 72 | { 73 | let len = self.len(); 74 | &mut self.0[len - index - 1] 75 | } 76 | } 77 | 78 | /// Indexable sequence with length 79 | pub(in crate::dimension) trait Sequence: Index 80 | { 81 | fn len(&self) -> usize; 82 | } 83 | 84 | /// Indexable sequence with length (mut) 85 | pub(in crate::dimension) trait SequenceMut: Sequence + IndexMut {} 86 | 87 | impl Sequence for Forward<&D> 88 | where D: Dimension 89 | { 90 | #[inline] 91 | fn len(&self) -> usize 92 | { 93 | self.0.ndim() 94 | } 95 | } 96 | 97 | impl Sequence for Forward<&mut D> 98 | where D: Dimension 99 | { 100 | #[inline] 101 | fn len(&self) -> usize 102 | { 103 | self.0.ndim() 104 | } 105 | } 106 | 107 | impl SequenceMut for Forward<&mut D> where D: Dimension {} 108 | 109 | impl Sequence for Reverse<&D> 110 | where D: Dimension 111 | { 112 | #[inline] 113 | fn len(&self) -> usize 114 | { 115 | self.0.ndim() 116 | } 117 | } 118 | 119 | impl Sequence for Reverse<&mut D> 120 | where D: Dimension 121 | { 122 | #[inline] 123 | fn len(&self) -> usize 124 | { 125 | self.0.ndim() 126 | } 127 | } 128 | 129 | impl SequenceMut for Reverse<&mut D> where D: Dimension {} 130 | -------------------------------------------------------------------------------- /src/doc/crate_feature_flags.rs: -------------------------------------------------------------------------------- 1 | //! Crate Feature Flags 2 | //! 3 | //! The following crate feature flags are available. They are configured in your 4 | //! `Cargo.toml` where the dependency on `ndarray` is defined. 5 | //! 6 | //! ## `std` 7 | //! - Rust standard library (enabled by default) 8 | //! - This crate can be used without the standard library by disabling the 9 | //! default `std` feature. To do so, use `default-features = false` in 10 | //! your `Cargo.toml`. 11 | //! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` 12 | //! and `std_axis` methods are only available when `std` is enabled. 13 | //! 14 | //! ## `serde` 15 | //! - Enables serialization support for serde 1.x 16 | //! 17 | //! ## `rayon` 18 | //! - Enables parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. 19 | //! - Implies std 20 | //! 21 | //! ## `approx` 22 | //! - Enables implementations of traits of the [`approx`] crate. 23 | //! 24 | //! ## `blas` 25 | //! - Enable transparent BLAS support for matrix multiplication. 26 | //! Uses ``blas-src`` for pluggable backend, which needs to be configured 27 | //! separately (see the README). 28 | //! 29 | //! ## `matrixmultiply-threading` 30 | //! - Enable the ``threading`` feature in the matrixmultiply package 31 | //! 32 | //! [`parallel`]: crate::parallel 33 | 34 | #[cfg(doc)] 35 | use crate::parallel::par_azip; 36 | -------------------------------------------------------------------------------- /src/doc/mod.rs: -------------------------------------------------------------------------------- 1 | //! Standalone documentation pages. 2 | 3 | pub mod crate_feature_flags; 4 | pub mod ndarray_for_numpy_users; 5 | -------------------------------------------------------------------------------- /src/doc/ndarray_for_numpy_users/coord_transform.rs: -------------------------------------------------------------------------------- 1 | //! Example of rotation with Euler angles. 2 | //! 3 | //! This is an example of some coordinate transformations (using Euler angles) 4 | //! for illustrative purposes. Note that other crates such as 5 | //! [`cgmath`](https://crates.io/crates/cgmath) or 6 | //! [`nalgebra`](https://crates.io/crates/nalgebra) may be better-suited if 7 | //! most of your work is coordinate transformations, since they have built-in 8 | //! geometry primitives. 9 | //! 10 | //! This is the original Python program: 11 | //! 12 | //! ```python 13 | //! # Euler angles (rows) for four coordinate systems (columns). 14 | //! nelems = 4 15 | //! bunge = np.ones((3, nelems)) 16 | //! 17 | //! # Precompute sines and cosines 18 | //! s1 = np.sin(bunge[0, :]) 19 | //! c1 = np.cos(bunge[0, :]) 20 | //! s2 = np.sin(bunge[1, :]) 21 | //! c2 = np.cos(bunge[1, :]) 22 | //! s3 = np.sin(bunge[2, :]) 23 | //! c3 = np.cos(bunge[2, :]) 24 | //! 25 | //! # Rotation matrices. 26 | //! rmat = np.zeros((3, 3, nelems), order='F') 27 | //! for i in range(nelems): 28 | //! rmat[0, 0, i] = c1[i] * c3[i] - s1[i] * s3[i] * c2[i] 29 | //! rmat[0, 1, i] = -c1[i] * s3[i] - s1[i] * c2[i] * c3[i] 30 | //! rmat[0, 2, i] = s1[i] * s2[i] 31 | //! 32 | //! rmat[1, 0, i] = s1[i] * c3[i] + c1[i] * c2[i] * s3[i] 33 | //! rmat[1, 1, i] = -s1[i] * s3[i] + c1[i] * c2[i] * c3[i] 34 | //! rmat[1, 2, i] = -c1[i] * s2[i] 35 | //! 36 | //! rmat[2, 0, i] = s2[i] * s3[i] 37 | //! rmat[2, 1, i] = s2[i] * c3[i] 38 | //! rmat[2, 2, i] = c2[i] 39 | //! 40 | //! # Unit vectors of coordinate systems to rotate. 41 | //! eye2d = np.eye(3) 42 | //! 43 | //! # Unit vectors after rotation. 44 | //! rotated = np.zeros((3, 3, nelems), order='F') 45 | //! for i in range(nelems): 46 | //! rotated[:,:,i] = rmat[:,:,i].dot(eye2d) 47 | //! ``` 48 | //! 49 | //! This is a direct translation to `ndarray`: 50 | //! 51 | //! ``` 52 | //! use ndarray::prelude::*; 53 | //! 54 | //! let nelems = 4; 55 | //! let bunge = Array::ones((3, nelems)); 56 | //! 57 | //! let s1 = bunge.slice(s![0, ..]).mapv(f64::sin); 58 | //! let c1 = bunge.slice(s![0, ..]).mapv(f64::cos); 59 | //! let s2 = bunge.slice(s![1, ..]).mapv(f64::sin); 60 | //! let c2 = bunge.slice(s![1, ..]).mapv(f64::cos); 61 | //! let s3 = bunge.slice(s![2, ..]).mapv(f64::sin); 62 | //! let c3 = bunge.slice(s![2, ..]).mapv(f64::cos); 63 | //! 64 | //! let mut rmat = Array::zeros((3, 3, nelems).f()); 65 | //! for i in 0..nelems { 66 | //! rmat[[0, 0, i]] = c1[i] * c3[i] - s1[i] * s3[i] * c2[i]; 67 | //! rmat[[0, 1, i]] = -c1[i] * s3[i] - s1[i] * c2[i] * c3[i]; 68 | //! rmat[[0, 2, i]] = s1[i] * s2[i]; 69 | //! 70 | //! rmat[[1, 0, i]] = s1[i] * c3[i] + c1[i] * c2[i] * s3[i]; 71 | //! rmat[[1, 1, i]] = -s1[i] * s3[i] + c1[i] * c2[i] * c3[i]; 72 | //! rmat[[1, 2, i]] = -c1[i] * s2[i]; 73 | //! 74 | //! rmat[[2, 0, i]] = s2[i] * s3[i]; 75 | //! rmat[[2, 1, i]] = s2[i] * c3[i]; 76 | //! rmat[[2, 2, i]] = c2[i]; 77 | //! } 78 | //! 79 | //! let eye2d = Array::eye(3); 80 | //! 81 | //! let mut rotated = Array::zeros((3, 3, nelems).f()); 82 | //! for i in 0..nelems { 83 | //! rotated 84 | //! .slice_mut(s![.., .., i]) 85 | //! .assign(&rmat.slice(s![.., .., i]).dot(&eye2d)); 86 | //! } 87 | //! ``` 88 | //! 89 | //! Instead of looping over indices, a cleaner (and usually faster) option is 90 | //! to zip arrays together. It's also possible to avoid some of the temporary 91 | //! memory allocations in the original program. The improved version looks like 92 | //! this: 93 | //! 94 | //! ``` 95 | //! use ndarray::prelude::*; 96 | //! 97 | //! let nelems = 4; 98 | //! let bunge = Array2::::ones((3, nelems)); 99 | //! 100 | //! let mut rmat = Array::zeros((3, 3, nelems).f()); 101 | //! azip!((mut rmat in rmat.axis_iter_mut(Axis(2)), bunge in bunge.axis_iter(Axis(1))) { 102 | //! let s1 = bunge[0].sin(); 103 | //! let c1 = bunge[0].cos(); 104 | //! let s2 = bunge[1].sin(); 105 | //! let c2 = bunge[1].cos(); 106 | //! let s3 = bunge[2].sin(); 107 | //! let c3 = bunge[2].cos(); 108 | //! 109 | //! rmat[[0, 0]] = c1 * c3 - s1 * s3 * c2; 110 | //! rmat[[0, 1]] = -c1 * s3 - s1 * c2 * c3; 111 | //! rmat[[0, 2]] = s1 * s2; 112 | //! 113 | //! rmat[[1, 0]] = s1 * c3 + c1 * c2 * s3; 114 | //! rmat[[1, 1]] = -s1 * s3 + c1 * c2 * c3; 115 | //! rmat[[1, 2]] = -c1 * s2; 116 | //! 117 | //! rmat[[2, 0]] = s2 * s3; 118 | //! rmat[[2, 1]] = s2 * c3; 119 | //! rmat[[2, 2]] = c2; 120 | //! }); 121 | //! 122 | //! let eye2d = Array2::::eye(3); 123 | //! 124 | //! let mut rotated = Array3::::zeros((3, 3, nelems).f()); 125 | //! azip!((mut rotated in rotated.axis_iter_mut(Axis(2)), rmat in rmat.axis_iter(Axis(2))) { 126 | //! rotated.assign(&rmat.dot(&eye2d)); 127 | //! }); 128 | //! ``` 129 | -------------------------------------------------------------------------------- /src/doc/ndarray_for_numpy_users/simple_math.rs: -------------------------------------------------------------------------------- 1 | //! Example of simple math operations on 2-D arrays. 2 | //! 3 | //! 4 | //! 5 | //! 6 | //! 11 | //! 16 | //! 17 | //! 18 | //! 51 | //! 84 | //! 85 | //!
7 | //! 8 | //! NumPy 9 | //! 10 | //! 12 | //! 13 | //! `ndarray` 14 | //! 15 | //!
19 | //! 20 | //! ```python 21 | //! import numpy as np 22 | //! 23 | //! 24 | //! 25 | //! 26 | //! a = np.full((5, 4), 3.) 27 | //! 28 | //! 29 | //! a[::2, :] = 2. 30 | //! 31 | //! 32 | //! a[:, 1] = np.sin(a[:, 1]) + 1. 33 | //! 34 | //! 35 | //! a[a < 1.5] = 4. 36 | //! 37 | //! 38 | //! odd_sum = a[:, 1::2].sum() 39 | //! 40 | //! 41 | //! b = np.exp(np.arange(4)) 42 | //! 43 | //! 44 | //! c = a + b 45 | //! 46 | //! 47 | //! d = c.T.dot(a) 48 | //! ``` 49 | //! 50 | //! 52 | //! 53 | //! ``` 54 | //! use ndarray::prelude::*; 55 | //! 56 | //! # fn main() { 57 | //! // Create a 5×4 array of threes. 58 | //! let mut a = Array2::::from_elem((5, 4), 3.); 59 | //! 60 | //! // Fill the even-index rows with twos. 61 | //! a.slice_mut(s![..;2, ..]).fill(2.); 62 | //! 63 | //! // Change column 1 to sin(x) + 1. 64 | //! a.column_mut(1).mapv_inplace(|x| x.sin() + 1.); 65 | //! 66 | //! // Change values less than 1.5 to 4. 67 | //! a.mapv_inplace(|x| if x < 1.5 { 4. } else { x }); 68 | //! 69 | //! // Compute the sum of the odd-index columns. 70 | //! let odd_sum = a.slice(s![.., 1..;2]).sum(); 71 | //! 72 | //! // Create a 1-D array of exp(index). 73 | //! let b = Array::from_shape_fn(4, |i| (i as f64).exp()); 74 | //! 75 | //! // Add b to a (broadcasting to rows). 76 | //! let c = a + &b; 77 | //! 78 | //! // Matrix product of c transpose with c. 79 | //! let d = c.t().dot(&c); 80 | //! # } 81 | //! ``` 82 | //! 83 | //!
86 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | use super::Dimension; 9 | #[cfg(feature = "std")] 10 | use std::error::Error; 11 | use std::fmt; 12 | 13 | /// An error related to array shape or layout. 14 | #[derive(Clone)] 15 | pub struct ShapeError 16 | { 17 | // we want to be able to change this representation later 18 | repr: ErrorKind, 19 | } 20 | 21 | impl ShapeError 22 | { 23 | /// Return the `ErrorKind` of this error. 24 | #[inline] 25 | pub fn kind(&self) -> ErrorKind 26 | { 27 | self.repr 28 | } 29 | 30 | /// Create a new `ShapeError` 31 | pub fn from_kind(error: ErrorKind) -> Self 32 | { 33 | from_kind(error) 34 | } 35 | } 36 | 37 | /// Error code for an error related to array shape or layout. 38 | /// 39 | /// This enumeration is not exhaustive. The representation of the enum 40 | /// is not guaranteed. 41 | #[non_exhaustive] 42 | #[derive(Copy, Clone, Debug)] 43 | pub enum ErrorKind 44 | { 45 | /// incompatible shape 46 | IncompatibleShape = 1, 47 | /// incompatible memory layout 48 | IncompatibleLayout, 49 | /// the shape does not fit inside type limits 50 | RangeLimited, 51 | /// out of bounds indexing 52 | OutOfBounds, 53 | /// aliasing array elements 54 | Unsupported, 55 | /// overflow when computing offset, length, etc. 56 | Overflow, 57 | } 58 | 59 | #[inline(always)] 60 | pub fn from_kind(k: ErrorKind) -> ShapeError 61 | { 62 | ShapeError { repr: k } 63 | } 64 | 65 | impl PartialEq for ErrorKind 66 | { 67 | #[inline(always)] 68 | fn eq(&self, rhs: &Self) -> bool 69 | { 70 | *self as u8 == *rhs as u8 71 | } 72 | } 73 | 74 | impl PartialEq for ShapeError 75 | { 76 | #[inline(always)] 77 | fn eq(&self, rhs: &Self) -> bool 78 | { 79 | self.repr == rhs.repr 80 | } 81 | } 82 | 83 | #[cfg(feature = "std")] 84 | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 85 | impl Error for ShapeError {} 86 | 87 | impl fmt::Display for ShapeError 88 | { 89 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 90 | { 91 | let description = match self.kind() { 92 | ErrorKind::IncompatibleShape => "incompatible shapes", 93 | ErrorKind::IncompatibleLayout => "incompatible memory layout", 94 | ErrorKind::RangeLimited => "the shape does not fit in type limits", 95 | ErrorKind::OutOfBounds => "out of bounds indexing", 96 | ErrorKind::Unsupported => "unsupported operation", 97 | ErrorKind::Overflow => "arithmetic overflow", 98 | }; 99 | write!(f, "ShapeError/{:?}: {}", self.kind(), description) 100 | } 101 | } 102 | 103 | impl fmt::Debug for ShapeError 104 | { 105 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 106 | { 107 | write!(f, "{}", self) 108 | } 109 | } 110 | 111 | pub fn incompatible_shapes(_a: &D, _b: &E) -> ShapeError 112 | where 113 | D: Dimension, 114 | E: Dimension, 115 | { 116 | from_kind(ErrorKind::IncompatibleShape) 117 | } 118 | -------------------------------------------------------------------------------- /src/extension.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Extension traits and utility functions for types from outside ndarray 10 | 11 | pub(crate) mod nonnull; 12 | -------------------------------------------------------------------------------- /src/extension/nonnull.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "std"))] 2 | use alloc::vec::Vec; 3 | use std::ptr::NonNull; 4 | 5 | /// Return a NonNull pointer to the vector's data 6 | pub(crate) fn nonnull_from_vec_data(v: &mut Vec) -> NonNull 7 | { 8 | // this pointer is guaranteed to be non-null 9 | unsafe { NonNull::new_unchecked(v.as_mut_ptr()) } 10 | } 11 | 12 | /// Converts `ptr` to `NonNull` 13 | /// 14 | /// Safety: `ptr` *must* be non-null. 15 | /// This is checked with a debug assertion, and will panic if this is not true, 16 | /// but treat this as an unconditional conversion. 17 | #[inline] 18 | pub(crate) unsafe fn nonnull_debug_checked_from_ptr(ptr: *mut T) -> NonNull 19 | { 20 | debug_assert!(!ptr.is_null()); 21 | NonNull::new_unchecked(ptr) 22 | } 23 | -------------------------------------------------------------------------------- /src/impl_1d.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Methods for one-dimensional arrays. 10 | #[cfg(not(feature = "std"))] 11 | use alloc::vec::Vec; 12 | use std::mem::MaybeUninit; 13 | 14 | use crate::imp_prelude::*; 15 | use crate::low_level_util::AbortIfPanic; 16 | 17 | /// # Methods For 1-D Arrays 18 | impl
ArrayRef 19 | { 20 | /// Return an vector with the elements of the one-dimensional array. 21 | pub fn to_vec(&self) -> Vec 22 | where A: Clone 23 | { 24 | if let Some(slc) = self.as_slice() { 25 | slc.to_vec() 26 | } else { 27 | crate::iterators::to_vec(self.iter().cloned()) 28 | } 29 | } 30 | 31 | /// Rotate the elements of the array by 1 element towards the front; 32 | /// the former first element becomes the last. 33 | pub(crate) fn rotate1_front(&mut self) 34 | { 35 | // use swapping to keep all elements initialized (as required by owned storage) 36 | let mut lane_iter = self.iter_mut(); 37 | let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; 38 | 39 | // Logically we do a circular swap here, all elements in a chain 40 | // Using MaybeUninit to avoid unnecessary writes in the safe swap solution 41 | // 42 | // for elt in lane_iter { 43 | // std::mem::swap(dst, elt); 44 | // dst = elt; 45 | // } 46 | // 47 | let guard = AbortIfPanic(&"rotate1_front: temporarily moving out of owned value"); 48 | let mut slot = MaybeUninit::::uninit(); 49 | unsafe { 50 | slot.as_mut_ptr().copy_from_nonoverlapping(dst, 1); 51 | for elt in lane_iter { 52 | (dst as *mut A).copy_from_nonoverlapping(elt, 1); 53 | dst = elt; 54 | } 55 | (dst as *mut A).copy_from_nonoverlapping(slot.as_ptr(), 1); 56 | } 57 | guard.defuse(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/impl_arc_array.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::imp_prelude::*; 10 | 11 | #[cfg(target_has_atomic = "ptr")] 12 | use alloc::sync::Arc; 13 | 14 | #[cfg(not(target_has_atomic = "ptr"))] 15 | use portable_atomic_util::Arc; 16 | 17 | /// Methods specific to `ArcArray`. 18 | /// 19 | /// ***See also all methods for [`ArrayBase`]*** 20 | impl ArcArray 21 | where D: Dimension 22 | { 23 | /// Returns `true` iff the inner `Arc` is not shared. 24 | /// If you want to ensure the `Arc` is not concurrently cloned, you need to provide a `&mut self` to this function. 25 | pub fn is_unique(&self) -> bool 26 | { 27 | // Only strong pointers are used in this crate. 28 | Arc::strong_count(&self.data.0) == 1 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/impl_clone.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::imp_prelude::*; 10 | use crate::LayoutRef; 11 | use crate::RawDataClone; 12 | 13 | impl Clone for ArrayBase 14 | { 15 | fn clone(&self) -> ArrayBase 16 | { 17 | // safe because `clone_with_ptr` promises to provide equivalent data and ptr 18 | unsafe { 19 | let (data, ptr) = self.data.clone_with_ptr(self.layout.ptr); 20 | ArrayBase { 21 | data, 22 | layout: LayoutRef { 23 | ptr, 24 | dim: self.layout.dim.clone(), 25 | strides: self.layout.strides.clone(), 26 | }, 27 | } 28 | } 29 | } 30 | 31 | /// `Array` implements `.clone_from()` to reuse an array's existing 32 | /// allocation. Semantically equivalent to `*self = other.clone()`, but 33 | /// potentially more efficient. 34 | fn clone_from(&mut self, other: &Self) 35 | { 36 | unsafe { 37 | self.layout.ptr = self.data.clone_from_with_ptr(&other.data, other.layout.ptr); 38 | self.layout.dim.clone_from(&other.layout.dim); 39 | self.layout.strides.clone_from(&other.layout.strides); 40 | } 41 | } 42 | } 43 | 44 | impl Copy for ArrayBase {} 45 | -------------------------------------------------------------------------------- /src/impl_cow.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::imp_prelude::*; 10 | 11 | /// Methods specific to `CowArray`. 12 | /// 13 | /// ***See also all methods for [`ArrayBase`]*** 14 | impl CowArray<'_, A, D> 15 | where D: Dimension 16 | { 17 | /// Returns `true` iff the array is the view (borrowed) variant. 18 | pub fn is_view(&self) -> bool 19 | { 20 | self.data.is_view() 21 | } 22 | 23 | /// Returns `true` iff the array is the owned variant. 24 | pub fn is_owned(&self) -> bool 25 | { 26 | self.data.is_owned() 27 | } 28 | } 29 | 30 | impl<'a, A, D> From> for CowArray<'a, A, D> 31 | where D: Dimension 32 | { 33 | fn from(view: ArrayView<'a, A, D>) -> CowArray<'a, A, D> 34 | { 35 | // safe because equivalent data 36 | unsafe { 37 | ArrayBase::from_data_ptr(CowRepr::View(view.data), view.ptr) 38 | .with_strides_dim(view.layout.strides, view.layout.dim) 39 | } 40 | } 41 | } 42 | 43 | impl<'a, A, D> From> for CowArray<'a, A, D> 44 | where D: Dimension 45 | { 46 | fn from(array: Array) -> CowArray<'a, A, D> 47 | { 48 | // safe because equivalent data 49 | unsafe { 50 | ArrayBase::from_data_ptr(CowRepr::Owned(array.data), array.layout.ptr) 51 | .with_strides_dim(array.layout.strides, array.layout.dim) 52 | } 53 | } 54 | } 55 | 56 | impl<'a, A, Slice: ?Sized> From<&'a Slice> for CowArray<'a, A, Ix1> 57 | where Slice: AsRef<[A]> 58 | { 59 | /// Create a one-dimensional clone-on-write view of the data in `slice`. 60 | /// 61 | /// **Panics** if the slice length is greater than [`isize::MAX`]. 62 | /// 63 | /// ``` 64 | /// use ndarray::{array, CowArray}; 65 | /// 66 | /// let array = CowArray::from(&[1., 2., 3., 4.]); 67 | /// assert!(array.is_view()); 68 | /// assert_eq!(array, array![1., 2., 3., 4.]); 69 | /// ``` 70 | fn from(slice: &'a Slice) -> Self 71 | { 72 | Self::from(ArrayView1::from(slice)) 73 | } 74 | } 75 | 76 | impl<'a, A, S, D> From<&'a ArrayBase> for CowArray<'a, A, D> 77 | where 78 | S: Data, 79 | D: Dimension, 80 | { 81 | /// Create a read-only clone-on-write view of the array. 82 | fn from(array: &'a ArrayBase) -> Self 83 | { 84 | Self::from(array.view()) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/impl_internal_constructors.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::ptr::NonNull; 10 | 11 | use crate::{imp_prelude::*, LayoutRef}; 12 | 13 | // internal "builder-like" methods 14 | impl ArrayBase 15 | where S: RawData 16 | { 17 | /// Create an (initially) empty one-dimensional array from the given data and array head 18 | /// pointer 19 | /// 20 | /// ## Safety 21 | /// 22 | /// The caller must ensure that the data storage and pointer is valid. 23 | /// 24 | /// See ArrayView::from_shape_ptr for general pointer validity documentation. 25 | #[inline] 26 | pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self 27 | { 28 | let array = ArrayBase { 29 | data, 30 | layout: LayoutRef { 31 | ptr, 32 | dim: Ix1(0), 33 | strides: Ix1(1), 34 | }, 35 | }; 36 | debug_assert!(array.pointer_is_inbounds()); 37 | array 38 | } 39 | } 40 | 41 | // internal "builder-like" methods 42 | impl ArrayBase 43 | where 44 | S: RawData, 45 | D: Dimension, 46 | { 47 | /// Set strides and dimension of the array to the new values 48 | /// 49 | /// The argument order with strides before dimensions is used because strides are often 50 | /// computed as derived from the dimension. 51 | /// 52 | /// ## Safety 53 | /// 54 | /// The caller needs to ensure that the new strides and dimensions are correct 55 | /// for the array data. 56 | #[inline] 57 | pub(crate) unsafe fn with_strides_dim(self, strides: E, dim: E) -> ArrayBase 58 | where E: Dimension 59 | { 60 | debug_assert_eq!(strides.ndim(), dim.ndim()); 61 | ArrayBase { 62 | data: self.data, 63 | layout: LayoutRef { 64 | ptr: self.layout.ptr, 65 | dim, 66 | strides, 67 | }, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/impl_special_element_types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::mem::MaybeUninit; 10 | 11 | use crate::imp_prelude::*; 12 | use crate::LayoutRef; 13 | use crate::RawDataSubst; 14 | 15 | /// Methods specific to arrays with `MaybeUninit` elements. 16 | /// 17 | /// ***See also all methods for [`ArrayBase`]*** 18 | impl ArrayBase 19 | where 20 | S: RawDataSubst>, 21 | D: Dimension, 22 | { 23 | /// **Promise** that the array's elements are all fully initialized, and convert 24 | /// the array from element type `MaybeUninit` to `A`. 25 | /// 26 | /// For example, it can convert an `Array, D>` to `Array`. 27 | /// 28 | /// ## Safety 29 | /// 30 | /// Safe to use if all the array's elements have been initialized. 31 | /// 32 | /// Note that for owned and shared ownership arrays, the promise must include all of the 33 | /// array's storage; it is for example possible to slice these in place, but that must 34 | /// only be done after all elements have been initialized. 35 | pub unsafe fn assume_init(self) -> ArrayBase<>::Output, D> 36 | { 37 | let ArrayBase { 38 | data, 39 | layout: LayoutRef { ptr, dim, strides }, 40 | } = self; 41 | 42 | // "transmute" from storage of MaybeUninit to storage of A 43 | let data = S::data_subst(data); 44 | let ptr = ptr.cast::(); 45 | ArrayBase::from_data_ptr(data, ptr).with_strides_dim(strides, dim) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/impl_views/mod.rs: -------------------------------------------------------------------------------- 1 | mod constructors; 2 | mod conversions; 3 | mod indexing; 4 | mod splitting; 5 | 6 | pub use indexing::*; 7 | -------------------------------------------------------------------------------- /src/iterators/into_iter.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::mem; 10 | use std::ptr::NonNull; 11 | 12 | use crate::imp_prelude::*; 13 | use crate::OwnedRepr; 14 | 15 | use super::Baseiter; 16 | use crate::impl_owned_array::drop_unreachable_raw; 17 | 18 | /// By-value iterator for an array 19 | pub struct IntoIter 20 | where D: Dimension 21 | { 22 | array_data: OwnedRepr, 23 | inner: Baseiter, 24 | data_len: usize, 25 | /// first memory address of an array element 26 | array_head_ptr: NonNull, 27 | // if true, the array owns elements that are not reachable by indexing 28 | // through all the indices of the dimension. 29 | has_unreachable_elements: bool, 30 | } 31 | 32 | impl IntoIter 33 | where D: Dimension 34 | { 35 | /// Create a new by-value iterator that consumes `array` 36 | pub(crate) fn new(array: Array) -> Self 37 | { 38 | unsafe { 39 | let array_head_ptr = array.ptr; 40 | let mut array_data = array.data; 41 | let data_len = array_data.release_all_elements(); 42 | debug_assert!(data_len >= array.layout.dim.size()); 43 | let has_unreachable_elements = array.layout.dim.size() != data_len; 44 | let inner = Baseiter::new(array_head_ptr, array.layout.dim, array.layout.strides); 45 | 46 | IntoIter { 47 | array_data, 48 | inner, 49 | data_len, 50 | array_head_ptr, 51 | has_unreachable_elements, 52 | } 53 | } 54 | } 55 | } 56 | 57 | impl Iterator for IntoIter 58 | { 59 | type Item = A; 60 | 61 | #[inline] 62 | fn next(&mut self) -> Option 63 | { 64 | self.inner.next().map(|p| unsafe { p.as_ptr().read() }) 65 | } 66 | 67 | fn size_hint(&self) -> (usize, Option) 68 | { 69 | self.inner.size_hint() 70 | } 71 | } 72 | 73 | impl ExactSizeIterator for IntoIter 74 | { 75 | fn len(&self) -> usize 76 | { 77 | self.inner.len() 78 | } 79 | } 80 | 81 | impl Drop for IntoIter 82 | where D: Dimension 83 | { 84 | fn drop(&mut self) 85 | { 86 | if !self.has_unreachable_elements || mem::size_of::() == 0 || !mem::needs_drop::() { 87 | return; 88 | } 89 | 90 | // iterate til the end 91 | while let Some(_) = self.next() {} 92 | 93 | unsafe { 94 | let data_ptr = self.array_data.as_nonnull_mut(); 95 | let view = RawArrayViewMut::new(self.array_head_ptr, self.inner.dim.clone(), self.inner.strides.clone()); 96 | debug_assert!(self.inner.dim.size() < self.data_len, "data_len {} and dim size {}", 97 | self.data_len, self.inner.dim.size()); 98 | drop_unreachable_raw(view, data_ptr, self.data_len); 99 | } 100 | } 101 | } 102 | 103 | impl IntoIterator for Array 104 | where D: Dimension 105 | { 106 | type Item = A; 107 | type IntoIter = IntoIter; 108 | 109 | fn into_iter(self) -> Self::IntoIter 110 | { 111 | IntoIter::new(self) 112 | } 113 | } 114 | 115 | impl IntoIterator for ArcArray 116 | where 117 | D: Dimension, 118 | A: Clone, 119 | { 120 | type Item = A; 121 | type IntoIter = IntoIter; 122 | 123 | fn into_iter(self) -> Self::IntoIter 124 | { 125 | IntoIter::new(self.into_owned()) 126 | } 127 | } 128 | 129 | impl IntoIterator for CowArray<'_, A, D> 130 | where 131 | D: Dimension, 132 | A: Clone, 133 | { 134 | type Item = A; 135 | type IntoIter = IntoIter; 136 | 137 | fn into_iter(self) -> Self::IntoIter 138 | { 139 | IntoIter::new(self.into_owned()) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/iterators/iter.rs: -------------------------------------------------------------------------------- 1 | //! Producers, iterables and iterators. 2 | //! 3 | //! This module collects all concrete producer, iterable and iterator 4 | //! implementation structs. 5 | //! 6 | //! 7 | //! See also [`NdProducer`](crate::NdProducer). 8 | 9 | pub use crate::dimension::Axes; 10 | pub use crate::indexes::{Indices, IndicesIter}; 11 | pub use crate::iterators::{ 12 | AxisChunksIter, 13 | AxisChunksIterMut, 14 | AxisIter, 15 | AxisIterMut, 16 | AxisWindows, 17 | ExactChunks, 18 | ExactChunksIter, 19 | ExactChunksIterMut, 20 | ExactChunksMut, 21 | IndexedIter, 22 | IndexedIterMut, 23 | IntoIter, 24 | Iter, 25 | IterMut, 26 | Lanes, 27 | LanesIter, 28 | LanesIterMut, 29 | LanesMut, 30 | Windows, 31 | }; 32 | -------------------------------------------------------------------------------- /src/iterators/lanes.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use super::LanesIter; 4 | use super::LanesIterMut; 5 | use crate::imp_prelude::*; 6 | use crate::{Layout, NdProducer}; 7 | 8 | impl_ndproducer! { 9 | ['a, A, D: Dimension] 10 | [Clone => 'a, A, D: Clone ] 11 | Lanes { 12 | base, 13 | inner_len, 14 | inner_stride, 15 | } 16 | Lanes<'a, A, D> { 17 | type Item = ArrayView<'a, A, Ix1>; 18 | type Dim = D; 19 | 20 | unsafe fn item(&self, ptr) { 21 | ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) 22 | } 23 | } 24 | } 25 | 26 | /// See [`.lanes()`](crate::ArrayRef::lanes) 27 | /// for more information. 28 | pub struct Lanes<'a, A, D> 29 | { 30 | base: ArrayView<'a, A, D>, 31 | inner_len: Ix, 32 | inner_stride: Ixs, 33 | } 34 | 35 | impl<'a, A, D: Dimension> Lanes<'a, A, D> 36 | { 37 | pub(crate) fn new(v: ArrayView<'a, A, Di>, axis: Axis) -> Self 38 | where Di: Dimension 39 | { 40 | let ndim = v.ndim(); 41 | let len; 42 | let stride; 43 | let iter_v = if ndim == 0 { 44 | len = 1; 45 | stride = 1; 46 | v.try_remove_axis(Axis(0)) 47 | } else { 48 | let i = axis.index(); 49 | len = v.dim[i]; 50 | stride = v.strides[i] as isize; 51 | v.try_remove_axis(axis) 52 | }; 53 | Lanes { 54 | inner_len: len, 55 | inner_stride: stride, 56 | base: iter_v, 57 | } 58 | } 59 | } 60 | 61 | impl_ndproducer! { 62 | ['a, A, D: Dimension] 63 | [Clone =>] 64 | LanesMut { 65 | base, 66 | inner_len, 67 | inner_stride, 68 | } 69 | LanesMut<'a, A, D> { 70 | type Item = ArrayViewMut<'a, A, Ix1>; 71 | type Dim = D; 72 | 73 | unsafe fn item(&self, ptr) { 74 | ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) 75 | } 76 | } 77 | } 78 | 79 | impl<'a, A, D> IntoIterator for Lanes<'a, A, D> 80 | where D: Dimension 81 | { 82 | type Item = ::Item; 83 | type IntoIter = LanesIter<'a, A, D>; 84 | fn into_iter(self) -> Self::IntoIter 85 | { 86 | LanesIter { 87 | iter: self.base.into_base_iter(), 88 | inner_len: self.inner_len, 89 | inner_stride: self.inner_stride, 90 | life: PhantomData, 91 | } 92 | } 93 | } 94 | 95 | /// See [`.lanes_mut()`](crate::ArrayRef::lanes_mut) 96 | /// for more information. 97 | pub struct LanesMut<'a, A, D> 98 | { 99 | base: ArrayViewMut<'a, A, D>, 100 | inner_len: Ix, 101 | inner_stride: Ixs, 102 | } 103 | 104 | impl<'a, A, D: Dimension> LanesMut<'a, A, D> 105 | { 106 | pub(crate) fn new(v: ArrayViewMut<'a, A, Di>, axis: Axis) -> Self 107 | where Di: Dimension 108 | { 109 | let ndim = v.ndim(); 110 | let len; 111 | let stride; 112 | let iter_v = if ndim == 0 { 113 | len = 1; 114 | stride = 1; 115 | v.try_remove_axis(Axis(0)) 116 | } else { 117 | let i = axis.index(); 118 | len = v.dim[i]; 119 | stride = v.strides[i] as isize; 120 | v.try_remove_axis(axis) 121 | }; 122 | LanesMut { 123 | inner_len: len, 124 | inner_stride: stride, 125 | base: iter_v, 126 | } 127 | } 128 | } 129 | 130 | impl<'a, A, D> IntoIterator for LanesMut<'a, A, D> 131 | where D: Dimension 132 | { 133 | type Item = ::Item; 134 | type IntoIter = LanesIterMut<'a, A, D>; 135 | fn into_iter(self) -> Self::IntoIter 136 | { 137 | LanesIterMut { 138 | iter: self.base.into_base_iter(), 139 | inner_len: self.inner_len, 140 | inner_stride: self.inner_stride, 141 | life: PhantomData, 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/iterators/macros.rs: -------------------------------------------------------------------------------- 1 | // Send and Sync 2 | // All the iterators are thread safe the same way the slice's iterator are 3 | 4 | // read-only iterators use Sync => Send rules, same as `std::slice::Iter`. 5 | macro_rules! send_sync_read_only { 6 | ($name:ident) => { 7 | unsafe impl<'a, A, D> Send for $name<'a, A, D> 8 | where 9 | A: Sync, 10 | D: Send, 11 | { 12 | } 13 | unsafe impl<'a, A, D> Sync for $name<'a, A, D> 14 | where 15 | A: Sync, 16 | D: Sync, 17 | { 18 | } 19 | }; 20 | } 21 | 22 | // read-write iterators use Send => Send rules, same as `std::slice::IterMut`. 23 | macro_rules! send_sync_read_write { 24 | ($name:ident) => { 25 | unsafe impl<'a, A, D> Send for $name<'a, A, D> 26 | where 27 | A: Send, 28 | D: Send, 29 | { 30 | } 31 | unsafe impl<'a, A, D> Sync for $name<'a, A, D> 32 | where 33 | A: Sync, 34 | D: Sync, 35 | { 36 | } 37 | }; 38 | } 39 | 40 | macro_rules! impl_ndproducer { 41 | ( 42 | [$($typarm:tt)*] 43 | [Clone => $($cloneparm:tt)*] 44 | $typename:ident { 45 | $base:ident, 46 | $( 47 | $fieldname:ident, 48 | )* 49 | } 50 | $fulltype:ty { 51 | $( 52 | type $atyn:ident = $atyv:ty; 53 | )* 54 | 55 | unsafe fn item(&$self_:ident, $ptr:pat) { 56 | $refexpr:expr 57 | } 58 | }) => { 59 | impl<$($typarm)*> NdProducer for $fulltype { 60 | $( 61 | type $atyn = $atyv; 62 | )* 63 | type Ptr = *mut A; 64 | type Stride = isize; 65 | 66 | fn raw_dim(&self) -> D { 67 | self.$base.raw_dim() 68 | } 69 | 70 | fn layout(&self) -> Layout { 71 | self.$base.layout() 72 | } 73 | 74 | fn as_ptr(&self) -> *mut A { 75 | self.$base.as_ptr() as *mut _ 76 | } 77 | 78 | fn contiguous_stride(&self) -> isize { 79 | self.$base.contiguous_stride() 80 | } 81 | 82 | unsafe fn as_ref(&$self_, $ptr: *mut A) -> Self::Item { 83 | $refexpr 84 | } 85 | 86 | unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { 87 | self.$base.uget_ptr(i) as *mut _ 88 | } 89 | 90 | fn stride_of(&self, axis: Axis) -> isize { 91 | self.$base.stride_of(axis) 92 | } 93 | 94 | fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { 95 | let (a, b) = self.$base.split_at(axis, index); 96 | ($typename { 97 | $base: a, 98 | $( 99 | $fieldname: self.$fieldname.clone(), 100 | )* 101 | }, 102 | $typename { 103 | $base: b, 104 | $( 105 | $fieldname: self.$fieldname, 106 | )* 107 | }) 108 | } 109 | 110 | private_impl!{} 111 | } 112 | 113 | expand_if!(@nonempty [$($cloneparm)*] 114 | impl<$($cloneparm)*> Clone for $fulltype { 115 | fn clone(&self) -> Self { 116 | $typename { 117 | $base: self.base.clone(), 118 | $( 119 | $fieldname: self.$fieldname.clone(), 120 | )* 121 | } 122 | } 123 | } 124 | ); 125 | 126 | }; 127 | } 128 | 129 | macro_rules! impl_iterator { 130 | ( 131 | [$($typarm:tt)*] 132 | [Clone => $($cloneparm:tt)*] 133 | $typename:ident { 134 | $base:ident, 135 | $( 136 | $fieldname:ident, 137 | )* 138 | } 139 | $fulltype:ty { 140 | type Item = $ity:ty; 141 | 142 | fn item(&mut $self_:ident, $elt:pat) { 143 | $refexpr:expr 144 | } 145 | }) => { 146 | expand_if!(@nonempty [$($cloneparm)*] 147 | 148 | impl<$($cloneparm)*> Clone for $fulltype { 149 | fn clone(&self) -> Self { 150 | $typename { 151 | $base: self.$base.clone(), 152 | $( 153 | $fieldname: self.$fieldname.clone(), 154 | )* 155 | } 156 | } 157 | } 158 | 159 | ); 160 | impl<$($typarm)*> Iterator for $fulltype { 161 | type Item = $ity; 162 | 163 | fn next(&mut $self_) -> Option { 164 | $self_.$base.next().map(|$elt| { 165 | $refexpr 166 | }) 167 | } 168 | 169 | fn size_hint(&self) -> (usize, Option) { 170 | self.$base.size_hint() 171 | } 172 | } 173 | }; 174 | } 175 | -------------------------------------------------------------------------------- /src/itertools.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 bluss and ndarray developers 2 | // and Michał Krasnoborski (krdln) 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | //! A few iterator-related utilities and tools 11 | 12 | use std::iter; 13 | 14 | /// Iterate `iterable` with a running index. 15 | /// 16 | /// `IntoIterator` enabled version of `.enumerate()`. 17 | /// 18 | /// ``` 19 | /// use itertools::enumerate; 20 | /// 21 | /// for (i, elt) in enumerate(&[1, 2, 3]) { 22 | /// /* loop body */ 23 | /// } 24 | /// ``` 25 | pub(crate) fn enumerate(iterable: I) -> iter::Enumerate 26 | where I: IntoIterator 27 | { 28 | iterable.into_iter().enumerate() 29 | } 30 | 31 | /// Iterate `i` and `j` in lock step. 32 | /// 33 | /// `IntoIterator` enabled version of `i.zip(j)`. 34 | /// 35 | /// ``` 36 | /// use itertools::zip; 37 | /// 38 | /// let data = [1, 2, 3, 4, 5]; 39 | /// for (a, b) in zip(&data, &data[1..]) { 40 | /// /* loop body */ 41 | /// } 42 | /// ``` 43 | pub(crate) fn zip(i: I, j: J) -> iter::Zip 44 | where 45 | I: IntoIterator, 46 | J: IntoIterator, 47 | { 48 | i.into_iter().zip(j) 49 | } 50 | 51 | /// Create an iterator running multiple iterators in lockstep. 52 | /// 53 | /// The `izip!` iterator yields elements until any subiterator 54 | /// returns `None`. 55 | /// 56 | /// This is a version of the standard ``.zip()`` that's supporting more than 57 | /// two iterators. The iterator element type is a tuple with one element 58 | /// from each of the input iterators. Just like ``.zip()``, the iteration stops 59 | /// when the shortest of the inputs reaches its end. 60 | /// 61 | /// **Note:** The result of this macro is in the general case an iterator 62 | /// composed of repeated `.zip()` and a `.map()`; it has an anonymous type. 63 | /// The special cases of one and two arguments produce the equivalent of 64 | /// `$a.into_iter()` and `$a.into_iter().zip($b)` respectively. 65 | /// 66 | /// Prefer this macro `izip!()` over `multizip` for the performance benefits 67 | /// of using the standard library `.zip()`. 68 | /// 69 | /// ``` 70 | /// #[macro_use] extern crate itertools; 71 | /// # fn main() { 72 | /// 73 | /// // iterate over three sequences side-by-side 74 | /// let mut results = [0, 0, 0, 0]; 75 | /// let inputs = [3, 7, 9, 6]; 76 | /// 77 | /// for (r, index, input) in izip!(&mut results, 0..10, &inputs) { 78 | /// *r = index * 10 + input; 79 | /// } 80 | /// 81 | /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); 82 | /// # } 83 | /// ``` 84 | /// 85 | /// **Note:** To enable the macros in this crate, use the `#[macro_use]` 86 | /// attribute when importing the crate: 87 | /// 88 | /// ```no_run 89 | /// # #[allow(unused_imports)] 90 | /// #[macro_use] extern crate itertools; 91 | /// # fn main() { } 92 | /// ``` 93 | macro_rules! izip { 94 | // @closure creates a tuple-flattening closure for .map() call. usage: 95 | // @closure partial_pattern => partial_tuple , rest , of , iterators 96 | // eg. izip!( @closure ((a, b), c) => (a, b, c) , dd , ee ) 97 | ( @closure $p:pat => $tup:expr ) => { 98 | |$p| $tup 99 | }; 100 | 101 | // The "b" identifier is a different identifier on each recursion level thanks to hygiene. 102 | ( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => { 103 | izip!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*) 104 | }; 105 | 106 | // unary 107 | ($first:expr $(,)*) => { 108 | IntoIterator::into_iter($first) 109 | }; 110 | 111 | // binary 112 | ($first:expr, $second:expr $(,)*) => { 113 | izip!($first) 114 | .zip($second) 115 | }; 116 | 117 | // n-ary where n > 2 118 | ( $first:expr $( , $rest:expr )* $(,)* ) => { 119 | izip!($first) 120 | $( 121 | .zip($rest) 122 | )* 123 | .map( 124 | izip!(@closure a => (a) $( , $rest )*) 125 | ) 126 | }; 127 | } 128 | -------------------------------------------------------------------------------- /src/layout/layoutfmt.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use super::Layout; 10 | 11 | const LAYOUT_NAMES: &[&str] = &["C", "F", "c", "f"]; 12 | 13 | use std::fmt; 14 | 15 | impl fmt::Debug for Layout 16 | { 17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 18 | { 19 | if self.0 == 0 { 20 | write!(f, "Custom")? 21 | } else { 22 | (0..32).filter(|&i| self.is(1 << i)).try_fold((), |_, i| { 23 | if let Some(name) = LAYOUT_NAMES.get(i) { 24 | write!(f, "{}", name) 25 | } else { 26 | write!(f, "{:#x}", i) 27 | } 28 | })?; 29 | }; 30 | write!(f, " ({:#x})", self.0) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/linalg/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Linear algebra. 10 | 11 | pub use self::impl_linalg::general_mat_mul; 12 | pub use self::impl_linalg::general_mat_vec_mul; 13 | pub use self::impl_linalg::kron; 14 | pub use self::impl_linalg::Dot; 15 | 16 | mod impl_linalg; 17 | -------------------------------------------------------------------------------- /src/linalg_traits.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #[cfg(feature = "std")] 10 | use num_traits::Float; 11 | use num_traits::{One, Zero}; 12 | 13 | #[cfg(feature = "std")] 14 | use std::fmt; 15 | use std::ops::{Add, Div, Mul, Sub}; 16 | #[cfg(feature = "std")] 17 | use std::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; 18 | 19 | #[cfg(feature = "std")] 20 | use crate::ScalarOperand; 21 | 22 | /// Elements that support linear algebra operations. 23 | /// 24 | /// `'static` for type-based specialization, `Copy` so that they don't need move 25 | /// semantics or destructors, and the rest are numerical traits. 26 | pub trait LinalgScalar: 27 | 'static + Copy + Zero + One + Add + Sub + Mul + Div 28 | { 29 | } 30 | 31 | impl LinalgScalar for T where T: 'static + Copy + Zero + One + Add + Sub + Mul + Div 32 | {} 33 | 34 | /// Floating-point element types `f32` and `f64`. 35 | /// 36 | /// Trait `NdFloat` is only implemented for `f32` and `f64` but encompasses as 37 | /// much float-relevant ndarray functionality as possible, including the traits 38 | /// needed for linear algebra and for *right hand side* scalar 39 | /// operations (`ScalarOperand`). 40 | /// 41 | /// This trait can only be implemented by `f32` and `f64`. 42 | /// 43 | /// **Requires default crate feature `"std"`** 44 | #[cfg(feature = "std")] 45 | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 46 | pub trait NdFloat: 47 | Float 48 | + AddAssign 49 | + SubAssign 50 | + MulAssign 51 | + DivAssign 52 | + RemAssign 53 | + fmt::Display 54 | + fmt::Debug 55 | + fmt::LowerExp 56 | + fmt::UpperExp 57 | + ScalarOperand 58 | + LinalgScalar 59 | + Send 60 | + Sync 61 | { 62 | } 63 | 64 | #[cfg(feature = "std")] 65 | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 66 | impl NdFloat for f32 {} 67 | #[cfg(feature = "std")] 68 | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 69 | impl NdFloat for f64 {} 70 | -------------------------------------------------------------------------------- /src/linspace.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | #![cfg(feature = "std")] 9 | use num_traits::Float; 10 | 11 | /// An iterator of a sequence of evenly spaced floats. 12 | /// 13 | /// Iterator element type is `F`. 14 | pub struct Linspace 15 | { 16 | start: F, 17 | step: F, 18 | index: usize, 19 | len: usize, 20 | } 21 | 22 | impl Iterator for Linspace 23 | where F: Float 24 | { 25 | type Item = F; 26 | 27 | #[inline] 28 | fn next(&mut self) -> Option 29 | { 30 | if self.index >= self.len { 31 | None 32 | } else { 33 | // Calculate the value just like numpy.linspace does 34 | let i = self.index; 35 | self.index += 1; 36 | Some(self.start + self.step * F::from(i).unwrap()) 37 | } 38 | } 39 | 40 | #[inline] 41 | fn size_hint(&self) -> (usize, Option) 42 | { 43 | let n = self.len - self.index; 44 | (n, Some(n)) 45 | } 46 | } 47 | 48 | impl DoubleEndedIterator for Linspace 49 | where F: Float 50 | { 51 | #[inline] 52 | fn next_back(&mut self) -> Option 53 | { 54 | if self.index >= self.len { 55 | None 56 | } else { 57 | // Calculate the value just like numpy.linspace does 58 | self.len -= 1; 59 | let i = self.len; 60 | Some(self.start + self.step * F::from(i).unwrap()) 61 | } 62 | } 63 | } 64 | 65 | impl ExactSizeIterator for Linspace where Linspace: Iterator {} 66 | 67 | /// Return an iterator of evenly spaced floats. 68 | /// 69 | /// The `Linspace` has `n` elements from `a` to `b` (inclusive). 70 | /// 71 | /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. 72 | /// [`f32`] or [`f64`]. 73 | /// 74 | /// **Panics** if converting `n - 1` to type `F` fails. 75 | #[inline] 76 | pub fn linspace(a: F, b: F, n: usize) -> Linspace 77 | where F: Float 78 | { 79 | let step = if n > 1 { 80 | let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); 81 | (b - a) / num_steps 82 | } else { 83 | F::zero() 84 | }; 85 | Linspace { 86 | start: a, 87 | step, 88 | index: 0, 89 | len: n, 90 | } 91 | } 92 | 93 | /// Return an iterator of floats from `a` to `b` (exclusive), 94 | /// incrementing by `step`. 95 | /// 96 | /// Numerical reasons can result in `b` being included in the result. 97 | /// 98 | /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. 99 | /// [`f32`] or [`f64`]. 100 | /// 101 | /// **Panics** if converting `((b - a) / step).ceil()` to type `F` fails. 102 | #[inline] 103 | pub fn range(a: F, b: F, step: F) -> Linspace 104 | where F: Float 105 | { 106 | let len = b - a; 107 | let steps = F::ceil(len / step); 108 | Linspace { 109 | start: a, 110 | step, 111 | len: steps.to_usize().expect( 112 | "Converting the length to `usize` must not fail. The most likely \ 113 | cause of this failure is if the sign of `end - start` is \ 114 | different from the sign of `step`.", 115 | ), 116 | index: 0, 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/logspace.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | #![cfg(feature = "std")] 9 | use num_traits::Float; 10 | 11 | /// An iterator of a sequence of logarithmically spaced number. 12 | /// 13 | /// Iterator element type is `F`. 14 | pub struct Logspace 15 | { 16 | sign: F, 17 | base: F, 18 | start: F, 19 | step: F, 20 | index: usize, 21 | len: usize, 22 | } 23 | 24 | impl Iterator for Logspace 25 | where F: Float 26 | { 27 | type Item = F; 28 | 29 | #[inline] 30 | fn next(&mut self) -> Option 31 | { 32 | if self.index >= self.len { 33 | None 34 | } else { 35 | // Calculate the value just like numpy.linspace does 36 | let i = self.index; 37 | self.index += 1; 38 | let exponent = self.start + self.step * F::from(i).unwrap(); 39 | Some(self.sign * self.base.powf(exponent)) 40 | } 41 | } 42 | 43 | #[inline] 44 | fn size_hint(&self) -> (usize, Option) 45 | { 46 | let n = self.len - self.index; 47 | (n, Some(n)) 48 | } 49 | } 50 | 51 | impl DoubleEndedIterator for Logspace 52 | where F: Float 53 | { 54 | #[inline] 55 | fn next_back(&mut self) -> Option 56 | { 57 | if self.index >= self.len { 58 | None 59 | } else { 60 | // Calculate the value just like numpy.linspace does 61 | self.len -= 1; 62 | let i = self.len; 63 | let exponent = self.start + self.step * F::from(i).unwrap(); 64 | Some(self.sign * self.base.powf(exponent)) 65 | } 66 | } 67 | } 68 | 69 | impl ExactSizeIterator for Logspace where Logspace: Iterator {} 70 | 71 | /// An iterator of a sequence of logarithmically spaced numbers. 72 | /// 73 | /// The [`Logspace`] has `n` elements, where the first element is `base.powf(a)` 74 | /// and the last element is `base.powf(b)`. If `base` is negative, this 75 | /// iterator will return all negative values. 76 | /// 77 | /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. 78 | /// [`f32`] or [`f64`]. 79 | /// 80 | /// **Panics** if converting `n - 1` to type `F` fails. 81 | #[inline] 82 | pub fn logspace(base: F, a: F, b: F, n: usize) -> Logspace 83 | where F: Float 84 | { 85 | let step = if n > 1 { 86 | let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); 87 | (b - a) / num_steps 88 | } else { 89 | F::zero() 90 | }; 91 | Logspace { 92 | sign: base.signum(), 93 | base: base.abs(), 94 | start: a, 95 | step, 96 | index: 0, 97 | len: n, 98 | } 99 | } 100 | 101 | #[cfg(test)] 102 | mod tests 103 | { 104 | use super::logspace; 105 | 106 | #[test] 107 | #[cfg(feature = "approx")] 108 | fn valid() 109 | { 110 | use crate::{arr1, Array1}; 111 | use approx::assert_abs_diff_eq; 112 | 113 | let array: Array1<_> = logspace(10.0, 0.0, 3.0, 4).collect(); 114 | assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3])); 115 | 116 | let array: Array1<_> = logspace(10.0, 3.0, 0.0, 4).collect(); 117 | assert_abs_diff_eq!(array, arr1(&[1e3, 1e2, 1e1, 1e0])); 118 | 119 | let array: Array1<_> = logspace(-10.0, 3.0, 0.0, 4).collect(); 120 | assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0])); 121 | 122 | let array: Array1<_> = logspace(-10.0, 0.0, 3.0, 4).collect(); 123 | assert_abs_diff_eq!(array, arr1(&[-1e0, -1e1, -1e2, -1e3])); 124 | } 125 | 126 | #[test] 127 | fn iter_forward() 128 | { 129 | let mut iter = logspace(10.0f64, 0.0, 3.0, 4); 130 | 131 | assert!(iter.size_hint() == (4, Some(4))); 132 | 133 | assert!((iter.next().unwrap() - 1e0).abs() < 1e-5); 134 | assert!((iter.next().unwrap() - 1e1).abs() < 1e-5); 135 | assert!((iter.next().unwrap() - 1e2).abs() < 1e-5); 136 | assert!((iter.next().unwrap() - 1e3).abs() < 1e-5); 137 | assert!(iter.next().is_none()); 138 | 139 | assert!(iter.size_hint() == (0, Some(0))); 140 | } 141 | 142 | #[test] 143 | fn iter_backward() 144 | { 145 | let mut iter = logspace(10.0f64, 0.0, 3.0, 4); 146 | 147 | assert!(iter.size_hint() == (4, Some(4))); 148 | 149 | assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5); 150 | assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5); 151 | assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5); 152 | assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5); 153 | assert!(iter.next_back().is_none()); 154 | 155 | assert!(iter.size_hint() == (0, Some(0))); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/low_level_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | /// Guard value that will abort if it is dropped. 10 | /// To defuse, this value must be forgotten before the end of the scope. 11 | /// 12 | /// The string value is added to the message printed if aborting. 13 | #[must_use] 14 | pub(crate) struct AbortIfPanic(pub(crate) &'static &'static str); 15 | 16 | impl AbortIfPanic 17 | { 18 | /// Defuse the AbortIfPanic guard. This *must* be done when finished. 19 | #[inline] 20 | pub(crate) fn defuse(self) 21 | { 22 | std::mem::forget(self); 23 | } 24 | } 25 | 26 | impl Drop for AbortIfPanic 27 | { 28 | // The compiler should be able to remove this, if it can see through that there 29 | // is no panic in the code section. 30 | fn drop(&mut self) 31 | { 32 | #[cfg(feature = "std")] 33 | { 34 | eprintln!("ndarray: panic in no-panic section, aborting: {}", self.0); 35 | std::process::abort() 36 | } 37 | #[cfg(not(feature = "std"))] 38 | { 39 | // no-std uses panic-in-panic (should abort) 40 | panic!("ndarray: panic in no-panic section, bailing out: {}", self.0); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/macro_utils.rs: -------------------------------------------------------------------------------- 1 | /// Derive Copy and Clone using the parameters (and bounds) as specified in [] 2 | macro_rules! copy_and_clone { 3 | ([$($parm:tt)*] $type_:ty) => { 4 | impl<$($parm)*> Copy for $type_ { } 5 | impl<$($parm)*> Clone for $type_ { 6 | #[inline(always)] 7 | fn clone(&self) -> Self { *self } 8 | } 9 | }; 10 | ($type_:ty) => { 11 | copy_and_clone!{ [] $type_ } 12 | }; 13 | } 14 | 15 | macro_rules! clone_bounds { 16 | ([$($parmbounds:tt)*] $typename:ident [$($parm:tt)*] { 17 | @copy { 18 | $($copyfield:ident,)* 19 | } 20 | $($field:ident,)* 21 | }) => { 22 | impl<$($parmbounds)*> Clone for $typename<$($parm)*> { 23 | fn clone(&self) -> Self { 24 | $typename { 25 | $( 26 | $copyfield: self.$copyfield, 27 | )* 28 | $( 29 | $field: self.$field.clone(), 30 | )* 31 | } 32 | } 33 | } 34 | }; 35 | } 36 | 37 | /// This assertion is always enabled but only verbose (formatting when 38 | /// debug assertions are enabled). 39 | #[cfg(debug_assertions)] 40 | macro_rules! ndassert { 41 | ($e:expr, $($t:tt)*) => { assert!($e, $($t)*) }; 42 | } 43 | 44 | #[cfg(not(debug_assertions))] 45 | macro_rules! ndassert { 46 | ($e:expr, $($_ignore:tt)*) => { 47 | assert!($e) 48 | }; 49 | } 50 | 51 | macro_rules! expand_if { 52 | (@bool [true] $($body:tt)*) => { $($body)* }; 53 | (@bool [false] $($body:tt)*) => { }; 54 | (@nonempty [$($if_present:tt)+] $($body:tt)*) => { 55 | $($body)* 56 | }; 57 | (@nonempty [] $($body:tt)*) => { }; 58 | } 59 | 60 | // Macro to insert more informative out of bounds message in debug builds 61 | #[cfg(debug_assertions)] 62 | macro_rules! debug_bounds_check { 63 | ($self_:ident, $index:expr) => { 64 | if $index.index_checked(&$self_.dim, &$self_.strides).is_none() { 65 | panic!( 66 | "ndarray: index {:?} is out of bounds for array of shape {:?}", 67 | $index, 68 | $self_.shape() 69 | ); 70 | } 71 | }; 72 | } 73 | 74 | #[cfg(not(debug_assertions))] 75 | macro_rules! debug_bounds_check { 76 | ($self_:ident, $index:expr) => {}; 77 | } 78 | -------------------------------------------------------------------------------- /src/math_cell.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::cmp::Ordering; 3 | use std::fmt; 4 | 5 | use std::ops::{Deref, DerefMut}; 6 | 7 | /// A transparent wrapper of [`Cell`](std::cell::Cell) which is identical in every way, except 8 | /// it will implement arithmetic operators as well. 9 | /// 10 | /// The purpose of `MathCell` is to be used from [.cell_view()](crate::ArrayRef::cell_view). 11 | /// The `MathCell` derefs to `Cell`, so all the cell's methods are available. 12 | #[repr(transparent)] 13 | #[derive(Default)] 14 | pub struct MathCell(Cell); 15 | 16 | impl MathCell 17 | { 18 | /// Create a new cell with the given value 19 | #[inline(always)] 20 | pub const fn new(value: T) -> Self 21 | { 22 | MathCell(Cell::new(value)) 23 | } 24 | 25 | /// Return the inner value 26 | pub fn into_inner(self) -> T 27 | { 28 | Cell::into_inner(self.0) 29 | } 30 | 31 | /// Swap value with another cell 32 | pub fn swap(&self, other: &Self) 33 | { 34 | Cell::swap(&self.0, &other.0) 35 | } 36 | } 37 | 38 | impl Deref for MathCell 39 | { 40 | type Target = Cell; 41 | #[inline(always)] 42 | fn deref(&self) -> &Self::Target 43 | { 44 | &self.0 45 | } 46 | } 47 | 48 | impl DerefMut for MathCell 49 | { 50 | #[inline(always)] 51 | fn deref_mut(&mut self) -> &mut Self::Target 52 | { 53 | &mut self.0 54 | } 55 | } 56 | 57 | impl Clone for MathCell 58 | where T: Copy 59 | { 60 | fn clone(&self) -> Self 61 | { 62 | MathCell::new(self.get()) 63 | } 64 | } 65 | 66 | impl PartialEq for MathCell 67 | where T: Copy + PartialEq 68 | { 69 | fn eq(&self, rhs: &Self) -> bool 70 | { 71 | self.get() == rhs.get() 72 | } 73 | } 74 | 75 | impl Eq for MathCell where T: Copy + Eq {} 76 | 77 | impl PartialOrd for MathCell 78 | where T: Copy + PartialOrd 79 | { 80 | fn partial_cmp(&self, rhs: &Self) -> Option 81 | { 82 | self.get().partial_cmp(&rhs.get()) 83 | } 84 | 85 | fn lt(&self, rhs: &Self) -> bool 86 | { 87 | self.get().lt(&rhs.get()) 88 | } 89 | fn le(&self, rhs: &Self) -> bool 90 | { 91 | self.get().le(&rhs.get()) 92 | } 93 | fn gt(&self, rhs: &Self) -> bool 94 | { 95 | self.get().gt(&rhs.get()) 96 | } 97 | fn ge(&self, rhs: &Self) -> bool 98 | { 99 | self.get().ge(&rhs.get()) 100 | } 101 | } 102 | 103 | impl Ord for MathCell 104 | where T: Copy + Ord 105 | { 106 | fn cmp(&self, rhs: &Self) -> Ordering 107 | { 108 | self.get().cmp(&rhs.get()) 109 | } 110 | } 111 | 112 | impl fmt::Debug for MathCell 113 | where T: Copy + fmt::Debug 114 | { 115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result 116 | { 117 | self.get().fmt(f) 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | mod tests 123 | { 124 | use super::MathCell; 125 | 126 | #[test] 127 | fn test_basic() 128 | { 129 | let c = &MathCell::new(0); 130 | c.set(1); 131 | assert_eq!(c.get(), 1); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/numeric/mod.rs: -------------------------------------------------------------------------------- 1 | mod impl_numeric; 2 | 3 | mod impl_float_maths; 4 | -------------------------------------------------------------------------------- /src/numeric_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::cmp; 10 | 11 | use crate::LinalgScalar; 12 | 13 | /// Fold over the manually unrolled `xs` with `f` 14 | pub fn unrolled_fold(mut xs: &[A], init: I, f: F) -> A 15 | where 16 | A: Clone, 17 | I: Fn() -> A, 18 | F: Fn(A, A) -> A, 19 | { 20 | // eightfold unrolled so that floating point can be vectorized 21 | // (even with strict floating point accuracy semantics) 22 | let mut acc = init(); 23 | let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = 24 | (init(), init(), init(), init(), init(), init(), init(), init()); 25 | while xs.len() >= 8 { 26 | p0 = f(p0, xs[0].clone()); 27 | p1 = f(p1, xs[1].clone()); 28 | p2 = f(p2, xs[2].clone()); 29 | p3 = f(p3, xs[3].clone()); 30 | p4 = f(p4, xs[4].clone()); 31 | p5 = f(p5, xs[5].clone()); 32 | p6 = f(p6, xs[6].clone()); 33 | p7 = f(p7, xs[7].clone()); 34 | 35 | xs = &xs[8..]; 36 | } 37 | acc = f(acc.clone(), f(p0, p4)); 38 | acc = f(acc.clone(), f(p1, p5)); 39 | acc = f(acc.clone(), f(p2, p6)); 40 | acc = f(acc.clone(), f(p3, p7)); 41 | 42 | // make it clear to the optimizer that this loop is short 43 | // and can not be autovectorized. 44 | for (i, x) in xs.iter().enumerate() { 45 | if i >= 7 { 46 | break; 47 | } 48 | acc = f(acc.clone(), x.clone()) 49 | } 50 | acc 51 | } 52 | 53 | /// Compute the dot product. 54 | /// 55 | /// `xs` and `ys` must be the same length 56 | pub fn unrolled_dot(xs: &[A], ys: &[A]) -> A 57 | where A: LinalgScalar 58 | { 59 | debug_assert_eq!(xs.len(), ys.len()); 60 | // eightfold unrolled so that floating point can be vectorized 61 | // (even with strict floating point accuracy semantics) 62 | let len = cmp::min(xs.len(), ys.len()); 63 | let mut xs = &xs[..len]; 64 | let mut ys = &ys[..len]; 65 | let mut sum = A::zero(); 66 | let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = 67 | (A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero()); 68 | while xs.len() >= 8 { 69 | p0 = p0 + xs[0] * ys[0]; 70 | p1 = p1 + xs[1] * ys[1]; 71 | p2 = p2 + xs[2] * ys[2]; 72 | p3 = p3 + xs[3] * ys[3]; 73 | p4 = p4 + xs[4] * ys[4]; 74 | p5 = p5 + xs[5] * ys[5]; 75 | p6 = p6 + xs[6] * ys[6]; 76 | p7 = p7 + xs[7] * ys[7]; 77 | 78 | xs = &xs[8..]; 79 | ys = &ys[8..]; 80 | } 81 | sum = sum + (p0 + p4); 82 | sum = sum + (p1 + p5); 83 | sum = sum + (p2 + p6); 84 | sum = sum + (p3 + p7); 85 | 86 | for (i, (&x, &y)) in xs.iter().zip(ys).enumerate() { 87 | if i >= 7 { 88 | break; 89 | } 90 | sum = sum + x * y; 91 | } 92 | sum 93 | } 94 | 95 | /// Compute pairwise equality 96 | /// 97 | /// `xs` and `ys` must be the same length 98 | pub fn unrolled_eq(xs: &[A], ys: &[B]) -> bool 99 | where A: PartialEq 100 | { 101 | debug_assert_eq!(xs.len(), ys.len()); 102 | // eightfold unrolled for performance (this is not done by llvm automatically) 103 | let len = cmp::min(xs.len(), ys.len()); 104 | let mut xs = &xs[..len]; 105 | let mut ys = &ys[..len]; 106 | 107 | while xs.len() >= 8 { 108 | if (xs[0] != ys[0]) 109 | | (xs[1] != ys[1]) 110 | | (xs[2] != ys[2]) 111 | | (xs[3] != ys[3]) 112 | | (xs[4] != ys[4]) 113 | | (xs[5] != ys[5]) 114 | | (xs[6] != ys[6]) 115 | | (xs[7] != ys[7]) 116 | { 117 | return false; 118 | } 119 | xs = &xs[8..]; 120 | ys = &ys[8..]; 121 | } 122 | 123 | for i in 0..xs.len() { 124 | if xs[i] != ys[i] { 125 | return false; 126 | } 127 | } 128 | 129 | true 130 | } 131 | -------------------------------------------------------------------------------- /src/order.rs: -------------------------------------------------------------------------------- 1 | /// Array order 2 | /// 3 | /// Order refers to indexing order, or how a linear sequence is translated 4 | /// into a two-dimensional or multi-dimensional array. 5 | /// 6 | /// - `RowMajor` means that the index along the row is the most rapidly changing 7 | /// - `ColumnMajor` means that the index along the column is the most rapidly changing 8 | /// 9 | /// Given a sequence like: 1, 2, 3, 4, 5, 6 10 | /// 11 | /// If it is laid it out in a 2 x 3 matrix using row major ordering, it results in: 12 | /// 13 | /// ```text 14 | /// 1 2 3 15 | /// 4 5 6 16 | /// ``` 17 | /// 18 | /// If it is laid using column major ordering, it results in: 19 | /// 20 | /// ```text 21 | /// 1 3 5 22 | /// 2 4 6 23 | /// ``` 24 | /// 25 | /// It can be seen as filling in "rows first" or "columns first". 26 | /// 27 | /// `Order` can be used both to refer to logical ordering as well as memory ordering or memory 28 | /// layout. The orderings have common short names, also seen in other environments, where 29 | /// row major is called "C" order (after the C programming language) and column major is called "F" 30 | /// or "Fortran" order. 31 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 32 | #[non_exhaustive] 33 | pub enum Order 34 | { 35 | /// Row major or "C" order 36 | RowMajor, 37 | /// Column major or "F" order 38 | ColumnMajor, 39 | } 40 | 41 | impl Order 42 | { 43 | /// "C" is an alias for row major ordering 44 | pub const C: Order = Order::RowMajor; 45 | 46 | /// "F" (for Fortran) is an alias for column major ordering 47 | pub const F: Order = Order::ColumnMajor; 48 | 49 | /// Return true if input is Order::RowMajor, false otherwise 50 | #[inline] 51 | pub fn is_row_major(self) -> bool 52 | { 53 | match self { 54 | Order::RowMajor => true, 55 | Order::ColumnMajor => false, 56 | } 57 | } 58 | 59 | /// Return true if input is Order::ColumnMajor, false otherwise 60 | #[inline] 61 | pub fn is_column_major(self) -> bool 62 | { 63 | !self.is_row_major() 64 | } 65 | 66 | /// Return Order::RowMajor if the input is true, Order::ColumnMajor otherwise 67 | #[inline] 68 | pub fn row_major(row_major: bool) -> Order 69 | { 70 | if row_major { 71 | Order::RowMajor 72 | } else { 73 | Order::ColumnMajor 74 | } 75 | } 76 | 77 | /// Return Order::ColumnMajor if the input is true, Order::RowMajor otherwise 78 | #[inline] 79 | pub fn column_major(column_major: bool) -> Order 80 | { 81 | Self::row_major(!column_major) 82 | } 83 | 84 | /// Return the transpose: row major becomes column major and vice versa. 85 | #[inline] 86 | pub fn transpose(self) -> Order 87 | { 88 | match self { 89 | Order::RowMajor => Order::ColumnMajor, 90 | Order::ColumnMajor => Order::RowMajor, 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/parallel/into_impls.rs: -------------------------------------------------------------------------------- 1 | use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Dimension}; 2 | 3 | use super::prelude::IntoParallelIterator; 4 | use super::Parallel; 5 | 6 | /// Requires crate feature `rayon`. 7 | impl<'a, A, D> IntoParallelIterator for &'a Array 8 | where 9 | D: Dimension, 10 | A: Sync, 11 | { 12 | type Item = &'a A; 13 | type Iter = Parallel>; 14 | fn into_par_iter(self) -> Self::Iter 15 | { 16 | self.view().into_par_iter() 17 | } 18 | } 19 | 20 | // This is allowed: goes through `.view()` 21 | /// Requires crate feature `rayon`. 22 | impl<'a, A, D> IntoParallelIterator for &'a ArcArray 23 | where 24 | D: Dimension, 25 | A: Sync, 26 | { 27 | type Item = &'a A; 28 | type Iter = Parallel>; 29 | fn into_par_iter(self) -> Self::Iter 30 | { 31 | self.view().into_par_iter() 32 | } 33 | } 34 | 35 | /// Requires crate feature `rayon`. 36 | impl<'a, A, D> IntoParallelIterator for &'a mut Array 37 | where 38 | D: Dimension, 39 | A: Sync + Send, 40 | { 41 | type Item = &'a mut A; 42 | type Iter = Parallel>; 43 | fn into_par_iter(self) -> Self::Iter 44 | { 45 | self.view_mut().into_par_iter() 46 | } 47 | } 48 | 49 | // This is allowed: goes through `.view_mut()`, which is unique access 50 | /// Requires crate feature `rayon`. 51 | impl<'a, A, D> IntoParallelIterator for &'a mut ArcArray 52 | where 53 | D: Dimension, 54 | A: Sync + Send + Clone, 55 | { 56 | type Item = &'a mut A; 57 | type Iter = Parallel>; 58 | fn into_par_iter(self) -> Self::Iter 59 | { 60 | self.view_mut().into_par_iter() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/parallel/send_producer.rs: -------------------------------------------------------------------------------- 1 | use crate::imp_prelude::*; 2 | use crate::{Layout, NdProducer}; 3 | use std::ops::{Deref, DerefMut}; 4 | 5 | /// An NdProducer that is unconditionally `Send`. 6 | #[repr(transparent)] 7 | pub(crate) struct SendProducer 8 | { 9 | inner: T, 10 | } 11 | 12 | impl SendProducer 13 | { 14 | /// Create an unconditionally `Send` ndproducer from the producer 15 | pub(crate) unsafe fn new(producer: T) -> Self 16 | { 17 | Self { inner: producer } 18 | } 19 | } 20 | 21 | unsafe impl

Send for SendProducer

{} 22 | 23 | impl

Deref for SendProducer

24 | { 25 | type Target = P; 26 | fn deref(&self) -> &P 27 | { 28 | &self.inner 29 | } 30 | } 31 | 32 | impl

DerefMut for SendProducer

33 | { 34 | fn deref_mut(&mut self) -> &mut P 35 | { 36 | &mut self.inner 37 | } 38 | } 39 | 40 | impl NdProducer for SendProducer

41 | where P: NdProducer 42 | { 43 | type Item = P::Item; 44 | type Dim = P::Dim; 45 | type Ptr = P::Ptr; 46 | type Stride = P::Stride; 47 | 48 | private_impl! {} 49 | 50 | #[inline(always)] 51 | fn raw_dim(&self) -> Self::Dim 52 | { 53 | self.inner.raw_dim() 54 | } 55 | 56 | #[inline(always)] 57 | fn equal_dim(&self, dim: &Self::Dim) -> bool 58 | { 59 | self.inner.equal_dim(dim) 60 | } 61 | 62 | #[inline(always)] 63 | fn as_ptr(&self) -> Self::Ptr 64 | { 65 | self.inner.as_ptr() 66 | } 67 | 68 | #[inline(always)] 69 | fn layout(&self) -> Layout 70 | { 71 | self.inner.layout() 72 | } 73 | 74 | #[inline(always)] 75 | unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item 76 | { 77 | self.inner.as_ref(ptr) 78 | } 79 | 80 | #[inline(always)] 81 | unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr 82 | { 83 | self.inner.uget_ptr(i) 84 | } 85 | 86 | #[inline(always)] 87 | fn stride_of(&self, axis: Axis) -> Self::Stride 88 | { 89 | self.inner.stride_of(axis) 90 | } 91 | 92 | #[inline(always)] 93 | fn contiguous_stride(&self) -> Self::Stride 94 | { 95 | self.inner.contiguous_stride() 96 | } 97 | 98 | fn split_at(self, axis: Axis, index: usize) -> (Self, Self) 99 | { 100 | let (a, b) = self.inner.split_at(axis, index); 101 | (Self { inner: a }, Self { inner: b }) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/parallel/zipmacro.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #[macro_export] 10 | /// Parallelized array zip macro: lock step function application across several 11 | /// arrays and producers. 12 | /// 13 | /// This is a version of the [`azip`] macro that requires the crate feature 14 | /// `rayon` to be enabled. 15 | /// 16 | /// See the [`azip`] macro for more details about the macro syntax! 17 | /// 18 | /// This example: 19 | /// 20 | /// ```rust,ignore 21 | /// par_azip!((a in &mut a, &b in &b, &c in &c) { *a = b + c }) 22 | /// ``` 23 | /// 24 | /// Is equivalent to: 25 | /// 26 | /// ```rust,ignore 27 | /// Zip::from(&mut a).and(&b).and(&c).par_for_each(|a, &b, &c| { 28 | /// *a = b + c; 29 | /// }); 30 | /// ``` 31 | /// 32 | /// **Panics** if any of the arrays are not of the same shape. 33 | /// 34 | /// ## Examples 35 | /// 36 | /// ```rust 37 | /// use ndarray::Array2; 38 | /// use ndarray::parallel::par_azip; 39 | /// 40 | /// type M = Array2; 41 | /// 42 | /// let mut a = M::zeros((16, 16)); 43 | /// let b = M::from_elem(a.dim(), 1.); 44 | /// let c = M::from_elem(a.dim(), 2.); 45 | /// 46 | /// // Compute a simple ternary operation: 47 | /// // elementwise addition of b and c, stored in a 48 | /// 49 | /// par_azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); 50 | /// 51 | /// assert_eq!(a, &b + &c); 52 | /// ``` 53 | macro_rules! par_azip { 54 | ($($t:tt)*) => { 55 | $crate::azip!(@build par_for_each $($t)*) 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /src/partial.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::ptr; 10 | 11 | /// Partial is a partially written contiguous slice of data; 12 | /// it is the owner of the elements, but not the allocation, 13 | /// and will drop the elements on drop. 14 | #[must_use] 15 | pub(crate) struct Partial 16 | { 17 | /// Data pointer 18 | ptr: *mut T, 19 | /// Current length 20 | pub(crate) len: usize, 21 | } 22 | 23 | impl Partial 24 | { 25 | /// Create an empty partial for this data pointer 26 | /// 27 | /// ## Safety 28 | /// 29 | /// Unless ownership is released, the Partial acts as an owner of the slice of data (not the 30 | /// allocation); and will free the elements on drop; the pointer must be dereferenceable and 31 | /// the `len` elements following it valid. 32 | /// 33 | /// The Partial has an accessible length field which must only be modified in trusted code. 34 | pub(crate) unsafe fn new(ptr: *mut T) -> Self 35 | { 36 | Self { ptr, len: 0 } 37 | } 38 | 39 | #[cfg(feature = "rayon")] 40 | #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] 41 | pub(crate) fn stub() -> Self 42 | { 43 | Self { 44 | len: 0, 45 | ptr: ptr::null_mut(), 46 | } 47 | } 48 | 49 | #[cfg(feature = "rayon")] 50 | #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] 51 | pub(crate) fn is_stub(&self) -> bool 52 | { 53 | self.ptr.is_null() 54 | } 55 | 56 | /// Release Partial's ownership of the written elements, and return the current length 57 | pub(crate) fn release_ownership(mut self) -> usize 58 | { 59 | let ret = self.len; 60 | self.len = 0; 61 | ret 62 | } 63 | 64 | #[cfg(feature = "rayon")] 65 | #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] 66 | /// Merge if they are in order (left to right) and contiguous. 67 | /// Skips merge if T does not need drop. 68 | pub(crate) fn try_merge(mut left: Self, right: Self) -> Self 69 | { 70 | if !std::mem::needs_drop::() { 71 | return left; 72 | } 73 | // Merge the partial collect results; the final result will be a slice that 74 | // covers the whole output. 75 | if left.is_stub() { 76 | right 77 | } else if left.ptr.wrapping_add(left.len) == right.ptr { 78 | left.len += right.release_ownership(); 79 | left 80 | } else { 81 | // failure to merge; this is a bug in collect, so we will never reach this 82 | debug_assert!(false, "Partial: failure to merge left and right parts"); 83 | left 84 | } 85 | } 86 | } 87 | 88 | unsafe impl Send for Partial where T: Send {} 89 | 90 | impl Drop for Partial 91 | { 92 | fn drop(&mut self) 93 | { 94 | if !self.ptr.is_null() { 95 | unsafe { 96 | ptr::drop_in_place(alloc::slice::from_raw_parts_mut(self.ptr, self.len)); 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 bluss and ndarray developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! ndarray prelude. 10 | //! 11 | //! This module contains the most used types, type aliases, traits, functions, 12 | //! and macros that you can import easily as a group. 13 | //! 14 | //! ``` 15 | //! use ndarray::prelude::*; 16 | //! 17 | //! # let _ = arr0(1); // use the import 18 | //! ``` 19 | 20 | #[doc(no_inline)] 21 | pub use crate::{ 22 | ArcArray, 23 | Array, 24 | ArrayBase, 25 | ArrayRef, 26 | ArrayView, 27 | ArrayViewMut, 28 | CowArray, 29 | LayoutRef, 30 | RawArrayView, 31 | RawArrayViewMut, 32 | RawRef, 33 | }; 34 | 35 | #[doc(no_inline)] 36 | pub use crate::{Axis, Dim, Dimension}; 37 | 38 | #[doc(no_inline)] 39 | pub use crate::{ArrayRef0, ArrayRef1, ArrayRef2, ArrayRef3, ArrayRef4, ArrayRef5, ArrayRef6, ArrayRefD}; 40 | 41 | #[doc(no_inline)] 42 | pub use crate::{Array0, Array1, Array2, Array3, Array4, Array5, Array6, ArrayD}; 43 | 44 | #[doc(no_inline)] 45 | pub use crate::{ArrayView0, ArrayView1, ArrayView2, ArrayView3, ArrayView4, ArrayView5, ArrayView6, ArrayViewD}; 46 | 47 | #[doc(no_inline)] 48 | pub use crate::{ 49 | ArrayViewMut0, 50 | ArrayViewMut1, 51 | ArrayViewMut2, 52 | ArrayViewMut3, 53 | ArrayViewMut4, 54 | ArrayViewMut5, 55 | ArrayViewMut6, 56 | ArrayViewMutD, 57 | }; 58 | 59 | #[doc(no_inline)] 60 | pub use crate::{Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; 61 | 62 | #[doc(no_inline)] 63 | pub use crate::{arr0, arr1, arr2, aview0, aview1, aview2, aview_mut1}; 64 | 65 | pub use crate::{array, azip, s}; 66 | 67 | #[doc(no_inline)] 68 | pub use crate::ShapeBuilder; 69 | 70 | #[doc(no_inline)] 71 | pub use crate::NewAxis; 72 | 73 | #[doc(no_inline)] 74 | pub use crate::AsArray; 75 | 76 | #[doc(no_inline)] 77 | #[cfg(feature = "std")] 78 | pub use crate::NdFloat; 79 | -------------------------------------------------------------------------------- /src/private.rs: -------------------------------------------------------------------------------- 1 | //! The public parts of this private module are used to create traits 2 | //! that cannot be implemented outside of our own crate. This way we 3 | //! can feel free to extend those traits without worrying about it 4 | //! being a breaking change for other implementations. 5 | 6 | /// If this type is pub but not publicly reachable, third parties 7 | /// can't name it and can't implement traits using it. 8 | pub struct PrivateMarker; 9 | 10 | macro_rules! private_decl { 11 | () => { 12 | /// This trait is private to implement; this method exists to make it 13 | /// impossible to implement outside the crate. 14 | #[doc(hidden)] 15 | fn __private__(&self) -> crate::private::PrivateMarker; 16 | }; 17 | } 18 | 19 | macro_rules! private_impl { 20 | () => { 21 | fn __private__(&self) -> crate::private::PrivateMarker { 22 | crate::private::PrivateMarker 23 | } 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/split_at.rs: -------------------------------------------------------------------------------- 1 | use crate::imp_prelude::*; 2 | 3 | /// Arrays and similar that can be split along an axis 4 | pub(crate) trait SplitAt 5 | { 6 | fn split_at(self, axis: Axis, index: usize) -> (Self, Self) 7 | where Self: Sized; 8 | } 9 | 10 | pub(crate) trait SplitPreference: SplitAt 11 | { 12 | #[allow(dead_code)] // used only when Rayon support is enabled 13 | fn can_split(&self) -> bool; 14 | fn split_preference(&self) -> (Axis, usize); 15 | fn split(self) -> (Self, Self) 16 | where Self: Sized 17 | { 18 | let (axis, index) = self.split_preference(); 19 | self.split_at(axis, index) 20 | } 21 | } 22 | 23 | impl SplitAt for D 24 | where D: Dimension 25 | { 26 | fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) 27 | { 28 | let mut d1 = self; 29 | let mut d2 = d1.clone(); 30 | let i = axis.index(); 31 | let len = d1[i]; 32 | d1[i] = index; 33 | d2[i] = len - index; 34 | (d1, d2) 35 | } 36 | } 37 | 38 | impl SplitAt for ArrayViewMut<'_, A, D> 39 | where D: Dimension 40 | { 41 | fn split_at(self, axis: Axis, index: usize) -> (Self, Self) 42 | { 43 | self.split_at(axis, index) 44 | } 45 | } 46 | 47 | impl SplitAt for RawArrayViewMut 48 | where D: Dimension 49 | { 50 | fn split_at(self, axis: Axis, index: usize) -> (Self, Self) 51 | { 52 | self.split_at(axis, index) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/zip/zipmacro.rs: -------------------------------------------------------------------------------- 1 | /// Array zip macro: lock step function application across several arrays and 2 | /// producers. 3 | /// 4 | /// This is a shorthand for [`Zip`](crate::Zip). 5 | /// 6 | /// This example: 7 | /// 8 | /// ```rust,ignore 9 | /// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); 10 | /// ``` 11 | /// 12 | /// Is equivalent to: 13 | /// 14 | /// ```rust,ignore 15 | /// Zip::from(&mut a).and(&b).and(&c).for_each(|a, &b, &c| { 16 | /// *a = b + c 17 | /// }); 18 | /// ``` 19 | /// 20 | /// The syntax is either 21 | /// 22 | /// `azip!((` *pat* `in` *expr* `,` *[* *pat* `in` *expr* `,` ... *]* `)` *body_expr* `)` 23 | /// 24 | /// or, to use `Zip::indexed` instead of `Zip::from`, 25 | /// 26 | /// `azip!((index` *pat* `,` *pat* `in` *expr* `,` *[* *pat* `in` *expr* `,` ... *]* `)` *body_expr* `)` 27 | /// 28 | /// The *expr* are expressions whose types must implement `IntoNdProducer`, the 29 | /// *pat* are the patterns of the parameters to the closure called by 30 | /// `Zip::for_each`, and *body_expr* is the body of the closure called by 31 | /// `Zip::for_each`. You can think of each *pat* `in` *expr* as being analogous to 32 | /// the `pat in expr` of a normal loop `for pat in expr { statements }`: a 33 | /// pattern, followed by `in`, followed by an expression that implements 34 | /// `IntoNdProducer` (analogous to `IntoIterator` for a `for` loop). 35 | /// 36 | /// **Panics** if any of the arrays are not of the same shape. 37 | /// 38 | /// ## Examples 39 | /// 40 | /// ```rust 41 | /// use ndarray::{azip, Array1, Array2, Axis}; 42 | /// 43 | /// type M = Array2; 44 | /// 45 | /// // Setup example arrays 46 | /// let mut a = M::zeros((16, 16)); 47 | /// let mut b = M::zeros(a.dim()); 48 | /// let mut c = M::zeros(a.dim()); 49 | /// 50 | /// // assign values 51 | /// b.fill(1.); 52 | /// for ((i, j), elt) in c.indexed_iter_mut() { 53 | /// *elt = (i + 10 * j) as f32; 54 | /// } 55 | /// 56 | /// // Example 1: Compute a simple ternary operation: 57 | /// // elementwise addition of b and c, stored in a 58 | /// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); 59 | /// 60 | /// assert_eq!(a, &b + &c); 61 | /// 62 | /// // Example 2: azip!() with index 63 | /// azip!((index (i, j), &b in &b, &c in &c) { 64 | /// a[[i, j]] = b - c; 65 | /// }); 66 | /// 67 | /// assert_eq!(a, &b - &c); 68 | /// 69 | /// 70 | /// // Example 3: azip!() on references 71 | /// // See the definition of the function below 72 | /// borrow_multiply(&mut a, &b, &c); 73 | /// 74 | /// assert_eq!(a, &b * &c); 75 | /// 76 | /// 77 | /// // Since this function borrows its inputs, the `IntoNdProducer` 78 | /// // expressions don't need to explicitly include `&mut` or `&`. 79 | /// fn borrow_multiply(a: &mut M, b: &M, c: &M) { 80 | /// azip!((a in a, &b in b, &c in c) *a = b * c); 81 | /// } 82 | /// 83 | /// 84 | /// // Example 4: using azip!() without dereference in pattern. 85 | /// // 86 | /// // Create a new array `totals` with one entry per row of `a`. 87 | /// // Use azip to traverse the rows of `a` and assign to the corresponding 88 | /// // entry in `totals` with the sum across each row. 89 | /// // 90 | /// // The row is an array view; it doesn't need to be dereferenced. 91 | /// let mut totals = Array1::zeros(a.nrows()); 92 | /// azip!((totals in &mut totals, row in a.rows()) *totals = row.sum()); 93 | /// 94 | /// // Check the result against the built in `.sum_axis()` along axis 1. 95 | /// assert_eq!(totals, a.sum_axis(Axis(1))); 96 | /// ``` 97 | #[macro_export] 98 | macro_rules! azip { 99 | // Indexed with a single producer 100 | // we allow an optional trailing comma after the producers in each rule. 101 | (@build $apply:ident (index $index:pat, $first_pat:pat in $first_prod:expr $(,)?) $body:expr) => { 102 | $crate::Zip::indexed($first_prod).$apply(|$index, $first_pat| $body) 103 | }; 104 | // Indexed with more than one producer 105 | (@build $apply:ident (index $index:pat, $first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),* $(,)?) $body:expr) => { 106 | $crate::Zip::indexed($first_prod) 107 | $(.and($prod))* 108 | .$apply(|$index, $first_pat, $($pat),*| $body) 109 | }; 110 | // Unindexed with a single producer 111 | (@build $apply:ident ($first_pat:pat in $first_prod:expr $(,)?) $body:expr) => { 112 | $crate::Zip::from($first_prod).$apply(|$first_pat| $body) 113 | }; 114 | // Unindexed with more than one producer 115 | (@build $apply:ident ($first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),* $(,)?) $body:expr) => { 116 | $crate::Zip::from($first_prod) 117 | $(.and($prod))* 118 | .$apply(|$first_pat, $($pat),*| $body) 119 | }; 120 | 121 | // Unindexed with one or more producer, no loop body 122 | (@build $apply:ident $first_prod:expr $(, $prod:expr)* $(,)?) => { 123 | $crate::Zip::from($first_prod) 124 | $(.and($prod))* 125 | }; 126 | // catch-all rule 127 | (@build $($t:tt)*) => { compile_error!("Invalid syntax in azip!()") }; 128 | ($($t:tt)*) => { 129 | $crate::azip!(@build for_each $($t)*) 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /tests/broadcast.rs: -------------------------------------------------------------------------------- 1 | use ndarray::prelude::*; 2 | 3 | #[test] 4 | #[cfg(feature = "std")] 5 | fn broadcast_1() 6 | { 7 | let a_dim = Dim([2, 4, 2, 2]); 8 | let b_dim = Dim([2, 1, 2, 1]); 9 | let a = ArcArray::linspace(0., 1., a_dim.size()) 10 | .into_shape_with_order(a_dim) 11 | .unwrap(); 12 | let b = ArcArray::linspace(0., 1., b_dim.size()) 13 | .into_shape_with_order(b_dim) 14 | .unwrap(); 15 | assert!(b.broadcast(a.dim()).is_some()); 16 | 17 | let c_dim = Dim([2, 1]); 18 | let c = ArcArray::linspace(0., 1., c_dim.size()) 19 | .into_shape_with_order(c_dim) 20 | .unwrap(); 21 | assert!(c.broadcast(1).is_none()); 22 | assert!(c.broadcast(()).is_none()); 23 | assert!(c.broadcast((2, 1)).is_some()); 24 | assert!(c.broadcast((2, 2)).is_some()); 25 | assert!(c.broadcast((32, 2, 1)).is_some()); 26 | assert!(c.broadcast((32, 1, 2)).is_none()); 27 | 28 | /* () can be broadcast to anything */ 29 | let z = ArcArray::::zeros(()); 30 | assert!(z.broadcast(()).is_some()); 31 | assert!(z.broadcast(1).is_some()); 32 | assert!(z.broadcast(3).is_some()); 33 | assert!(z.broadcast((7, 2, 9)).is_some()); 34 | } 35 | 36 | #[test] 37 | #[cfg(feature = "std")] 38 | fn test_add() 39 | { 40 | let a_dim = Dim([2, 4, 2, 2]); 41 | let b_dim = Dim([2, 1, 2, 1]); 42 | let mut a = ArcArray::linspace(0.0, 1., a_dim.size()) 43 | .into_shape_with_order(a_dim) 44 | .unwrap(); 45 | let b = ArcArray::linspace(0.0, 1., b_dim.size()) 46 | .into_shape_with_order(b_dim) 47 | .unwrap(); 48 | a += &b; 49 | let t = ArcArray::from_elem((), 1.0f32); 50 | a += &t; 51 | } 52 | 53 | #[test] 54 | #[should_panic] 55 | #[cfg(feature = "std")] 56 | fn test_add_incompat() 57 | { 58 | let a_dim = Dim([2, 4, 2, 2]); 59 | let mut a = ArcArray::linspace(0.0, 1., a_dim.size()) 60 | .into_shape_with_order(a_dim) 61 | .unwrap(); 62 | let incompat = ArcArray::from_elem(3, 1.0f32); 63 | a += &incompat; 64 | } 65 | 66 | #[test] 67 | fn test_broadcast() 68 | { 69 | let (_, n, k) = (16, 16, 16); 70 | let x1 = 1.; 71 | // b0 broadcast 1 -> n, k 72 | let x = Array::from(vec![x1]); 73 | let b0 = x.broadcast((n, k)).unwrap(); 74 | // b1 broadcast n -> n, k 75 | let b1 = Array::from_elem(n, x1); 76 | let b1 = b1.broadcast((n, k)).unwrap(); 77 | // b2 is n, k 78 | let b2 = Array::from_elem((n, k), x1); 79 | 80 | println!("b0=\n{:?}", b0); 81 | println!("b1=\n{:?}", b1); 82 | println!("b2=\n{:?}", b2); 83 | assert_eq!(b0, b1); 84 | assert_eq!(b0, b2); 85 | } 86 | 87 | #[test] 88 | fn test_broadcast_1d() 89 | { 90 | let n = 16; 91 | let x1 = 1.; 92 | // b0 broadcast 1 -> n 93 | let x = Array::from(vec![x1]); 94 | let b0 = x.broadcast(n).unwrap(); 95 | let b2 = Array::from_elem(n, x1); 96 | 97 | println!("b0=\n{:?}", b0); 98 | println!("b2=\n{:?}", b2); 99 | assert_eq!(b0, b2); 100 | } 101 | -------------------------------------------------------------------------------- /tests/clone.rs: -------------------------------------------------------------------------------- 1 | use ndarray::arr2; 2 | 3 | #[test] 4 | fn test_clone_from() 5 | { 6 | let a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); 7 | let b = arr2(&[[7, 7, 7]]); 8 | let mut c = b.clone(); 9 | c.clone_from(&a); 10 | assert_eq!(a, c); 11 | 12 | let mut bv = b.view(); 13 | bv.clone_from(&a.view()); 14 | assert_eq!(&a, &bv); 15 | } 16 | -------------------------------------------------------------------------------- /tests/complex.rs: -------------------------------------------------------------------------------- 1 | use ndarray::Array; 2 | use ndarray::{arr1, arr2, Axis}; 3 | use num_complex::Complex; 4 | use num_traits::Num; 5 | 6 | fn c(re: T, im: T) -> Complex 7 | { 8 | Complex::new(re, im) 9 | } 10 | 11 | #[test] 12 | fn complex_mat_mul() 13 | { 14 | let a = arr2(&[[c(3., 4.), c(2., 0.)], [c(0., -2.), c(3., 0.)]]); 15 | let b = (&a * c(3., 0.)).map(|c| 5. * c / c.norm_sqr()); 16 | println!("{:>8.2}", b); 17 | let e = Array::eye(2); 18 | let r = a.dot(&e); 19 | println!("{}", a); 20 | assert_eq!(r, a); 21 | assert_eq!( 22 | a.mean_axis(Axis(0)).unwrap(), 23 | arr1(&[c(1.5, 1.), c(2.5, 0.)]) 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tests/format.rs: -------------------------------------------------------------------------------- 1 | use ndarray::prelude::*; 2 | use ndarray::rcarr1; 3 | 4 | #[test] 5 | fn formatting() 6 | { 7 | let a = rcarr1::(&[1., 2., 3., 4.]); 8 | assert_eq!(format!("{}", a), "[1, 2, 3, 4]"); 9 | assert_eq!(format!("{:4}", a), "[ 1, 2, 3, 4]"); 10 | let a = a.into_shape_clone((4, 1, 1)).unwrap(); 11 | assert_eq!( 12 | format!("{}", a), 13 | "\ 14 | [[[1]], 15 | 16 | [[2]], 17 | 18 | [[3]], 19 | 20 | [[4]]]" 21 | ); 22 | assert_eq!( 23 | format!("{:4}", a), 24 | "\ 25 | [[[ 1]], 26 | 27 | [[ 2]], 28 | 29 | [[ 3]], 30 | 31 | [[ 4]]]", 32 | ); 33 | 34 | let a = a.into_shape_clone((2, 2)).unwrap(); 35 | assert_eq!( 36 | format!("{}", a), 37 | "\ 38 | [[1, 2], 39 | [3, 4]]" 40 | ); 41 | assert_eq!( 42 | format!("{:4}", a), 43 | "\ 44 | [[ 1, 2], 45 | [ 3, 4]]" 46 | ); 47 | 48 | let b = arr0::(3.5); 49 | assert_eq!(format!("{}", b), "3.5"); 50 | 51 | let s = format!("{:.3e}", aview1::(&[1.1, 2.2, 33., 440.])); 52 | assert_eq!(s, "[1.100e0, 2.200e0, 3.300e1, 4.400e2]"); 53 | 54 | let s = format!("{:02x}", aview1::(&[1, 0xff, 0xfe])); 55 | assert_eq!(s, "[01, ff, fe]"); 56 | } 57 | 58 | #[test] 59 | fn debug_format() 60 | { 61 | let a = Array2::::zeros((3, 4)); 62 | assert_eq!( 63 | format!("{:?}", a), 64 | "\ 65 | [[0, 0, 0, 0], 66 | [0, 0, 0, 0], 67 | [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=Cc (0x5), const ndim=2" 68 | ); 69 | assert_eq!( 70 | format!("{:?}", a.into_dyn()), 71 | "\ 72 | [[0, 0, 0, 0], 73 | [0, 0, 0, 0], 74 | [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=Cc (0x5), dynamic ndim=2" 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /tests/higher_order_f.rs: -------------------------------------------------------------------------------- 1 | use ndarray::prelude::*; 2 | 3 | #[test] 4 | #[should_panic] 5 | fn test_fold_axis_oob() 6 | { 7 | let a = arr2(&[[1., 2.], [3., 4.]]); 8 | a.fold_axis(Axis(2), 0., |x, y| x + y); 9 | } 10 | -------------------------------------------------------------------------------- /tests/indices.rs: -------------------------------------------------------------------------------- 1 | use ndarray::indices_of; 2 | use ndarray::prelude::*; 3 | use ndarray::Order; 4 | 5 | #[test] 6 | fn test_ixdyn_index_iterate() 7 | { 8 | for &order in &[Order::C, Order::F] { 9 | let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); 10 | let dim = a.shape().to_vec(); 11 | for ((i, j, k), elt) in a.indexed_iter_mut() { 12 | *elt = i + 10 * j + 100 * k; 13 | } 14 | let a = a.into_shape_with_order((dim, order)).unwrap(); 15 | println!("{:?}", a.dim()); 16 | let mut c = 0; 17 | for i in indices_of(&a) { 18 | let ans = i[0] + 10 * i[1] + 100 * i[2]; 19 | println!("{:?}", i); 20 | assert_eq!(a[i], ans); 21 | c += 1; 22 | } 23 | assert_eq!(c, a.len()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/into-ixdyn.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, 3 | clippy::float_cmp 4 | )] 5 | 6 | use ndarray::prelude::*; 7 | 8 | #[test] 9 | fn test_arr0_into_dyn() 10 | { 11 | assert!(arr0(1.234).into_dyn()[IxDyn(&[])] == 1.234); 12 | } 13 | 14 | #[test] 15 | fn test_arr2_into_arrd_nonstandard_strides() 16 | { 17 | let arr = Array2::from_shape_fn((12, 34).f(), |(i, j)| i * 34 + j).into_dyn(); 18 | let brr = ArrayD::from_shape_fn(vec![12, 34], |d| d[0] * 34 + d[1]); 19 | 20 | assert!(arr == brr); 21 | } 22 | -------------------------------------------------------------------------------- /tests/iterator_chunks.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, 3 | clippy::float_cmp 4 | )] 5 | 6 | use ndarray::prelude::*; 7 | 8 | #[test] 9 | #[cfg(feature = "std")] 10 | fn chunks() 11 | { 12 | use ndarray::NdProducer; 13 | let a = >::linspace(1., 100., 10 * 10) 14 | .into_shape_with_order((10, 10)) 15 | .unwrap(); 16 | 17 | let (m, n) = a.dim(); 18 | for i in 1..=m { 19 | for j in 1..=n { 20 | let c = a.exact_chunks((i, j)); 21 | 22 | let ly = n / j; 23 | for (index, elt) in c.into_iter().enumerate() { 24 | assert_eq!(elt.dim(), (i, j)); 25 | let cindex = (index / ly, index % ly); 26 | let cx = (cindex.0 * i) as isize; 27 | let cy = (cindex.1 * j) as isize; 28 | assert_eq!( 29 | elt, 30 | a.slice(s![cx.., cy..]) 31 | .slice(s![..i as isize, ..j as isize]) 32 | ); 33 | } 34 | let c = a.exact_chunks((i, j)); 35 | assert_eq!(c.into_iter().count(), (m / i) * (n / j)); 36 | 37 | let c = a.exact_chunks((i, j)); 38 | let (c1, c2) = c.split_at(Axis(0), (m / i) / 2); 39 | assert_eq!(c1.into_iter().count(), ((m / i) / 2) * (n / j)); 40 | assert_eq!(c2.into_iter().count(), (m / i - (m / i) / 2) * (n / j)); 41 | } 42 | } 43 | let c = a.exact_chunks((m + 1, n)); 44 | assert_eq!(c.raw_dim().size(), 0); 45 | assert_eq!(c.into_iter().count(), 0); 46 | } 47 | 48 | #[should_panic] 49 | #[test] 50 | fn chunks_different_size_1() 51 | { 52 | let a = Array::::zeros(vec![2, 3]); 53 | a.exact_chunks(vec![2]); 54 | } 55 | 56 | #[test] 57 | fn chunks_ok_size() 58 | { 59 | let mut a = Array::::zeros(vec![2, 3]); 60 | a.fill(1.); 61 | let mut c = 0; 62 | for elt in a.exact_chunks(vec![2, 1]) { 63 | assert!(elt.iter().all(|&x| x == 1.)); 64 | assert_eq!(elt.shape(), &[2, 1]); 65 | c += 1; 66 | } 67 | assert_eq!(c, 3); 68 | } 69 | 70 | #[should_panic] 71 | #[test] 72 | fn chunks_different_size_2() 73 | { 74 | let a = Array::::zeros(vec![2, 3]); 75 | a.exact_chunks(vec![2, 3, 4]); 76 | } 77 | 78 | #[test] 79 | fn chunks_mut() 80 | { 81 | let mut a = Array::zeros((7, 8)); 82 | for (i, mut chunk) in a.exact_chunks_mut((2, 3)).into_iter().enumerate() { 83 | chunk.fill(i); 84 | } 85 | println!("{:?}", a); 86 | let ans = array![ 87 | [0, 0, 0, 1, 1, 1, 0, 0], 88 | [0, 0, 0, 1, 1, 1, 0, 0], 89 | [2, 2, 2, 3, 3, 3, 0, 0], 90 | [2, 2, 2, 3, 3, 3, 0, 0], 91 | [4, 4, 4, 5, 5, 5, 0, 0], 92 | [4, 4, 4, 5, 5, 5, 0, 0], 93 | [0, 0, 0, 0, 0, 0, 0, 0] 94 | ]; 95 | assert_eq!(a, ans); 96 | } 97 | 98 | #[should_panic] 99 | #[test] 100 | fn chunks_different_size_3() 101 | { 102 | let mut a = Array::::zeros(vec![2, 3]); 103 | a.exact_chunks_mut(vec![2, 3, 4]); 104 | } 105 | -------------------------------------------------------------------------------- /tests/ix0.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, 3 | clippy::float_cmp 4 | )] 5 | 6 | use ndarray::Array; 7 | use ndarray::Ix0; 8 | use ndarray::ShapeBuilder; 9 | 10 | #[test] 11 | fn test_ix0() 12 | { 13 | let mut a = Array::zeros(Ix0()); 14 | assert_eq!(a[()], 0.); 15 | a[()] = 1.; 16 | assert_eq!(a[()], 1.); 17 | assert_eq!(a.len(), 1); 18 | assert!(!a.is_empty()); 19 | assert_eq!(a.as_slice().unwrap(), &[1.]); 20 | 21 | let mut a = Array::zeros(Ix0().f()); 22 | assert_eq!(a[()], 0.); 23 | a[()] = 1.; 24 | assert_eq!(a[()], 1.); 25 | assert_eq!(a.len(), 1); 26 | assert!(!a.is_empty()); 27 | assert_eq!(a.as_slice().unwrap(), &[1.]); 28 | } 29 | 30 | #[test] 31 | fn test_ix0_add() 32 | { 33 | let mut a = Array::zeros(Ix0()); 34 | a += 1.; 35 | assert_eq!(a[()], 1.); 36 | a += 2.; 37 | assert_eq!(a[()], 3.); 38 | } 39 | 40 | #[test] 41 | fn test_ix0_add_add() 42 | { 43 | let mut a = Array::zeros(Ix0()); 44 | a += 1.; 45 | let mut b = Array::zeros(Ix0()); 46 | b += 1.; 47 | a += &b; 48 | assert_eq!(a[()], 2.); 49 | } 50 | 51 | #[test] 52 | fn test_ix0_add_broad() 53 | { 54 | let mut b = Array::from(vec![5., 6.]); 55 | let mut a = Array::zeros(Ix0()); 56 | a += 1.; 57 | b += &a; 58 | assert_eq!(b[0], 6.); 59 | assert_eq!(b[1], 7.); 60 | } 61 | -------------------------------------------------------------------------------- /tests/ixdyn.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, 3 | clippy::float_cmp 4 | )] 5 | 6 | use ndarray::Array; 7 | use ndarray::IntoDimension; 8 | use ndarray::Ix3; 9 | use ndarray::Order; 10 | use ndarray::ShapeBuilder; 11 | 12 | #[test] 13 | fn test_ixdyn() 14 | { 15 | // check that we can use fixed size arrays for indexing 16 | let mut a = Array::zeros(vec![2, 3, 4]); 17 | a[[1, 1, 1]] = 1.; 18 | assert_eq!(a[[1, 1, 1]], 1.); 19 | } 20 | 21 | #[should_panic] 22 | #[test] 23 | fn test_ixdyn_wrong_dim() 24 | { 25 | // check that we can use but it panics at runtime, if number of axes is wrong 26 | let mut a = Array::zeros(vec![2, 3, 4]); 27 | a[[1, 1, 1]] = 1.; 28 | 29 | let _ = a[[0, 0]]; 30 | } 31 | 32 | #[test] 33 | fn test_ixdyn_out_of_bounds() 34 | { 35 | // check that we are out of bounds 36 | let a = Array::::zeros(vec![2, 3, 4]); 37 | let res = a.get([0, 3, 0]); 38 | assert_eq!(res, None); 39 | } 40 | 41 | #[test] 42 | fn test_ixdyn_iterate() 43 | { 44 | for &order in &[Order::C, Order::F] { 45 | let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); 46 | let dim = a.shape().to_vec(); 47 | for (i, elt) in a.iter_mut().enumerate() { 48 | *elt = i; 49 | } 50 | println!("{:?}", a.dim()); 51 | let mut a = a.into_shape_with_order((dim, order)).unwrap(); 52 | println!("{:?}", a.dim()); 53 | let mut c = 0; 54 | for (i, elt) in a.iter_mut().enumerate() { 55 | assert_eq!(i, *elt); 56 | c += 1; 57 | } 58 | assert_eq!(c, a.len()); 59 | } 60 | } 61 | 62 | #[test] 63 | fn test_ixdyn_index_iterate() 64 | { 65 | for &order in &[Order::C, Order::F] { 66 | let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); 67 | let dim = a.shape().to_vec(); 68 | for ((i, j, k), elt) in a.indexed_iter_mut() { 69 | *elt = i + 10 * j + 100 * k; 70 | } 71 | let a = a.into_shape_with_order((dim, order)).unwrap(); 72 | println!("{:?}", a.dim()); 73 | let mut c = 0; 74 | for (i, elt) in a.indexed_iter() { 75 | assert_eq!(a[i], *elt); 76 | c += 1; 77 | } 78 | assert_eq!(c, a.len()); 79 | } 80 | } 81 | 82 | #[test] 83 | fn test_ixdyn_uget() 84 | { 85 | // check that we are out of bounds 86 | let mut a = Array::::zeros(vec![2, 3, 4]); 87 | 88 | a[[1, 2, 0]] = 1.; 89 | a[[1, 2, 1]] = 2.; 90 | a[[1, 2, 3]] = 7.; 91 | 92 | let mut x = Ix3(1, 2, 0); 93 | let step = Ix3(0, 0, 1); 94 | let mut sum = 0.; 95 | while let Some(&v) = a.get(x) { 96 | sum += v; 97 | x += step; 98 | } 99 | assert_eq!(sum, 10.); 100 | 101 | let mut x = Ix3(1, 2, 0); 102 | let mut sum = 0.; 103 | unsafe { 104 | for _ in 0..4 { 105 | sum += *a.uget(x); 106 | x += step; 107 | } 108 | } 109 | assert_eq!(sum, 10.); 110 | } 111 | 112 | #[test] 113 | fn test_0() 114 | { 115 | let mut a = Array::zeros(vec![]); 116 | let z = vec![].into_dimension(); 117 | assert_eq!(a[z.clone()], 0.); 118 | a[[]] = 1.; 119 | assert_eq!(a[[]], 1.); 120 | assert_eq!(a.len(), 1); 121 | assert!(!a.is_empty()); 122 | assert_eq!(a.as_slice().unwrap(), &[1.]); 123 | 124 | let mut a = Array::zeros(vec![].f()); 125 | assert_eq!(a[[]], 0.); 126 | a[[]] = 1.; 127 | assert_eq!(a[[]], 1.); 128 | assert_eq!(a.len(), 1); 129 | assert!(!a.is_empty()); 130 | assert_eq!(a.as_slice().unwrap(), &[1.]); 131 | } 132 | 133 | #[test] 134 | fn test_0_add() 135 | { 136 | let mut a = Array::zeros(vec![]); 137 | a += 1.; 138 | assert_eq!(a[[]], 1.); 139 | a += 2.; 140 | assert_eq!(a[[]], 3.); 141 | } 142 | 143 | #[test] 144 | fn test_0_add_add() 145 | { 146 | let mut a = Array::zeros(vec![]); 147 | a += 1.; 148 | let mut b = Array::zeros(vec![]); 149 | b += 1.; 150 | a += &b; 151 | assert_eq!(a[[]], 2.); 152 | } 153 | 154 | #[test] 155 | fn test_0_add_broad() 156 | { 157 | let mut b = Array::from(vec![5., 6.]); 158 | let mut a = Array::zeros(vec![]); 159 | a += 1.; 160 | b += &a; 161 | assert_eq!(b[0], 6.); 162 | assert_eq!(b[1], 7.); 163 | } 164 | 165 | #[test] 166 | #[cfg(feature = "std")] 167 | fn test_into_dimension() 168 | { 169 | use ndarray::{Ix0, Ix1, Ix2, IxDyn}; 170 | 171 | let a = Array::linspace(0., 41., 6 * 7) 172 | .into_shape_with_order((6, 7)) 173 | .unwrap(); 174 | let a2 = a.clone().into_shape_with_order(IxDyn(&[6, 7])).unwrap(); 175 | let b = a2.clone().into_dimensionality::().unwrap(); 176 | assert_eq!(a, b); 177 | 178 | assert!(a2.clone().into_dimensionality::().is_err()); 179 | assert!(a2.clone().into_dimensionality::().is_err()); 180 | assert!(a2.clone().into_dimensionality::().is_err()); 181 | 182 | let c = a2.clone().into_dimensionality::().unwrap(); 183 | assert_eq!(a2, c); 184 | } 185 | -------------------------------------------------------------------------------- /tests/par_azip.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "rayon")] 2 | 3 | #[cfg(feature = "approx")] 4 | use itertools::enumerate; 5 | use ndarray::parallel::prelude::*; 6 | use ndarray::prelude::*; 7 | use std::sync::atomic::{AtomicUsize, Ordering}; 8 | 9 | #[test] 10 | fn test_par_azip1() 11 | { 12 | let mut a = Array::zeros(62); 13 | let b = Array::from_elem(62, 42); 14 | par_azip!((a in &mut a) { *a = 42 }); 15 | assert_eq!(a, b); 16 | } 17 | 18 | #[test] 19 | fn test_par_azip2() 20 | { 21 | let mut a = Array::zeros((5, 7)); 22 | let b = Array::from_shape_fn(a.dim(), |(i, j)| 1. / (i + 2 * j) as f32); 23 | par_azip!((a in &mut a, &b in &b, ) *a = b ); 24 | assert_eq!(a, b); 25 | } 26 | 27 | #[test] 28 | #[cfg(feature = "approx")] 29 | fn test_par_azip3() 30 | { 31 | use approx::assert_abs_diff_eq; 32 | 33 | let mut a = [0.; 32]; 34 | let mut b = [0.; 32]; 35 | let mut c = [0.; 32]; 36 | for (i, elt) in enumerate(&mut b) { 37 | *elt = i as f32; 38 | } 39 | 40 | par_azip!((a in &mut a[..], &b in &b[..], c in &mut c[..]) { 41 | *a += b / 10.; 42 | *c = a.sin(); 43 | }); 44 | let res = Array::linspace(0., 3.1, 32).mapv_into(f32::sin); 45 | assert_abs_diff_eq!(res, ArrayView::from(&c), epsilon = 1e-4); 46 | } 47 | 48 | #[should_panic] 49 | #[test] 50 | fn test_zip_dim_mismatch_1() 51 | { 52 | let mut a = Array::zeros((5, 7)); 53 | let mut d = a.raw_dim(); 54 | d[0] += 1; 55 | let b = Array::from_shape_fn(d, |(i, j)| 1. / (i + 2 * j) as f32); 56 | par_azip!((a in &mut a, &b in &b) { *a = b; }); 57 | } 58 | 59 | #[test] 60 | fn test_indices_1() 61 | { 62 | let mut a1 = Array::default(12); 63 | for (i, elt) in a1.indexed_iter_mut() { 64 | *elt = i; 65 | } 66 | 67 | let count = AtomicUsize::new(0); 68 | par_azip!((index i, &elt in &a1) { 69 | count.fetch_add(1, Ordering::SeqCst); 70 | assert_eq!(elt, i); 71 | }); 72 | assert_eq!(count.load(Ordering::SeqCst), a1.len()); 73 | } 74 | -------------------------------------------------------------------------------- /tests/par_rayon.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "rayon")] 2 | 3 | use ndarray::parallel::prelude::*; 4 | use ndarray::prelude::*; 5 | 6 | const M: usize = 1024 * 10; 7 | const N: usize = 100; 8 | const CHUNK_SIZE: usize = 100; 9 | const N_CHUNKS: usize = (M + CHUNK_SIZE - 1) / CHUNK_SIZE; 10 | 11 | #[test] 12 | fn test_axis_iter() 13 | { 14 | let mut a = Array2::::zeros((M, N)); 15 | for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { 16 | v.fill(i as _); 17 | } 18 | assert_eq!(a.axis_iter(Axis(0)).len(), M); 19 | let s: f64 = a.axis_iter(Axis(0)).into_par_iter().map(|x| x.sum()).sum(); 20 | println!("{:?}", a.slice(s![..10, ..5])); 21 | assert_eq!(s, a.sum()); 22 | } 23 | 24 | #[test] 25 | #[cfg(feature = "approx")] 26 | fn test_axis_iter_mut() 27 | { 28 | use approx::assert_abs_diff_eq; 29 | let mut a = Array::linspace(0., 1.0f64, M * N) 30 | .into_shape_with_order((M, N)) 31 | .unwrap(); 32 | let b = a.mapv(|x| x.exp()); 33 | a.axis_iter_mut(Axis(0)) 34 | .into_par_iter() 35 | .for_each(|mut v| v.mapv_inplace(|x| x.exp())); 36 | println!("{:?}", a.slice(s![..10, ..5])); 37 | assert_abs_diff_eq!(a, b, epsilon = 0.001); 38 | } 39 | 40 | #[test] 41 | fn test_regular_iter() 42 | { 43 | let mut a = Array2::::zeros((M, N)); 44 | for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { 45 | v.fill(i as _); 46 | } 47 | let s: f64 = a.view().into_par_iter().map(|&x| x).sum(); 48 | println!("{:?}", a.slice(s![..10, ..5])); 49 | assert_eq!(s, a.sum()); 50 | } 51 | 52 | #[test] 53 | fn test_regular_iter_collect() 54 | { 55 | let mut a = Array2::::zeros((M, N)); 56 | for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { 57 | v.fill(i as _); 58 | } 59 | let v = a.view().into_par_iter().map(|&x| x).collect::>(); 60 | assert_eq!(v.len(), a.len()); 61 | } 62 | 63 | #[test] 64 | fn test_axis_chunks_iter() 65 | { 66 | let mut a = Array2::::zeros((M, N)); 67 | for (i, mut v) in a.axis_chunks_iter_mut(Axis(0), CHUNK_SIZE).enumerate() { 68 | v.fill(i as _); 69 | } 70 | assert_eq!(a.axis_chunks_iter(Axis(0), CHUNK_SIZE).len(), N_CHUNKS); 71 | let s: f64 = a 72 | .axis_chunks_iter(Axis(0), CHUNK_SIZE) 73 | .into_par_iter() 74 | .map(|x| x.sum()) 75 | .sum(); 76 | println!("{:?}", a.slice(s![..10, ..5])); 77 | assert_eq!(s, a.sum()); 78 | } 79 | 80 | #[test] 81 | #[cfg(feature = "approx")] 82 | fn test_axis_chunks_iter_mut() 83 | { 84 | use approx::assert_abs_diff_eq; 85 | let mut a = Array::linspace(0., 1.0f64, M * N) 86 | .into_shape_with_order((M, N)) 87 | .unwrap(); 88 | let b = a.mapv(|x| x.exp()); 89 | a.axis_chunks_iter_mut(Axis(0), CHUNK_SIZE) 90 | .into_par_iter() 91 | .for_each(|mut v| v.mapv_inplace(|x| x.exp())); 92 | println!("{:?}", a.slice(s![..10, ..5])); 93 | assert_abs_diff_eq!(a, b, epsilon = 0.001); 94 | } 95 | -------------------------------------------------------------------------------- /tests/par_zip.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "rayon")] 2 | 3 | use ndarray::prelude::*; 4 | 5 | use ndarray::Zip; 6 | 7 | const M: usize = 1024 * 10; 8 | const N: usize = 100; 9 | 10 | #[test] 11 | fn test_zip_1() 12 | { 13 | let mut a = Array2::::zeros((M, N)); 14 | 15 | Zip::from(&mut a).par_for_each(|x| *x = x.exp()); 16 | } 17 | 18 | #[test] 19 | fn test_zip_index_1() 20 | { 21 | let mut a = Array2::default((10, 10)); 22 | 23 | Zip::indexed(&mut a).par_for_each(|i, x| { 24 | *x = i; 25 | }); 26 | 27 | for (i, elt) in a.indexed_iter() { 28 | assert_eq!(*elt, i); 29 | } 30 | } 31 | 32 | #[test] 33 | fn test_zip_index_2() 34 | { 35 | let mut a = Array2::default((M, N)); 36 | 37 | Zip::indexed(&mut a).par_for_each(|i, x| { 38 | *x = i; 39 | }); 40 | 41 | for (i, elt) in a.indexed_iter() { 42 | assert_eq!(*elt, i); 43 | } 44 | } 45 | 46 | #[test] 47 | fn test_zip_index_3() 48 | { 49 | let mut a = Array::default((1, 2, 1, 2, 3)); 50 | 51 | Zip::indexed(&mut a).par_for_each(|i, x| { 52 | *x = i; 53 | }); 54 | 55 | for (i, elt) in a.indexed_iter() { 56 | assert_eq!(*elt, i); 57 | } 58 | } 59 | 60 | #[test] 61 | fn test_zip_index_4() 62 | { 63 | let mut a = Array2::zeros((M, N)); 64 | let mut b = Array2::zeros((M, N)); 65 | 66 | Zip::indexed(&mut a) 67 | .and(&mut b) 68 | .par_for_each(|(i, j), x, y| { 69 | *x = i; 70 | *y = j; 71 | }); 72 | 73 | for ((i, _), elt) in a.indexed_iter() { 74 | assert_eq!(*elt, i); 75 | } 76 | for ((_, j), elt) in b.indexed_iter() { 77 | assert_eq!(*elt, j); 78 | } 79 | } 80 | 81 | #[test] 82 | #[cfg(feature = "approx")] 83 | fn test_zip_collect() 84 | { 85 | use approx::assert_abs_diff_eq; 86 | 87 | // test Zip::map_collect and that it preserves c/f layout. 88 | 89 | let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); 90 | let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); 91 | 92 | { 93 | let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); 94 | 95 | assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); 96 | assert_eq!(a.strides(), b.strides()); 97 | } 98 | 99 | { 100 | let b = b.t(); 101 | let c = c.t(); 102 | 103 | let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); 104 | 105 | assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); 106 | assert_eq!(a.strides(), b.strides()); 107 | } 108 | } 109 | 110 | #[test] 111 | #[cfg(feature = "approx")] 112 | fn test_zip_small_collect() 113 | { 114 | use approx::assert_abs_diff_eq; 115 | 116 | for m in 0..32 { 117 | for n in 0..16 { 118 | for &is_f in &[false, true] { 119 | let dim = (m, n).set_f(is_f); 120 | let b = Array::from_shape_fn(dim, |(i, j)| 1. / (i + 2 * j + 1) as f32); 121 | let c = Array::from_shape_fn(dim, |(i, j)| f32::ln((1 + i + j) as f32)); 122 | 123 | { 124 | let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); 125 | 126 | assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); 127 | if m > 1 && n > 1 { 128 | assert_eq!(a.strides(), b.strides(), 129 | "Failure for {}x{} c/f: {:?}", m, n, is_f); 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | #[test] 138 | #[cfg(feature = "approx")] 139 | fn test_zip_assign_into() 140 | { 141 | use approx::assert_abs_diff_eq; 142 | 143 | let mut a = Array::::zeros((M, N)); 144 | let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); 145 | let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); 146 | 147 | Zip::from(&b) 148 | .and(&c) 149 | .par_map_assign_into(&mut a, |x, y| x + y); 150 | 151 | assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); 152 | } 153 | -------------------------------------------------------------------------------- /tests/raw_views.rs: -------------------------------------------------------------------------------- 1 | use ndarray::prelude::*; 2 | use ndarray::Zip; 3 | 4 | use std::cell::Cell; 5 | 6 | #[test] 7 | fn raw_view_cast_cell() 8 | { 9 | // Test .cast() by creating an ArrayView> 10 | 11 | let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32); 12 | let answer = &a + 1.; 13 | 14 | { 15 | let raw_cell_view = a.raw_view_mut().cast::>(); 16 | let cell_view = unsafe { raw_cell_view.deref_into_view() }; 17 | 18 | Zip::from(cell_view).for_each(|elt| elt.set(elt.get() + 1.)); 19 | } 20 | assert_eq!(a, answer); 21 | } 22 | 23 | #[test] 24 | fn raw_view_cast_reinterpret() 25 | { 26 | // Test .cast() by reinterpreting u16 as [u8; 2] 27 | let a = Array::from_shape_fn((5, 5).f(), |(i, j)| (i as u16) << 8 | j as u16); 28 | let answer = a.mapv(u16::to_ne_bytes); 29 | 30 | let raw_view = a.raw_view().cast::<[u8; 2]>(); 31 | let view = unsafe { raw_view.deref_into_view() }; 32 | assert_eq!(view, answer); 33 | } 34 | 35 | #[test] 36 | fn raw_view_cast_zst() 37 | { 38 | struct Zst; 39 | 40 | let a = Array::<(), _>::default((250, 250)); 41 | let b: RawArrayView = a.raw_view().cast::(); 42 | assert_eq!(a.shape(), b.shape()); 43 | assert_eq!(a.as_ptr() as *const u8, b.as_ptr() as *const u8); 44 | } 45 | 46 | #[test] 47 | #[should_panic] 48 | fn raw_view_invalid_size_cast() 49 | { 50 | let data = [0i32; 16]; 51 | ArrayView::from(&data[..]).raw_view().cast::(); 52 | } 53 | 54 | #[test] 55 | #[should_panic] 56 | fn raw_view_mut_invalid_size_cast() 57 | { 58 | let mut data = [0i32; 16]; 59 | ArrayViewMut::from(&mut data[..]) 60 | .raw_view_mut() 61 | .cast::(); 62 | } 63 | 64 | #[test] 65 | fn raw_view_misaligned() 66 | { 67 | let data: [u16; 2] = [0x0011, 0x2233]; 68 | let ptr: *const u16 = data.as_ptr(); 69 | unsafe { 70 | let misaligned_ptr = (ptr as *const u8).add(1) as *const u16; 71 | RawArrayView::from_shape_ptr(1, misaligned_ptr); 72 | } 73 | } 74 | 75 | #[test] 76 | #[cfg(debug_assertions)] 77 | #[should_panic = "The pointer must be aligned."] 78 | fn raw_view_deref_into_view_misaligned() 79 | { 80 | fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> 81 | { 82 | let ptr: *const u16 = data.as_ptr(); 83 | unsafe { 84 | let misaligned_ptr = (ptr as *const u8).add(1) as *const u16; 85 | let raw_view = RawArrayView::from_shape_ptr(1, misaligned_ptr); 86 | raw_view.deref_into_view() 87 | } 88 | } 89 | let data: [u16; 2] = [0x0011, 0x2233]; 90 | misaligned_deref(&data); 91 | } 92 | 93 | #[test] 94 | #[cfg(debug_assertions)] 95 | #[should_panic = "Unsupported"] 96 | fn raw_view_negative_strides() 97 | { 98 | fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> 99 | { 100 | let ptr: *const u16 = data.as_ptr(); 101 | unsafe { 102 | let raw_view = RawArrayView::from_shape_ptr(1.strides((-1isize) as usize), ptr); 103 | raw_view.deref_into_view() 104 | } 105 | } 106 | let data: [u16; 2] = [0x0011, 0x2233]; 107 | misaligned_deref(&data); 108 | } 109 | -------------------------------------------------------------------------------- /tests/reserve.rs: -------------------------------------------------------------------------------- 1 | use ndarray::prelude::*; 2 | 3 | fn into_raw_vec_capacity(a: Array) -> usize 4 | { 5 | a.into_raw_vec_and_offset().0.capacity() 6 | } 7 | 8 | #[test] 9 | fn reserve_1d() 10 | { 11 | let mut a = Array1::::zeros((4,)); 12 | a.reserve(Axis(0), 1000).unwrap(); 13 | assert_eq!(a.shape(), &[4]); 14 | assert!(into_raw_vec_capacity(a) >= 1004); 15 | } 16 | 17 | #[test] 18 | fn reserve_3d() 19 | { 20 | let mut a = Array3::::zeros((0, 4, 8)); 21 | a.reserve(Axis(0), 10).unwrap(); 22 | assert_eq!(a.shape(), &[0, 4, 8]); 23 | assert!(into_raw_vec_capacity(a) >= 4 * 8 * 10); 24 | } 25 | 26 | #[test] 27 | fn reserve_empty_3d() 28 | { 29 | let mut a = Array3::::zeros((0, 0, 0)); 30 | a.reserve(Axis(0), 10).unwrap(); 31 | } 32 | 33 | #[test] 34 | fn reserve_3d_axis1() 35 | { 36 | let mut a = Array3::::zeros((2, 4, 8)); 37 | a.reserve(Axis(1), 10).unwrap(); 38 | assert!(into_raw_vec_capacity(a) >= 2 * 8 * 10); 39 | } 40 | 41 | #[test] 42 | fn reserve_3d_repeat() 43 | { 44 | let mut a = Array3::::zeros((2, 4, 8)); 45 | a.reserve(Axis(1), 10).unwrap(); 46 | a.reserve(Axis(2), 30).unwrap(); 47 | assert!(into_raw_vec_capacity(a) >= 2 * 4 * 30); 48 | } 49 | 50 | #[test] 51 | fn reserve_2d_with_data() 52 | { 53 | let mut a = array![[1, 2], [3, 4], [5, 6]]; 54 | a.reserve(Axis(1), 100).unwrap(); 55 | assert_eq!(a, array![[1, 2], [3, 4], [5, 6]]); 56 | assert!(into_raw_vec_capacity(a) >= 3 * 100); 57 | } 58 | 59 | #[test] 60 | fn reserve_2d_inverted_with_data() 61 | { 62 | let mut a = array![[1, 2], [3, 4], [5, 6]]; 63 | a.invert_axis(Axis(1)); 64 | assert_eq!(a, array![[2, 1], [4, 3], [6, 5]]); 65 | a.reserve(Axis(1), 100).unwrap(); 66 | assert_eq!(a, array![[2, 1], [4, 3], [6, 5]]); 67 | let (raw_vec, offset) = a.into_raw_vec_and_offset(); 68 | assert!(raw_vec.capacity() >= 3 * 100); 69 | assert_eq!(offset, Some(1)); 70 | } 71 | -------------------------------------------------------------------------------- /tests/s.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names 3 | )] 4 | 5 | use ndarray::{s, Array}; 6 | 7 | #[test] 8 | fn test_s() 9 | { 10 | let a = Array::::zeros((3, 4)); 11 | let vi = a.slice(s![1.., ..;2]); 12 | assert_eq!(vi.shape(), &[2, 2]); 13 | 14 | // trailing comma 15 | let vi = a.slice(s![1.., ..;2, ]); 16 | assert_eq!(vi.shape(), &[2, 2]); 17 | } 18 | -------------------------------------------------------------------------------- /tests/stacking.rs: -------------------------------------------------------------------------------- 1 | use ndarray::{arr2, arr3, aview1, aview2, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; 2 | 3 | #[test] 4 | fn concatenating() 5 | { 6 | let a = arr2(&[[2., 2.], [3., 3.]]); 7 | let b = ndarray::concatenate(Axis(0), &[a.view(), a.view()]).unwrap(); 8 | assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); 9 | 10 | let c = concatenate![Axis(0), a, b]; 11 | assert_eq!( 12 | c, 13 | arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.], [2., 2.], [3., 3.]]) 14 | ); 15 | 16 | let d = concatenate![Axis(0), a.row(0), &[9., 9.]]; 17 | assert_eq!(d, aview1(&[2., 2., 9., 9.])); 18 | 19 | let d = concatenate![Axis(1), a.row(0).insert_axis(Axis(1)), aview1(&[9., 9.]).insert_axis(Axis(1))]; 20 | assert_eq!(d, aview2(&[[2., 9.], 21 | [2., 9.]])); 22 | 23 | let d = concatenate![Axis(0), a.row(0).insert_axis(Axis(1)), aview1(&[9., 9.]).insert_axis(Axis(1))]; 24 | assert_eq!(d, aview2(&[[2.], [2.], [9.], [9.]])); 25 | 26 | let res = ndarray::concatenate(Axis(1), &[a.view(), c.view()]); 27 | assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); 28 | 29 | let res = ndarray::concatenate(Axis(2), &[a.view(), c.view()]); 30 | assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); 31 | 32 | let res: Result, _> = ndarray::concatenate(Axis(0), &[]); 33 | assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); 34 | } 35 | 36 | #[test] 37 | fn stacking() 38 | { 39 | let a = arr2(&[[2., 2.], [3., 3.]]); 40 | let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap(); 41 | assert_eq!(b, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); 42 | 43 | let c = stack![Axis(0), a, a]; 44 | assert_eq!(c, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); 45 | 46 | let c = arr2(&[[3., 2., 3.], [2., 3., 2.]]); 47 | let res = ndarray::stack(Axis(1), &[a.view(), c.view()]); 48 | assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); 49 | 50 | let res = ndarray::stack(Axis(3), &[a.view(), a.view()]); 51 | assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); 52 | 53 | let res: Result, _> = ndarray::stack::<_, Ix1>(Axis(0), &[]); 54 | assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); 55 | } 56 | -------------------------------------------------------------------------------- /tests/variance.rs: -------------------------------------------------------------------------------- 1 | use ndarray::{Array1, ArrayView1}; 2 | 3 | fn arrayview_covariant<'a: 'b, 'b>(x: ArrayView1<'a, f64>) -> ArrayView1<'b, f64> 4 | { 5 | x 6 | } 7 | 8 | #[test] 9 | fn test_covariance() 10 | { 11 | let x = Array1::zeros(2); 12 | let shorter_view = arrayview_covariant(x.view()); 13 | assert_eq!(shorter_view[0], 0.0); 14 | } 15 | -------------------------------------------------------------------------------- /tests/views.rs: -------------------------------------------------------------------------------- 1 | use ndarray::prelude::*; 2 | use ndarray::Zip; 3 | 4 | #[test] 5 | fn cell_view() 6 | { 7 | let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32); 8 | let answer = &a + 1.; 9 | 10 | { 11 | let cv1 = a.cell_view(); 12 | let cv2 = cv1; 13 | 14 | Zip::from(cv1).and(cv2).for_each(|a, b| a.set(b.get() + 1.)); 15 | } 16 | assert_eq!(a, answer); 17 | } 18 | -------------------------------------------------------------------------------- /tests/zst.rs: -------------------------------------------------------------------------------- 1 | use ndarray::arr2; 2 | use ndarray::ArcArray; 3 | 4 | #[test] 5 | fn test_swap() 6 | { 7 | let mut a = arr2(&[[(); 3]; 3]); 8 | 9 | let b = a.clone(); 10 | 11 | for i in 0..a.nrows() { 12 | for j in i + 1..a.ncols() { 13 | a.swap((i, j), (j, i)); 14 | } 15 | } 16 | assert_eq!(a, b.t()); 17 | } 18 | 19 | #[test] 20 | fn test() 21 | { 22 | let c = ArcArray::<(), _>::default((3, 4)); 23 | let mut d = c.clone(); 24 | for _ in d.iter_mut() {} 25 | } 26 | --------------------------------------------------------------------------------