ndarray-0.16.1/.cargo_vcs_info.json0000644000000001360000000000100125610ustar { "git": { "sha1": "6f77377d7d508550bf516e54c142cee3ab243aeb" }, "path_in_vcs": "" }ndarray-0.16.1/.git-blame-ignore-revs000064400000000000000000000001061046102023000154460ustar 00000000000000# rustfmt codebase (gh-1375) d07f5f33800e5240e7edb02bdbc4815ab30ef37e ndarray-0.16.1/.github/workflows/ci.yaml000064400000000000000000000102371046102023000162300ustar 00000000000000on: pull_request: merge_group: push: branches: - master name: Continuous integration env: CARGO_TERM_COLOR: always HOST: x86_64-unknown-linux-gnu FEATURES: "test docs" RUSTFLAGS: "-D warnings" jobs: clippy: runs-on: ubuntu-latest strategy: matrix: rust: - stable name: clippy/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} components: clippy - uses: Swatinem/rust-cache@v2 - run: cargo clippy --features docs format: runs-on: ubuntu-latest strategy: matrix: rust: - nightly name: format/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} components: rustfmt - run: cargo fmt --all --check nostd: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: include: - rust: stable experimental: false target: thumbv6m-none-eabi name: nostd/${{ matrix.target }}/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - name: Tests run: | cargo rustc "--target=${{ matrix.target }}" --no-default-features --features portable-atomic-critical-section tests: runs-on: ubuntu-latest strategy: matrix: rust: - stable - beta - nightly - 1.64.0 # MSRV name: tests/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - uses: rui314/setup-mold@v1 - uses: Swatinem/rust-cache@v2 - name: Install openblas run: sudo apt-get install libopenblas-dev gfortran - run: ./scripts/all-tests.sh "$FEATURES" ${{ matrix.rust }} cross_test: #if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest strategy: matrix: include: - rust: stable target: s390x-unknown-linux-gnu - rust: stable target: i686-unknown-linux-gnu name: cross_test/${{ matrix.target }}/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - uses: rui314/setup-mold@v1 - uses: Swatinem/rust-cache@v2 - name: Install cross run: cargo install cross - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} cargo-careful: #if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest name: cargo-careful steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: nightly - uses: Swatinem/rust-cache@v2 - name: Install cargo-careful run: cargo install cargo-careful - run: cargo careful test -Zcareful-sanitizer --features="$FEATURES" docs: #if: ${{ github.event_name == 'merge_group' }} runs-on: ubuntu-latest strategy: matrix: rust: - stable name: docs/${{ matrix.rust }} env: RUSTDOCFLAGS: "-Dwarnings" steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - run: cargo doc --no-deps --all-features conclusion: needs: - clippy - format # should format be required? - nostd - tests - cross_test - cargo-careful - docs if: always() runs-on: ubuntu-latest steps: - name: Result run: | jq -C <<< "${needs}" # Check if all needs were successful or skipped. "$(jq -r 'all(.result as $result | (["success", "skipped"] | contains([$result])))' <<< "${needs}")" env: needs: ${{ toJson(needs) }} ndarray-0.16.1/.gitignore000064400000000000000000000000231046102023000133340ustar 00000000000000Cargo.lock target/ ndarray-0.16.1/Cargo.lock0000644000000207170000000000100105430ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "cblas-sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6feecd82cce51b0204cf063f0041d69f24ce83f680d87514b004248e7b0fa65" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "critical-section" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "defmac" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5592fca31e96d8a748d03080b58be78c5383617aa4bd89e69f30607d8769891" [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "matrixmultiply" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" dependencies = [ "autocfg", "num_cpus", "once_cell", "rawpointer", "thread-tree", ] [[package]] name = "ndarray" version = "0.16.1" dependencies = [ "approx", "cblas-sys", "defmac", "itertools", "libc", "matrixmultiply", "num-complex", "num-integer", "num-traits", "portable-atomic", "portable-atomic-util", "quickcheck", "rawpointer", "rayon", "serde", ] [[package]] name = "num-complex" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "portable-atomic" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" dependencies = [ "critical-section", ] [[package]] name = "portable-atomic-util" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcdd8420072e66d54a407b3316991fe946ce3ab1083a7f575b2463866624704d" dependencies = [ "portable-atomic", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quickcheck" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "rand", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rawpointer" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "serde" version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.207" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thread-tree" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbd370cb847953a25954d9f63e14824a36113f8c72eecf6eccef5dc4b45d630" dependencies = [ "crossbeam-channel", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" ndarray-0.16.1/Cargo.toml0000644000000136250000000000100105660ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.64" name = "ndarray" version = "0.16.1" authors = [ 'Ulrik Sverdrup "bluss"', "Jim Turner", ] build = false exclude = ["docgen/images/*"] autobins = false autoexamples = false autotests = false autobenches = false description = "An n-dimensional array for general elements and for numerics. Lightweight array views and slicing; views support chunking and splitting." documentation = "https://docs.rs/ndarray/" readme = "README-crates.io.md" keywords = [ "array", "data-structure", "multidimensional", "matrix", "blas", ] categories = [ "data-structures", "science", ] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-ndarray/ndarray" resolver = "2" [package.metadata.docs.rs] features = ["docs"] [package.metadata.release] no-dev-version = true tag-name = "{{version}}" [profile.bench] debug = 2 [profile.test.package.blas-tests] opt-level = 2 [profile.test.package.numeric-tests] opt-level = 2 [lib] name = "ndarray" path = "src/lib.rs" test = true bench = false [[example]] name = "axis_ops" path = "examples/axis_ops.rs" [[example]] name = "bounds_check_elim" path = "examples/bounds_check_elim.rs" [[example]] name = "column_standardize" path = "examples/column_standardize.rs" [[example]] name = "convo" path = "examples/convo.rs" [[example]] name = "life" path = "examples/life.rs" [[example]] name = "rollaxis" path = "examples/rollaxis.rs" [[example]] name = "sort-axis" path = "examples/sort-axis.rs" [[example]] name = "type_conversion" path = "examples/type_conversion.rs" [[example]] name = "zip_many" path = "examples/zip_many.rs" [[test]] name = "append" path = "tests/append.rs" [[test]] name = "array" path = "tests/array.rs" [[test]] name = "array-construct" path = "tests/array-construct.rs" [[test]] name = "assign" path = "tests/assign.rs" [[test]] name = "azip" path = "tests/azip.rs" [[test]] name = "broadcast" path = "tests/broadcast.rs" [[test]] name = "clone" path = "tests/clone.rs" [[test]] name = "complex" path = "tests/complex.rs" [[test]] name = "dimension" path = "tests/dimension.rs" [[test]] name = "format" path = "tests/format.rs" [[test]] name = "higher_order_f" path = "tests/higher_order_f.rs" [[test]] name = "indices" path = "tests/indices.rs" [[test]] name = "into-ixdyn" path = "tests/into-ixdyn.rs" [[test]] name = "iterator_chunks" path = "tests/iterator_chunks.rs" [[test]] name = "iterators" path = "tests/iterators.rs" [[test]] name = "ix0" path = "tests/ix0.rs" [[test]] name = "ixdyn" path = "tests/ixdyn.rs" [[test]] name = "numeric" path = "tests/numeric.rs" [[test]] name = "oper" path = "tests/oper.rs" [[test]] name = "par_azip" path = "tests/par_azip.rs" [[test]] name = "par_rayon" path = "tests/par_rayon.rs" [[test]] name = "par_zip" path = "tests/par_zip.rs" [[test]] name = "raw_views" path = "tests/raw_views.rs" [[test]] name = "reserve" path = "tests/reserve.rs" [[test]] name = "reshape" path = "tests/reshape.rs" [[test]] name = "s" path = "tests/s.rs" [[test]] name = "stacking" path = "tests/stacking.rs" [[test]] name = "views" path = "tests/views.rs" [[test]] name = "windows" path = "tests/windows.rs" [[test]] name = "zst" path = "tests/zst.rs" [[bench]] name = "append" path = "benches/append.rs" [[bench]] name = "bench1" path = "benches/bench1.rs" [[bench]] name = "chunks" path = "benches/chunks.rs" [[bench]] name = "construct" path = "benches/construct.rs" [[bench]] name = "gemv_gemm" path = "benches/gemv_gemm.rs" [[bench]] name = "higher-order" path = "benches/higher-order.rs" [[bench]] name = "iter" path = "benches/iter.rs" [[bench]] name = "numeric" path = "benches/numeric.rs" [[bench]] name = "par_rayon" path = "benches/par_rayon.rs" [[bench]] name = "reserve" path = "benches/reserve.rs" [[bench]] name = "to_shape" path = "benches/to_shape.rs" [[bench]] name = "zip" path = "benches/zip.rs" [dependencies.approx] version = "0.5" optional = true default-features = false [dependencies.cblas-sys] version = "0.1.4" optional = true default-features = false [dependencies.libc] version = "0.2.82" optional = true [dependencies.matrixmultiply] version = "0.3.2" features = ["cgemm"] default-features = false [dependencies.num-complex] version = "0.4" default-features = false [dependencies.num-integer] version = "0.1.39" default-features = false [dependencies.num-traits] version = "0.2" default-features = false [dependencies.rawpointer] version = "0.2" [dependencies.rayon] version = "1.10.0" optional = true [dependencies.serde] version = "1.0" features = ["alloc"] optional = true default-features = false [dev-dependencies.approx] version = "0.5" default-features = true [dev-dependencies.defmac] version = "0.2" [dev-dependencies.itertools] version = "0.13.0" features = ["use_std"] default-features = false [dev-dependencies.quickcheck] version = "1.0" default-features = false [features] blas = [ "dep:cblas-sys", "dep:libc", ] default = ["std"] docs = [ "approx", "serde", "rayon", ] matrixmultiply-threading = ["matrixmultiply/threading"] portable-atomic-critical-section = ["portable-atomic/critical-section"] rayon = [ "dep:rayon", "std", ] serde = ["dep:serde"] serde-1 = ["dep:serde"] std = [ "num-traits/std", "matrixmultiply/std", ] test = [] [target.'cfg(not(target_has_atomic = "ptr"))'.dependencies.portable-atomic] version = "1.6.0" [target.'cfg(not(target_has_atomic = "ptr"))'.dependencies.portable-atomic-util] version = "0.2.0" features = ["alloc"] ndarray-0.16.1/Cargo.toml.orig000064400000000000000000000064551046102023000142520ustar 00000000000000[package] name = "ndarray" version = "0.16.1" edition = "2018" rust-version = "1.64" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" ] license = "MIT OR Apache-2.0" readme = "README-crates.io.md" repository = "https://github.com/rust-ndarray/ndarray" documentation = "https://docs.rs/ndarray/" description = "An n-dimensional array for general elements and for numerics. Lightweight array views and slicing; views support chunking and splitting." keywords = ["array", "data-structure", "multidimensional", "matrix", "blas"] categories = ["data-structures", "science"] exclude = ["docgen/images/*"] resolver = "2" [lib] name = "ndarray" bench = false test = true [dependencies] num-integer = { workspace = true } num-traits = { workspace = true } num-complex = { workspace = true } approx = { workspace = true, optional = true } rayon = { version = "1.10.0", optional = true } # Use via the `blas` crate feature cblas-sys = { workspace = true, optional = true } libc = { version = "0.2.82", optional = true } matrixmultiply = { version = "0.3.2", default-features = false, features=["cgemm"] } serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } rawpointer = { version = "0.2" } [dev-dependencies] defmac = "0.2" quickcheck = { workspace = true } approx = { workspace = true, default-features = true } itertools = { workspace = true } ndarray-gen = { workspace = true } [features] default = ["std"] # Enable blas usage # See README for more instructions blas = ["dep:cblas-sys", "dep:libc"] serde = ["dep:serde"] # Old name for the serde feature serde-1 = ["dep:serde"] # These features are used for testing test = [] # This feature is used for docs docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["dep:rayon", "std"] matrixmultiply-threading = ["matrixmultiply/threading"] portable-atomic-critical-section = ["portable-atomic/critical-section"] [target.'cfg(not(target_has_atomic = "ptr"))'.dependencies] portable-atomic = { version = "1.6.0" } portable-atomic-util = { version = "0.2.0", features = [ "alloc" ] } [workspace] members = [ "ndarray-rand", "crates/*", ] default-members = [ ".", "ndarray-rand", "crates/ndarray-gen", "crates/numeric-tests", "crates/serialization-tests", # exclude blas-tests and blas-mock-tests that activate "blas" feature ] [workspace.dependencies] ndarray = { version = "0.16", path = ".", default-features = false } ndarray-rand = { path = "ndarray-rand" } ndarray-gen = { path = "crates/ndarray-gen" } num-integer = { version = "0.1.39", default-features = false } num-traits = { version = "0.2", default-features = false } num-complex = { version = "0.4", default-features = false } approx = { version = "0.5", default-features = false } quickcheck = { version = "1.0", default-features = false } rand = { version = "0.8.0", features = ["small_rng"] } rand_distr = { version = "0.4.0" } itertools = { version = "0.13.0", default-features = false, features = ["use_std"] } cblas-sys = { version = "0.1.4", default-features = false } [profile.bench] debug = true [profile.test.package.numeric-tests] opt-level = 2 [profile.test.package.blas-tests] opt-level = 2 [package.metadata.release] no-dev-version = true tag-name = "{{version}}" [package.metadata.docs.rs] features = ["docs"] ndarray-0.16.1/LICENSE-APACHE000064400000000000000000000251371046102023000133050ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ndarray-0.16.1/LICENSE-MIT000064400000000000000000000021371046102023000130100ustar 00000000000000Copyright (c) 2015 - 2021 Ulrik Sverdrup "bluss", Jim Turner, and ndarray developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ndarray-0.16.1/README-crates.io.md000064400000000000000000000015431046102023000145200ustar 00000000000000 `ndarray` implements an *n*-dimensional container for general elements and for numerics. In *n*-dimensional we include for example 1-dimensional rows or columns, 2-dimensional matrices, and higher dimensional arrays. If the array has *n* dimensions, then an element in the array is accessed by using that many indices. Each dimension is also called an *axis*. ## Highlights - Generic *n*-dimensional array - Slicing, also with arbitrary step size, and negative indices to mean elements from the end of the axis. - Views and subviews of arrays; iterators that yield subviews. - Higher order operations and arithmetic are performant - Array views can be used to slice and mutate any `[T]` data using `ArrayView::from` and `ArrayViewMut::from`. - `Zip` for lock step function application across two or more arrays or other item producers (`NdProducer` trait). ndarray-0.16.1/README-quick-start.md000064400000000000000000000406241046102023000151030ustar 00000000000000# Quickstart tutorial If you are familiar with Python Numpy, do check out this [For Numpy User Doc](https://docs.rs/ndarray/0.13.0/ndarray/doc/ndarray_for_numpy_users/index.html) after you go through this tutorial. You can use [play.integer32.com](https://play.integer32.com/) to immediately try out the examples. ## The Basics You can create your first 2x3 floating-point ndarray as such: ```rust use ndarray::prelude::*; fn main() { let a = array![ [1.,2.,3.], [4.,5.,6.], ]; assert_eq!(a.ndim(), 2); // get the number of dimensions of array a assert_eq!(a.len(), 6); // get the number of elements in array a assert_eq!(a.shape(), [2, 3]); // get the shape of array a assert_eq!(a.is_empty(), false); // check if the array has zero elements println!("{:?}", a); } ``` This code will create a simple array, then print it to stdout as such: ``` [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], shape=[2, 3], strides=[3, 1], layout=C (0x1), const ndim=2 ``` ## Array Creation ### Element type and dimensionality Now let's create more arrays. A common operation on matrices is to create a matrix full of 0's of certain dimensions. Let's try to do that with dimensions (3, 2, 4) using the `Array::zeros` function: ```rust use ndarray::prelude::*; use ndarray::Array; fn main() { let a = Array::zeros((3, 2, 4).f()); println!("{:?}", a); } ``` Unfortunately, this code does not compile. ``` | let a = Array::zeros((3, 2, 4).f()); | - ^^^^^^^^^^^^ cannot infer type for type parameter `A` ``` Indeed, note that the compiler needs to infer the element type and dimensionality from context only. In this case the compiler does not have enough information. To fix the code, we can explicitly give the element type through turbofish syntax, and let it infer the dimensionality type: ```rust use ndarray::prelude::*; use ndarray::Array; fn main() { let a = Array::::zeros((3, 2, 4).f()); println!("{:?}", a); } ``` This code now compiles to what we wanted: ``` [[[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]], [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]], shape=[3, 2, 4], strides=[1, 3, 6], layout=F (0x2), const ndim=3 ``` We could also specify its dimensionality explicitly `Array::::zeros(...)`, with`Ix3` standing for 3D array type. Phew! We achieved type safety. If you tried changing the code above to `Array::::zeros((3, 2, 4, 5).f());`, which is not of dimension 3 anymore, Rust's type system would gracefully prevent you from compiling the code. ### Creating arrays with different initial values and/or different types The [`from_elem`](http://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.from_elem) method allows initializing an array of given dimension to a specific value of any type: ```rust use ndarray::{Array, Ix3}; fn main() { let a = Array::::from_elem((3, 2, 4), false); println!("{:?}", a); } ``` ### Some common array initializing helper functions `linspace` - Create a 1-D array with 11 elements with values 0., …, 5. ```rust use ndarray::prelude::*; use ndarray::{Array, Ix3}; fn main() { let a = Array::::linspace(0., 5., 11); println!("{:?}", a); } ``` The output is: ``` [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0], shape=[11], strides=[1], layout=C | F (0x3), const ndim=1 ``` Common array initializing methods include [`range`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.range), [`logspace`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.logspace), [`eye`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.eye), [`ones`](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#method.ones)... ## Basic operations Basic operations on arrays are all element-wise; you need to use specific methods for operations such as matrix multiplication (see later section). ```rust use ndarray::prelude::*; use ndarray::Array; use std::f64::INFINITY as inf; fn main() { let a = array![ [10.,20.,30., 40.,], ]; let b = Array::range(0., 4., 1.); // [0., 1., 2., 3, ] assert_eq!(&a + &b, array![[10., 21., 32., 43.,]]); // Allocates a new array. Note the explicit `&`. assert_eq!(&a - &b, array![[10., 19., 28., 37.,]]); assert_eq!(&a * &b, array![[0., 20., 60., 120.,]]); assert_eq!(&a / &b, array![[inf, 20., 15., 13.333333333333334,]]); } ``` Note that (for any binary operator `@`): * `&A @ &A` produces a new `Array` * `B @ A` consumes `B`, updates it with the result, and returns it * `B @ &A` consumes `B`, updates it with the result, and returns it * `C @= &A` performs an arithmetic operation in place Try removing all the `&` sign in front of `a` and `b` in the last example: it will not compile anymore because of those rules. For more info checkout https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#arithmetic-operations Some operations have `_axis` appended to the function name: they generally take in a parameter of type `Axis` as one of their inputs, such as `sum_axis`: ```rust use ndarray::{aview0, aview1, arr2, Axis}; fn main() { let a = arr2(&[[1., 2., 3.], [4., 5., 6.]]); assert!( a.sum_axis(Axis(0)) == aview1(&[5., 7., 9.]) && a.sum_axis(Axis(1)) == aview1(&[6., 15.]) && a.sum_axis(Axis(0)).sum_axis(Axis(0)) == aview0(&21.) && a.sum_axis(Axis(0)).sum_axis(Axis(0)) == aview0(&a.sum()) ); } ``` ### Matrix product ```rust use ndarray::prelude::*; use ndarray::Array; fn main() { let a = array![ [10.,20.,30., 40.,], ]; let b = Array::range(0., 4., 1.); // b = [0., 1., 2., 3, ] println!("a shape {:?}", &a.shape()); println!("b shape {:?}", &b.shape()); let b = b.into_shape_with_order((4,1)).unwrap(); // reshape b to shape [4, 1] println!("b shape after reshape {:?}", &b.shape()); println!("{}", a.dot(&b)); // [1, 4] x [4, 1] -> [1, 1] println!("{}", a.t().dot(&b.t())); // [4, 1] x [1, 4] -> [4, 4] } ``` The output is: ``` a shape [1, 4] b shape [4] b shape after reshape [4, 1] [[200]] [[0, 10, 20, 30], [0, 20, 40, 60], [0, 30, 60, 90], [0, 40, 80, 120]] ``` ## Indexing, Slicing and Iterating One-dimensional arrays can be indexed, sliced and iterated over, much like `numpy` arrays ```rust use ndarray::prelude::*; use ndarray::Array; fn main() { let a = Array::range(0., 10., 1.); let mut a = a.mapv(|a: f64| a.powi(3)); // numpy equivlant of `a ** 3`; https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.powi println!("{}", a); println!("{}", a[[2]]); println!("{}", a.slice(s![2])); println!("{}", a.slice(s![2..5])); a.slice_mut(s![..6;2]).fill(1000.); // numpy equivlant of `a[:6:2] = 1000` println!("{}", a); for i in a.iter() { print!("{}, ", i.powf(1./3.)) } } ``` The output is: ``` [0, 1, 8, 27, 64, 125, 216, 343, 512, 729] 8 8 [8, 27, 64] [1000, 1, 1000, 27, 1000, 125, 216, 343, 512, 729] 9.999999999999998, 1, 9.999999999999998, 3, 9.999999999999998, 4.999999999999999, 5.999999999999999, 6.999999999999999, 7.999999999999999, 8.999999999999998, ``` For more info about iteration see [Loops, Producers, and Iterators](https://docs.rs/ndarray/0.13.0/ndarray/struct.ArrayBase.html#loops-producers-and-iterators) Let's try a iterating over a 3D array with elements of type `isize`. This is how you index it: ```rust use ndarray::prelude::*; fn main() { let a = array![ [[ 0, 1, 2], // a 3D array 2 x 2 x 3 [ 10, 12, 13]], [[100,101,102], [110,112,113]] ]; let a = a.mapv(|a: isize| a.pow(1)); // numpy equivalent of `a ** 1`; // This line does nothing except illustrating mapv with isize type println!("a -> \n{}\n", a); println!("`a.slice(s![1, .., ..])` -> \n{}\n", a.slice(s![1, .., ..])); println!("`a.slice(s![.., .., 2])` -> \n{}\n", a.slice(s![.., .., 2])); println!("`a.slice(s![.., 1, 0..2])` -> \n{}\n", a.slice(s![.., 1, 0..2])); println!("`a.iter()` ->"); for i in a.iter() { print!("{}, ", i) // flat out to every element } println!("\n\n`a.outer_iter()` ->"); for i in a.outer_iter() { print!("row: {}, \n", i) // iterate through first dimension } } ``` The output is: ``` a -> [[[0, 1, 2], [10, 12, 13]], [[100, 101, 102], [110, 112, 113]]] `a.slice(s![1, .., ..])` -> [[100, 101, 102], [110, 112, 113]] `a.slice(s![.., .., 2])` -> [[2, 13], [102, 113]] `a.slice(s![.., 1, 0..2])` -> [[10, 12], [110, 112]] `a.iter()` -> 0, 1, 2, 10, 12, 13, 100, 101, 102, 110, 112, 113, `a.outer_iter()` -> row: [[0, 1, 2], [10, 12, 13]], row: [[100, 101, 102], [110, 112, 113]], ``` ## Shape Manipulation ### Changing the shape of an array The shape of an array can be changed with the `into_shape_with_order` or `to_shape` method. ````rust use ndarray::prelude::*; use ndarray::Array; use std::iter::FromIterator; // use ndarray_rand::RandomExt; // use ndarray_rand::rand_distr::Uniform; fn main() { // Or you may use ndarray_rand crate to generate random arrays // let a = Array::random((2, 5), Uniform::new(0., 10.)); let a = array![ [3., 7., 3., 4.], [1., 4., 2., 2.], [7., 2., 4., 9.]]; println!("a = \n{:?}\n", a); // use trait FromIterator to flatten a matrix to a vector let b = Array::from_iter(a.iter()); println!("b = \n{:?}\n", b); let c = b.into_shape_with_order([6, 2]).unwrap(); // consume b and generate c with new shape println!("c = \n{:?}", c); } ```` The output is: ``` a = [[3.0, 7.0, 3.0, 4.0], [1.0, 4.0, 2.0, 2.0], [7.0, 2.0, 4.0, 9.0]], shape=[3, 4], strides=[4, 1], layout=C (0x1), const ndim=2 b = [3.0, 7.0, 3.0, 4.0, 1.0, 4.0, 2.0, 2.0, 7.0, 2.0, 4.0, 9.0], shape=[12], strides=[1], layout=C | F (0x3), const ndim=1 c = [[3.0, 7.0], [3.0, 4.0], [1.0, 4.0], [2.0, 2.0], [7.0, 2.0], [4.0, 9.0]], shape=[6, 2], strides=[2, 1], layout=C (0x1), const ndim=2 ``` ### Stacking/concatenating together different arrays The `stack!` and `concatenate!` macros are helpful for stacking/concatenating arrays. The `stack!` macro stacks arrays along a new axis, while the `concatenate!` macro concatenates arrays along an existing axis: ```rust use ndarray::prelude::*; use ndarray::{concatenate, stack, Axis}; fn main() { let a = array![ [3., 7., 8.], [5., 2., 4.], ]; let b = array![ [1., 9., 0.], [5., 4., 1.], ]; println!("stack, axis 0:\n{:?}\n", stack![Axis(0), a, b]); println!("stack, axis 1:\n{:?}\n", stack![Axis(1), a, b]); println!("stack, axis 2:\n{:?}\n", stack![Axis(2), a, b]); println!("concatenate, axis 0:\n{:?}\n", concatenate![Axis(0), a, b]); println!("concatenate, axis 1:\n{:?}\n", concatenate![Axis(1), a, b]); } ``` The output is: ``` stack, axis 0: [[[3.0, 7.0, 8.0], [5.0, 2.0, 4.0]], [[1.0, 9.0, 0.0], [5.0, 4.0, 1.0]]], shape=[2, 2, 3], strides=[6, 3, 1], layout=Cc (0x5), const ndim=3 stack, axis 1: [[[3.0, 7.0, 8.0], [1.0, 9.0, 0.0]], [[5.0, 2.0, 4.0], [5.0, 4.0, 1.0]]], shape=[2, 2, 3], strides=[3, 6, 1], layout=c (0x4), const ndim=3 stack, axis 2: [[[3.0, 1.0], [7.0, 9.0], [8.0, 0.0]], [[5.0, 5.0], [2.0, 4.0], [4.0, 1.0]]], shape=[2, 3, 2], strides=[1, 2, 6], layout=Ff (0xa), const ndim=3 concatenate, axis 0: [[3.0, 7.0, 8.0], [5.0, 2.0, 4.0], [1.0, 9.0, 0.0], [5.0, 4.0, 1.0]], shape=[4, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2 concatenate, axis 1: [[3.0, 7.0, 8.0, 1.0, 9.0, 0.0], [5.0, 2.0, 4.0, 5.0, 4.0, 1.0]], shape=[2, 6], strides=[1, 2], layout=Ff (0xa), const ndim=2 ``` ### Splitting one array into several smaller ones More to see here [ArrayView::split_at](https://docs.rs/ndarray/latest/ndarray/type.ArrayView.html#method.split_at) ```rust use ndarray::prelude::*; use ndarray::Axis; fn main() { let a = array![ [6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.], [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]]; let (s1, s2) = a.view().split_at(Axis(0), 1); println!("Split a from Axis(0), at index 1:"); println!("s1 = \n{}", s1); println!("s2 = \n{}\n", s2); let (s1, s2) = a.view().split_at(Axis(1), 4); println!("Split a from Axis(1), at index 4:"); println!("s1 = \n{}", s1); println!("s2 = \n{}\n", s2); } ``` The output is: ``` Split a from Axis(0), at index 1: s1 = [[6, 7, 6, 9, 0, 5, 4, 0, 6, 8, 5, 2]] s2 = [[8, 5, 5, 7, 1, 8, 6, 7, 1, 8, 1, 0]] Split a from Axis(1), at index 4: s1 = [[6, 7, 6, 9], [8, 5, 5, 7]] s2 = [[0, 5, 4, 0, 6, 8, 5, 2], [1, 8, 6, 7, 1, 8, 1, 0]] ``` ## Copies and Views ### View, Ref or Shallow Copy Rust has ownership, so we cannot simply update an element of an array while we have a shared view of it. This brings guarantees & helps having more robust code. ```rust use ndarray::prelude::*; use ndarray::{Array, Axis}; fn main() { let mut a = Array::range(0., 12., 1.).into_shape_with_order([3 ,4]).unwrap(); println!("a = \n{}\n", a); { let (s1, s2) = a.view().split_at(Axis(1), 2); // with s as a view sharing the ref of a, we cannot update a here // a.slice_mut(s![1, 1]).fill(1234.); println!("Split a from Axis(0), at index 1:"); println!("s1 = \n{}", s1); println!("s2 = \n{}\n", s2); } // now we can update a again here, as views of s1, s2 are dropped already a.slice_mut(s![1, 1]).fill(1234.); let (s1, s2) = a.view().split_at(Axis(1), 2); println!("Split a from Axis(0), at index 1:"); println!("s1 = \n{}", s1); println!("s2 = \n{}\n", s2); } ``` The output is: ``` a = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]] Split a from Axis(0), at index 1: s1 = [[0, 1], [4, 5], [8, 9]] s2 = [[2, 3], [6, 7], [10, 11]] Split a from Axis(0), at index 1: s1 = [[0, 1], [4, 1234], [8, 9]] s2 = [[2, 3], [6, 7], [10, 11]] ``` ### Deep Copy As the usual way in Rust, a `clone()` call will make a copy of your array: ```rust use ndarray::prelude::*; use ndarray::Array; fn main() { let mut a = Array::range(0., 4., 1.).into_shape_with_order([2 ,2]).unwrap(); let b = a.clone(); println!("a = \n{}\n", a); println!("b clone of a = \n{}\n", a); a.slice_mut(s![1, 1]).fill(1234.); println!("a updated..."); println!("a = \n{}\n", a); println!("b clone of a = \n{}\n", b); } ``` The output is: ``` a = [[0, 1], [2, 3]] b clone of a = [[0, 1], [2, 3]] a updated... a = [[0, 1], [2, 1234]] b clone of a = [[0, 1], [2, 3]] ``` Notice that using `clone()` (or cloning) an `Array` type also copies the array's elements. It creates an independently owned array of the same type. Cloning an `ArrayView` does not clone or copy the underlying elements - it only clones the view reference (as it happens in Rust when cloning a `&` reference). ## Broadcasting Arrays support limited broadcasting, where arithmetic operations with array operands of different sizes can be carried out by repeating the elements of the smaller dimension array. ```rust use ndarray::prelude::*; fn main() { let a = array![ [1., 1.], [1., 2.], [0., 3.], [0., 4.]]; let b = array![[0., 1.]]; let c = array![ [1., 2.], [1., 3.], [0., 4.], [0., 5.]]; // We can add because the shapes are compatible even if not equal. // The `b` array is shape 1 × 2 but acts like a 4 × 2 array. assert!(c == a + b); } ``` See [.broadcast()](https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.broadcast) for a more detailed description. And here is a short example of it: ```rust use ndarray::prelude::*; fn main() { let a = array![ [1., 2.], [3., 4.], ]; let b = a.broadcast((3, 2, 2)).unwrap(); println!("shape of a is {:?}", a.shape()); println!("a is broadcased to 3x2x2 = \n{}", b); } ``` The output is: ``` shape of a is [2, 2] a is broadcased to 3x2x2 = [[[1, 2], [3, 4]], [[1, 2], [3, 4]], [[1, 2], [3, 4]]] ``` ## Want to learn more? Please checkout these docs for more information * [`ArrayBase` doc page](https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html) * [`ndarray` for `numpy` user doc page](https://docs.rs/ndarray/latest/ndarray/doc/ndarray_for_numpy_users/index.html) ndarray-0.16.1/README.rst000064400000000000000000000130341046102023000130410ustar 00000000000000ndarray ========= The ``ndarray`` crate provides an *n*-dimensional container for general elements and for numerics. Please read the `API documentation on docs.rs`__ or take a look at the `quickstart tutorial <./README-quick-start.md>`_. __ https://docs.rs/ndarray/ |build_status|_ |crates|_ |matrix-chat|_ |irc|_ .. |build_status| image:: https://github.com/rust-ndarray/ndarray/actions/workflows/ci.yaml/badge.svg :alt: CI build status .. _build_status: https://github.com/rust-ndarray/ndarray/actions .. |crates| image:: https://img.shields.io/crates/v/ndarray.svg :alt: ndarray at crates.io .. _crates: https://crates.io/crates/ndarray .. |matrix-chat| image:: https://img.shields.io/badge/Matrix-%23rust--sci%3Amatrix.org-lightgrey :alt: Matrix chat at #rust-sci:matrix.org .. _matrix-chat: https://matrix.to/#/#rust-sci:matrix.org .. |irc| image:: https://img.shields.io/badge/IRC-%23rust--sci%20on%20OFTC-lightgrey :alt: IRC at #rust-sci on OFTC .. _irc: https://webchat.oftc.net/?channels=rust-sci Highlights ---------- - Generic 1, 2, ..., *n*-dimensional arrays - Owned arrays and array views - Slicing, also with arbitrary step size, and negative indices to mean elements from the end of the axis. - Views and subviews of arrays; iterators that yield subviews. Status and Lookout ------------------ - Still iterating on and evolving the crate + The crate is continuously developing, and breaking changes are expected during evolution from version to version. We adopt the newest stable rust features if we need them. - Performance: + Prefer higher order methods and arithmetic operations on arrays first, then iteration, and as a last priority using indexed algorithms. + Efficient floating point matrix multiplication even for very large matrices; can optionally use BLAS to improve it further. Crate Feature Flags ------------------- The following crate feature flags are available. They are configured in your `Cargo.toml`. - ``std`` - Rust standard library (enabled by default) - This crate can be used without the standard library by disabling the default `std` feature. To do so, use this in your `Cargo.toml`: :: [dependencies] ndarray = { version = "0.x.y", default-features = false } - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` and `std_axis` methods are only available when `std` is enabled. - ``serde`` - Enables serialization support for serde 1.x - ``rayon`` - Enables parallel iterators, parallelized methods and ``par_azip!``. - Implies std - ``approx`` - Implementations of traits from version 0.5 of the [`approx`] crate. - ``blas`` - Enable transparent BLAS support for matrix multiplication. Uses ``blas-src`` for pluggable backend, which needs to be configured separately (see below). - ``matrixmultiply-threading`` - Enable the ``threading`` feature in the matrixmultiply package - ``portable-atomic-critical-section`` - Whether ``portable-atomic`` should use ``critical-section`` How to use with cargo --------------------- :: [dependencies] ndarray = "0.16.0" How to enable BLAS integration ------------------------------ Blas integration is an optional add-on. Without BLAS, ndarray uses the ``matrixmultiply`` crate for matrix multiplication for ``f64`` and ``f32`` arrays (and it's always enabled as a fallback since it supports matrices of arbitrary strides in both dimensions). Depend and link to ``blas-src`` directly to pick a blas provider. Ndarray presently requires a blas provider that provides the ``cblas-sys`` interface. If further feature selection is wanted or needed then you might need to depend directly on the backend crate's source too. The backend version **must** be the one that ``blas-src`` also depends on. An example configuration using system openblas is shown below. Note that only end-user projects (not libraries) should select provider:: [dependencies] ndarray = { version = "0.16.0", features = ["blas"] } blas-src = { version = "0.10", features = ["openblas"] } openblas-src = { version = "0.10", features = ["cblas", "system"] } Using system-installed dependencies can save a long time building dependencies. An example configuration using (compiled) netlib is shown below anyway:: [dependencies] ndarray = { version = "0.16.0", features = ["blas"] } blas-src = { version = "0.10.0", default-features = false, features = ["netlib"] } When this is done, your program must also link to ``blas_src`` by using it or explicitly including it in your code:: extern crate blas_src; The following versions have been verified to work together. For ndarray 0.15 or later, there is no tight coupling to the ``blas-src`` version, so version selection is more flexible. =========== ============ ================ ============== ``ndarray`` ``blas-src`` ``openblas-src`` ``netlib-src`` =========== ============ ================ ============== 0.16 0.10 0.10 0.8 0.15 0.8 0.10 0.8 0.15 0.7 0.9 0.8 0.14 0.6.1 0.9.0 0.13 0.2.0 0.6.0 =========== ============ ================ ============== Recent Changes -------------- See `RELEASES.md <./RELEASES.md>`_. License ======= Dual-licensed to be compatible with the Rust project. Licensed under the Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 or the MIT license http://opensource.org/licenses/MIT, at your option. This file may not be copied, modified, or distributed except according to those terms. ndarray-0.16.1/RELEASES.md000064400000000000000000002163221046102023000131040ustar 00000000000000Version 0.16.1 (2024-08-14) =========================== - Refactor and simplify BLAS gemm call further by [@bluss](https://github.com/bluss) [#1421](https://github.com/rust-ndarray/ndarray/pull/1421) - Fix infinite recursion and off-by-one error in triu/tril by [@akern40](https://github.com/akern40) [#1418](https://github.com/rust-ndarray/ndarray/pull/1418) - Fix using BLAS for all compatible cases of memory layout by [@bluss](https://github.com/bluss) [#1419](https://github.com/rust-ndarray/ndarray/pull/1419) - Use PR check instead of Merge Queue, and check rustdoc by [@bluss](https://github.com/bluss) [#1420](https://github.com/rust-ndarray/ndarray/pull/1420) - Make iterators covariant in element type by [@bluss](https://github.com/bluss) [#1417](https://github.com/rust-ndarray/ndarray/pull/1417) Version 0.16.0 (2024-08-03) =========================== Featured Changes ---------------- - Better shape: Deprecate reshape, into_shape by [@bluss](https://github.com/bluss) [#1310](https://github.com/rust-ndarray/ndarray/pull/1310)
`.into_shape()` **is now deprecated**. Use `.into_shape_with_order()` or `.to_shape()` instead, which don't have `into_shape`'s drawbacks. New Features and Improvements ----------------------------- - Check for aliasing in `RawViewMut::from_shape_ptr` with a debug assertion by [@bluss](https://github.com/bluss) [#1413](https://github.com/rust-ndarray/ndarray/pull/1413) - Allow aliasing in ArrayView::from_shape by [@bluss](https://github.com/bluss) [#1410](https://github.com/rust-ndarray/ndarray/pull/1410) - Remove deprecations from 0.15.x by [@bluss](https://github.com/bluss) [#1409](https://github.com/rust-ndarray/ndarray/pull/1409) - Make `CowArray` an owned storage array, require Clone bound for `into_shared` by [@jturner314](https://github.com/jturner314) [#1028](https://github.com/rust-ndarray/ndarray/pull/1028) - Change `NdProducer::Dim` of `axis_windows()` to `Ix1` by [@jonasBoss](https://github.com/jonasBoss) [#1305](https://github.com/rust-ndarray/ndarray/pull/1305) - Add `squeeze()` to dynamic dimension arrays by [@barakugav](https://github.com/barakugav) [#1396](https://github.com/rust-ndarray/ndarray/pull/1396) - Add `flatten`, `flatten_with_order` and `into_flat` to arrays by [@barakugav](https://github.com/barakugav) [#1397](https://github.com/rust-ndarray/ndarray/pull/1397) - Make compatible with thumbv6m-none-eabi by [@BjornTheProgrammer](https://github.com/BjornTheProgrammer) [#1384](https://github.com/rust-ndarray/ndarray/pull/1384) - `is_unique` for `ArcArray` by [@daniellga](https://github.com/daniellga) [#1399](https://github.com/rust-ndarray/ndarray/pull/1399) - Add `triu` and `tril` methods directly to ArrayBase by [@akern40](https://github.com/akern40) [#1386](https://github.com/rust-ndarray/ndarray/pull/1386) - Fix styling of the BLAS integration heading. by [@adamreichold](https://github.com/adamreichold) [#1390](https://github.com/rust-ndarray/ndarray/pull/1390) - Implement `product_axis` by [@akern40](https://github.com/akern40) [#1387](https://github.com/rust-ndarray/ndarray/pull/1387) - Add reserve method for owned arrays by [@ssande7](https://github.com/ssande7) [#1268](https://github.com/rust-ndarray/ndarray/pull/1268) - Use inline on spit_at and smaller methods by [@bluss](https://github.com/bluss) [#1381](https://github.com/rust-ndarray/ndarray/pull/1381) - Update to Approx 0.5 by [@bluss](https://github.com/bluss) [#1380](https://github.com/rust-ndarray/ndarray/pull/1380) - Add .into_raw_vec_with_offset() and deprecate .into_raw_vec() by [@bluss](https://github.com/bluss) [#1379](https://github.com/rust-ndarray/ndarray/pull/1379) - Add additional array -> array view conversions by [@bluss](https://github.com/bluss) [#1130](https://github.com/rust-ndarray/ndarray/pull/1130) - implement DoubleEndedIterator for 1d `LanesIter` by [@Muthsera](https://github.com/Muthsera) [#1237](https://github.com/rust-ndarray/ndarray/pull/1237) - Add Zip::any by [@nilgoyette](https://github.com/nilgoyette) [#1228](https://github.com/rust-ndarray/ndarray/pull/1228) - Make the aview0, aview1, and aview2 free functions be const fns by [@jturner314](https://github.com/jturner314) [#1132](https://github.com/rust-ndarray/ndarray/pull/1132) - Add missing safety checks to `From<&[[A; N]]> for ArrayView` and `From<&mut [[A; N]]> for ArrayViewMut` by [@jturner314](https://github.com/jturner314) [#1131](https://github.com/rust-ndarray/ndarray/pull/1131) - derived Debug for Iter and IterMut by [@biskwikman](https://github.com/biskwikman) [#1353](https://github.com/rust-ndarray/ndarray/pull/1353) - Fix Miri errors for WindowsIter and ExactChunksIter/Mut by [@jturner314](https://github.com/jturner314) [#1142](https://github.com/rust-ndarray/ndarray/pull/1142) - Fix Miri failure with -Zmiri-tag-raw-pointers by [@jturner314](https://github.com/jturner314) [#1138](https://github.com/rust-ndarray/ndarray/pull/1138) - Track-caller panics by [@xd009642](https://github.com/xd009642) [#975](https://github.com/rust-ndarray/ndarray/pull/975) - Add slice_axis_move method by [@jturner314](https://github.com/jturner314) [#1211](https://github.com/rust-ndarray/ndarray/pull/1211) - iterators: Re-export IntoIter by [@bluss](https://github.com/bluss) [#1370](https://github.com/rust-ndarray/ndarray/pull/1370) - Fix unsafe blocks in `s![]` macro by [@jturner314](https://github.com/jturner314) [#1196](https://github.com/rust-ndarray/ndarray/pull/1196) - Fix comparison with NumPy of slicing with negative step by [@venkat0791](https://github.com/venkat0791) [#1319](https://github.com/rust-ndarray/ndarray/pull/1319) - Updated Windows `base` Computations to be Safer by [@LazaroHurtado](https://github.com/LazaroHurtado) [#1297](https://github.com/rust-ndarray/ndarray/pull/1297) - Update README-quick-start.md by [@fumseckk](https://github.com/fumseckk) [#1246](https://github.com/rust-ndarray/ndarray/pull/1246) - Added stride support to `Windows` by [@LazaroHurtado](https://github.com/LazaroHurtado) [#1249](https://github.com/rust-ndarray/ndarray/pull/1249) - Added select example to numpy user docs by [@WillAyd](https://github.com/WillAyd) [#1294](https://github.com/rust-ndarray/ndarray/pull/1294) - Add both approx features to the readme by [@nilgoyette](https://github.com/nilgoyette) [#1289](https://github.com/rust-ndarray/ndarray/pull/1289) - Add NumPy examples combining slicing and assignment by [@jturner314](https://github.com/jturner314) [#1210](https://github.com/rust-ndarray/ndarray/pull/1210) - Fix contig check for single element arrays by [@bluss](https://github.com/bluss) [#1362](https://github.com/rust-ndarray/ndarray/pull/1362) - Export Linspace and Logspace iterators by [@johann-cm](https://github.com/johann-cm) [#1348](https://github.com/rust-ndarray/ndarray/pull/1348) - Use `clone_from()` in two places by [@ChayimFriedman2](https://github.com/ChayimFriedman2) [#1347](https://github.com/rust-ndarray/ndarray/pull/1347) - Update README-quick-start.md by [@joelchen](https://github.com/joelchen) [#1344](https://github.com/rust-ndarray/ndarray/pull/1344) - Provide element-wise math functions for floats by [@KmolYuan](https://github.com/KmolYuan) [#1042](https://github.com/rust-ndarray/ndarray/pull/1042) - Improve example in doc for columns method by [@gkobeaga](https://github.com/gkobeaga) [#1221](https://github.com/rust-ndarray/ndarray/pull/1221) - Fix description of stack! in quick start by [@jturner314](https://github.com/jturner314) [#1156](https://github.com/rust-ndarray/ndarray/pull/1156) Tests, CI and Maintainer tasks ------------------------------ - CI: require rustfmt, nostd by [@bluss](https://github.com/bluss) [#1411](https://github.com/rust-ndarray/ndarray/pull/1411) - Prepare changelog for 0.16.0 by [@bluss](https://github.com/bluss) [#1401](https://github.com/rust-ndarray/ndarray/pull/1401) - Organize dependencies with workspace = true (cont.) by [@bluss](https://github.com/bluss) [#1407](https://github.com/rust-ndarray/ndarray/pull/1407) - Update to use dep: for features by [@bluss](https://github.com/bluss) [#1406](https://github.com/rust-ndarray/ndarray/pull/1406) - Organize the workspace of test crates a bit better by [@bluss](https://github.com/bluss) [#1405](https://github.com/rust-ndarray/ndarray/pull/1405) - Add rustfmt commit to ignored revisions for git blame by [@lucascolley](https://github.com/lucascolley) [#1376](https://github.com/rust-ndarray/ndarray/pull/1376) - The minimum amount of work required to fix our CI by [@adamreichold](https://github.com/adamreichold) [#1388](https://github.com/rust-ndarray/ndarray/pull/1388) - Fixed broke continuous integration badge by [@juhotuho10](https://github.com/juhotuho10) [#1382](https://github.com/rust-ndarray/ndarray/pull/1382) - Use mold linker to speed up ci by [@bluss](https://github.com/bluss) [#1378](https://github.com/rust-ndarray/ndarray/pull/1378) - Add rustformat config and CI by [@bluss](https://github.com/bluss) [#1375](https://github.com/rust-ndarray/ndarray/pull/1375) - Add docs to CI by [@jturner314](https://github.com/jturner314) [#925](https://github.com/rust-ndarray/ndarray/pull/925) - Test using cargo-careful by [@bluss](https://github.com/bluss) [#1371](https://github.com/rust-ndarray/ndarray/pull/1371) - Further ci updates - numeric tests, and run all tests on PRs by [@bluss](https://github.com/bluss) [#1369](https://github.com/rust-ndarray/ndarray/pull/1369) - Setup ci so that most checks run in merge queue only by [@bluss](https://github.com/bluss) [#1368](https://github.com/rust-ndarray/ndarray/pull/1368) - Use merge queue by [@bluss](https://github.com/bluss) [#1367](https://github.com/rust-ndarray/ndarray/pull/1367) - Try to make the master branch shipshape by [@adamreichold](https://github.com/adamreichold) [#1286](https://github.com/rust-ndarray/ndarray/pull/1286) - Update ci - run cross tests only on master by [@bluss](https://github.com/bluss) [#1366](https://github.com/rust-ndarray/ndarray/pull/1366) - ndarray_for_numpy_users some example to code not pointed out to clippy by [@higumachan](https://github.com/higumachan) [#1360](https://github.com/rust-ndarray/ndarray/pull/1360) - Fix minimum rust version mismatch in lib.rs by [@HoKim98](https://github.com/HoKim98) [#1352](https://github.com/rust-ndarray/ndarray/pull/1352) - Fix MSRV build by pinning crossbeam crates. by [@adamreichold](https://github.com/adamreichold) [#1345](https://github.com/rust-ndarray/ndarray/pull/1345) - Fix new rustc lints to make the CI pass. by [@adamreichold](https://github.com/adamreichold) [#1337](https://github.com/rust-ndarray/ndarray/pull/1337) - Make Clippy happy and fix MSRV build by [@adamreichold](https://github.com/adamreichold) [#1320](https://github.com/rust-ndarray/ndarray/pull/1320) - small formatting fix in README.rst by [@podusowski](https://github.com/podusowski) [#1199](https://github.com/rust-ndarray/ndarray/pull/1199) - Fix CI failures (mostly linting with clippy) by [@aganders3](https://github.com/aganders3) [#1171](https://github.com/rust-ndarray/ndarray/pull/1171) - Remove doc(hidden) attr from items in trait impls by [@jturner314](https://github.com/jturner314) [#1165](https://github.com/rust-ndarray/ndarray/pull/1165) Version 0.15.6 (2022-07-30) =========================== New features ------------ - Add `get_ptr` and `get_mut_ptr` methods for getting an element's pointer from an index, by [@adamreichold]. https://github.com/rust-ndarray/ndarray/pull/1151 Other changes ------------- - Various fixes to resolve compiler and Clippy warnings/errors, by [@aganders3] and [@jturner314]. https://github.com/rust-ndarray/ndarray/pull/1171 - Fix description of `stack!` in quick start docs, by [@jturner314]. Thanks to [@HyeokSuLee] for pointing out the issue. https://github.com/rust-ndarray/ndarray/pull/1156 - Add MSRV to `Cargo.toml`. https://github.com/rust-ndarray/ndarray/pull/1191 Version 0.15.5 (2022-07-30) =========================== Enhancements ------------ - The `s!` macro now works in `no_std` environments, by [@makotokato]. https://github.com/rust-ndarray/ndarray/pull/1154 Other changes ------------- - Improve docs and fix typos, by [@steffahn] and [@Rikorose]. https://github.com/rust-ndarray/ndarray/pull/1134
https://github.com/rust-ndarray/ndarray/pull/1164 Version 0.15.4 (2021-11-23) =========================== The Dr. Turner release 🚀 New features ------------ - Complex matrix multiplication now uses BLAS ``cgemm``/``zgemm`` when enabled (and matrix layout allows), by [@ethanhs]. https://github.com/rust-ndarray/ndarray/pull/1106 - Use `matrixmultiply` as fallback for complex matrix multiplication when BLAS is not available or the matrix layout requires it by [@bluss] https://github.com/rust-ndarray/ndarray/pull/1118 - Add ``into/to_slice_memory_order`` methods for views, lifetime-preserving versions of existing similar methods by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1015 - ``kron`` function for Kronecker product by [@ethanhs]. https://github.com/rust-ndarray/ndarray/pull/1105 - ``split_complex`` method for splitting complex arrays into separate real and imag view parts by [@jturner314] and [@ethanhs]. https://github.com/rust-ndarray/ndarray/pull/1107 - New method ``try_into_owned_nocopy`` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1022 - New producer and iterable ``axis_windows`` by [@VasanthakumarV] and [@jturner314]. https://github.com/rust-ndarray/ndarray/pull/1022 - New method ``Zip::par_fold`` by [@adamreichold] https://github.com/rust-ndarray/ndarray/pull/1095 - New constructor ``from_diag_elem`` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1076 - ``Parallel::with_min_len`` method for parallel iterators by [@adamreichold] https://github.com/rust-ndarray/ndarray/pull/1081 - Allocation-preserving map function ``.mapv_into_any()`` added by [@benkay86] Enhancements ------------ - Improve performance of ``.sum_axis()`` for some cases by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1061 Bug fixes --------- - Fix error in calling dgemv (matrix-vector multiplication) with BLAS and broadcasted arrays, by [@jturner314]. https://github.com/rust-ndarray/ndarray/pull/1088 API changes ----------- - Support approx 0.5 partially alongside the already existing approx 0.4 support. New feature flag is `approx-0_5`, by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1025 - Slice and reference-to-array conversions to CowArray added for by [@jturner314]. https://github.com/rust-ndarray/ndarray/pull/1038 - Allow trailing comma in stack and concatenate macros by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1044 - ``Zip`` now has a ``must_use`` marker to help users by [@adamreichold] https://github.com/rust-ndarray/ndarray/pull/1082 Other changes ------------- - Fixing the crates.io badge on github by [@atouchet] https://github.com/rust-ndarray/ndarray/pull/1104 - Use intra-doc links in docs by [@LeSeulArtichaut] https://github.com/rust-ndarray/ndarray/pull/1033 - Clippy fixes by [@adamreichold] https://github.com/rust-ndarray/ndarray/pull/1092
https://github.com/rust-ndarray/ndarray/pull/1091 - Minor fixes in links and punctuation in docs by [@jimblandy] https://github.com/rust-ndarray/ndarray/pull/1056 - Minor fixes in docs by [@chohner] https://github.com/rust-ndarray/ndarray/pull/1119 - Update tests to quickcheck 1.0 by [@bluss] https://github.com/rust-ndarray/ndarray/pull/1114 Version 0.15.3 (2021-06-05) =========================== New features ------------ - New methods `.last/_mut()` for arrays and array views by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1013 Bug fixes --------- - Fix `as_slice_memory_order_mut()` so that it never changes strides (the memory layout) of the array when called. This was a bug that impacted `ArcArray` (and for example not `Array` or `ArrayView/Mut`), and multiple methods on `ArcArray` that use `as_slice_memory_order_mut` (for example `map_mut`). Fix by [@jturner314]. https://github.com/rust-ndarray/ndarray/pull/1019 API changes ----------- - Array1 now implements `From>` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1016 - ArcArray now implements `From>` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1021 - CowArray now implements RawDataSubst by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1020 Other changes ------------- - Mention unsharing in `.as_mut_ptr` docs by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/1017 - Clarify and fix minor errors in push/append method docs by [@bluss] f21c668a - Fix several warnings in doc example code by [@bluss] https://github.com/rust-ndarray/ndarray/pull/1009 Version 0.15.2 (2021-05-17 🇳🇴) ================================ New features ------------ - New methods for growing/appending to owned `Array`s. These methods allow building an array efficiently chunk by chunk. By [@bluss]. - `.push_row()`, `.push_column()` - `.push(axis, array)`, `.append(axis, array)` `stack`, `concatenate` and `.select()` now support all `Clone`-able elements as a result. https://github.com/rust-ndarray/ndarray/pull/932
https://github.com/rust-ndarray/ndarray/pull/990 - New reshaping method `.to_shape(...)`, called with new shape and optional ordering parameter, this is the first improvement for reshaping in terms of added features and increased consistency, with more to come. By [@bluss]. https://github.com/rust-ndarray/ndarray/pull/982 - `Array` now implements a by-value iterator, by [@bluss]. https://github.com/rust-ndarray/ndarray/pull/986 - New methods `.move_into()` and `.move_into_uninit()` which allow assigning into an array by moving values from an array into another, by [@bluss]. https://github.com/rust-ndarray/ndarray/pull/932
https://github.com/rust-ndarray/ndarray/pull/997 - New method `.remove_index()` for owned arrays by [@bluss] https://github.com/rust-ndarray/ndarray/pull/967 - New constructor `build_uninit` which makes it easier to initialize uninitialized arrays in a way that's generic over all owned array kinds. By [@bluss]. https://github.com/rust-ndarray/ndarray/pull/1001 Enhancements ------------ - Preserve the allocation of the input array in some more cases for arithmetic ops by [@SparrowLii] https://github.com/rust-ndarray/ndarray/pull/963 - Improve broadcasting performance for &array + &array arithmetic ops by [@SparrowLii] https://github.com/rust-ndarray/ndarray/pull/965 Bug fixes --------- - Fix an error in construction of empty array with negative strides, by [@jturner314]. https://github.com/rust-ndarray/ndarray/pull/998 - Fix minor performance bug with loop order selection in Zip by [@bluss] https://github.com/rust-ndarray/ndarray/pull/977 API changes ----------- - Add dimension getters to `Shape` and `StrideShape` by [@stokhos] https://github.com/rust-ndarray/ndarray/pull/978 Other changes ------------- - Rustdoc now uses the ndarray logo that [@jturner314] created previously https://github.com/rust-ndarray/ndarray/pull/981 - Minor doc changes by [@stokhos], [@cassiersg] and [@jturner314] https://github.com/rust-ndarray/ndarray/pull/968
https://github.com/rust-ndarray/ndarray/pull/971
https://github.com/rust-ndarray/ndarray/pull/974 - A little refactoring to reduce generics bloat in a few places by [@bluss]. https://github.com/rust-ndarray/ndarray/pull/1004 Version 0.15.1 (2021-03-29) =========================== Enhancements ------------ - Arrays and views now have additional PartialEq impls so that it's possible to compare arrays with references to arrays and vice versa by [@bluss] https://github.com/rust-ndarray/ndarray/pull/958 Bug fixes --------- - Fix panic in creation of `.windows()` producer from negative stride array by [@bluss] https://github.com/rust-ndarray/ndarray/pull/957 Other changes ------------- - Update BLAS documentation further by @bluss https://github.com/rust-ndarray/ndarray/pull/955
https://github.com/rust-ndarray/ndarray/pull/959 Version 0.15.0 (2021-03-25) =========================== New features ------------ - Support inserting new axes while slicing by [@jturner314]. This is an example: ```rust let view = arr.slice(s![.., -1, 2..;-1, NewAxis]); ``` https://github.com/rust-ndarray/ndarray/pull/570 - Support two-sided broadcasting in arithmetic operations with arrays by [@SparrowLii] This now allows, for example, addition of a 3 x 1 with a 1 x 3 array; the operands are in this case broadcast to 3 x 3 which is the shape of the result. Note that this means that a new trait bound is required in some places when mixing dimensionality types of arrays in arithmetic operations. https://github.com/rust-ndarray/ndarray/pull/898 - Support for compiling ndarray as `no_std` (using core and alloc) by [@xd009642] and [@bluss] https://github.com/rust-ndarray/ndarray/pull/861
https://github.com/rust-ndarray/ndarray/pull/889 - New methods `.cell_view()` and `ArrayViewMut::into_cell_view` that enable new ways of working with array elements as if they were in Cells - setting elements through shared views and broadcast views, by [@bluss]. https://github.com/rust-ndarray/ndarray/pull/877 - New methods `slice_each_axis/_mut/_inplace` that make it easier to slice a dynamic number of axes in some situations, by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/913 - New method `a.assign_to(b)` with the inverse argument order compared to the existing `b.assign(a)` and some extra features like assigning into uninitialized arrays, By [@bluss]. https://github.com/rust-ndarray/ndarray/pull/947 - New methods `.std()` and `.var()` for standard deviation and variance by [@kdubovikov] https://github.com/rust-ndarray/ndarray/pull/790 Enhancements ------------ - Ndarray can now correctly determine that arrays can be contiguous, even if they have negative strides, by [@SparrowLii] https://github.com/rust-ndarray/ndarray/pull/885
https://github.com/rust-ndarray/ndarray/pull/948 - Improvements to `map_inplace` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/911 - `.into_dimensionality` performance was improved for the `IxDyn` to `IxDyn` case by [@bluss] https://github.com/rust-ndarray/ndarray/pull/906 - Improved performance for scalar + &array and &array + scalar operations by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/890 API changes ----------- - New constructors `Array::from_iter` and `Array::from_vec` by [@bluss]. No new functionality, just that these constructors are available without trait imports. https://github.com/rust-ndarray/ndarray/pull/921 - `NdProducer::raw_dim` is now a documented method by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/918 - `AxisDescription` is now a struct with field names, not a tuple struct by [@jturner314]. Its accessor methods are now deprecated. https://github.com/rust-ndarray/ndarray/pull/915 - Methods for array comparison `abs_diff_eq` and `relative_eq` are now exposed as inherent methods too (no trait import needed), still under the approx feature flag by [@bluss] https://github.com/rust-ndarray/ndarray/pull/946 - Changes to the slicing-related types and macro by [@jturner314] and [@bluss]: - Remove the `Dimension::SliceArg` associated type, and add a new `SliceArg` trait for this purpose. - Change the return type of the `s![]` macro to an owned `SliceInfo` rather than a reference. - Replace the `SliceOrIndex` enum with `SliceInfoElem`, which has an additional `NewAxis` variant and does not have a `step_by` method. - Change the type parameters of `SliceInfo` in order to support the `NewAxis` functionality and remove some tricky `unsafe` code. - Mark the `SliceInfo::new` method as `unsafe`. The new implementations of `TryFrom` can be used as a safe alternative. - Remove the `AsRef> for SliceInfo` implementation. Add the similar `From<&'a SliceInfo> for SliceInfo<&'a [SliceInfoElem], Din, Dout>` conversion as an alternative. - Change the *expr* `;` *step* case in the `s![]` macro to error at compile time if an unsupported type for *expr* is used, instead of panicking at runtime. https://github.com/rust-ndarray/ndarray/pull/570
https://github.com/rust-ndarray/ndarray/pull/940
https://github.com/rust-ndarray/ndarray/pull/943
https://github.com/rust-ndarray/ndarray/pull/945
- Removed already deprecated methods by [@bluss]: - Remove deprecated `.all_close()` - use approx feature and methods like `.abs_diff_eq` instead - Mark `.scalar_sum()` as deprecated - use `.sum()` instead - Remove deprecated `DataClone` - use `Data + RawDataClone` instead - Remove deprecated `ArrayView::into_slice` - use `to_slice()` instead. https://github.com/rust-ndarray/ndarray/pull/874 - Remove already deprecated methods: rows, cols (for row and column count; the new names are nrows and ncols) by [@bluss] https://github.com/rust-ndarray/ndarray/pull/872 - Renamed `Zip` methods by [@bluss] and [@SparrowLii]: - `apply` -> `for_each` - `apply_collect` -> `map_collect` - `apply_collect_into` -> `map_collect_into` - (`par_` prefixed methods renamed accordingly) https://github.com/rust-ndarray/ndarray/pull/894
https://github.com/rust-ndarray/ndarray/pull/904
- Deprecate `Array::uninitialized` and revamped its replacement by [@bluss] Please use new new `Array::uninit` which is based on `MaybeUninit` (renamed from `Array::maybe_uninit`, the old name is also deprecated). https://github.com/rust-ndarray/ndarray/pull/902
https://github.com/rust-ndarray/ndarray/pull/876 - Renamed methods (old names are now deprecated) by [@bluss] and [@jturner314] - `genrows/_mut` -> `rows/_mut` - `gencolumns/_mut` -> `columns/_mut` - `stack_new_axis` -> `stack` (the new name already existed) - `visit` -> `for_each` https://github.com/rust-ndarray/ndarray/pull/872
https://github.com/rust-ndarray/ndarray/pull/937
https://github.com/rust-ndarray/ndarray/pull/907
- Updated `matrixmultiply` dependency to 0.3.0 by [@bluss] and adding new feature flag `matrixmultiply-threading` to enable its threading https://github.com/rust-ndarray/ndarray/pull/888
https://github.com/rust-ndarray/ndarray/pull/938
- Updated `num-complex` dependency to 0.4.0 by [@bluss] https://github.com/rust-ndarray/ndarray/pull/952 Bug fixes --------- - Fix `Zip::indexed` for the 0-dimensional case by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/862 - Fix bug in layout computation that broke parallel collect to f-order array in some circumstances by [@bluss] https://github.com/rust-ndarray/ndarray/pull/900 - Fix an unwanted panic in shape overflow checking by [@bluss] https://github.com/rust-ndarray/ndarray/pull/855 - Mark the `SliceInfo::new` method as `unsafe` due to the requirement that `indices.as_ref()` always return the same value when called multiple times, by [@bluss] and [@jturner314] https://github.com/rust-ndarray/ndarray/pull/570 Other changes ------------- - It was changed how we integrate with BLAS and `blas-src`. Users of BLAS need to read the README for the updated instructions. Ndarray itself no longer has public dependency on `blas-src`. Changes by [@bluss]. https://github.com/rust-ndarray/ndarray/pull/891
https://github.com/rust-ndarray/ndarray/pull/951 - Various improvements to tests and CI by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/934
https://github.com/rust-ndarray/ndarray/pull/924
- The `sort-axis.rs` example file's implementation of sort was bugfixed and now has tests, by [@dam5h] and [@bluss] https://github.com/rust-ndarray/ndarray/pull/916
https://github.com/rust-ndarray/ndarray/pull/930 - We now link to the #rust-sci room on matrix in the readme by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/619 - Internal cleanup with builder-like methods for creating arrays by [@bluss] https://github.com/rust-ndarray/ndarray/pull/908 - Implementation fix of `.swap(i, j)` by [@bluss] https://github.com/rust-ndarray/ndarray/pull/903 - Minimum supported Rust version (MSRV) is Rust 1.49. https://github.com/rust-ndarray/ndarray/pull/902 - Minor improvements to docs by [@insideoutclub] https://github.com/rust-ndarray/ndarray/pull/887 Version 0.14.0 (2020-11-28) =========================== New features ------------ - `Zip::apply_collect` and `Zip::par_apply_collect` now support all elements (not just `Copy` elements) by [@bluss] https://github.com/rust-ndarray/ndarray/pull/814 https://github.com/rust-ndarray/ndarray/pull/817 - New function `stack` by [@andrei-papou] https://github.com/rust-ndarray/ndarray/pull/844 https://github.com/rust-ndarray/ndarray/pull/850 Enhancements ------------ - Handle inhomogeneous shape inputs better in Zip, in practice: guess better whether to prefer c- or f-order for the inner loop by [@bluss] https://github.com/rust-ndarray/ndarray/pull/809 - Improve code sharing in some commonly used code by [@bluss] https://github.com/rust-ndarray/ndarray/pull/819 API changes ----------- - The **old function** `stack` has been renamed to `concatenate`. A new function `stack` with numpy-like semantics have taken its place. Old usages of `stack` should change to use `concatenate`. `concatenate` produces an array with the same number of axes as the inputs. `stack` produces an array that has one more axis than the inputs. This change was unfortunately done without a deprecation period, due to the long period between releases. https://github.com/rust-ndarray/ndarray/pull/844 https://github.com/rust-ndarray/ndarray/pull/850 - Enum ErrorKind is now properly non-exhaustive and has lost its old placeholder invalid variant. By [@Zuse64] https://github.com/rust-ndarray/ndarray/pull/848 - Remove deprecated items: - RcArray (deprecated alias for ArcArray) - Removed `subview_inplace` use `collapse_axis` - Removed `subview_mut` use `index_axis_mut` - Removed `into_subview` use `index_axis_move` - Removed `subview` use `index_axis` - Removed `slice_inplace` use `slice_collapse` - Undeprecated `remove_axis` because its replacement is hard to find out on your own. - Update public external dependencies to new versions by [@Eijebong] and [@bluss] - num-complex 0.3 - approx 0.4 (optional) - blas-src 0.6.1 and openblas-src 0.9.0 (optional) https://github.com/rust-ndarray/ndarray/pull/810 https://github.com/rust-ndarray/ndarray/pull/851 Other changes ------------- - Minor doc fixes by [@acj] https://github.com/rust-ndarray/ndarray/pull/834 - Minor doc fixes by [@xd009642] https://github.com/rust-ndarray/ndarray/pull/847 - The minimum required rust version is Rust 1.42. - Release management by [@bluss] Version 0.13.1 (2020-04-21) =========================== New features ------------ - New *amazing* slicing methods `multi_slice_*` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/717 - New method `.cast()` for raw views by [@bluss] https://github.com/rust-ndarray/ndarray/pull/734 - New aliases `ArcArray1`, `ArcArray2` by [@d-dorazio] https://github.com/rust-ndarray/ndarray/pull/741 - New array constructor `from_shape_simple_fn` by [@bluss] https://github.com/rust-ndarray/ndarray/pull/728 - `Dimension::Larger` now requires `RemoveAxis` by [@TheLortex] https://github.com/rust-ndarray/ndarray/pull/792 - New methods for collecting Zip into an array by [@bluss] https://github.com/rust-ndarray/ndarray/pull/797 - New `Array::maybe_uninit` and `.assume_init()` by [@bluss] https://github.com/rust-ndarray/ndarray/pull/803 Enhancements ------------ - Remove itertools as dependency by [@bluss] https://github.com/rust-ndarray/ndarray/pull/730 - Improve `zip_mut_with` (and thus arithmetic ops) for f-order arrays by [@nilgoyette] https://github.com/rust-ndarray/ndarray/pull/754 - Implement `fold` for `IndicesIter` by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/733 - New Quick Start readme by [@lifuyang] https://github.com/rust-ndarray/ndarray/pull/785 API changes ----------- - Remove alignment restriction on raw views by [@jturner314] https://github.com/rust-ndarray/ndarray/pull/738 Other changes ------------- - Fix documentation in ndarray for numpy users by [@jturner314] - Improve blas version documentation by [@jturner314] - Doc improvements by [@mockersf] https://github.com/rust-ndarray/ndarray/pull/751 - Doc and lint related improvements by [@viniciusd] https://github.com/rust-ndarray/ndarray/pull/750 - Minor fixes related to best practices for unsafe code by [@bluss] https://github.com/rust-ndarray/ndarray/pull/799 https://github.com/rust-ndarray/ndarray/pull/802 - Release management by [@bluss] Version 0.13.0 (2019-09-23) =========================== New features ------------ - `ndarray-parallel` is merged into `ndarray`. Use the `rayon` feature-flag to get access to parallel iterators and other parallelized methods. ([#563](https://github.com/rust-ndarray/ndarray/pull/563/files) by [@bluss]) - Add `logspace` and `geomspace` constructors ([#617](https://github.com/rust-ndarray/ndarray/pull/617) by [@JP-Ellis]) - Implement approx traits for `ArrayBase`. They can be enabled using the `approx` feature-flag. ([#581](https://github.com/rust-ndarray/ndarray/pull/581) by [@jturner314]) - Add `mean` method ([#580](https://github.com/rust-ndarray/ndarray/pull/580) by [@LukeMathWalker]) - Add `Zip::all` to check if all elements satisfy a predicate ([#615](https://github.com/rust-ndarray/ndarray/pull/615) by [@mneumann]) - Add `RawArrayView` and `RawArrayViewMut` types and `RawData`, `RawDataMut`, and `RawDataClone` traits ([#496](https://github.com/rust-ndarray/ndarray/pull/496) by [@jturner314]) - Add `CowArray`, `C`lone `o`n `write` array ([#632](https://github.com/rust-ndarray/ndarray/pull/632) by [@jturner314] and [@andrei-papou]) - Add `as_standard_layout` to `ArrayBase`: it takes an array by reference and returns a `CoWArray` in standard layout ([#616](https://github.com/rust-ndarray/ndarray/pull/616) by [@jturner314] and [@andrei-papou]) - Add `Array2::from_diag` method to create 2D arrays from a diagonal ([#673](https://github.com/rust-ndarray/ndarray/pull/673) by [@rth]) - Add `fold` method to `Zip` ([#684](https://github.com/rust-ndarray/ndarray/pull/684) by [@jturner314]) - Add `split_at` method to `AxisChunksIter/Mut` ([#691](https://github.com/rust-ndarray/ndarray/pull/691) by [@jturner314]) - Implement parallel iteration for `AxisChunksIter/Mut` ([#639](https://github.com/rust-ndarray/ndarray/pull/639) by [@nitsky]) - Add `into_scalar` method to `ArrayView0` and `ArrayViewMut0` ([#700](https://github.com/rust-ndarray/ndarray/pull/700) by [@LukeMathWalker]) - Add `accumulate_axis_inplace` method to `ArrayBase` ([#611](https://github.com/rust-ndarray/ndarray/pull/611) by [@jturner314] and [@bluss]) - Add the `array!`, `azip!`, and `s!` macros to `ndarray::prelude` ([#517](https://github.com/rust-ndarray/ndarray/pull/517) by [@jturner314]) Enhancements ------------ - Improve performance for matrix multiplications when using the pure-Rust backend thanks to `matrix-multiply:v0.2` (leverage SIMD instructions on x86-64 with runtime feature detection) ([#556](https://github.com/rust-ndarray/ndarray/pull/556) by [@bluss]) - Improve performance of `fold` for iterators ([#574](https://github.com/rust-ndarray/ndarray/pull/574) by [@jturner314]) - Improve performance of `nth_back` for iterators ([#686](https://github.com/rust-ndarray/ndarray/pull/686) by [@jturner314]) - Improve performance of iterators for 1-d arrays ([#614](https://github.com/rust-ndarray/ndarray/pull/614) by [@andrei-papou]) - Improve formatting for large arrays ([#606](https://github.com/rust-ndarray/ndarray/pull/606) by [@andrei-papou] and [@LukeMathWalker], [#633](https://github.com/rust-ndarray/ndarray/pull/633) and [#707](https://github.com/rust-ndarray/ndarray/pull/707) by [@jturner314], and [#713](https://github.com/rust-ndarray/ndarray/pull/713) by [@bluss]) - Arithmetic operations between arrays with different element types are now allowed when there is a scalar equivalent ([#588](https://github.com/rust-ndarray/ndarray/pull/588) by [@jturner314]) - `.map_axis/_mut` won't panic on 0-length `axis` ([#579](https://github.com/rust-ndarray/ndarray/pull/612) by [@andrei-papou]) - Various documentation improvements (by [@jturner314], [@JP-Ellis], [@LukeMathWalker], [@bluss]) API changes ----------- - The `into_slice` method on ArrayView is deprecated and renamed to `to_slice` ([#646](https://github.com/rust-ndarray/ndarray/pull/646) by [@max-sixty]) - `RcArray` is deprecated in favour of `ArcArray` ([#560](https://github.com/rust-ndarray/ndarray/pull/560) by [@bluss]) - `into_slice` is renamed to `to_slice`. `into_slice` is now deprecated ([#646](https://github.com/rust-ndarray/ndarray/pull/646) by [@max-sixty]) - `from_vec` is deprecated in favour of using the `From` to convert a `Vec` into an `Array` ([#648](https://github.com/rust-ndarray/ndarray/pull/648) by [@max-sixty]) - `mean_axis` returns `Option` instead of `A`, to avoid panicking when invoked on a 0-length axis ([#580](https://github.com/rust-ndarray/ndarray/pull/580) by [@LukeMathWalker]) - Remove `rustc-serialize` feature-flag. `serde` is the recommended feature-flag for serialization ([#557](https://github.com/rust-ndarray/ndarray/pull/557) by [@bluss]) - `rows`/`cols` are renamed to `nrows`/`ncols`. `rows`/`cols` are now deprecated ([#701](https://github.com/rust-ndarray/ndarray/pull/701) by [@bluss]) - The usage of the `azip!` macro has changed to be more similar to `for` loops ([#626](https://github.com/rust-ndarray/ndarray/pull/626) by [@jturner314]) - For `var_axis` and `std_axis`, the constraints on `ddof` and the trait bounds on `A` have been made more strict ([#515](https://github.com/rust-ndarray/ndarray/pull/515) by [@jturner314]) - For `mean_axis`, the constraints on `A` have changed ([#518](https://github.com/rust-ndarray/ndarray/pull/518) by [@jturner314]) - `DataClone` is deprecated in favor of using `Data + RawDataClone` ([#496](https://github.com/rust-ndarray/ndarray/pull/496) by [@jturner314]) - The `Dimension::Pattern` associated type now has more trait bounds ([#634](https://github.com/rust-ndarray/ndarray/pull/634) by [@termoshtt]) - `Axis::index()` now takes `self` instead of `&self` ([#642](https://github.com/rust-ndarray/ndarray/pull/642) by [@max-sixty]) - The bounds on the implementation of `Hash` for `Dim` have changed ([#642](https://github.com/rust-ndarray/ndarray/pull/642) by [@max-sixty]) Bug fixes --------- - Prevent overflow when computing strides in `do_slice` ([#575](https://github.com/rust-ndarray/ndarray/pull/575) by [@jturner314]) - Fix issue with BLAS matrix-vector multiplication for array with only 1 non-trivial dimension ([#585](https://github.com/rust-ndarray/ndarray/pull/585) by [@sebasv]) - Fix offset computation to avoid UB/panic when slicing in some edge cases ([#636](https://github.com/rust-ndarray/ndarray/pull/636) by [@jturner314]) - Fix issues with axis iterators ([#669](https://github.com/rust-ndarray/ndarray/pull/669) by [@jturner314]) - Fix handling of empty input to `s!` macro ([#714](https://github.com/rust-ndarray/ndarray/pull/714) by [@bluss] and [#715](https://github.com/rust-ndarray/ndarray/pull/715) by [@jturner314]) Other changes ------------- - Various improvements to `ndarray`'s CI pipeline (`clippy`, `cargo fmt`, etc. by [@max-sixty] and [@termoshtt]) - Bump minimum required Rust version to 1.37. Version 0.12.1 (2018-11-21) =========================== - Add `std_axis` method for computing standard deviation by @LukeMathWalker. - Add `product` method for computing product of elements in an array by @sebasv. - Add `first` and `first_mut` methods for getting the first element of an array. - Add `into_scalar` method for converting an `Array0` into its element. - Add `insert_axis_inplace` and `index_axis_inplace` methods for inserting and removing axes in dynamic-dimensional (`IxDyn`) arrays without taking ownership. - Add `stride_of` method for getting the stride of an axis. - Add public `ndim` and `zeros` methods to `Dimension` trait. - Rename `scalar_sum` to `sum`, `subview` to `index_axis`, `subview_mut` to `index_axis_mut`, `subview_inplace` to `collapse_axis`, `into_subview` to `index_axis_move`, and `slice_inplace` to `slice_collapse` (deprecating the old names, except for `scalar_sum` which will be in 0.13). - Deprecate `remove_axis` and fix soundness hole when removing a zero-length axis. - Implement `Clone` for `LanesIter`. - Implement `Debug`, `Copy`, and `Clone` for `FoldWhile`. - Relax constraints on `sum_axis`, `mean_axis`, and `into_owned`. - Add number of dimensions (and whether it's const or dynamic) to array `Debug` format. - Allow merging axes with `merge_axes` when either axis length is ≤ 1. - Clarify and check more precise safety requirements for constructing arrays. This fixes undefined behavior in some edge cases. (See [#543](https://github.com/rust-ndarray/ndarray/pull/543).) - Fix `is_standard_layout` in some edge cases. (See [#543](https://github.com/rust-ndarray/ndarray/pull/543).) - Fix chunk sizes in `axis_chunks_iter` and `axis_chunks_iter_mut` when the stride is zero or the array element type is zero-sized by @bluss. - Improve documentation by @jturner314, @bluss, and @paulkernfeld. - Improve element iterators with implementations of `Iterator::rfold`. - Miscellaneous internal implementation improvements by @jturner314 and @bluss. Version 0.12.0 (2018-09-01) =========================== - Add `var_axis` method for computing variance by @LukeMathWalker. - Add `map_mut` and `map_axis_mut` methods (mutable variants of `map` and `map_axis`) by @LukeMathWalker. - Add support for 128-bit integer scalars (`i128` and `u128`). - Add support for slicing with inclusive ranges (`start..=end` and `..=end`). - Relax constraint on closure from `Fn` to `FnMut` for `mapv`, `mapv_into`, `map_inplace` and `mapv_inplace`. - Implement `TrustedIterator` for `IterMut`. - Bump `num-traits` and `num-complex` to version `0.2`. - Bump `blas-src` to version `0.2`. - Bump minimum required Rust version to 1.27. - Additional contributors to this release: @ExpHP, @jturner314, @alexbool, @messense, @danmack, @nbro Version 0.11.2 (2018-03-21) =========================== - New documentation; @jturner314 has written a large “ndarray for NumPy users” document, which we include in rustdoc. [Read it here](https://docs.rs/ndarray/0.11/ndarray/doc/ndarray_for_numpy_users/) a useful quick guide for any user, and in particular if you are familiar with numpy. - Add `ArcArray`. `RcArray` has become `ArcArray`; it is now using thread safe reference counting just like `Arc`; this means that shared ownership arrays are now `Send/Sync` if the corresponding element type is `Send + Sync`. - Add array method `.permute_axes()` by @jturner314 - Add array constructor `Array::ones` by @ehsanmok - Add the method `.reborrow()` to `ArrayView/Mut`, which can be used to shorten the lifetime of an array view; in a reference-like type this normally happens implicitly but for technical reasons the views have an invariant lifetime parameter. - Fix an issue with type inference, the dimensionality of an array should not infer correctly in more cases when using slicing. By @jturner314. Version 0.11.1 (2018-01-21) =========================== - Dimension types (`Ix1, Ix2, .., IxDyn`) now implement `Hash` by @jturner314 - Blas integration can now use *gemv* for matrix-vector multiplication also when the matrix is f-order by @maciejkula - Encapsulated `unsafe` code blocks in the `s![]` macro are now exempted from the `unsafe_code` lint by @jturner314 Version 0.11.0 (2017-12-29) =========================== [Release announcement](https://jim.turner.link/pages/ndarray-0.11/) - Allow combined slicing and subviews in a single operation by @jturner314 and @bluss * Add support for individual indices (to indicate subviews) to the `s![]` macro, and change the return type to `&SliceInfo<[SliceOrIndex; n], Do>`. * Change the argument type of the slicing methods to correspond to the new `s![]` macro. * Replace the `Si` type with `SliceOrIndex`. * Add a new `Slice` type that is similar to the old `Si` type. - Add support for more index types (e.g. `usize`) to the `s![]` macro by @jturner314 - Rename `.islice()` to `.slice_inplace()` by @jturner314 - Rename `.isubview()` to `.subview_inplace()` by @jturner314 - Add `.slice_move()`, `.slice_axis()`, `.slice_axis_mut()`, and `.slice_axis_inplace()` methods by @jturner314 - Add `Dimension::NDIM` associated constant by @jturner314 - Change trait bounds for arithmetic ops between an array (by value) and a reference to an array or array view (“array1 (op) &array2”); before, an `ArrayViewMut` was supported on the left hand side, now, the left hand side must not be a view. ([#380](https://github.com/rust-ndarray/ndarray/pull/380)) by @jturner314 - Remove deprecated methods (`.whole_chunks()`, `.whole_chunks_mut()`, `.sum()`, and `.mean()`; replaced by `.exact_chunks()`, `.exact_chunks_mut()`, `.sum_axis()`, and `.mean_axis()`, respectively) by @bluss - Updated to the latest blas (optional) dependencies. See instructions in the README. - Minimum required Rust version is 1.22. Earlier releases ================ - 0.10.13 - Add an extension trait for longer-life indexing methods for array views (`IndexLonger`) by @termoshtt and @bluss - The `a.dot(b)` method now supports a vector times matrix multiplication by @jturner314 - More general `.into_owned()` method by @jturner314 - 0.10.12 - Implement serde serialization for `IxDyn`, so that arrays and array views using it are serializable as well. - 0.10.11 - Add method `.uswap(a, b)` for unchecked swap by @jturner314 - Bump private dependencies (itertools 0.7) - 0.10.10 - Fix crash with zero size arrays in the fallback matrix multiplication code (#365) by @jturner314 - 0.10.9 - Fix crash in `Array::from_shape_fn` when creating an f-order array with zero elements (#361) by @jturner314 - 0.10.8 - Add method `.insert_axis()` to arrays and array views by @jturner314 - 0.10.7 - Add method `.is_empty()` to arrays and array views by @iamed2 - Support optional trailing commas in the `array![]` macro by Alex Burka - Added an example of permuting/sorting along an axis to the sources - 0.10.6 - Tweak the implementation for (bounds checked) indexing of arrays ([] operator). The new code will have the optimizer elide the bounds checks in more situations. - 0.10.5 - Add method `.into_dimensionality::()` for dimensionality conversion (From `IxDyn` to fixed size and back). - New names `.sum_axis` and `.mean_axis` for sum and mean functions. Old names deprecated to make room for scalar-returning methods, making a proper convention. - Fix deserialization using ron (#345) by @Libbum - 0.10.4 - Fix unused mut warnings in `azip!()` macro - Fix bug #340 by @lloydmeta; uses blas gemm for more memory layouts of column matrices. Only relevant if using blas. - 0.10.3 - Fix docs.rs doc build - 0.10.2 - Support trailing commas in the `s![]` macro - Some documentation improvements for the introduction, for `azip!()` and other places. - Added two more examples in the source - 0.10.1 - Add method `.into_dyn()` to convert to a dynamic dimensionality array or array view. By @bobogei81123 - Edit docs for the fact that type alias pages now show methods. See the doc pages for `Array` and `ArrayView` and the other aliases. - Edit docs for `Zip` - 0.10.0 - Upgrade to Serde 1.0. Crate feature name is `serde-1`. - Require Rust 1.18. The `pub(crate)` feature is that important. - 0.9.1 - Fix `Array::from_shape_fn` to give correct indices for f-order shapes - Fix `Array::from_shape_fn` to panic correctly on shape size overflow - 0.9.0 [Release Announcement](https://bluss.github.io//rust/2017/04/09/ndarray-0.9/) - Add `Zip::indexed` - New methods `genrows/_mut, gencolumns/_mut, lanes/_mut` that return iterable producers (producer means `Zip` compatible). - New method `.windows()` by @Robbepop, returns an iterable producer - New function `general_mat_vec_mul` (with fast default and blas acceleration) - `Zip::apply` and `fold_while` now take `self` as the first argument - `indices/_of` now return iterable producers (not iterator) - No allocation for short `IxDyn`. - Remove `Ix, Ixs` from the prelude - Remove deprecated `Axis::axis` method (use `.index()`) - Rename `.whole_chunks` to `.exact_chunks`. - Remove `.inner_iter` in favour of the new `.genrows()` method. - Iterators and similar structs are now scoped under `ndarray::iter` - `IntoNdProducer` now has the `Item` associated type - Owned array storage types are now encapsulated in newtypes - `FoldWhile` got the method `is_done`. - Arrays now implement formatting trait `Binary` if elements do - Internal changes. `NdProducer` generalized. `Dimension` gets the `Smaller` type parameter. Internal traits have the private marker now. - `#` (alternate) in formatting does nothing now. - Require Rust 1.15 - 0.8.4 - Use `Zip` in `.all_close()` (performance improvement) - Use `#[inline]` on a function used for higher dimensional checked indexing (performance improvement for arrays of ndim >= 3) - `.subview()` has a more elaborate panic message - 0.8.3 - Fix a bug in `Zip` / `NdProducer` if an array of at least 3 dimensions was contig but not c- nor f-contig. - `WholeChunksIter/Mut` now impl `Send/Sync` as appropriate - Misc cleanup and using dimension-reducing versions of inner_iter internally. Remove a special case in `zip_mut_with` that only made it slower (1D not-contig arrays). - 0.8.2 - Add more documentation and an example for dynamic dimensions: see [`IxDyn`](https://docs.rs/ndarray/0.8.2/ndarray/type.IxDyn.html). `IxDyn` will have a representation change next incompatible version. Use it as a type alias for best forward compatibility. - Add iterable and producer `.whole_chunks_mut(size)`. - Fix a bug in `whole_chunks`: it didn't check the dimensionality of the requested chunk size properly (an `IxDyn`-only bug). - Improve performance of `zip_mut_with` (and thus all binary operators) for block slices of row major arrays. - `AxisChunksIter` creation sped up and it implements `Clone`. - Dimension mismatch in `Zip` has a better panic message. - 0.8.1 - Add `Zip` and macro `azip!()` which implement lock step function application across elements from one up to six arrays (or in general producers) + Apart from array views, axis iterators and the whole chunks iterable are also producers - Add constructor `Array::uninitialized` - Add iterable and producer `.whole_chunks(size)` - Implement a prettier `Debug` for `Si`. - Fix `Array::default` so that it panics as documented if the size of the array would wrap around integer type limits. - Output more verbose panics for errors when slicing arrays (only in debug mode). - 0.8.0 - Update serde dependency to 0.9 - Remove deprecated type alias `OwnedArray` (use `Array`) - Remove deprecated `.assign_scalar()` (use `fill`) - 0.7.3 - Add macro `array![]` for creating one-, two-, or three-dimensional arrays (with ownership semantics like `vec![]`) - `Array` now implements `Clone::clone_from()` specifically, so that its allocation is (possibly) reused. - Add `.to_vec()` for one-dimensional arrays - Add `RcArray::into_owned(self) -> Array`. - Add crate categories - 0.7.2 - Add array methods `.remove_axis()`, `.merge_axes()` and `.invert_axis()` - Rename `Axis`’ accessor `axis` to `index`, old name is deprecated. - 0.7.1 - Fix two bugs in `Array::clone()`; it did not support zero-size elements like `()`, and for some negatively strided arrays it did not update the first element offset correctly. - Add `.axes()` which is an iterator over the axes of an array, yielding its index, length and stride. - Add method `.max_stride_axis()`. - 0.6.10 - Fix two bugs in `Array::clone()`; it did not support zero-size elements like `()`, and for some negatively strided arrays it did not update the first element offset correctly. - 0.7.0 - Big overhaul of dimensions: Add type `Dim` with aliases `Ix1, Ix2, Ix3, ...` etc for specific dimensionalities. Instead of `Ix` for dimension use `Ix1`, instead of `(Ix, Ix)` use `Ix2`, and so on. - The dimension type `Dim` supports indexing and arithmetic. See `Dimension` trait for new methods and inherited traits. - Constructors and methods that take tuples for array sizes, like `Array::zeros,` `Array::from_shape_vec`, `.into_shape()` and so on will continue to work with tuples. - The array method `.raw_dim()` returns the shape description `D` as it is. `.dim()` continues to return the dimension as a tuple. - Renamed iterators for consistency (each iterator is named for the method that creates it, for example `.iter()` returns `Iter`). - The index iterator is now created with free functions `indices` or `indices_of`. - Expanded the `ndarray::prelude` module with the dimensionality-specific type aliases, and some other items - `LinalgScalar` and related features no longer need to use `Any` for static type dispatch. - Serialization with `serde` now supports binary encoders like bincode and others. - `.assign_scalar()` was deprecated and replaced by `.fill()`, which takes an element by value. - Require Rust 1.13 - 0.6.9 - Implement `ExactSizeIterator` for the indexed iterators - 0.6.8 - Fix a bug in a partially consumed elements iterator's `.fold()`. (**Note** that users are recommended to not use the elements iterator, but the higher level functions which are the maps, folds and other methods of the array types themselves.) - 0.6.7 - Improve performance of a lot of basic operations for arrays where the innermost dimension is not contiguous (`.fold(), .map(), .to_owned()`, arithmetic operations with scalars). - Require Rust 1.11 - 0.6.6 - Add dimensionality specific type aliases: `Array0, Array1, Array2, ...` and so on (there are many), also `Ix0, Ix1, Ix2, ...`. - Add constructor `Array::from_shape_fn(D, |D| -> A)`. - Improve performance of `Array::default`, and `.fold()` for noncontiguous array iterators. - 0.6.5 - Add method `.into_raw_vec()` to turn an `Array` into the its underlying element storage vector, in whatever element order it is using. - 0.6.4 - Add method `.map_axis()` which is used to flatten an array along one axis by mapping it to a scalar. - 0.6.3 - Work around compilation issues in nightly (issue #217) - Add `Default` implementations for owned arrays - 0.6.2 - Add serialization support for serde 0.8, under the crate feature name `serde` - 0.6.1 - Add `unsafe` array view constructors `ArrayView::from_shape_ptr` for read-only and read-write array views. These make it easier to create views from raw pointers. - 0.6.0 - Rename `OwnedArray` to `Array`. The old name is deprecated. - Remove deprecated constructor methods. Use zeros, from_elem, from_shape_vec or from_shape_vec_unchecked instead. - Remove deprecated in place arithmetic methods like iadd et.c. Use += et.c. instead. - Remove deprecated method mat_mul, use dot instead. - Require Rust 1.9 - 0.5.2 - Use num-traits, num-complex instead of num. - 0.5.1 - Fix theoretical well-formedness issue with Data trait - 0.5.0 - Require Rust 1.8 and enable +=, -=, and the other assign operators. All `iadd, iadd_scalar` and similar methods are now deprecated. - ndarray now has a prelude: `use ndarray::prelude::*;`. - Constructors from_elem, zeros, from_shape_vec now all support passing a custom memory layout. A lot of specific constructors were deprecated. - Add method `.select(Axis, &[Ix]) -> OwnedArray`, to create an array from a non-contiguous pick of subviews along an axis. - Rename `.mat_mul()` to just `.dot()` and add a function `general_mat_mul` for matrix multiplication with scaling into an existing array. - **Change .fold() to use arbitrary order.** - See below for more details - 0.5.0-alpha.2 - Fix a namespace bug in the stack![] macro. - Add method .select() that can pick an arbitrary set of rows (for example) into a new array. - 0.4.9 - Fix a namespace bug in the stack![] macro. - Add deprecation messages to .iadd() and similar methods (use += instead). - 0.5.0-alpha.1 - Add .swap(i, j) for swapping two elements - Add a prelude module `use ndarray::prelude::*;` - Add ndarray::linalg::general_mat_mul which computes *C ← α A B + β C*, i.e matrix multiplication into an existing array, with optional scaling. - Add .fold_axis(Axis, folder) - Implement .into_shape() for f-order arrays - 0.5.0-alpha.0 - Requires Rust 1.8. Compound assignment operators are now enabled by default. - Rename `.mat_mul()` to `.dot()`. The same method name now handles dot product and matrix multiplication. - Remove deprecated items: raw_data, raw_data_mut, allclose, zeros, Array. Docs for 0.4. lists the replacements. - Remove deprecated crate features: rblas, assign_ops - A few consuming arithmetic ops with ArrayViewMut were removed (this was missed in the last version). - **Change .fold() to use arbitrary order.** Its specification and implementation has changed, to pick the most appropriate element traversal order depending on memory layout. - 0.4.8 - Fix an error in `.dot()` when using BLAS and arrays with negative stride. - 0.4.7 - Add dependency matrixmultiply to handle matrix multiplication for floating point elements. It supports matrices of general stride and is a great improvement for performance. See PR #175. - 0.4.6 - Fix bug with crate feature blas; it would not compute matrix multiplication correctly for arrays with negative or zero stride. - Update blas-sys version (optional dependency). - 0.4.5 - Add `.all_close()` which replaces the now deprecated `.allclose()`. The new method has a stricter protocol: it panics if the array shapes are not compatible. We don't want errors to pass silently. - Add a new illustration to the doc for `.axis_iter()`. - Rename `OuterIter, OuterIterMut` to `AxisIter, AxisIterMut`. The old name is now deprecated. - 0.4.4 - Add mapping methods `.mapv(), .mapv_into(), .map_inplace(),` `.mapv_inplace(), .visit()`. The `mapv` versions have the transformation function receive the element by value (hence *v*). - Add method `.scaled_add()` (a.k.a axpy) and constructor `from_vec_dim_f`. - Add 2d array methods `.rows(), .cols()`. - Deprecate method `.fold()` because it dictates a specific visit order. - 0.4.3 - Add array method `.t()` as a shorthand to create a transposed view. - Fix `mat_mul` so that it accepts arguments of different array kind - Fix a bug in `mat_mul` when using BLAS and multiplying with a column matrix (#154) - 0.4.2 - Add new BLAS integration used by matrix multiplication (selected with crate feature `blas`). Uses pluggable backend. - Deprecate module `ndarray::blas` and crate feature `rblas`. This module was moved to the crate `ndarray-rblas`. - Add array methods `as_slice_memory_order, as_slice_memory_order_mut, as_ptr, as_mut_ptr`. - Deprecate `raw_data, raw_data_mut`. - Add `Send + Sync` to `NdFloat`. - Arrays now show shape & stride in their debug formatter. - Fix a bug where `from_vec_dim_stride` did not accept arrays with unitary axes. - Performance improvements for contiguous arrays in non-c order when using methods `to_owned, map, scalar_sum, assign_scalar`, and arithmetic operations between array and scalar. - Some methods now return arrays in the same memory order of the input if the input is contiguous: `to_owned, map, mat_mul` (matrix multiplication only if both inputs are the same memory order), and arithmetic operations that allocate a new result. - Slight performance improvements in `dot, mat_mul` due to more efficient glue code for calling BLAS. - Performance improvements in `.assign_scalar`. - 0.4.1 - Mark iterators `Send + Sync` when possible. - **0.4.0** [Release Announcement](http://bluss.github.io/rust/2016/03/06/ndarray-0.4/) - New array splitting via `.split_at(Axis, Ix)` and `.axis_chunks_iter()` - Added traits `NdFloat`, `AsArray` and `From for ArrayView` which improve generic programming. - Array constructors panic when attempting to create an array whose element count overflows `usize`. (Would be a debug assertion for overflow before.) - Performance improvements for `.map()`. - Added `stack` and macro `stack![axis, arrays..]` to concatenate arrays. - Added constructor `OwnedArray::range(start, end, step)`. - The type alias `Array` was renamed to `RcArray` (and the old name deprecated). - Binary operators are not defined when consuming a mutable array view as the left hand side argument anymore. - Remove methods and items deprecated since 0.3 or earlier; deprecated methods have notes about replacements in 0.3 docs. - See below for full changelog through alphas. - 0.4.0-alpha.8 - In debug mode, indexing an array out of bounds now has a detailed message about index and shape. (In release mode it does not.) - Enable assign_ops feature automatically when it is supported (Rust 1.8 beta or later). - Add trait `NdFloat` which makes it easy to be generic over `f32, f64`. - Add `From` implementations that convert slices or references to arrays into array views. This replaces `from_slice` from a previous alpha. - Add `AsArray` trait, which is simply based on those `From` implementations. - Improve `.map()` so that it can autovectorize. - Use `Axis` argument in `RemoveAxis` too. - Require `DataOwned` in the raw data methods. - Merged error types into a single `ShapeError`, which uses no allocated data. - 0.4.0-alpha.7 - Fix too strict lifetime bound in arithmetic operations like `&a @ &b`. - Rename trait Scalar to ScalarOperand (and improve its docs). - Implement <<= and >>= for arrays. - 0.4.0-alpha.6 - All axis arguments must now be wrapped in newtype `Axis`. - Add method `.split_at(Axis, Ix)` to read-only and read-write array views. - Add constructors `ArrayView{,Mut}::from_slice` and array view methods are now visible in the docs. - 0.4.0-alpha.5 - Use new trait `LinalgScalar` for operations where we want type-based specialization. This shrinks the set of types that allow dot product, matrix multiply, mean. - Use BLAS acceleration transparently in `.dot()` (this is the first step). - Only OwnedArray and RcArray and not ArrayViewMut can now be used as consumed left hand operand for arithmetic operators. [See arithmetic operations docs!]( https://docs.rs/ndarray/0.4.0-alpha.5/ndarray/struct.ArrayBase.html#arithmetic-operations) - Remove deprecated module `linalg` (it was already mostly empty) - Deprecate free function `zeros` in favour of static method `zeros`. - 0.4.0-alpha.4 - Rename `Array` to `RcArray`. Old name is deprecated. - Add methods `OuterIter::split_at`, `OuterIterMut::split_at` - Change `arr0, arr1, arr2, arr3` to return `OwnedArray`. Add `rcarr1, rcarr2, rcarr3` that return `RcArray`. - 0.4.0-alpha.3 - Improve arithmetic operations where the RHS is a broadcast 0-dimensional array. - Add read-only and read-write array views to the `rblas` integration. Added methods `AsBlas::{blas_view_checked, blas_view_mut_checked, bv, bvm}`. - Use hash_slice in `Hash` impl for arrays. - 0.4.0-alpha.2 - Add `ArrayBase::reversed_axes` which transposes an array. - 0.4.0-alpha.1 - Add checked and unchecked constructor methods for creating arrays from a vector and explicit dimension and stride, or with fortran (column major) memory order (marked `f`): + `ArrayBase::from_vec_dim`, `from_vec_dim_stride`, `from_vec_dim_stride_unchecked`, + `from_vec_dim_unchecked_f`, `from_elem_f`, `zeros_f` + View constructors `ArrayView::from_slice_dim_stride`, `ArrayViewMut::from_slice_dim_stride`. + Rename old `ArrayBase::from_vec_dim` to `from_vec_dim_unchecked`. - Check better for wraparound when computing the number of elements in a shape; this adds error cases that **panic** in `from_elem`, `zeros` etc, however *the new check will only ever panic in cases that would trigger debug assertions for overflow in the previous versions*!. - Add an array chunks iterator `.axis_chunks_iter()` and mutable version; it allows traversing the array in for example chunks of *n* rows at a time. - Remove methods and items deprecated since 0.3 or earlier; deprecated methods have notes about replacements in 0.3 docs. - 0.3.1 - Add `.row_mut()`, `.column_mut()` - Add `.axis_iter()`, `.axis_iter_mut()` - **0.3.0** - Second round of API & consistency update is done - 0.3.0 highlight: **Index type** `Ix` **changed to** `usize`. - 0.3.0 highlight: Operator overloading for scalar and array arithmetic. - 0.3.0 highlight: Indexing with `a[[i, j, k]]` syntax. - Add `ArrayBase::eye(n)` - See below for more info - 0.3.0-alpha.4 - Shrink array view structs by removing their redundant slice field (see #45). Changed the definition of the view `type` aliases. - `.mat_mul()` and `.mat_mul_col()` now return `OwnedArray`. Use `.into_shared()` if you need an `Array`. - impl ExactSizeIterator where possible for iterators. - impl DoubleEndedIterator for `.outer_iter()` (and _mut). - 0.3.0-alpha.3 - `.subview()` changed to return an array view, also added `into_subview()`. - Add `.outer_iter()` and `.outer_iter_mut()` for iteration along the greatest axis of the array. Views also implement `into_outer_iter()` for “lifetime preserving” iterators. - 0.3.0-alpha.2 - Improve the strided last dimension case in `zip_mut_with` slightly (affects all binary operations). - Add `.row(i), .column(i)` for 2D arrays. - Deprecate `.row_iter(), .col_iter()`. - Add method `.dot()` for computing the dot product between two 1D arrays. - 0.3.0-alpha.1 - **Index type** `Ix` **changed to** `usize` (#9). Gives better iterator codegen and 64-bit size arrays. - Support scalar operands with arithmetic operators. - Change `.slice()` and `.diag()` to return array views, add `.into_diag()`. - Add ability to use fixed size arrays for array indexing, enabling syntax like `a[[i, j]]` for indexing. - Add `.ndim()` - **0.2.0** - First chapter of API and performance evolution is done \\o/ - 0.2.0 highlight: Vectorized (efficient) arithmetic operations - 0.2.0 highlight: Easier slicing using `s![]` - 0.2.0 highlight: Nicer API using views - 0.2.0 highlight: Bridging to BLAS functions. - See below for more info - 0.2.0-alpha.9 - Support strided matrices in `rblas` bridge, and fix a bug with non square matrices. - Deprecated all of module `linalg`. - 0.2.0-alpha.8 - **Note:** PACKAGE NAME CHANGED TO `ndarray`. Having package != crate ran into many quirks of various tools. Changing the package name is easier for everyone involved! - Optimized `scalar_sum()` so that it will vectorize for the floating point element case too. - 0.2.0-alpha.7 - Optimized arithmetic operations! - For c-contiguous arrays or arrays with c-contiguous lowest dimension they optimize very well, and can vectorize! - Add `.inner_iter()`, `.inner_iter_mut()` - Add `.fold()`, `.zip_mut_with()` - Add `.scalar_sum()` - Add example `examples/life.rs` - 0.2.0-alpha.6 - Add `#[deprecated]` attributes (enabled with new enough nightly) - Add `ArrayBase::linspace`, deprecate constructor `range`. - 0.2.0-alpha.5 - Add `s![...]`, a slice argument macro. - Add `aview_mut1()`, `zeros()` - Add `.diag_mut()` and deprecate `.diag_iter_mut()`, `.sub_iter_mut()` - Add `.uget()`, `.uget_mut()` for unchecked indexing and deprecate the old names. - Improve `ArrayBase::from_elem` - Removed `SliceRange`, replaced by `From` impls for `Si`. - 0.2.0-alpha.4 - Slicing methods like `.slice()` now take a fixed size array of `Si` as the slice description. This allows more type checking to verify that the number of axes is correct. - Add experimental `rblas` integration. - Add `into_shape()` which allows reshaping any array or view kind. - 0.2.0-alpha.3 - Add and edit a lot of documentation - 0.2.0-alpha.2 - Improve performance for iterators when the array data is in the default memory layout. The iterator then wraps the default slice iterator and loops will autovectorize. - Remove method `.indexed()` on iterators. Changed `Indexed` and added `ÌndexedMut`. - Added `.as_slice(), .as_mut_slice()` - Support rustc-serialize - 0.2.0-alpha - Alpha release! - Introduce `ArrayBase`, `OwnedArray`, `ArrayView`, `ArrayViewMut` - All arithmetic operations should accept any array type - `Array` continues to refer to the default reference counted copy on write array - Add `.view()`, `.view_mut()`, `.to_owned()`, `.into_shared()` - Add `.slice_mut()`, `.subview_mut()` - Some operations now return `OwnedArray`: - `.map()` - `.sum()` - `.mean()` - Add `get`, `get_mut` to replace the now deprecated `at`, `at_mut`. - Fix bug in assign_scalar - 0.1.1 - Add Array::default - Fix bug in raw_data_mut - 0.1.0 - First release on crates.io - Starting point for evolution to come [@adamreichold]: https://github.com/adamreichold [@aganders3]: https://github.com/aganders3 [@bluss]: https://github.com/bluss [@jturner314]: https://github.com/jturner314 [@LukeMathWalker]: https://github.com/LukeMathWalker [@acj]: https://github.com/acj [@adamreichold]: https://github.com/adamreichold [@atouchet]: https://github.com/atouchet [@andrei-papou]: https://github.com/andrei-papou [@benkay]: https://github.com/benkay [@cassiersg]: https://github.com/cassiersg [@chohner]: https://github.com/chohner [@dam5h]: https://github.com/dam5h [@ethanhs]: https://github.com/ethanhs [@d-dorazio]: https://github.com/d-dorazio [@Eijebong]: https://github.com/Eijebong [@HyeokSuLee]: https://github.com/HyeokSuLee [@insideoutclub]: https://github.com/insideoutclub [@JP-Ellis]: https://github.com/JP-Ellis [@jimblandy]: https://github.com/jimblandy [@LeSeulArtichaut]: https://github.com/LeSeulArtichaut [@lifuyang]: https://github.com/liufuyang [@kdubovikov]: https://github.com/kdubovikov [@makotokato]: https://github.com/makotokato [@max-sixty]: https://github.com/max-sixty [@mneumann]: https://github.com/mneumann [@mockersf]: https://github.com/mockersf [@nilgoyette]: https://github.com/nilgoyette [@nitsky]: https://github.com/nitsky [@Rikorose]: https://github.com/Rikorose [@rth]: https://github.com/rth [@sebasv]: https://github.com/sebasv [@SparrowLii]: https://github.com/SparrowLii [@steffahn]: https://github.com/steffahn [@stokhos]: https://github.com/stokhos [@termoshtt]: https://github.com/termoshtt [@TheLortex]: https://github.com/TheLortex [@viniciusd]: https://github.com/viniciusd [@VasanthakumarV]: https://github.com/VasanthakumarV [@xd009642]: https://github.com/xd009642 [@Zuse64]: https://github.com/Zuse64 ndarray-0.16.1/benches/append.rs000064400000000000000000000015241046102023000145770ustar 00000000000000#![feature(test)] extern crate test; use test::Bencher; use ndarray::prelude::*; #[bench] fn select_axis0(bench: &mut Bencher) { let a = Array::::zeros((256, 256)); let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; bench.iter(|| a.select(Axis(0), &selectable)); } #[bench] fn select_axis1(bench: &mut Bencher) { let a = Array::::zeros((256, 256)); let selectable = vec![0, 1, 2, 0, 1, 3, 0, 4, 16, 32, 128, 147, 149, 220, 221, 255, 221, 0, 1]; bench.iter(|| a.select(Axis(1), &selectable)); } #[bench] fn select_1d(bench: &mut Bencher) { let a = Array::::zeros(1024); let mut selectable = (0..a.len()).step_by(17).collect::>(); selectable.extend(selectable.clone().iter().rev()); bench.iter(|| a.select(Axis(0), &selectable)); } ndarray-0.16.1/benches/bench1.rs000064400000000000000000000617101046102023000144730ustar 00000000000000#![feature(test)] #![allow(unused_imports)] #![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use std::mem::MaybeUninit; use ndarray::{arr0, arr1, arr2, azip, s}; use ndarray::{Array, Array1, Array2, Axis, Ix, Zip}; use ndarray::{Array3, Array4, ShapeBuilder}; use ndarray::{Ix1, Ix2, Ix3, Ix5, IxDyn}; use test::black_box; #[bench] fn iter_sum_1d_regular(bench: &mut test::Bencher) { let a = Array::::zeros(64 * 64); let a = black_box(a); bench.iter(|| { let mut sum = 0; for &elt in a.iter() { sum += elt; } sum }); } #[bench] fn iter_sum_1d_raw(bench: &mut test::Bencher) { // this is autovectorized to death (= great performance) let a = Array::::zeros(64 * 64); let a = black_box(a); bench.iter(|| { let mut sum = 0; for &elt in a.as_slice_memory_order().unwrap() { sum += elt; } sum }); } #[bench] fn iter_sum_2d_regular(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let a = black_box(a); bench.iter(|| { let mut sum = 0; for &elt in a.iter() { sum += elt; } sum }); } #[bench] fn iter_sum_2d_by_row(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let a = black_box(a); bench.iter(|| { let mut sum = 0; for row in a.rows() { for &elt in row { sum += elt; } } sum }); } #[bench] fn iter_sum_2d_raw(bench: &mut test::Bencher) { // this is autovectorized to death (= great performance) let a = Array::::zeros((64, 64)); let a = black_box(a); bench.iter(|| { let mut sum = 0; for &elt in a.as_slice_memory_order().unwrap() { sum += elt; } sum }); } #[bench] fn iter_sum_2d_cutout(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); bench.iter(|| { let mut sum = 0; for &elt in a.iter() { sum += elt; } sum }); } #[bench] fn iter_sum_2d_cutout_by_row(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); bench.iter(|| { let mut sum = 0; for row in 0..a.shape()[0] { for &elt in a.row(row) { sum += elt; } } sum }); } #[bench] fn iter_sum_2d_cutout_outer_iter(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); bench.iter(|| { let mut sum = 0; for row in a.rows() { for &elt in row { sum += elt; } } sum }); } #[bench] fn iter_sum_2d_transpose_regular(bench: &mut test::Bencher) { let mut a = Array::::zeros((64, 64)); a.swap_axes(0, 1); let a = black_box(a); bench.iter(|| { let mut sum = 0; for &elt in a.iter() { sum += elt; } sum }); } #[bench] fn iter_sum_2d_transpose_by_row(bench: &mut test::Bencher) { let mut a = Array::::zeros((64, 64)); a.swap_axes(0, 1); let a = black_box(a); bench.iter(|| { let mut sum = 0; for row in 0..a.shape()[0] { for &elt in a.row(row) { sum += elt; } } sum }); } #[bench] fn sum_2d_regular(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let a = black_box(a); bench.iter(|| a.sum()); } #[bench] fn sum_2d_cutout(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); bench.iter(|| a.sum()); } #[bench] fn sum_2d_float(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let a = black_box(a.view()); bench.iter(|| a.sum()); } #[bench] fn sum_2d_float_cutout(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); bench.iter(|| a.sum()); } #[bench] fn sum_2d_float_t_cutout(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]).reversed_axes(); let a = black_box(av); bench.iter(|| a.sum()); } #[bench] fn fold_sum_i32_2d_regular(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); bench.iter(|| a.fold(0, |acc, &x| acc + x)); } #[bench] fn fold_sum_i32_2d_cutout(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = black_box(av); bench.iter(|| a.fold(0, |acc, &x| acc + x)); } #[bench] fn fold_sum_i32_2d_stride(bench: &mut test::Bencher) { let a = Array::::zeros((64, 128)); let av = a.slice(s![.., ..;2]); let a = black_box(av); bench.iter(|| a.fold(0, |acc, &x| acc + x)); } #[bench] fn fold_sum_i32_2d_transpose(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let a = a.t(); bench.iter(|| a.fold(0, |acc, &x| acc + x)); } #[bench] fn fold_sum_i32_2d_cutout_transpose(bench: &mut test::Bencher) { let a = Array::::zeros((66, 66)); let mut av = a.slice(s![1..-1, 1..-1]); av.swap_axes(0, 1); let a = black_box(av); bench.iter(|| a.fold(0, |acc, &x| acc + x)); } const ADD2DSZ: usize = 64; #[bench] fn add_2d_regular(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); bench.iter(|| { a += &bv; }); } #[bench] fn add_2d_zip(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } #[bench] fn add_2d_alloc_plus(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| &a + &b); } #[bench] fn add_2d_alloc_zip_uninit(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| unsafe { let mut c = Array::::uninit(a.dim()); azip!((&a in &a, &b in &b, c in c.raw_view_mut().cast::()) c.write(a + b) ); c }); } #[bench] fn add_2d_alloc_zip_collect(bench: &mut test::Bencher) { let a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| Zip::from(&a).and(&b).map_collect(|&x, &y| x + y)); } #[bench] fn vec_string_collect(bench: &mut test::Bencher) { let v = vec![""; 10240]; bench.iter(|| v.iter().map(|s| s.to_owned()).collect::>()); } #[bench] fn array_string_collect(bench: &mut test::Bencher) { let v = Array::from(vec![""; 10240]); bench.iter(|| Zip::from(&v).map_collect(|s| s.to_owned())); } #[bench] fn vec_f64_collect(bench: &mut test::Bencher) { let v = vec![1.; 10240]; bench.iter(|| v.iter().map(|s| s + 1.).collect::>()); } #[bench] fn array_f64_collect(bench: &mut test::Bencher) { let v = Array::from(vec![1.; 10240]); bench.iter(|| Zip::from(&v).map_collect(|s| s + 1.)); } #[bench] fn add_2d_assign_ops(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); bench.iter(|| { let mut x = a.view_mut(); x += &bv; black_box(x); }); } #[bench] fn add_2d_cutout(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ + 2, ADD2DSZ + 2)); let mut acut = a.slice_mut(s![1..-1, 1..-1]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); bench.iter(|| { acut += &bv; }); } #[bench] fn add_2d_zip_cutout(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ + 2, ADD2DSZ + 2)); let mut acut = a.slice_mut(s![1..-1, 1..-1]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { Zip::from(&mut acut).and(&b).for_each(|a, &b| *a += b); }); } #[bench] #[allow(clippy::identity_op)] fn add_2d_cutouts_by_4(bench: &mut test::Bencher) { let mut a = Array::::zeros((64 * 1, 64 * 1)); let b = Array::::zeros((64 * 1, 64 * 1)); let chunksz = (4, 4); bench.iter(|| { Zip::from(a.exact_chunks_mut(chunksz)) .and(b.exact_chunks(chunksz)) .for_each(|mut a, b| a += &b); }); } #[bench] #[allow(clippy::identity_op)] fn add_2d_cutouts_by_16(bench: &mut test::Bencher) { let mut a = Array::::zeros((64 * 1, 64 * 1)); let b = Array::::zeros((64 * 1, 64 * 1)); let chunksz = (16, 16); bench.iter(|| { Zip::from(a.exact_chunks_mut(chunksz)) .and(b.exact_chunks(chunksz)) .for_each(|mut a, b| a += &b); }); } #[bench] #[allow(clippy::identity_op)] fn add_2d_cutouts_by_32(bench: &mut test::Bencher) { let mut a = Array::::zeros((64 * 1, 64 * 1)); let b = Array::::zeros((64 * 1, 64 * 1)); let chunksz = (32, 32); bench.iter(|| { Zip::from(a.exact_chunks_mut(chunksz)) .and(b.exact_chunks(chunksz)) .for_each(|mut a, b| a += &b); }); } #[bench] fn add_2d_broadcast_1_to_2(bench: &mut test::Bencher) { let mut a = Array2::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array1::::zeros(ADD2DSZ); let bv = b.view(); bench.iter(|| { a += &bv; }); } #[bench] fn add_2d_broadcast_0_to_2(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros(()); let bv = b.view(); bench.iter(|| { a += &bv; }); } #[bench] fn scalar_toowned(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); bench.iter(|| a.to_owned()); } #[bench] fn scalar_add_1(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| &a + n); } #[bench] fn scalar_add_2(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| n + &a); } #[bench] fn scalar_add_strided_1(bench: &mut test::Bencher) { let a = Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); let n = 1.; bench.iter(|| &a + n); } #[bench] fn scalar_add_strided_2(bench: &mut test::Bencher) { let a = Array::from_shape_fn((64, 64 * 2), |(i, j)| (i * 64 + j) as f32).slice_move(s![.., ..;2]); let n = 1.; bench.iter(|| n + &a); } #[bench] fn scalar_sub_1(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| &a - n); } #[bench] fn scalar_sub_2(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let n = 1.; bench.iter(|| n - &a); } // This is for comparison with add_2d_broadcast_0_to_2 #[bench] fn add_2d_0_to_2_iadd_scalar(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let n = black_box(0); bench.iter(|| { a += n; }); } #[bench] fn add_2d_strided(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ * 2)); let mut a = a.slice_mut(s![.., ..;2]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); bench.iter(|| { a += &bv; }); } #[bench] fn add_2d_regular_dyn(bench: &mut test::Bencher) { let mut a = Array::::zeros(&[ADD2DSZ, ADD2DSZ][..]); let b = Array::::zeros(&[ADD2DSZ, ADD2DSZ][..]); let bv = b.view(); bench.iter(|| { a += &bv; }); } #[bench] fn add_2d_strided_dyn(bench: &mut test::Bencher) { let mut a = Array::::zeros(&[ADD2DSZ, ADD2DSZ * 2][..]); let mut a = a.slice_mut(s![.., ..;2]); let b = Array::::zeros(&[ADD2DSZ, ADD2DSZ][..]); let bv = b.view(); bench.iter(|| { a += &bv; }); } #[bench] fn add_2d_zip_strided(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ * 2)); let mut a = a.slice_mut(s![.., ..;2]); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } #[bench] fn add_2d_one_transposed(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { a += &b; }); } #[bench] fn add_2d_zip_one_transposed(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } #[bench] fn add_2d_both_transposed(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut b = Array::::zeros((ADD2DSZ, ADD2DSZ)); b.swap_axes(0, 1); bench.iter(|| { a += &b; }); } #[bench] fn add_2d_zip_both_transposed(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut b = Array::::zeros((ADD2DSZ, ADD2DSZ)); b.swap_axes(0, 1); bench.iter(|| { Zip::from(&mut a).and(&b).for_each(|a, &b| *a += b); }); } #[bench] fn add_2d_f32_regular(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); let b = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = b.view(); bench.iter(|| { a += &bv; }); } const ADD3DSZ: usize = 16; #[bench] fn add_3d_strided(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD3DSZ, ADD3DSZ, ADD3DSZ * 2)); let mut a = a.slice_mut(s![.., .., ..;2]); let b = Array::::zeros(a.dim()); let bv = b.view(); bench.iter(|| { a += &bv; }); } #[bench] fn add_3d_strided_dyn(bench: &mut test::Bencher) { let mut a = Array::::zeros(&[ADD3DSZ, ADD3DSZ, ADD3DSZ * 2][..]); let mut a = a.slice_mut(s![.., .., ..;2]); let b = Array::::zeros(a.dim()); let bv = b.view(); bench.iter(|| { a += &bv; }); } const ADD1D_SIZE: usize = 64 * 64; #[bench] fn add_1d_regular(bench: &mut test::Bencher) { let mut a = Array::::zeros(ADD1D_SIZE); let b = Array::::zeros(a.dim()); bench.iter(|| { a += &b; }); } #[bench] fn add_1d_strided(bench: &mut test::Bencher) { let mut a = Array::::zeros(ADD1D_SIZE * 2); let mut av = a.slice_mut(s![..;2]); let b = Array::::zeros(av.dim()); bench.iter(|| { av += &b; }); } #[bench] fn iadd_scalar_2d_regular(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ)); bench.iter(|| { a += 1.; }); } #[bench] fn iadd_scalar_2d_strided(bench: &mut test::Bencher) { let mut a = Array::::zeros((ADD2DSZ, ADD2DSZ * 2)); let mut a = a.slice_mut(s![.., ..;2]); bench.iter(|| { a += 1.; }); } #[bench] fn iadd_scalar_2d_regular_dyn(bench: &mut test::Bencher) { let mut a = Array::::zeros(vec![ADD2DSZ, ADD2DSZ]); bench.iter(|| { a += 1.; }); } #[bench] fn iadd_scalar_2d_strided_dyn(bench: &mut test::Bencher) { let mut a = Array::::zeros(vec![ADD2DSZ, ADD2DSZ * 2]); let mut a = a.slice_mut(s![.., ..;2]); bench.iter(|| { a += 1.; }); } #[bench] fn scaled_add_2d_f32_regular(bench: &mut test::Bencher) { let mut av = Array::::zeros((ADD2DSZ, ADD2DSZ)); let bv = Array::::zeros((ADD2DSZ, ADD2DSZ)); let scalar = std::f32::consts::PI; bench.iter(|| { av.scaled_add(scalar, &bv); }); } #[bench] fn assign_scalar_2d_corder(bench: &mut test::Bencher) { let a = Array::zeros((ADD2DSZ, ADD2DSZ)); let mut a = black_box(a); let s = 3.; bench.iter(move || a.fill(s)) } #[bench] fn assign_scalar_2d_cutout(bench: &mut test::Bencher) { let mut a = Array::zeros((66, 66)); let a = a.slice_mut(s![1..-1, 1..-1]); let mut a = black_box(a); let s = 3.; bench.iter(move || a.fill(s)) } #[bench] fn assign_scalar_2d_forder(bench: &mut test::Bencher) { let mut a = Array::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut a = black_box(a); let s = 3.; bench.iter(move || a.fill(s)) } #[bench] fn assign_zero_2d_corder(bench: &mut test::Bencher) { let a = Array::zeros((ADD2DSZ, ADD2DSZ)); let mut a = black_box(a); bench.iter(|| a.fill(0.)) } #[bench] fn assign_zero_2d_cutout(bench: &mut test::Bencher) { let mut a = Array::zeros((66, 66)); let a = a.slice_mut(s![1..-1, 1..-1]); let mut a = black_box(a); bench.iter(|| a.fill(0.)) } #[bench] fn assign_zero_2d_forder(bench: &mut test::Bencher) { let mut a = Array::zeros((ADD2DSZ, ADD2DSZ)); a.swap_axes(0, 1); let mut a = black_box(a); bench.iter(|| a.fill(0.)) } #[bench] fn bench_iter_diag(bench: &mut test::Bencher) { let a = Array::::zeros((1024, 1024)); bench.iter(|| { for elt in a.diag() { black_box(elt); } }) } #[bench] fn bench_row_iter(bench: &mut test::Bencher) { let a = Array::::zeros((1024, 1024)); let it = a.row(17); bench.iter(|| { for elt in it { black_box(elt); } }) } #[bench] fn bench_col_iter(bench: &mut test::Bencher) { let a = Array::::zeros((1024, 1024)); let it = a.column(17); bench.iter(|| { for elt in it { black_box(elt); } }) } macro_rules! mat_mul { ($modname:ident, $ty:ident, $(($name:ident, $m:expr, $n:expr, $k:expr))+) => { mod $modname { use test::{black_box, Bencher}; use ndarray::Array; $( #[bench] fn $name(bench: &mut Bencher) { let a = Array::<$ty, _>::zeros(($m, $n)); let b = Array::<$ty, _>::zeros(($n, $k)); let a = black_box(a.view()); let b = black_box(b.view()); bench.iter(|| a.dot(&b)); } )+ } }; } mat_mul! {mat_mul_f32, f32, (m004, 4, 4, 4) (m007, 7, 7, 7) (m008, 8, 8, 8) (m012, 12, 12, 12) (m016, 16, 16, 16) (m032, 32, 32, 32) (m064, 64, 64, 64) (m127, 127, 127, 127) (mix16x4, 32, 4, 32) (mix32x2, 32, 2, 32) (mix10000, 128, 10000, 128) } mat_mul! {mat_mul_f64, f64, (m004, 4, 4, 4) (m007, 7, 7, 7) (m008, 8, 8, 8) (m012, 12, 12, 12) (m016, 16, 16, 16) (m032, 32, 32, 32) (m064, 64, 64, 64) (m127, 127, 127, 127) (mix16x4, 32, 4, 32) (mix32x2, 32, 2, 32) (mix10000, 128, 10000, 128) } mat_mul! {mat_mul_i32, i32, (m004, 4, 4, 4) (m007, 7, 7, 7) (m008, 8, 8, 8) (m012, 12, 12, 12) (m016, 16, 16, 16) (m032, 32, 32, 32) (m064, 64, 64, 64) (m127, 127, 127, 127) } #[bench] fn create_iter_4d(bench: &mut test::Bencher) { let mut a = Array::from_elem((4, 5, 3, 2), 1.0); a.swap_axes(0, 1); a.swap_axes(2, 1); let v = black_box(a.view()); bench.iter(|| v.into_iter()); } #[bench] fn bench_to_owned_n(bench: &mut test::Bencher) { let a = Array::::zeros((32, 32)); bench.iter(|| a.to_owned()); } #[bench] fn bench_to_owned_t(bench: &mut test::Bencher) { let mut a = Array::::zeros((32, 32)); a.swap_axes(0, 1); bench.iter(|| a.to_owned()); } #[bench] fn bench_to_owned_strided(bench: &mut test::Bencher) { let a = Array::::zeros((32, 64)); let a = a.slice(s![.., ..;2]); bench.iter(|| a.to_owned()); } #[bench] fn equality_i32(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let b = Array::::zeros((64, 64)); bench.iter(|| a == b); } #[bench] fn equality_f32(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let b = Array::::zeros((64, 64)); bench.iter(|| a == b); } #[bench] fn equality_f32_mixorder(bench: &mut test::Bencher) { let a = Array::::zeros((64, 64)); let b = Array::::zeros((64, 64).f()); bench.iter(|| a == b); } #[bench] fn dot_f32_16(bench: &mut test::Bencher) { let a = Array::::zeros(16); let b = Array::::zeros(16); bench.iter(|| a.dot(&b)); } #[bench] fn dot_f32_20(bench: &mut test::Bencher) { let a = Array::::zeros(20); let b = Array::::zeros(20); bench.iter(|| a.dot(&b)); } #[bench] fn dot_f32_32(bench: &mut test::Bencher) { let a = Array::::zeros(32); let b = Array::::zeros(32); bench.iter(|| a.dot(&b)); } #[bench] fn dot_f32_256(bench: &mut test::Bencher) { let a = Array::::zeros(256); let b = Array::::zeros(256); bench.iter(|| a.dot(&b)); } #[bench] fn dot_f32_1024(bench: &mut test::Bencher) { let av = Array::::zeros(1024); let bv = Array::::zeros(1024); bench.iter(|| av.dot(&bv)); } #[bench] fn dot_f32_10e6(bench: &mut test::Bencher) { let n = 1_000_000; let av = Array::::zeros(n); let bv = Array::::zeros(n); bench.iter(|| av.dot(&bv)); } #[bench] fn dot_extended(bench: &mut test::Bencher) { let m = 10; let n = 33; let k = 10; let av = Array::::zeros((m, n)); let bv = Array::::zeros((n, k)); let mut res = Array::::zeros((m, k)); // make a manual simple matrix multiply to test bench.iter(|| { for i in 0..m { for j in 0..k { unsafe { *res.uget_mut((i, j)) = av.row(i).dot(&bv.column(j)); } } } }) } const MEAN_SUM_N: usize = 127; fn range_mat(m: Ix, n: Ix) -> Array2 { assert!(m * n != 0); Array::linspace(0., (m * n - 1) as f32, m * n) .into_shape_with_order((m, n)) .unwrap() } #[bench] fn mean_axis0(bench: &mut test::Bencher) { let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.mean_axis(Axis(0))); } #[bench] fn mean_axis1(bench: &mut test::Bencher) { let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.mean_axis(Axis(1))); } #[bench] fn sum_axis0(bench: &mut test::Bencher) { let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.sum_axis(Axis(0))); } #[bench] fn sum_axis1(bench: &mut test::Bencher) { let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.sum_axis(Axis(1))); } #[bench] fn into_dimensionality_ix1_ok(bench: &mut test::Bencher) { let a = Array::::zeros(Ix1(10)); let a = a.view(); bench.iter(|| a.into_dimensionality::()); } #[bench] fn into_dimensionality_ix3_ok(bench: &mut test::Bencher) { let a = Array::::zeros(Ix3(10, 10, 10)); let a = a.view(); bench.iter(|| a.into_dimensionality::()); } #[bench] fn into_dimensionality_ix3_err(bench: &mut test::Bencher) { let a = Array::::zeros(Ix3(10, 10, 10)); let a = a.view(); bench.iter(|| a.into_dimensionality::()); } #[bench] fn into_dimensionality_dyn_to_ix3(bench: &mut test::Bencher) { let a = Array::::zeros(IxDyn(&[10, 10, 10])); let a = a.view(); bench.iter(|| a.clone().into_dimensionality::()); } #[bench] fn into_dimensionality_dyn_to_dyn(bench: &mut test::Bencher) { let a = Array::::zeros(IxDyn(&[10, 10, 10])); let a = a.view(); bench.iter(|| a.clone().into_dimensionality::()); } #[bench] fn into_dyn_ix3(bench: &mut test::Bencher) { let a = Array::::zeros(Ix3(10, 10, 10)); let a = a.view(); bench.iter(|| a.into_dyn()); } #[bench] fn into_dyn_ix5(bench: &mut test::Bencher) { let a = Array::::zeros(Ix5(2, 2, 2, 2, 2)); let a = a.view(); bench.iter(|| a.into_dyn()); } #[bench] fn into_dyn_dyn(bench: &mut test::Bencher) { let a = Array::::zeros(IxDyn(&[10, 10, 10])); let a = a.view(); bench.iter(|| a.clone().into_dyn()); } #[bench] fn broadcast_same_dim(bench: &mut test::Bencher) { let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; let s = Array4::from_shape_vec((2, 2, 3, 2), s.to_vec()).unwrap(); let a = s.slice(s![.., ..;-1, ..;2, ..]); let b = s.slice(s![.., .., ..;2, ..]); bench.iter(|| &a + &b); } #[bench] fn broadcast_one_side(bench: &mut test::Bencher) { let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; let s2 = [1, 2, 3, 4, 5, 6]; let a = Array4::from_shape_vec((4, 1, 3, 2), s.to_vec()).unwrap(); let b = Array3::from_shape_vec((1, 3, 2), s2.to_vec()).unwrap(); bench.iter(|| &a + &b); } ndarray-0.16.1/benches/chunks.rs000064400000000000000000000042621046102023000146250ustar 00000000000000#![feature(test)] extern crate test; use test::Bencher; use ndarray::prelude::*; use ndarray::NdProducer; #[bench] fn chunk2x2_iter_sum(bench: &mut Bencher) { let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::zeros(a.exact_chunks(chunksz).raw_dim()); bench.iter(|| { azip!((a in a.exact_chunks(chunksz), sum in &mut sum) { *sum = a.iter().sum::(); }); }); } #[bench] fn chunk2x2_sum(bench: &mut Bencher) { let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::zeros(a.exact_chunks(chunksz).raw_dim()); bench.iter(|| { azip!((a in a.exact_chunks(chunksz), sum in &mut sum) { *sum = a.sum(); }); }); } #[bench] fn chunk2x2_sum_get1(bench: &mut Bencher) { let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); bench.iter(|| { let (m, n) = a.dim(); for i in 0..m { for j in 0..n { sum[[i / 2, j / 2]] += a[[i, j]]; } } }); } #[bench] fn chunk2x2_sum_uget1(bench: &mut Bencher) { let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); bench.iter(|| { let (m, n) = a.dim(); for i in 0..m { for j in 0..n { unsafe { *sum.uget_mut([i / 2, j / 2]) += *a.uget([i, j]); } } } }); } #[bench] #[allow(clippy::identity_op)] fn chunk2x2_sum_get2(bench: &mut Bencher) { let a = Array::::zeros((256, 256)); let chunksz = (2, 2); let mut sum = Array::::zeros(a.exact_chunks(chunksz).raw_dim()); bench.iter(|| { let (m, n) = sum.dim(); for i in 0..m { for j in 0..n { sum[[i, j]] += a[[i * 2 + 0, j * 2 + 0]]; sum[[i, j]] += a[[i * 2 + 0, j * 2 + 1]]; sum[[i, j]] += a[[i * 2 + 1, j * 2 + 1]]; sum[[i, j]] += a[[i * 2 + 1, j * 2 + 0]]; } } }); } ndarray-0.16.1/benches/construct.rs000064400000000000000000000015221046102023000153520ustar 00000000000000#![feature(test)] #![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use test::Bencher; use ndarray::prelude::*; #[bench] fn default_f64(bench: &mut Bencher) { bench.iter(|| Array::::default((128, 128))) } #[bench] fn zeros_f64(bench: &mut Bencher) { bench.iter(|| Array::::zeros((128, 128))) } #[bench] fn map_regular(bench: &mut test::Bencher) { let a = Array::linspace(0., 127., 128) .into_shape_with_order((8, 16)) .unwrap(); bench.iter(|| a.map(|&x| 2. * x)); } #[bench] fn map_stride(bench: &mut test::Bencher) { let a = Array::linspace(0., 127., 256) .into_shape_with_order((8, 32)) .unwrap(); let av = a.slice(s![.., ..;2]); bench.iter(|| av.map(|&x| 2. * x)); } ndarray-0.16.1/benches/gemv_gemm.rs000064400000000000000000000032171046102023000152740ustar 00000000000000#![feature(test)] #![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use test::Bencher; use num_complex::Complex; use num_traits::{Float, One, Zero}; use ndarray::prelude::*; use ndarray::linalg::general_mat_mul; use ndarray::linalg::general_mat_vec_mul; use ndarray::LinalgScalar; #[bench] fn gemv_64_64c(bench: &mut Bencher) { let a = Array::zeros((64, 64)); let (m, n) = a.dim(); let x = Array::zeros(n); let mut y = Array::zeros(m); bench.iter(|| { general_mat_vec_mul(1.0, &a, &x, 1.0, &mut y); }); } #[bench] fn gemv_64_64f(bench: &mut Bencher) { let a = Array::zeros((64, 64).f()); let (m, n) = a.dim(); let x = Array::zeros(n); let mut y = Array::zeros(m); bench.iter(|| { general_mat_vec_mul(1.0, &a, &x, 1.0, &mut y); }); } #[bench] fn gemv_64_32(bench: &mut Bencher) { let a = Array::zeros((64, 32)); let (m, n) = a.dim(); let x = Array::zeros(n); let mut y = Array::zeros(m); bench.iter(|| { general_mat_vec_mul(1.0, &a, &x, 1.0, &mut y); }); } #[bench] fn cgemm_100(bench: &mut Bencher) { cgemm_bench::(100, bench); } #[bench] fn zgemm_100(bench: &mut Bencher) { cgemm_bench::(100, bench); } fn cgemm_bench(size: usize, bench: &mut Bencher) where A: LinalgScalar + Float { let (m, k, n) = (size, size, size); let a = Array::, _>::zeros((m, k)); let x = Array::zeros((k, n)); let mut y = Array::zeros((m, n)); bench.iter(|| { general_mat_mul(Complex::one(), &a, &x, Complex::zero(), &mut y); }); } ndarray-0.16.1/benches/higher-order.rs000064400000000000000000000037541046102023000157160ustar 00000000000000#![feature(test)] #![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use test::black_box; use test::Bencher; use ndarray::prelude::*; const N: usize = 1024; const X: usize = 64; const Y: usize = 16; #[bench] fn map_regular(bench: &mut Bencher) { let a = Array::linspace(0., 127., N) .into_shape_with_order((X, Y)) .unwrap(); bench.iter(|| a.map(|&x| 2. * x)); } pub fn double_array(mut a: ArrayViewMut2<'_, f64>) { a *= 2.0; } #[bench] fn map_stride_double_f64(bench: &mut Bencher) { let mut a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); let mut av = a.slice_mut(s![.., ..;2]); bench.iter(|| { double_array(av.view_mut()); }); } #[bench] fn map_stride_f64(bench: &mut Bencher) { let a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); let av = a.slice(s![.., ..;2]); bench.iter(|| av.map(|&x| 2. * x)); } #[bench] fn map_stride_u32(bench: &mut Bencher) { let a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); let b = a.mapv(|x| x as u32); let av = b.slice(s![.., ..;2]); bench.iter(|| av.map(|&x| 2 * x)); } #[bench] fn fold_axis(bench: &mut Bencher) { let a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); bench.iter(|| a.fold_axis(Axis(0), 0., |&acc, &elt| acc + elt)); } const MA: usize = 64; const MASZ: usize = MA * MA; #[bench] fn map_axis_0(bench: &mut Bencher) { let a = Array::from_iter(0..MASZ as i32) .into_shape_with_order([MA, MA]) .unwrap(); bench.iter(|| a.map_axis(Axis(0), black_box)); } #[bench] fn map_axis_1(bench: &mut Bencher) { let a = Array::from_iter(0..MASZ as i32) .into_shape_with_order([MA, MA]) .unwrap(); bench.iter(|| a.map_axis(Axis(1), black_box)); } ndarray-0.16.1/benches/iter.rs000064400000000000000000000231521046102023000142740ustar 00000000000000#![feature(test)] #![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] extern crate test; use rawpointer::PointerExt; use test::black_box; use test::Bencher; use ndarray::prelude::*; use ndarray::Slice; use ndarray::{FoldWhile, Zip}; #[bench] fn iter_sum_2d_regular(bench: &mut Bencher) { let a = Array::::zeros((64, 64)); bench.iter(|| a.iter().sum::()); } #[bench] fn iter_sum_2d_cutout(bench: &mut Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = av; bench.iter(|| a.iter().sum::()); } #[bench] fn iter_all_2d_cutout(bench: &mut Bencher) { let a = Array::::zeros((66, 66)); let av = a.slice(s![1..-1, 1..-1]); let a = av; bench.iter(|| a.iter().all(|&x| x >= 0)); } #[bench] fn iter_sum_2d_transpose(bench: &mut Bencher) { let a = Array::::zeros((66, 66)); let a = a.t(); bench.iter(|| a.iter().sum::()); } #[bench] fn iter_filter_sum_2d_u32(bench: &mut Bencher) { let a = Array::linspace(0., 1., 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a.mapv(|x| (x * 100.) as u32); bench.iter(|| b.iter().filter(|&&x| x < 75).sum::()); } #[bench] fn iter_filter_sum_2d_f32(bench: &mut Bencher) { let a = Array::linspace(0., 1., 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a * 100.; bench.iter(|| b.iter().filter(|&&x| x < 75.).sum::()); } #[bench] fn iter_filter_sum_2d_stride_u32(bench: &mut Bencher) { let a = Array::linspace(0., 1., 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a.mapv(|x| (x * 100.) as u32); let b = b.slice(s![.., ..;2]); bench.iter(|| b.iter().filter(|&&x| x < 75).sum::()); } #[bench] fn iter_filter_sum_2d_stride_f32(bench: &mut Bencher) { let a = Array::linspace(0., 1., 256) .into_shape_with_order((16, 16)) .unwrap(); let b = a * 100.; let b = b.slice(s![.., ..;2]); bench.iter(|| b.iter().filter(|&&x| x < 75.).sum::()); } #[bench] fn iter_rev_step_by_contiguous(bench: &mut Bencher) { let a = Array::linspace(0., 1., 512); bench.iter(|| { a.iter().rev().step_by(2).for_each(|x| { black_box(x); }) }); } #[bench] fn iter_rev_step_by_discontiguous(bench: &mut Bencher) { let mut a = Array::linspace(0., 1., 1024); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); bench.iter(|| { a.iter().rev().step_by(2).for_each(|x| { black_box(x); }) }); } const ZIPSZ: usize = 10_000; #[bench] fn sum_3_std_zip1(bench: &mut Bencher) { let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; bench.iter(|| { a.iter() .zip(b.iter().zip(&c)) .fold(0, |acc, (&a, (&b, &c))| acc + a + b + c) }); } #[bench] fn sum_3_std_zip2(bench: &mut Bencher) { let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; bench.iter(|| { a.iter() .zip(b.iter()) .zip(&c) .fold(0, |acc, ((&a, &b), &c)| acc + a + b + c) }); } #[bench] fn sum_3_std_zip3(bench: &mut Bencher) { let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; bench.iter(|| { let mut s = 0; for ((&a, &b), &c) in a.iter().zip(b.iter()).zip(&c) { s += a + b + c } s }); } #[bench] fn vector_sum_3_std_zip(bench: &mut Bencher) { let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; bench.iter(|| { for ((&a, &b), c) in a.iter().zip(b.iter()).zip(&mut c) { *c += a + b; } }); } #[bench] fn sum_3_azip(bench: &mut Bencher) { let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; bench.iter(|| { let mut s = 0; azip!((&a in &a, &b in &b, &c in &c) { s += a + b + c; }); s }); } #[bench] fn sum_3_azip_fold(bench: &mut Bencher) { let a = vec![1; ZIPSZ]; let b = vec![1; ZIPSZ]; let c = vec![1; ZIPSZ]; bench.iter(|| { Zip::from(&a) .and(&b) .and(&c) .fold_while(0, |acc, &a, &b, &c| FoldWhile::Continue(acc + a + b + c)) .into_inner() }); } #[bench] fn vector_sum_3_azip(bench: &mut Bencher) { let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; bench.iter(|| { azip!((&a in &a, &b in &b, c in &mut c) { *c += a + b; }); }); } fn vector_sum3_unchecked(a: &[f64], b: &[f64], c: &mut [f64]) { for i in 0..c.len() { unsafe { *c.get_unchecked_mut(i) += *a.get_unchecked(i) + *b.get_unchecked(i); } } } #[bench] fn vector_sum_3_zip_unchecked(bench: &mut Bencher) { let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; bench.iter(move || { vector_sum3_unchecked(&a, &b, &mut c); }); } #[bench] fn vector_sum_3_zip_unchecked_manual(bench: &mut Bencher) { let a = vec![1.; ZIPSZ]; let b = vec![1.; ZIPSZ]; let mut c = vec![1.; ZIPSZ]; bench.iter(move || unsafe { let mut ap = a.as_ptr(); let mut bp = b.as_ptr(); let mut cp = c.as_mut_ptr(); let cend = cp.add(c.len()); while cp != cend { *cp.post_inc() += *ap.post_inc() + *bp.post_inc(); } }); } // index iterator size const ISZ: usize = 16; const I2DSZ: usize = 64; #[bench] fn indexed_iter_1d_ix1(bench: &mut Bencher) { let mut a = Array::::zeros(I2DSZ * I2DSZ); for (i, elt) in a.indexed_iter_mut() { *elt = i as _; } bench.iter(|| { for (i, &_elt) in a.indexed_iter() { //assert!(a[i] == elt); black_box(i); } }) } #[bench] fn indexed_zip_1d_ix1(bench: &mut Bencher) { let mut a = Array::::zeros(I2DSZ * I2DSZ); for (i, elt) in a.indexed_iter_mut() { *elt = i as _; } bench.iter(|| { Zip::indexed(&a).for_each(|i, &_elt| { black_box(i); //assert!(a[i] == elt); }); }) } #[bench] fn indexed_iter_2d_ix2(bench: &mut Bencher) { let mut a = Array::::zeros((I2DSZ, I2DSZ)); for ((i, j), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j) as _; } bench.iter(|| { for (i, &_elt) in a.indexed_iter() { //assert!(a[i] == elt); black_box(i); } }) } #[bench] fn indexed_zip_2d_ix2(bench: &mut Bencher) { let mut a = Array::::zeros((I2DSZ, I2DSZ)); for ((i, j), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j) as _; } bench.iter(|| { Zip::indexed(&a).for_each(|i, &_elt| { black_box(i); //assert!(a[i] == elt); }); }) } #[bench] fn indexed_iter_3d_ix3(bench: &mut Bencher) { let mut a = Array::::zeros((ISZ, ISZ, ISZ)); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j + 10000 * k) as _; } bench.iter(|| { for (i, &_elt) in a.indexed_iter() { //assert!(a[i] == elt); black_box(i); } }) } #[bench] fn indexed_zip_3d_ix3(bench: &mut Bencher) { let mut a = Array::::zeros((ISZ, ISZ, ISZ)); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j + 10000 * k) as _; } bench.iter(|| { Zip::indexed(&a).for_each(|i, &_elt| { black_box(i); //assert!(a[i] == elt); }); }) } #[bench] fn indexed_iter_3d_dyn(bench: &mut Bencher) { let mut a = Array::::zeros((ISZ, ISZ, ISZ)); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = (i + 100 * j + 10000 * k) as _; } let a = a.into_shape_with_order(&[ISZ; 3][..]).unwrap(); bench.iter(|| { for (i, &_elt) in a.indexed_iter() { //assert!(a[i] == elt); black_box(i); } }) } #[bench] fn iter_sum_1d_strided_fold(bench: &mut Bencher) { let mut a = Array::::ones(10240); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); bench.iter(|| a.iter().sum::()); } #[bench] fn iter_sum_1d_strided_rfold(bench: &mut Bencher) { let mut a = Array::::ones(10240); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); bench.iter(|| a.iter().rfold(0, |acc, &x| acc + x)); } #[bench] fn iter_axis_iter_sum(bench: &mut Bencher) { let a = Array::::zeros((64, 64)); bench.iter(|| a.axis_iter(Axis(0)).map(|plane| plane.sum()).sum::()); } #[bench] fn iter_axis_chunks_1_iter_sum(bench: &mut Bencher) { let a = Array::::zeros((64, 64)); bench.iter(|| { a.axis_chunks_iter(Axis(0), 1) .map(|plane| plane.sum()) .sum::() }); } #[bench] fn iter_axis_chunks_5_iter_sum(bench: &mut Bencher) { let a = Array::::zeros((64, 64)); bench.iter(|| { a.axis_chunks_iter(Axis(0), 5) .map(|plane| plane.sum()) .sum::() }); } pub fn zip_mut_with(data: &Array3, out: &mut Array3) { out.zip_mut_with(&data, |o, &i| { *o = i; }); } #[bench] fn zip_mut_with_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ)); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_mut_with(&data, &mut out)); } #[bench] fn zip_mut_with_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros((ISZ, ISZ, ISZ).f()); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_mut_with(&data, &mut out)); } ndarray-0.16.1/benches/numeric.rs000064400000000000000000000010471046102023000147720ustar 00000000000000#![feature(test)] extern crate test; use test::Bencher; use ndarray::prelude::*; const N: usize = 1024; const X: usize = 64; const Y: usize = 16; #[bench] fn clip(bench: &mut Bencher) { let mut a = Array::linspace(0., 127., N * 2) .into_shape_with_order([X, Y * 2]) .unwrap(); let min = 2.; let max = 5.; bench.iter(|| { a.mapv_inplace(|mut x| { if x < min { x = min } if x > max { x = max } x }) }); } ndarray-0.16.1/benches/par_rayon.rs000064400000000000000000000103221046102023000153160ustar 00000000000000#![cfg(feature = "rayon")] #![feature(test)] use ndarray::parallel::prelude::*; use ndarray::prelude::*; extern crate test; use test::Bencher; use ndarray::Zip; const EXP_N: usize = 256; const ADDN: usize = 512; fn set_threads() { // Consider setting a fixed number of threads here, for example to avoid // oversubscribing on hyperthreaded cores. // let n = 4; // let _ = rayon::ThreadPoolBuilder::new().num_threads(n).build_global(); } #[bench] fn map_exp_regular(bench: &mut Bencher) { let mut a = Array2::::zeros((EXP_N, EXP_N)); a.swap_axes(0, 1); bench.iter(|| { a.mapv_inplace(|x| x.exp()); }); } #[bench] fn rayon_exp_regular(bench: &mut Bencher) { set_threads(); let mut a = Array2::::zeros((EXP_N, EXP_N)); a.swap_axes(0, 1); bench.iter(|| { a.view_mut().into_par_iter().for_each(|x| *x = x.exp()); }); } const FASTEXP: usize = EXP_N; #[inline] fn fastexp(x: f64) -> f64 { let x = 1. + x / 1024.; x.powi(1024) } #[bench] fn map_fastexp_regular(bench: &mut Bencher) { let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| a.mapv_inplace(|x| fastexp(x))); } #[bench] fn rayon_fastexp_regular(bench: &mut Bencher) { set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x)); }); } #[bench] fn map_fastexp_cut(bench: &mut Bencher) { let mut a = Array2::::zeros((FASTEXP, FASTEXP)); let mut a = a.slice_mut(s![.., ..-1]); bench.iter(|| a.mapv_inplace(|x| fastexp(x))); } #[bench] fn rayon_fastexp_cut(bench: &mut Bencher) { set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); let mut a = a.slice_mut(s![.., ..-1]); bench.iter(|| { a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x)); }); } #[bench] fn map_fastexp_by_axis(bench: &mut Bencher) { let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { for mut sheet in a.axis_iter_mut(Axis(0)) { sheet.mapv_inplace(fastexp) } }); } #[bench] fn rayon_fastexp_by_axis(bench: &mut Bencher) { set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { a.axis_iter_mut(Axis(0)) .into_par_iter() .for_each(|mut sheet| sheet.mapv_inplace(fastexp)); }); } #[bench] fn rayon_fastexp_zip(bench: &mut Bencher) { set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); bench.iter(|| { Zip::from(&mut a) .into_par_iter() .for_each(|(elt,)| *elt = fastexp(*elt)); }); } #[bench] fn add(bench: &mut Bencher) { let mut a = Array2::::zeros((ADDN, ADDN)); let b = Array2::::zeros((ADDN, ADDN)); let c = Array2::::zeros((ADDN, ADDN)); let d = Array2::::zeros((ADDN, ADDN)); bench.iter(|| { azip!((a in &mut a, &b in &b, &c in &c, &d in &d) { *a += b.exp() + c.exp() + d.exp(); }); }); } #[bench] fn rayon_add(bench: &mut Bencher) { set_threads(); let mut a = Array2::::zeros((ADDN, ADDN)); let b = Array2::::zeros((ADDN, ADDN)); let c = Array2::::zeros((ADDN, ADDN)); let d = Array2::::zeros((ADDN, ADDN)); bench.iter(|| { par_azip!((a in &mut a, b in &b, c in &c, d in &d) { *a += b.exp() + c.exp() + d.exp(); }); }); } const COLL_STRING_N: usize = 64; const COLL_F64_N: usize = 128; #[bench] fn vec_string_collect(bench: &mut test::Bencher) { let v = vec![""; COLL_STRING_N * COLL_STRING_N]; bench.iter(|| v.iter().map(|s| s.to_owned()).collect::>()); } #[bench] fn array_string_collect(bench: &mut test::Bencher) { let v = Array::from_elem((COLL_STRING_N, COLL_STRING_N), ""); bench.iter(|| Zip::from(&v).par_map_collect(|s| s.to_owned())); } #[bench] fn vec_f64_collect(bench: &mut test::Bencher) { let v = vec![1.; COLL_F64_N * COLL_F64_N]; bench.iter(|| v.iter().map(|s| s + 1.).collect::>()); } #[bench] fn array_f64_collect(bench: &mut test::Bencher) { let v = Array::from_elem((COLL_F64_N, COLL_F64_N), 1.); bench.iter(|| Zip::from(&v).par_map_collect(|s| s + 1.)); } ndarray-0.16.1/benches/reserve.rs000064400000000000000000000012061046102023000150000ustar 00000000000000#![feature(test)] extern crate test; use test::Bencher; use ndarray::prelude::*; #[bench] fn push_reserve(bench: &mut Bencher) { let ones: Array = array![1f32]; bench.iter(|| { let mut a: Array = array![]; a.reserve(Axis(0), 100).unwrap(); for _ in 0..100 { a.append(Axis(0), ones.view()).unwrap(); } }); } #[bench] fn push_no_reserve(bench: &mut Bencher) { let ones: Array = array![1f32]; bench.iter(|| { let mut a: Array = array![]; for _ in 0..100 { a.append(Axis(0), ones.view()).unwrap(); } }); } ndarray-0.16.1/benches/to_shape.rs000064400000000000000000000040561046102023000151350ustar 00000000000000#![feature(test)] extern crate test; use test::Bencher; use ndarray::prelude::*; use ndarray::Order; #[bench] fn to_shape2_1(bench: &mut Bencher) { let a = Array::::zeros((4, 5)); let view = a.view(); bench.iter(|| view.to_shape(4 * 5).unwrap()); } #[bench] fn to_shape2_2_same(bench: &mut Bencher) { let a = Array::::zeros((4, 5)); let view = a.view(); bench.iter(|| view.to_shape((4, 5)).unwrap()); } #[bench] fn to_shape2_2_flip(bench: &mut Bencher) { let a = Array::::zeros((4, 5)); let view = a.view(); bench.iter(|| view.to_shape((5, 4)).unwrap()); } #[bench] fn to_shape2_3(bench: &mut Bencher) { let a = Array::::zeros((4, 5)); let view = a.view(); bench.iter(|| view.to_shape((2, 5, 2)).unwrap()); } #[bench] fn to_shape3_1(bench: &mut Bencher) { let a = Array::::zeros((3, 4, 5)); let view = a.view(); bench.iter(|| view.to_shape(3 * 4 * 5).unwrap()); } #[bench] fn to_shape3_2_order(bench: &mut Bencher) { let a = Array::::zeros((3, 4, 5)); let view = a.view(); bench.iter(|| view.to_shape((12, 5)).unwrap()); } #[bench] fn to_shape3_2_outoforder(bench: &mut Bencher) { let a = Array::::zeros((3, 4, 5)); let view = a.view(); bench.iter(|| view.to_shape((4, 15)).unwrap()); } #[bench] fn to_shape3_3c(bench: &mut Bencher) { let a = Array::::zeros((3, 4, 5)); let view = a.view(); bench.iter(|| view.to_shape((3, 4, 5)).unwrap()); } #[bench] fn to_shape3_3f(bench: &mut Bencher) { let a = Array::::zeros((3, 4, 5).f()); let view = a.view(); bench.iter(|| view.to_shape(((3, 4, 5), Order::F)).unwrap()); } #[bench] fn to_shape3_4c(bench: &mut Bencher) { let a = Array::::zeros((3, 4, 5)); let view = a.view(); bench.iter(|| view.to_shape(((2, 3, 2, 5), Order::C)).unwrap()); } #[bench] fn to_shape3_4f(bench: &mut Bencher) { let a = Array::::zeros((3, 4, 5).f()); let view = a.view(); bench.iter(|| view.to_shape(((2, 3, 2, 5), Order::F)).unwrap()); } ndarray-0.16.1/benches/zip.rs000064400000000000000000000064241046102023000141360ustar 00000000000000#![feature(test)] extern crate test; use ndarray::s; use ndarray::IntoNdProducer; use ndarray::{Array3, ShapeBuilder, Zip}; use test::{black_box, Bencher}; pub fn zip_copy<'a, A, P, Q>(data: P, out: Q) where P: IntoNdProducer, Q: IntoNdProducer, A: Copy + 'a, { Zip::from(data).and(out).for_each(|&i, o| { *o = i; }); } pub fn zip_copy_split<'a, A, P, Q>(data: P, out: Q) where P: IntoNdProducer, Q: IntoNdProducer, A: Copy + 'a, { let z = Zip::from(data).and(out); let (z1, z2) = z.split(); let (z11, z12) = z1.split(); let (z21, z22) = z2.split(); let f = |&i: &A, o: &mut A| *o = i; z11.for_each(f); z12.for_each(f); z21.for_each(f); z22.for_each(f); } pub fn zip_indexed(data: &Array3, out: &mut Array3) { Zip::indexed(data).and(out).for_each(|idx, &i, o| { let _ = black_box(idx); *o = i; }); } // array size in benchmarks const SZ3: (usize, usize, usize) = (100, 110, 100); #[bench] fn zip_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn zip_cf(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn zip_fc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn zip_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn zip_indexed_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); b.iter(|| zip_indexed(&data, &mut out)); } #[bench] fn zip_indexed_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); b.iter(|| zip_indexed(&data, &mut out)); } #[bench] fn slice_zip_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); let data = data.slice(s![1.., 1.., 1..]); let mut out = out.slice_mut(s![1.., 1.., 1..]); b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn slice_zip_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); let data = data.slice(s![1.., 1.., 1..]); let mut out = out.slice_mut(s![1.., 1.., 1..]); b.iter(|| zip_copy(&data, &mut out)); } #[bench] fn slice_split_zip_cc(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3); let mut out = Array3::zeros(data.dim()); let data = data.slice(s![1.., 1.., 1..]); let mut out = out.slice_mut(s![1.., 1.., 1..]); b.iter(|| zip_copy_split(&data, &mut out)); } #[bench] fn slice_split_zip_ff(b: &mut Bencher) { let data: Array3 = Array3::zeros(SZ3.f()); let mut out = Array3::zeros(data.dim().f()); let data = data.slice(s![1.., 1.., 1..]); let mut out = out.slice_mut(s![1.., 1.., 1..]); b.iter(|| zip_copy_split(&data, &mut out)); } ndarray-0.16.1/clippy.toml000064400000000000000000000000531046102023000135440ustar 00000000000000single-char-binding-names-threshold = 1000 ndarray-0.16.1/examples/axis_ops.rs000064400000000000000000000047701046102023000153720ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; /// Reorder a's axes so that they are in "standard" axis order; /// make sure axes are in positive stride direction, and merge adjacent /// axes if possible. /// /// This changes the logical order of the elements in the /// array, so that if we read them in row-major order after regularization, /// it corresponds to their order in memory. /// /// Errors if array has a 0-stride axis fn regularize(a: &mut Array) -> Result<(), &'static str> where D: Dimension, A: ::std::fmt::Debug, { println!("Regularize:\n{:?}", a); // reverse all neg axes while let Some(ax) = a.axes().find(|ax| ax.stride <= 0) { if ax.stride == 0 { // no real reason to error on this case; other than // stride == 0 is incompatible with an owned array. return Err("Cannot regularize array with stride == 0 axis"); } // reverse ax println!("Reverse {:?}", ax.axis); a.invert_axis(ax.axis); } // sort by least stride let mut i = 0; let n = a.ndim(); while let Some(ax) = a.axes().rev().skip(i).min_by_key(|ax| ax.stride.abs()) { let cur_axis = Axis(n - 1 - i); if ax.axis != cur_axis { a.swap_axes(cur_axis.index(), ax.axis.index()); println!("Swap {:?} <=> {:?}", cur_axis, ax.axis); } i += 1; } // merge the lower axes if possible for j in (0..n).rev().skip(1) { if a.merge_axes(Axis(j), Axis(n - 1)) { println!("Merged {:?} into {:?}", Axis(j), Axis(n - 1)); } else { break; } } println!("Result:\n{:?}\n", a); Ok(()) } fn main() { let mut a = Array::::zeros((2, 3, 4)); for (i, elt) in (0..).zip(&mut a) { *elt = i; } a.swap_axes(0, 1); a.swap_axes(0, 2); a.slice_collapse(s![.., ..;-1, ..]); regularize(&mut a).unwrap(); let mut b = Array::::zeros((2, 3, 4)); for (i, elt) in (0..).zip(&mut b) { *elt = i; } regularize(&mut b).unwrap(); let mut b = b.into_shape_with_order(a.len()).unwrap(); regularize(&mut b).unwrap(); b.invert_axis(Axis(0)); regularize(&mut b).unwrap(); let mut a = Array::::zeros((2, 3, 4)); for (i, elt) in (0..).zip(&mut a) { *elt = i; } a.slice_collapse(s![..;-1, ..;2, ..]); regularize(&mut a).unwrap(); } ndarray-0.16.1/examples/bounds_check_elim.rs000064400000000000000000000035611046102023000171770ustar 00000000000000#![crate_type = "lib"] #![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] // Test cases for bounds check elimination use ndarray::prelude::*; /* pub fn testslice(a: &[f64]) -> f64 { let mut sum = 0.; for i in 0..a.len() { sum += a[i]; } sum } pub fn testvec(a: &Vec) -> f64 { let mut sum = 0.; for i in 0..a.len() { sum += a[i]; } sum } pub fn testvec_as_slice(a: &Vec) -> f64 { let a = a.as_slice(); let mut sum = 0.; for i in 0..a.len() { sum += a[i]; } sum } */ #[no_mangle] pub fn test1d_single(a: &Array1, i: usize) -> f64 { if i < a.len() { a[i] } else { 0. } } #[no_mangle] pub fn test1d_single_mut(a: &mut Array1, i: usize) -> f64 { if i < a.len() { *&mut a[i] } else { 0. } } #[no_mangle] pub fn test1d_len_of(a: &Array1) -> f64 { let a = &*a; let mut sum = 0.; for i in 0..a.len_of(Axis(0)) { sum += a[i]; } sum } #[no_mangle] pub fn test1d_range(a: &Array1) -> f64 { let mut sum = 0.; for i in 0..a.len() { sum += a[i]; } sum } #[no_mangle] pub fn test1d_while(a: &Array1) -> f64 { let mut sum = 0.; let mut i = 0; while i < a.len() { sum += a[i]; i += 1; } sum } #[no_mangle] pub fn test2d_ranges(a: &Array2) -> f64 { let mut sum = 0.; for i in 0..a.nrows() { for j in 0..a.ncols() { sum += a[[i, j]]; } } sum } #[no_mangle] pub fn test2d_whiles(a: &Array2) -> f64 { let mut sum = 0.; let mut i = 0; while i < a.nrows() { let mut j = 0; while j < a.ncols() { sum += a[[i, j]]; j += 1; } i += 1; } sum } fn main() {} ndarray-0.16.1/examples/column_standardize.rs000064400000000000000000000014451046102023000174260ustar 00000000000000#[cfg(feature = "std")] use ndarray::prelude::*; #[cfg(feature = "std")] fn main() { // This example recreates the following from python/numpy // counts -= np.mean(counts, axis=0) // counts /= np.std(counts, axis=0) let mut data = array![[-1., -2., -3.], [1., -3., 5.], [2., 2., 2.]]; println!("{:8.4}", data); println!("Mean along axis=0 (along columns):\n{:8.4}", data.mean_axis(Axis(0)).unwrap()); data -= &data.mean_axis(Axis(0)).unwrap(); println!("Centered around mean:\n{:8.4}", data); data /= &data.std_axis(Axis(0), 0.); println!("Scaled to normalize std:\n{:8.4}", data); println!("New mean:\n{:8.4}", data.mean_axis(Axis(0)).unwrap()); println!("New std: \n{:8.4}", data.std_axis(Axis(0), 0.)); } #[cfg(not(feature = "std"))] fn main() {} ndarray-0.16.1/examples/convo.rs000064400000000000000000000034611046102023000146650ustar 00000000000000#![allow(unused)] extern crate ndarray; #[cfg(feature = "std")] use num_traits::Float; use ndarray::prelude::*; const SOBEL_X: [[f32; 3]; 3] = [[-1., 0., 1.], [-2., 0., 2.], [-1., 0., 1.]]; const SOBEL_Y: [[f32; 3]; 3] = [[1., 2., 1.], [0., 0., 0.], [-1., -2., -1.]]; const SHARPEN: [[f32; 3]; 3] = [[0., -1., 0.], [-1., 5., -1.], [0., -1., 0.]]; type Kernel3x3 = [[A; 3]; 3]; #[inline(never)] #[cfg(feature = "std")] fn conv_3x3(a: &ArrayView2<'_, F>, out: &mut ArrayViewMut2<'_, F>, kernel: &Kernel3x3) where F: Float { let (n, m) = a.dim(); let (np, mp) = out.dim(); if n < 3 || m < 3 { return; } assert!(np >= n && mp >= m); // i, j offset by -1 so that we can use unsigned indices unsafe { for i in 0..n - 2 { for j in 0..m - 2 { let mut conv = F::zero(); #[allow(clippy::needless_range_loop)] for k in 0..3 { for l in 0..3 { conv = conv + *a.uget((i + k, j + l)) * kernel[k][l]; //conv += a[[i + k, j + l]] * x_kernel[k][l]; } } *out.uget_mut((i + 1, j + 1)) = conv; } } } } #[cfg(feature = "std")] fn main() { let n = 16; let mut a = Array::zeros((n, n)); // make a circle let c = (8., 8.); for ((i, j), elt) in a.indexed_iter_mut() { { let s = ((i as f32) - c.0).powi(2) + (j as f32 - c.1).powi(2); if s.sqrt() > 3. && s.sqrt() < 6. { *elt = 1.; } } } println!("{:2}", a); let mut res = Array::zeros(a.dim()); for _ in 0..1000 { conv_3x3(&a.view(), &mut res.view_mut(), &SOBEL_X); } println!("{:2}", res); } #[cfg(not(feature = "std"))] fn main() {} ndarray-0.16.1/examples/life.rs000064400000000000000000000043221046102023000144550ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; const INPUT: &[u8] = include_bytes!("life.txt"); const N: usize = 100; type Board = Array2; fn parse(x: &[u8]) -> Board { // make a border of 0 cells let mut map = Board::from_elem(((N + 2), (N + 2)), 0); let a = Array::from_iter(x.iter().filter_map(|&b| match b { b'#' => Some(1), b'.' => Some(0), _ => None, })); let a = a.into_shape_with_order((N, N)).unwrap(); map.slice_mut(s![1..-1, 1..-1]).assign(&a); map } // Rules // // 2 or 3 neighbors: stay alive // 3 neighbors: birth // otherwise: death fn iterate(z: &mut Board, scratch: &mut Board) { // compute number of neighbors let mut neigh = scratch.view_mut(); neigh.fill(0); neigh += &z.slice(s![0..-2, 0..-2]); neigh += &z.slice(s![0..-2, 1..-1]); neigh += &z.slice(s![0..-2, 2..]); neigh += &z.slice(s![1..-1, 0..-2]); neigh += &z.slice(s![1..-1, 2..]); neigh += &z.slice(s![2.., 0..-2]); neigh += &z.slice(s![2.., 1..-1]); neigh += &z.slice(s![2.., 2..]); // birth where n = 3 and z[i] = 0, // survive where n = 2 || n = 3 and z[i] = 1 let mut zv = z.slice_mut(s![1..-1, 1..-1]); // this is autovectorized amazingly well! zv.zip_mut_with(&neigh, |y, &n| *y = ((n == 3) || (n == 2 && *y > 0)) as u8); } fn turn_on_corners(z: &mut Board) { let n = z.nrows(); let m = z.ncols(); z[[1, 1]] = 1; z[[1, m - 2]] = 1; z[[n - 2, 1]] = 1; z[[n - 2, m - 2]] = 1; } fn render(a: &Board) { for row in a.rows() { for &x in row { if x > 0 { print!("#"); } else { print!("."); } } println!(); } } fn main() { let mut a = parse(INPUT); let mut scratch = Board::zeros((N, N)); let steps = 100; turn_on_corners(&mut a); for _ in 0..steps { iterate(&mut a, &mut scratch); turn_on_corners(&mut a); //render(&a); } render(&a); let alive = a.iter().filter(|&&x| x > 0).count(); println!("After {} steps there are {} cells alive", steps, alive); } ndarray-0.16.1/examples/life.txt000064400000000000000000000235641046102023000146610ustar 00000000000000####.#.##.###.#.#.##.#..###.#..#.#.#..##....#.###...##..###.##.#.#.#.##...##..#..#....#.#.##..#...## .##...##.##.######.#.#.##...#.#.#.#.#...#.##.#..#.#.####...#....#....###.#.#.#####....#.#.##.#.#.##. ###.##..#..#####.......#.########...#.####.###....###.###...#...####.######.#..#####.#.###....####.. ....#..#..#....###.##.#.....##...#.###.#.#.#..#.#..##...#....#.##.###.#...######......#..#.#..####.# ..###.####..#.#.#..##.#.#....#......#.##.##..##.#.....##.###.#..###...###.#.##..#.#..###....####.#.# #.#...#......####.#..##.####.#.#.#...##..###.##.#...#..#..###....#.#....#..##..#....##.....##.#...#. ....##.#.#.#.##..##...##..##..#....#....###...####.###...##.#...#..#....##.....#..#.#####.###.###.## #...##..#.#..#....#..########.##....##..##.###..#.#..#..#.##.##.#..##..######....####..#####.#.###.. .####...######.#..#.##.#.#..####...####.##.#.#......#...##....##..#...###..#.####......###......#.## .####.###..#..#####.##...###......#...###..#..##..#.#....##.##.#.##.###..#..#..###.#..#.#....####.## #..#..##.##.##.###.#.##.##.#.#.#....#....#.####.#.##...#####...###.#####.#.#.#....####..###..###..## #.##....#...########..##...#.#.##.......#.#..##...####...#.####.####..##...##.#....###.#.####...#.## #.#...##..#.##.##..##....#.....##.##.....#...###...#..#...####.##.####..#...##..##.##.##.##..##...## .#..###...#.#.....#######..##.###....##..#.##.#......###.##....#......###...#.##....#.....##......## ..##....#.###...###..####.##..#..##.##......##.#.....#...#..#..##...###..#.####...#...#..##.#..##..# ...#.#.#...#.#..#.##....##..#...#.##..#......#.#.....#####.##.#...#######.#.#..#.####..###.....###.# .#....#.#.##..####.#####..#.#######..#.##.###...##.##....##..###..#.##.###.......#....#..######.#### #..#.##.##..#..#..##.####.#.#.#.#..#.##...#..######....#.##.#..##.##.######.###.###.###...#.....#.#. .#.......#...#.####.##...#####..##..#.#....##..#.#.#.####.#.##....#..##.##..#.###.....#.##.##.#.##.# #..##..##...#....#.##.#...#.#....#......####...##..#...##.##.#..#########..#..#.##.##..#.#.#######.. #.......#####..###..######.#..##.#.#####..##...###...#.####.##...###..#.#.#####....#...#.##...#.#..# .##..#...#####.##.##......#...#.#.#.###.#.#.#...##.#..#....###.....#..#.#.###......#####.###.#..##.# .....###.#.#.#..##...#...###..#...#.#.##..###.##.#####.##..#.#.#.#.#####....#.#.#####...##.#..#.#.#. ###...##.#..#.####..##.#..##.#.#.#...#.#..#..##..##..#.#.#.#.##...##..#..#.....#....#####.#.#.####.# ....##....#.#.....#...###.#...##..##.##..#..###..##.###..#####..#...#####.##.#..#.#.#.###...####.### ##.##.##.#...#..#...........##.##.###.#...###.####.#..#..#...#..#..####.#.###########..#.###.###.#.# ##.##..##.####..###...##...#....###.###.#..##..#..#.###.#..####.#..##.#.#...#..#.#.##.##...#...#.... ..##...#.#.##....##...#.#.#......##.##.#.#.####.####....####.#.###.##.#.#..####..#..######..#..#.#.. ####.#.##.......##.###....##.#..####.#.#######..#...###..##.##..#...#...####........#.#..##...#....# #..#.#.....#..#.###..#.#...###..##...#.#..#.#.##..#...##.##.##.#.#.#..#.####.########....########..# #...#..##.##..#.#.#.##.##.##.#..#..#.##....#....###.#.###.#.#..#....#...##..#.....####...##.#..#...# .###...##...####....###.##.#..####...##.#.##.#..##..##....#....##.#...#..#..##..##..##.#...#...###.. .#..##.#..##..####..#.#.##..###.#...#....##.###...#.###....#.#.#........#..#.#.#..##..#####..#..#.#. .#.##.....#..#...#.##.....#.##..#..#....#..#..#....#.##..##...#.##.##..##..#.#.#.##..####.##..#.#..# ...###.#.....#...#.##.#.###.#...##..#.###..#..#..#.#..#...###.#.##.##.##.#.##.#####.#..#.#..#.#...## #.#.#.#.##.#.....##..#.###......##.#.##..#...#.########.##.###..#..#..##..##.#..##..###.#.###...#.#. ..##...##...#...###.#..##..#..#..#.#.##..##......##..##.....##.....####..#.##......#..####...###..## ##.......#..##....###...###......#.##.##....######..###.##...##.#...#...#.....#.###.#.#..#.##..#..#. #.#..#..#.#####.##.##.###..#...###.....#..##..####...#.#.###....#..#.#.###.####..#.#........##.#.... ..###.#...##.#.####.#.##.##.....##...#.##.#.###.#.#..##.#..##..#..##.##....#.#####.##..#######.....# ###.###..##.#..##...#####..##.####....#.##......##......#.#....##.####.#.#.#.###...#..####..#.###### #..###...#.#.......#..####.####...#....###.###...#.##..##..#..##.##.......####.##...#.#.#.##.#.#..#. ..#...#..###.##..#.#.#.##..#..#.#.......###..###..#####.#.#.#.#.#..#.#.#.#..###....#.####..###...#.. ...######.###....#..####.####......#...#.###.#....#...####.##........##...##.#..##.###.#..#..##..### .#..###.####.###.#.#..#..#..#.##.#.#.###.##..####.#####..##....##.#.##...###.####.#.#######.#..#..#. .#..##.#..##..#...##...#..#..##.#.#....##.##...###.#.#...##..##..#.###.#.#.#.#...#....#.#..#.#.###.# .###..#.#..####.#########...####....####.#.##...##.##..#.##.#........#.....###.###.######.##.....### ..##.##..##..#.####.#..#####.#....##.##.#####.....#.#......##...#####..####....###..#.#...#..####..# .#..##..##.##.##.##.#.###.###.#..#..#...###.#.##..##...##...###...##.###..#.#.#####.#.#.##....#.##.. ...#.#....##.#.....###.##...#..##....#...###....#..#.###...##.#...###.#....#...##..###.#.....##....# .#######..#...##.#.###.##.#.###...##......#.###.#...#.###.#.#.#..#..#####..#########...##..##...#..# .#..#.##...#.#..#.##..#.#.#.##.....####.#..#.###..##.#.#.#...#....#.#..##.######...#.#..##.##...#..# #.#######.#####..#####.##.##.#.#.##.###..#....####.#..##.##.######..###...#.#..#.####.##.##....####. ...##..#...##..#..#.....#.##...#.....##.#####.###.########.######..#...###..#.##.#.#.##..#.#.##..##. #..#..#.#....###.#...##..####.#.##..#.####.###..##.#...#.###.#..#.##..#######.#...#..#.#..##.#....## ..#.##.#.####..##.###.###..#.##.#.####..##....##.###.#..##.#.###.###.##.##.#####..#.#...########.... .#.#.###..###...#...#..##.##......#..#...#.#.#.######.#.#...##..##........#....###..##...#..##.##... ##..#....##.###...##.#.##.##.##..#....#.#.#..#..####.##..#...#...#..#..#####.###...#..###..#...#.#.. ##.#.#.##.###.....######.#.....#...#.##....###.#.##.#.#.##..##.######.#####....#.#####...##.#..###.# ######.#...####..###..##..#..##...#.#....##.#...##...#.....#...##....#.##..###..###...###..#..###### .....##.........#####.#.##..#..#.#.#.#.##...#....#.....###.########...#..####..#...#...##..#.##.##.# #..###...#.##.##.#.#..####.#.....##..###....##..#...#.#...##.##..###..####...#.####..##..#..##..#... #.####.#..##.#..#.....#..#.#..###...######.#.........####....###..#.#.#.##.#..#...#..####.....##..#. ..##....#.###.......##.#...#.####..##....##.#..#....#######...####.##..#####.#.#.#.#.##..##..#.#.#.. #.#.#.###..#..#.#..#.#.###....#...#####.###...........#.#....#####...#..####....#...###.#..#..####.. .......#.####.##...#..#.##..###..#..#.#.#.#.###....#....#.#.#..#.#..##.#####.#.....#.##.#.###.###.## ..###...#..#...####.#..##..##.#.#..#...#.#..#....###.#..####..######...####.#.##..#.#..###...##.#### ..#.###..#.#...##...#.#....#..#...#.#..##.######.######.#.##.....#..##.#..###..#..#.##.###...#..#.## ####..##.####.....#...#.#.###..#...####.###.#.#.#.......##...#....#..#....#.#......###...#####.#.##. #..##..#..#.####...#####.#.###.##.#.##.....#.#..#.##........######.#.#.###....##.##..##..########.## #.#....###.##....#######.#...#.#.#.#..##.#.##...#.###...#.#.#..#.#..####.#.#..#..#.##.####....#..##. ####.##....#.......###..#..##.#.#.##..#...#...##.###....##..###.#.#...#..#.....##.###.##...###....## ..##.#..#....######..#.##.#.#...##..####.#####...##.#..###.##...#..####..###.##..##.##.#####.#..#.#. .#.##..#..##.#.###.###....#.#..#....#...###.##.#.#.####.....#....#...#.....#....#.#.###.#..#.##..### ..###.#.#.##...##.##.##.#...#####.#..##.#....##..####...###..#....#.##...#........#####.#.###.#..#.. ....#..##..##....#.#....#.#..##...##.#...##.###.#.#..###..##.##.##..#.#.#..#.#.##.......#.##.###..#. .#..##.##.####.##....##.##.....###..##.#.##...#..###....###.###....#.#....#....#.##.#.##.#.##.....## #.#..#.##.###.#.######.....###.#..#...#.#.....##.###.#...#.#..###.#.....##.###.#.###.####..#####.#.. #.#.##......#.##.#.#..##....#..###.#.###...##...###.#..#.##...#..#.##..##.#...######.##.....#####.## #.#..#####....###.###...#.......#....###.##...#..#.##..#...#####..#..#.##......###...#...###..#.#..# #.##..##.##.#..#.##.##..#.###.##.........###.#.#..#.#.....#.#...#.#.##.#.##.#...#...####.#.......##. .#...####.##..#..##....####..######...#.#..##.##.....#####.#...#..#.####.#######...#.#####..#.###... .#..######.#.##..##...##.....###.#..##..#...####..###...###.###..#..######.#....########..#####...#. #..##.......#####...###..#.#.##.#..###.#...##.#..#.##.###...###...##.#..##..########..#.#..##..#.### .#.#..#...#.#..#..##...#.#.##...###..#..#....###.#....#.##....###.###..##..#.#.####..####.#######.## ...##..##.##.###.##.###...##.#.#.....##.####..#..##.#..#.####...##..#..#.##...##...###.##.#.......## .#.....#.##..#.#.....#.##.##..###..#....###...#.#....##########.##.###.#...#.####..####.#..#.#..###. .##.#.#.##..#..###.###.##.#########.#.#.#.#.##.###..##..#.##.####......#####...#..####.#.##..#####.# ..#....###...##....#.###..##..#..####.##..####.#..####.###.#....####.....#.###..##...##..####...##.# .###.....###.##.##..###.###.....##..#.######.#.#..##..#.##.#..#.#.#....#...#.#.#...#...##....#..##.# ..##....#..#####....#..####.#.#...##.#....##..##.###.###....###......#...#.#####.......#...#.....### ###.#..#.#.##..#..#...#.#....###.##.#.###.#...#.##.#..#.#.......#.#.#.###.####.###....#..##..#####.. .#..#######.#..###.#.##.#####.#####...##..#.####.#.#.##..###...#..##.##..#.#.###..#....#..#...###.#. ..#####..#.##.....###..##.#...#.#.#..#######.#..#...#.##.##.#.#....####...###..##...#....####.#..#.# .####..#.#.##.###.#.##.....#..##.#.....###.....#..##...#....###.###..#......###.#.#.#.##.#.##..#...# ##.#..##.#..##...#.#....##..######..#.....#..#...#####....##......####.##..#...##..#.##.#.#######..# ##..####.#...##...#.#####.#.#..#....#.#..##.####.#..######.#..#..#.......#####..#..#..###.##...##.## #.####......#.###...#..####.#..##.##..#.#...##.###.#...#####..####.#..#.#.....#.##...###...#.#....## ###.#.#.##.######......#.#.#.#.#........#..#..###.#.#.#..#.........#..#....#.#..#..#..###.##......## ##.#########...#...###..#.###.....#.#.##.........###....#.####.#...###.#..##..#.###..#..##......#.## ndarray-0.16.1/examples/lifelite.txt000064400000000000000000000001101046102023000155150ustar 00000000000000####.#.# .##...## ###.##.. ....#..# ..###.## #.#...#. ....##.# #...##.. ndarray-0.16.1/examples/rollaxis.rs000064400000000000000000000013751046102023000154000ustar 00000000000000use ndarray::prelude::*; use ndarray::Data; pub fn roll_axis(mut a: ArrayBase, to: Axis, from: Axis) -> ArrayBase where S: Data, D: Dimension, { let i = to.index(); let mut j = from.index(); if j > i { while i != j { a.swap_axes(i, j); j -= 1; } } else { while i != j { a.swap_axes(i, j); j += 1; } } a } fn main() { let mut data = array![ [[-1., 0., -2.], [1., 7., -3.]], [[1., 0., -3.], [1., 7., 5.]], [[1., 0., -3.], [1., 7., 5.]], [[2., 0., 2.], [1., 7., 2.]] ]; println!("{:8.4?}", data); data = roll_axis(data, Axis(2), Axis(0)); println!("{:8.4?}", data); } ndarray-0.16.1/examples/sort-axis.rs000064400000000000000000000162151046102023000154730ustar 00000000000000//! This is an example of sorting arrays along an axis. //! This file may not be so instructive except for advanced users, instead it //! could be a "feature preview" before sorting is added to the main crate. //! use ndarray::prelude::*; use ndarray::{Data, RemoveAxis, Zip}; use rawpointer::PointerExt; use std::cmp::Ordering; use std::ptr::copy_nonoverlapping; // Type invariant: Each index appears exactly once #[derive(Clone, Debug)] pub struct Permutation { indices: Vec, } impl Permutation { /// Checks if the permutation is correct pub fn from_indices(v: Vec) -> Result { let perm = Permutation { indices: v }; if perm.correct() { Ok(perm) } else { Err(()) } } fn correct(&self) -> bool { let axis_len = self.indices.len(); let mut seen = vec![false; axis_len]; for &i in &self.indices { match seen.get_mut(i) { None => return false, Some(s) => if *s { return false; } else { *s = true; }, } } true } } pub trait SortArray { /// ***Panics*** if `axis` is out of bounds. fn identity(&self, axis: Axis) -> Permutation; fn sort_axis_by(&self, axis: Axis, less_than: F) -> Permutation where F: FnMut(usize, usize) -> bool; } pub trait PermuteArray { type Elem; type Dim; fn permute_axis(self, axis: Axis, perm: &Permutation) -> Array where Self::Elem: Clone, Self::Dim: RemoveAxis; } impl SortArray for ArrayBase where S: Data, D: Dimension, { fn identity(&self, axis: Axis) -> Permutation { Permutation { indices: (0..self.len_of(axis)).collect(), } } fn sort_axis_by(&self, axis: Axis, mut less_than: F) -> Permutation where F: FnMut(usize, usize) -> bool { let mut perm = self.identity(axis); perm.indices.sort_by(move |&a, &b| { if less_than(a, b) { Ordering::Less } else if less_than(b, a) { Ordering::Greater } else { Ordering::Equal } }); perm } } impl PermuteArray for Array where D: Dimension { type Elem = A; type Dim = D; fn permute_axis(self, axis: Axis, perm: &Permutation) -> Array where D: RemoveAxis { let axis_len = self.len_of(axis); let axis_stride = self.stride_of(axis); assert_eq!(axis_len, perm.indices.len()); debug_assert!(perm.correct()); if self.is_empty() { return self; } let mut result = Array::uninit(self.dim()); unsafe { // logically move ownership of all elements from self into result // the result realizes this ownership at .assume_init() further down let mut moved_elements = 0; // the permutation vector is used like this: // // index: 0 1 2 3 (index in result) // permut: 2 3 0 1 (index in the source) // // move source 2 -> result 0, // move source 3 -> result 1, // move source 0 -> result 2, // move source 1 -> result 3, // et.c. let source_0 = self.raw_view().index_axis_move(axis, 0); Zip::from(&perm.indices) .and(result.axis_iter_mut(axis)) .for_each(|&perm_i, result_pane| { // Use a shortcut to avoid bounds checking in `index_axis` for the source. // // It works because for any given element pointer in the array we have the // relationship: // // .index_axis(axis, 0) + .stride_of(axis) * j == .index_axis(axis, j) // // where + is pointer arithmetic on the element pointers. // // Here source_0 and the offset is equivalent to self.index_axis(axis, perm_i) Zip::from(result_pane) .and(source_0.clone()) .for_each(|to, from_0| { let from = from_0.stride_offset(axis_stride, perm_i); copy_nonoverlapping(from, to.as_mut_ptr(), 1); moved_elements += 1; }); }); debug_assert_eq!(result.len(), moved_elements); // forget the old elements but not the allocation let mut old_storage = self.into_raw_vec_and_offset().0; old_storage.set_len(0); // transfer ownership of the elements into the result result.assume_init() } } } #[cfg(feature = "std")] fn main() { let a = Array::linspace(0., 63., 64) .into_shape_with_order((8, 8)) .unwrap(); let strings = a.map(|x| x.to_string()); let perm = a.sort_axis_by(Axis(1), |i, j| a[[i, 0]] > a[[j, 0]]); println!("{:?}", perm); let b = a.permute_axis(Axis(0), &perm); println!("{:?}", b); println!("{:?}", strings); let c = strings.permute_axis(Axis(1), &perm); println!("{:?}", c); } #[cfg(not(feature = "std"))] fn main() {} #[cfg(test)] mod tests { use super::*; #[test] fn test_permute_axis() { let a = array![ [107998.96, 1.], [107999.08, 2.], [107999.20, 3.], [108000.33, 4.], [107999.45, 5.], [107999.57, 6.], [108010.69, 7.], [107999.81, 8.], [107999.94, 9.], [75600.09, 10.], [75600.21, 11.], [75601.33, 12.], [75600.45, 13.], [75600.58, 14.], [109000.70, 15.], [75600.82, 16.], [75600.94, 17.], [75601.06, 18.], ]; let answer = array![ [75600.09, 10.], [75600.21, 11.], [75600.45, 13.], [75600.58, 14.], [75600.82, 16.], [75600.94, 17.], [75601.06, 18.], [75601.33, 12.], [107998.96, 1.], [107999.08, 2.], [107999.20, 3.], [107999.45, 5.], [107999.57, 6.], [107999.81, 8.], [107999.94, 9.], [108000.33, 4.], [108010.69, 7.], [109000.70, 15.], ]; // f layout copy of a let mut af = Array::zeros(a.dim().f()); af.assign(&a); // transposed copy of a let at = a.t().to_owned(); // c layout permute let perm = a.sort_axis_by(Axis(0), |i, j| a[[i, 0]] < a[[j, 0]]); let b = a.permute_axis(Axis(0), &perm); assert_eq!(b, answer); // f layout permute let bf = af.permute_axis(Axis(0), &perm); assert_eq!(bf, answer); // transposed permute let bt = at.permute_axis(Axis(1), &perm); assert_eq!(bt, answer.t()); } } ndarray-0.16.1/examples/type_conversion.rs000064400000000000000000000133361046102023000167710ustar 00000000000000#[cfg(feature = "approx")] use std::convert::TryFrom; #[cfg(feature = "approx")] use approx::assert_abs_diff_eq; #[cfg(feature = "approx")] use ndarray::prelude::*; #[cfg(feature = "approx")] fn main() { // Converting an array from one datatype to another is implemented with the // `ArrayBase::mapv()` function. We pass a closure that is applied to each // element independently. This allows for more control and flexiblity in // converting types. // // Below, we illustrate four different approaches for the actual conversion // in the closure. // - `From` ensures lossless conversions known at compile time and is the // best default choice. // - `TryFrom` either converts data losslessly or panics, ensuring that the // rest of the program does not continue with unexpected data. // - `as` never panics and may silently convert in a lossy way, depending // on the source and target datatypes. More details can be found in the // reference: https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast // - Using custom logic in the closure, e.g. to clip values or for NaN // handling in floats. // // For a brush-up on casting between numeric types in Rust, refer to: // https://doc.rust-lang.org/rust-by-example/types/cast.html // Infallible, lossless conversion with `From` // The trait `std::convert::From` is only implemented for conversions that // can be guaranteed to be lossless at compile time. This is the safest // approach. let a_u8: Array = array![[1, 2, 3], [4, 5, 6]]; let a_f32 = a_u8.mapv(|element| f32::from(element)); assert_abs_diff_eq!(a_f32, array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]); // Fallible, lossless conversion with `TryFrom` // `i8` numbers can be negative, in such a case, there is no perfect // conversion to `u8` defined. In this example, all numbers are positive and // in bounds and can be converted at runtime. But for unknown runtime input, // this would panic with the message provided in `.expect()`. Note that you // can also use `.unwrap()` to be more concise. let a_i8: Array = array![120, 8, 0]; let a_u8 = a_i8.mapv(|element| u8::try_from(element).expect("Could not convert i8 to u8")); assert_eq!(a_u8, array![120u8, 8u8, 0u8]); // Unsigned to signed integer conversion with `as` // A real-life example of this would be coordinates on a grid. // A `usize` value can be larger than what fits into a `isize`, therefore, // it would be safer to use `TryFrom`. Nevertheless, `as` can be used for // either simplicity or performance. // The example includes `usize::MAX` to illustrate potentially undesired // behavior. It will be interpreted as -1 (noop-casting + 2-complement), see // https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast let a_usize: Array = array![1, 2, 3, usize::MAX]; let a_isize = a_usize.mapv(|element| element as isize); assert_eq!(a_isize, array![1_isize, 2_isize, 3_isize, -1_isize]); // Simple upcasting with `as` // Every `u8` fits perfectly into a `u32`, therefore this is a lossless // conversion. // Note that it is up to the programmer to ensure the validity of the // conversion over the lifetime of a program. With type inference, subtle // bugs can creep in since conversions with `as` will always compile, so a // programmer might not notice that a prior lossless conversion became a // lossy conversion. With `From`, this would be noticed at compile-time and // with `TryFrom`, it would also be either handled or make the program // panic. let a_u8: Array = array![[1, 2, 3], [4, 5, 6]]; let a_u32 = a_u8.mapv(|element| element as u32); assert_eq!(a_u32, array![[1u32, 2u32, 3u32], [4u32, 5u32, 6u32]]); // Saturating cast with `as` // The `as` keyword performs a *saturating cast* When casting floats to // ints. This means that numbers which do not fit into the target datatype // will silently be clipped to the maximum/minimum numbers. Since this is // not obvious, we discourage the intentional use of casting with `as` with // silent saturation and recommend a custom logic instead which makes the // intent clear. let a_f32: Array = array![ 256.0, // saturated to 255 255.7, // saturated to 255 255.1, // saturated to 255 254.7, // rounded down to 254 by cutting the decimal part 254.1, // rounded down to 254 by cutting the decimal part -1.0, // saturated to 0 on the lower end f32::INFINITY, // saturated to 255 f32::NAN, // converted to zero ]; let a_u8 = a_f32.mapv(|element| element as u8); assert_eq!(a_u8, array![255, 255, 255, 254, 254, 0, 255, 0]); // Custom mapping logic // Given that we pass a closure for the conversion, we can also define // custom logic to e.g. replace NaN values and clip others. This also // makes the intent clear. let a_f32: Array = array![ 270.0, // clipped to 200 -1.2, // clipped to 0 4.7, // rounded up to 5 instead of just stripping decimals f32::INFINITY, // clipped to 200 f32::NAN, // replaced with upper bound 200 ]; let a_u8_custom = a_f32.mapv(|element| { if element == f32::INFINITY || element.is_nan() { return 200; } if let Some(std::cmp::Ordering::Less) = element.partial_cmp(&0.0) { return 0; } 200.min(element.round() as u8) }); assert_eq!(a_u8_custom, array![200, 0, 5, 200, 200]); } #[cfg(not(feature = "approx"))] fn main() {} ndarray-0.16.1/examples/zip_many.rs000064400000000000000000000025261046102023000153700ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; use ndarray::Zip; fn main() { let n = 6; let mut a = Array::::zeros((n, n)); let mut b = Array::::zeros((n, n)); for ((i, j), elt) in b.indexed_iter_mut() { *elt = 1. / (1. + (i + 2 * j) as f32); } let c = Array::::from_elem((n, n + 1), 1.7); let c = c.slice(s![.., ..-1]); // Using Zip for arithmetic ops across a, b, c Zip::from(&mut a) .and(&b) .and(&c) .for_each(|a, &b, &c| *a = b + c); assert_eq!(a, &b + &c); // and this is how to do the *same thing* with azip!() azip!((a in &mut a, &b in &b, &c in c) *a = b + c); println!("{:8.4}", a); // sum of each row let mut sums = Array::zeros(a.nrows()); Zip::from(a.rows()) .and(&mut sums) .for_each(|row, sum| *sum = row.sum()); // show sums as a column matrix println!("{:8.4}", sums.insert_axis(Axis(1))); // sum of each 2x2 chunk let chunk_sz = (2, 2); let nchunks = (n / chunk_sz.0, n / chunk_sz.1); let mut sums = Array::zeros(nchunks); Zip::from(a.exact_chunks(chunk_sz)) .and(&mut sums) .for_each(|chunk, sum| *sum = chunk.sum()); println!("{:8.4}", sums); } ndarray-0.16.1/misc/axis_iter.svg000064400000000000000000000746651046102023000150360ustar 00000000000000 image/svg+xml Axis(0) Axis(1) Axis(2) .axis_iter(Axis(2)) Input shape: (3, 4, 5)Output shapes: (3, 4) 0 1 2 3 4 ndarray-0.16.1/misc/split_at.svg000064400000000000000000000656461046102023000146650ustar 00000000000000 image/svg+xml Axis(0) Axis(1) Axis(2) .split_at(Axis(2), 2) Input shape: (3, 5, 5)Output shapes: (3, 5, 2) and (3, 5, 3) ndarray-0.16.1/rustfmt.toml000064400000000000000000000012531046102023000137530ustar 00000000000000edition = "2018" array_width = 100 chain_width = 60 fn_call_width = 100 max_width = 120 brace_style = "AlwaysNextLine" control_brace_style = "AlwaysSameLine" fn_params_layout = "Compressed" # ? format_macro_bodies = false imports_granularity = "Preserve" imports_indent = "Block" imports_layout = "HorizontalVertical" inline_attribute_width = 0 indent_style = "Block" match_arm_blocks = false match_arm_leading_pipes = "Preserve" merge_derives = false overflow_delimited_expr = true reorder_modules = false # impacts rustdoc order short_array_element_width_threshold = 32 skip_macro_invocations = ["*"] unstable_features = true where_single_line = true # ignored files ignore = [] ndarray-0.16.1/scripts/all-tests.sh000075500000000000000000000015111046102023000153050ustar 00000000000000#!/bin/sh set -x set -e FEATURES=$1 CHANNEL=$2 QC_FEAT=--features=ndarray-rand/quickcheck # build check with no features cargo build -v --no-default-features # ndarray with no features cargo test -p ndarray -v --no-default-features # all with features cargo test -v --features "$FEATURES" $QC_FEAT # all with features and release (ignore test crates which is already optimized) cargo test -v -p ndarray -p ndarray-rand --release --features "$FEATURES" $QC_FEAT --lib --tests # BLAS tests cargo test -p ndarray --lib -v --features blas cargo test -p blas-mock-tests -v cargo test -p blas-tests -v --features blas-tests/openblas-system cargo test -p numeric-tests -v --features numeric-tests/test_blas # Examples cargo test --examples # Benchmarks ([ "$CHANNEL" != "nightly" ] || cargo bench --no-run --verbose --features "$FEATURES") ndarray-0.16.1/scripts/cross-tests.sh000075500000000000000000000004271046102023000156730ustar 00000000000000#!/bin/sh set -x set -e FEATURES=$1 CHANNEL=$2 TARGET=$3 QC_FEAT=--features=ndarray-rand/quickcheck cross build -v --features="$FEATURES" $QC_FEAT --target=$TARGET cross test -v --no-fail-fast --features="$FEATURES" $QC_FEAT --target=$TARGET cross test -v -p blas-mock-tests ndarray-0.16.1/scripts/makechangelog.sh000075500000000000000000000010341046102023000161620ustar 00000000000000#!/bin/bash # Usage: makechangelog # # This script depends on and uses the github `gh` binary # which needs to be authenticated to use. # # Will produce some duplicates for PRs integrated using rebase, # but those will not occur with current merge queue. git log --first-parent --pretty="tformat:%H" "$@" | while IFS= read -r commit_sha do gh api "/repos/:owner/:repo/commits/${commit_sha}/pulls" \ -q ".[] | \"- \(.title) by [@\(.user.login)](\(.user.html_url)) [#\(.number)](\(.html_url))\"" done | uniq ndarray-0.16.1/src/aliases.rs000064400000000000000000000115601046102023000141320ustar 00000000000000//! Type aliases for common array sizes //! use crate::dimension::Dim; use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Ix, IxDynImpl}; /// Create a zero-dimensional index #[allow(non_snake_case)] #[inline(always)] pub const fn Ix0() -> Ix0 { Dim::new([]) } /// Create a one-dimensional index #[allow(non_snake_case)] #[inline(always)] pub const fn Ix1(i0: Ix) -> Ix1 { Dim::new([i0]) } /// Create a two-dimensional index #[allow(non_snake_case)] #[inline(always)] pub const fn Ix2(i0: Ix, i1: Ix) -> Ix2 { Dim::new([i0, i1]) } /// Create a three-dimensional index #[allow(non_snake_case)] #[inline(always)] pub const fn Ix3(i0: Ix, i1: Ix, i2: Ix) -> Ix3 { Dim::new([i0, i1, i2]) } /// Create a four-dimensional index #[allow(non_snake_case)] #[inline(always)] pub const fn Ix4(i0: Ix, i1: Ix, i2: Ix, i3: Ix) -> Ix4 { Dim::new([i0, i1, i2, i3]) } /// Create a five-dimensional index #[allow(non_snake_case)] #[inline(always)] pub const fn Ix5(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix) -> Ix5 { Dim::new([i0, i1, i2, i3, i4]) } /// Create a six-dimensional index #[allow(non_snake_case)] #[inline(always)] pub const fn Ix6(i0: Ix, i1: Ix, i2: Ix, i3: Ix, i4: Ix, i5: Ix) -> Ix6 { Dim::new([i0, i1, i2, i3, i4, i5]) } /// Create a dynamic-dimensional index #[allow(non_snake_case)] #[inline(always)] pub fn IxDyn(ix: &[Ix]) -> IxDyn { Dim(ix) } /// zero-dimensionial pub type Ix0 = Dim<[Ix; 0]>; /// one-dimensional pub type Ix1 = Dim<[Ix; 1]>; /// two-dimensional pub type Ix2 = Dim<[Ix; 2]>; /// three-dimensional pub type Ix3 = Dim<[Ix; 3]>; /// four-dimensional pub type Ix4 = Dim<[Ix; 4]>; /// five-dimensional pub type Ix5 = Dim<[Ix; 5]>; /// six-dimensional pub type Ix6 = Dim<[Ix; 6]>; /// dynamic-dimensional /// /// You can use the `IxDyn` function to create a dimension for an array with /// dynamic number of dimensions. (`Vec` and `&[usize]` also implement /// `IntoDimension` to produce `IxDyn`). /// /// ``` /// use ndarray::ArrayD; /// use ndarray::IxDyn; /// /// // Create a 5 × 6 × 3 × 4 array using the dynamic dimension type /// let mut a = ArrayD::::zeros(IxDyn(&[5, 6, 3, 4])); /// // Create a 1 × 3 × 4 array using the dynamic dimension type /// let mut b = ArrayD::::zeros(IxDyn(&[1, 3, 4])); /// /// // We can use broadcasting to add arrays of compatible shapes together: /// a += &b; /// /// // We can index into a, b using fixed size arrays: /// a[[0, 0, 0, 0]] = 0.; /// b[[0, 2, 3]] = a[[0, 0, 2, 3]]; /// // Note: indexing will panic at runtime if the number of indices given does /// // not match the array. /// /// // We can keep them in the same vector because both the arrays have /// // the same type `Array` a.k.a `ArrayD`: /// let arrays = vec![a, b]; /// ``` pub type IxDyn = Dim; /// zero-dimensional array pub type Array0 = Array; /// one-dimensional array pub type Array1 = Array; /// two-dimensional array pub type Array2 = Array; /// three-dimensional array pub type Array3 = Array; /// four-dimensional array pub type Array4 = Array; /// five-dimensional array pub type Array5 = Array; /// six-dimensional array pub type Array6 = Array; /// dynamic-dimensional array pub type ArrayD = Array; /// zero-dimensional array view pub type ArrayView0<'a, A> = ArrayView<'a, A, Ix0>; /// one-dimensional array view pub type ArrayView1<'a, A> = ArrayView<'a, A, Ix1>; /// two-dimensional array view pub type ArrayView2<'a, A> = ArrayView<'a, A, Ix2>; /// three-dimensional array view pub type ArrayView3<'a, A> = ArrayView<'a, A, Ix3>; /// four-dimensional array view pub type ArrayView4<'a, A> = ArrayView<'a, A, Ix4>; /// five-dimensional array view pub type ArrayView5<'a, A> = ArrayView<'a, A, Ix5>; /// six-dimensional array view pub type ArrayView6<'a, A> = ArrayView<'a, A, Ix6>; /// dynamic-dimensional array view pub type ArrayViewD<'a, A> = ArrayView<'a, A, IxDyn>; /// zero-dimensional read-write array view pub type ArrayViewMut0<'a, A> = ArrayViewMut<'a, A, Ix0>; /// one-dimensional read-write array view pub type ArrayViewMut1<'a, A> = ArrayViewMut<'a, A, Ix1>; /// two-dimensional read-write array view pub type ArrayViewMut2<'a, A> = ArrayViewMut<'a, A, Ix2>; /// three-dimensional read-write array view pub type ArrayViewMut3<'a, A> = ArrayViewMut<'a, A, Ix3>; /// four-dimensional read-write array view pub type ArrayViewMut4<'a, A> = ArrayViewMut<'a, A, Ix4>; /// five-dimensional read-write array view pub type ArrayViewMut5<'a, A> = ArrayViewMut<'a, A, Ix5>; /// six-dimensional read-write array view pub type ArrayViewMut6<'a, A> = ArrayViewMut<'a, A, Ix6>; /// dynamic-dimensional read-write array view pub type ArrayViewMutD<'a, A> = ArrayViewMut<'a, A, IxDyn>; /// one-dimensional shared ownership array pub type ArcArray1 = ArcArray; /// two-dimensional shared ownership array pub type ArcArray2 = ArcArray; ndarray-0.16.1/src/argument_traits.rs000064400000000000000000000020151046102023000157140ustar 00000000000000use std::cell::Cell; use std::mem::MaybeUninit; use crate::math_cell::MathCell; /// A producer element that can be assigned to once pub trait AssignElem { /// Assign the value `input` to the element that self represents. fn assign_elem(self, input: T); } /// Assignable element, simply `*self = input`. impl<'a, T> AssignElem for &'a mut T { fn assign_elem(self, input: T) { *self = input; } } /// Assignable element, simply `self.set(input)`. impl<'a, T> AssignElem for &'a Cell { fn assign_elem(self, input: T) { self.set(input); } } /// Assignable element, simply `self.set(input)`. impl<'a, T> AssignElem for &'a MathCell { fn assign_elem(self, input: T) { self.set(input); } } /// Assignable element, the item in the MaybeUninit is overwritten (prior value, if any, is not /// read or dropped). impl<'a, T> AssignElem for &'a mut MaybeUninit { fn assign_elem(self, input: T) { *self = MaybeUninit::new(input); } } ndarray-0.16.1/src/array_approx.rs000064400000000000000000000156051046102023000152240ustar 00000000000000#[cfg(feature = "approx")] mod approx_methods { use crate::imp_prelude::*; impl ArrayBase where S: Data, D: Dimension, { /// A test for equality that uses the elementwise absolute difference to compute the /// approximate equality of two arrays. /// /// **Requires crate feature `"approx"`** pub fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool where A: ::approx::AbsDiffEq, A::Epsilon: Clone, S2: Data, { >::abs_diff_eq(self, other, epsilon) } /// A test for equality that uses an elementwise relative comparison if the values are far /// apart; and the absolute difference otherwise. /// /// **Requires crate feature `"approx"`** pub fn relative_eq(&self, other: &ArrayBase, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool where A: ::approx::RelativeEq, A::Epsilon: Clone, S2: Data, { >::relative_eq(self, other, epsilon, max_relative) } } } macro_rules! impl_approx_traits { ($approx:ident, $doc:expr) => { mod $approx { use crate::imp_prelude::*; use crate::Zip; use $approx::{AbsDiffEq, RelativeEq, UlpsEq}; #[doc = $doc] impl AbsDiffEq> for ArrayBase where A: AbsDiffEq, A::Epsilon: Clone, S: Data, S2: Data, D: Dimension, { type Epsilon = A::Epsilon; fn default_epsilon() -> A::Epsilon { A::default_epsilon() } fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool { if self.shape() != other.shape() { return false; } Zip::from(self) .and(other) .all(move |a, b| A::abs_diff_eq(a, b, epsilon.clone())) } } #[doc = $doc] impl RelativeEq> for ArrayBase where A: RelativeEq, A::Epsilon: Clone, S: Data, S2: Data, D: Dimension, { fn default_max_relative() -> A::Epsilon { A::default_max_relative() } fn relative_eq( &self, other: &ArrayBase, epsilon: A::Epsilon, max_relative: A::Epsilon, ) -> bool { if self.shape() != other.shape() { return false; } Zip::from(self).and(other).all(move |a, b| { A::relative_eq(a, b, epsilon.clone(), max_relative.clone()) }) } } #[doc = $doc] impl UlpsEq> for ArrayBase where A: UlpsEq, A::Epsilon: Clone, S: Data, S2: Data, D: Dimension, { fn default_max_ulps() -> u32 { A::default_max_ulps() } fn ulps_eq( &self, other: &ArrayBase, epsilon: A::Epsilon, max_ulps: u32, ) -> bool { if self.shape() != other.shape() { return false; } Zip::from(self) .and(other) .all(move |a, b| A::ulps_eq(a, b, epsilon.clone(), max_ulps)) } } #[cfg(test)] mod tests { use crate::prelude::*; use alloc::vec; use $approx::{ assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne, assert_ulps_eq, assert_ulps_ne, }; #[test] fn abs_diff_eq() { let a: Array2 = array![[0., 2.], [-0.000010001, 100000000.]]; let mut b: Array2 = array![[0., 1.], [-0.000010002, 100000001.]]; assert_abs_diff_ne!(a, b); b[(0, 1)] = 2.; assert_abs_diff_eq!(a, b); // Check epsilon. assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); // Make sure we can compare different shapes without failure. let c = array![[1., 2.]]; assert_abs_diff_ne!(a, c); } #[test] fn relative_eq() { let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; assert_relative_ne!(a, b); b[(0, 1)] = 2.; assert_relative_eq!(a, b); // Check epsilon. assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); // Make sure we can compare different shapes without failure. let c = array![[1., 2.]]; assert_relative_ne!(a, c); } #[test] fn ulps_eq() { let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; assert_ulps_ne!(a, b); b[(0, 1)] = 2.; assert_ulps_eq!(a, b); // Check epsilon. assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); // Make sure we can compare different shapes without failure. let c = array![[1., 2.]]; assert_ulps_ne!(a, c); } } } }; } #[cfg(feature = "approx")] impl_approx_traits!(approx, "**Requires crate feature `\"approx\"`.**"); ndarray-0.16.1/src/array_serde.rs000064400000000000000000000174371046102023000150220ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use serde::de::{self, MapAccess, SeqAccess, Visitor}; use serde::ser::{SerializeSeq, SerializeStruct}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use alloc::format; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::fmt; use std::marker::PhantomData; use crate::imp_prelude::*; use super::arraytraits::ARRAY_FORMAT_VERSION; use super::Iter; use crate::IntoDimension; /// Verifies that the version of the deserialized array matches the current /// `ARRAY_FORMAT_VERSION`. pub fn verify_version(v: u8) -> Result<(), E> where E: de::Error { if v != ARRAY_FORMAT_VERSION { let err_msg = format!("unknown array version: {}", v); Err(de::Error::custom(err_msg)) } else { Ok(()) } } /// **Requires crate feature `"serde"`** impl Serialize for Dim where I: Serialize { fn serialize(&self, serializer: Se) -> Result where Se: Serializer { self.ix().serialize(serializer) } } /// **Requires crate feature `"serde"`** impl<'de, I> Deserialize<'de> for Dim where I: Deserialize<'de> { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { I::deserialize(deserializer).map(Dim::new) } } /// **Requires crate feature `"serde"`** impl Serialize for IxDyn { fn serialize(&self, serializer: Se) -> Result where Se: Serializer { self.ix().serialize(serializer) } } /// **Requires crate feature `"serde"`** impl<'de> Deserialize<'de> for IxDyn { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { let v = Vec::::deserialize(deserializer)?; Ok(v.into_dimension()) } } /// **Requires crate feature `"serde"`** impl Serialize for ArrayBase where A: Serialize, D: Dimension + Serialize, S: Data, { fn serialize(&self, serializer: Se) -> Result where Se: Serializer { let mut state = serializer.serialize_struct("Array", 3)?; state.serialize_field("v", &ARRAY_FORMAT_VERSION)?; state.serialize_field("dim", &self.raw_dim())?; state.serialize_field("data", &Sequence(self.iter()))?; state.end() } } // private iterator wrapper struct Sequence<'a, A, D>(Iter<'a, A, D>); impl<'a, A, D> Serialize for Sequence<'a, A, D> where A: Serialize, D: Dimension + Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer { let iter = &self.0; let mut seq = serializer.serialize_seq(Some(iter.len()))?; for elt in iter.clone() { seq.serialize_element(elt)?; } seq.end() } } struct ArrayVisitor { _marker_a: PhantomData, _marker_b: PhantomData, } enum ArrayField { Version, Dim, Data, } impl ArrayVisitor { pub fn new() -> Self { ArrayVisitor { _marker_a: PhantomData, _marker_b: PhantomData, } } } static ARRAY_FIELDS: &[&str] = &["v", "dim", "data"]; /// **Requires crate feature `"serde"`** impl<'de, A, Di, S> Deserialize<'de> for ArrayBase where A: Deserialize<'de>, Di: Deserialize<'de> + Dimension, S: DataOwned, { fn deserialize(deserializer: D) -> Result, D::Error> where D: Deserializer<'de> { deserializer.deserialize_struct("Array", ARRAY_FIELDS, ArrayVisitor::new()) } } impl<'de> Deserialize<'de> for ArrayField { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { struct ArrayFieldVisitor; impl<'de> Visitor<'de> for ArrayFieldVisitor { type Value = ArrayField; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(r#""v", "dim", or "data""#) } fn visit_str(self, value: &str) -> Result where E: de::Error { match value { "v" => Ok(ArrayField::Version), "dim" => Ok(ArrayField::Dim), "data" => Ok(ArrayField::Data), other => Err(de::Error::unknown_field(other, ARRAY_FIELDS)), } } fn visit_bytes(self, value: &[u8]) -> Result where E: de::Error { match value { b"v" => Ok(ArrayField::Version), b"dim" => Ok(ArrayField::Dim), b"data" => Ok(ArrayField::Data), other => Err(de::Error::unknown_field(&format!("{:?}", other), ARRAY_FIELDS)), } } } deserializer.deserialize_identifier(ArrayFieldVisitor) } } impl<'de, A, Di, S> Visitor<'de> for ArrayVisitor where A: Deserialize<'de>, Di: Deserialize<'de> + Dimension, S: DataOwned, { type Value = ArrayBase; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("ndarray representation") } fn visit_seq(self, mut visitor: V) -> Result, V::Error> where V: SeqAccess<'de> { let v: u8 = match visitor.next_element()? { Some(value) => value, None => { return Err(de::Error::invalid_length(0, &self)); } }; verify_version(v)?; let dim: Di = match visitor.next_element()? { Some(value) => value, None => { return Err(de::Error::invalid_length(1, &self)); } }; let data: Vec = match visitor.next_element()? { Some(value) => value, None => { return Err(de::Error::invalid_length(2, &self)); } }; if let Ok(array) = ArrayBase::from_shape_vec(dim, data) { Ok(array) } else { Err(de::Error::custom("data and dimension must match in size")) } } fn visit_map(self, mut visitor: V) -> Result, V::Error> where V: MapAccess<'de> { let mut v: Option = None; let mut data: Option> = None; let mut dim: Option = None; while let Some(key) = visitor.next_key()? { match key { ArrayField::Version => { let val = visitor.next_value()?; verify_version(val)?; v = Some(val); } ArrayField::Data => { data = Some(visitor.next_value()?); } ArrayField::Dim => { dim = Some(visitor.next_value()?); } } } let _v = match v { Some(v) => v, None => return Err(de::Error::missing_field("v")), }; let data = match data { Some(data) => data, None => return Err(de::Error::missing_field("data")), }; let dim = match dim { Some(dim) => dim, None => return Err(de::Error::missing_field("dim")), }; if let Ok(array) = ArrayBase::from_shape_vec(dim, data) { Ok(array) } else { Err(de::Error::custom("data and dimension must match in size")) } } } ndarray-0.16.1/src/arrayformat.rs000064400000000000000000000537421046102023000150500ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::{ArrayBase, ArrayView, Axis, Data, Dimension, NdProducer}; use crate::aliases::{Ix1, IxDyn}; use alloc::format; use std::fmt; /// Default threshold, below this element count, we don't ellipsize const ARRAY_MANY_ELEMENT_LIMIT: usize = 500; /// Limit of element count for non-last axes before overflowing with an ellipsis. const AXIS_LIMIT_STACKED: usize = 6; /// Limit for next to last axis (printed as column) /// An odd number because one element uses the same space as the ellipsis. const AXIS_LIMIT_COL: usize = 11; /// Limit for last axis (printed as row) /// An odd number because one element uses approximately the space of the ellipsis. const AXIS_LIMIT_ROW: usize = 11; #[cfg(test)] // Test value to use for size of overflowing 2D arrays const AXIS_2D_OVERFLOW_LIMIT: usize = 22; /// The string used as an ellipsis. const ELLIPSIS: &str = "..."; #[derive(Clone, Debug)] struct FormatOptions { axis_collapse_limit: usize, axis_collapse_limit_next_last: usize, axis_collapse_limit_last: usize, } impl FormatOptions { pub(crate) fn default_for_array(nelem: usize, no_limit: bool) -> Self { let default = Self { axis_collapse_limit: AXIS_LIMIT_STACKED, axis_collapse_limit_next_last: AXIS_LIMIT_COL, axis_collapse_limit_last: AXIS_LIMIT_ROW, }; default.set_no_limit(no_limit || nelem < ARRAY_MANY_ELEMENT_LIMIT) } fn set_no_limit(mut self, no_limit: bool) -> Self { if no_limit { self.axis_collapse_limit = usize::MAX; self.axis_collapse_limit_next_last = usize::MAX; self.axis_collapse_limit_last = usize::MAX; } self } /// Axis length collapse limit before ellipsizing, where `axis_rindex` is /// the index of the axis from the back. pub(crate) fn collapse_limit(&self, axis_rindex: usize) -> usize { match axis_rindex { 0 => self.axis_collapse_limit_last, 1 => self.axis_collapse_limit_next_last, _ => self.axis_collapse_limit, } } } /// Formats the contents of a list of items, using an ellipsis to indicate when /// the `length` of the list is greater than `limit`. /// /// # Parameters /// /// * `f`: The formatter. /// * `length`: The length of the list. /// * `limit`: The maximum number of items before overflow. /// * `separator`: Separator to write between items. /// * `ellipsis`: Ellipsis for indicating overflow. /// * `fmt_elem`: A function that formats an element in the list, given the /// formatter and the index of the item in the list. fn format_with_overflow( f: &mut fmt::Formatter<'_>, length: usize, limit: usize, separator: &str, ellipsis: &str, fmt_elem: &mut dyn FnMut(&mut fmt::Formatter, usize) -> fmt::Result, ) -> fmt::Result { if length == 0 { // no-op } else if length <= limit { fmt_elem(f, 0)?; for i in 1..length { f.write_str(separator)?; fmt_elem(f, i)? } } else { let edge = limit / 2; fmt_elem(f, 0)?; for i in 1..edge { f.write_str(separator)?; fmt_elem(f, i)?; } f.write_str(separator)?; f.write_str(ellipsis)?; for i in length - edge..length { f.write_str(separator)?; fmt_elem(f, i)? } } Ok(()) } fn format_array( array: &ArrayBase, f: &mut fmt::Formatter<'_>, format: F, fmt_opt: &FormatOptions, ) -> fmt::Result where F: FnMut(&A, &mut fmt::Formatter<'_>) -> fmt::Result + Clone, D: Dimension, S: Data, { // Cast into a dynamically dimensioned view // This is required to be able to use `index_axis` for the recursive case format_array_inner(array.view().into_dyn(), f, format, fmt_opt, 0, array.ndim()) } fn format_array_inner( view: ArrayView, f: &mut fmt::Formatter<'_>, mut format: F, fmt_opt: &FormatOptions, depth: usize, full_ndim: usize, ) -> fmt::Result where F: FnMut(&A, &mut fmt::Formatter<'_>) -> fmt::Result + Clone, { // If any of the axes has 0 length, we return the same empty array representation // e.g. [[]] for 2-d arrays if view.is_empty() { write!(f, "{}{}", "[".repeat(view.ndim()), "]".repeat(view.ndim()))?; return Ok(()); } match view.shape() { // If it's 0 dimensional, we just print out the scalar &[] => format(&view[[]], f)?, // We handle 1-D arrays as a special case &[len] => { let view = view.view().into_dimensionality::().unwrap(); f.write_str("[")?; format_with_overflow(f, len, fmt_opt.collapse_limit(0), ", ", ELLIPSIS, &mut |f, index| { format(&view[index], f) })?; f.write_str("]")?; } // For n-dimensional arrays, we proceed recursively shape => { let blank_lines = "\n".repeat(shape.len() - 2); let indent = " ".repeat(depth + 1); let separator = format!(",\n{}{}", blank_lines, indent); f.write_str("[")?; let limit = fmt_opt.collapse_limit(full_ndim - depth - 1); format_with_overflow(f, shape[0], limit, &separator, ELLIPSIS, &mut |f, index| { format_array_inner(view.index_axis(Axis(0), index), f, format.clone(), fmt_opt, depth + 1, full_ndim) })?; f.write_str("]")?; } } Ok(()) } // NOTE: We can impl other fmt traits here /// Format the array using `Display` and apply the formatting parameters used /// to each element. /// /// The array is shown in multiline style. impl fmt::Display for ArrayBase where S: Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } } /// Format the array using `Debug` and apply the formatting parameters used /// to each element. /// /// The array is shown in multiline style. impl fmt::Debug for ArrayBase where S: Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt)?; // Add extra information for Debug write!( f, ", shape={:?}, strides={:?}, layout={:?}", self.shape(), self.strides(), self.view().layout(), )?; match D::NDIM { Some(ndim) => write!(f, ", const ndim={}", ndim)?, None => write!(f, ", dynamic ndim={}", self.ndim())?, } Ok(()) } } /// Format the array using `LowerExp` and apply the formatting parameters used /// to each element. /// /// The array is shown in multiline style. impl fmt::LowerExp for ArrayBase where S: Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } } /// Format the array using `UpperExp` and apply the formatting parameters used /// to each element. /// /// The array is shown in multiline style. impl fmt::UpperExp for ArrayBase where S: Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } } /// Format the array using `LowerHex` and apply the formatting parameters used /// to each element. /// /// The array is shown in multiline style. impl fmt::LowerHex for ArrayBase where S: Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } } /// Format the array using `Binary` and apply the formatting parameters used /// to each element. /// /// The array is shown in multiline style. impl fmt::Binary for ArrayBase where S: Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fmt_opt = FormatOptions::default_for_array(self.len(), f.alternate()); format_array(self, f, <_>::fmt, &fmt_opt) } } #[cfg(test)] mod formatting_with_omit { #[cfg(not(feature = "std"))] use alloc::string::String; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use itertools::Itertools; use super::*; use crate::prelude::*; fn assert_str_eq(expected: &str, actual: &str) { // use assert to avoid printing the strings twice on failure assert!( expected == actual, "formatting assertion failed\nexpected:\n{}\nactual:\n{}\n", expected, actual, ); } fn ellipsize(limit: usize, sep: &str, elements: impl IntoIterator) -> String { let elements = elements.into_iter().collect::>(); let edge = limit / 2; if elements.len() <= limit { format!("{}", elements.iter().format(sep)) } else { format!( "{}{}{}{}{}", elements[..edge].iter().format(sep), sep, ELLIPSIS, sep, elements[elements.len() - edge..].iter().format(sep) ) } } #[test] fn empty_arrays() { let a: Array2 = arr2(&[[], []]); let actual = format!("{}", a); let expected = "[[]]"; assert_str_eq(expected, &actual); } #[test] fn zero_length_axes() { let a = Array3::::zeros((3, 0, 4)); let actual = format!("{}", a); let expected = "[[[]]]"; assert_str_eq(expected, &actual); } #[test] fn dim_0() { let element = 12; let a = arr0(element); let actual = format!("{}", a); let expected = "12"; assert_str_eq(expected, &actual); } #[test] fn dim_1() { let overflow: usize = 2; let a = Array1::from_elem(ARRAY_MANY_ELEMENT_LIMIT + overflow, 1); let actual = format!("{}", a); let expected = format!("[{}]", ellipsize(AXIS_LIMIT_ROW, ", ", a.iter())); assert_str_eq(&expected, &actual); } #[test] fn dim_1_alternate() { let overflow: usize = 2; let a = Array1::from_elem(ARRAY_MANY_ELEMENT_LIMIT + overflow, 1); let actual = format!("{:#}", a); let expected = format!("[{}]", a.iter().format(", ")); assert_str_eq(&expected, &actual); } #[test] fn dim_2_last_axis_overflow() { let overflow: usize = 2; let a = Array2::from_elem((AXIS_2D_OVERFLOW_LIMIT, AXIS_2D_OVERFLOW_LIMIT + overflow), 1); let actual = format!("{}", a); let expected = "\ [[1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], ..., [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, ..., 1, 1, 1, 1, 1]]"; assert_str_eq(expected, &actual); } #[test] fn dim_2_non_last_axis_overflow() { let a = Array2::from_elem((ARRAY_MANY_ELEMENT_LIMIT / 10, 10), 1); let actual = format!("{}", a); let row = format!("{}", a.row(0)); let expected = format!( "[{}]", ellipsize(AXIS_LIMIT_COL, ",\n ", (0..a.nrows()).map(|_| &row)) ); assert_str_eq(&expected, &actual); } #[test] fn dim_2_non_last_axis_overflow_alternate() { let a = Array2::from_elem((AXIS_LIMIT_COL * 4, 6), 1); let actual = format!("{:#}", a); let row = format!("{}", a.row(0)); let expected = format!("[{}]", (0..a.nrows()).map(|_| &row).format(",\n ")); assert_str_eq(&expected, &actual); } #[test] fn dim_2_multi_directional_overflow() { let overflow: usize = 2; let a = Array2::from_elem((AXIS_2D_OVERFLOW_LIMIT + overflow, AXIS_2D_OVERFLOW_LIMIT + overflow), 1); let actual = format!("{}", a); let row = format!("[{}]", ellipsize(AXIS_LIMIT_ROW, ", ", a.row(0))); let expected = format!( "[{}]", ellipsize(AXIS_LIMIT_COL, ",\n ", (0..a.nrows()).map(|_| &row)) ); assert_str_eq(&expected, &actual); } #[test] fn dim_2_multi_directional_overflow_alternate() { let overflow: usize = 2; let a = Array2::from_elem((AXIS_2D_OVERFLOW_LIMIT + overflow, AXIS_2D_OVERFLOW_LIMIT + overflow), 1); let actual = format!("{:#}", a); let row = format!("{}", a.row(0)); let expected = format!("[{}]", (0..a.nrows()).map(|_| &row).format(",\n ")); assert_str_eq(&expected, &actual); } #[test] fn dim_3_overflow_most() { let a = Array3::from_shape_fn((AXIS_LIMIT_STACKED + 1, AXIS_LIMIT_COL, AXIS_LIMIT_ROW + 1), |(i, j, k)| { 1000. + (100. * ((i as f64).sqrt() + (j as f64).sin() + k as f64)).round() / 100. }); let actual = format!("{:6.1}", a); let expected = "\ [[[1000.0, 1001.0, 1002.0, 1003.0, 1004.0, ..., 1007.0, 1008.0, 1009.0, 1010.0, 1011.0], [1000.8, 1001.8, 1002.8, 1003.8, 1004.8, ..., 1007.8, 1008.8, 1009.8, 1010.8, 1011.8], [1000.9, 1001.9, 1002.9, 1003.9, 1004.9, ..., 1007.9, 1008.9, 1009.9, 1010.9, 1011.9], [1000.1, 1001.1, 1002.1, 1003.1, 1004.1, ..., 1007.1, 1008.1, 1009.1, 1010.1, 1011.1], [ 999.2, 1000.2, 1001.2, 1002.2, 1003.2, ..., 1006.2, 1007.2, 1008.2, 1009.2, 1010.2], [ 999.0, 1000.0, 1001.0, 1002.0, 1003.0, ..., 1006.0, 1007.0, 1008.0, 1009.0, 1010.0], [ 999.7, 1000.7, 1001.7, 1002.7, 1003.7, ..., 1006.7, 1007.7, 1008.7, 1009.7, 1010.7], [1000.7, 1001.7, 1002.7, 1003.7, 1004.7, ..., 1007.7, 1008.7, 1009.7, 1010.7, 1011.7], [1001.0, 1002.0, 1003.0, 1004.0, 1005.0, ..., 1008.0, 1009.0, 1010.0, 1011.0, 1012.0], [1000.4, 1001.4, 1002.4, 1003.4, 1004.4, ..., 1007.4, 1008.4, 1009.4, 1010.4, 1011.4], [ 999.5, 1000.5, 1001.5, 1002.5, 1003.5, ..., 1006.5, 1007.5, 1008.5, 1009.5, 1010.5]], [[1001.0, 1002.0, 1003.0, 1004.0, 1005.0, ..., 1008.0, 1009.0, 1010.0, 1011.0, 1012.0], [1001.8, 1002.8, 1003.8, 1004.8, 1005.8, ..., 1008.8, 1009.8, 1010.8, 1011.8, 1012.8], [1001.9, 1002.9, 1003.9, 1004.9, 1005.9, ..., 1008.9, 1009.9, 1010.9, 1011.9, 1012.9], [1001.1, 1002.1, 1003.1, 1004.1, 1005.1, ..., 1008.1, 1009.1, 1010.1, 1011.1, 1012.1], [1000.2, 1001.2, 1002.2, 1003.2, 1004.2, ..., 1007.2, 1008.2, 1009.2, 1010.2, 1011.2], [1000.0, 1001.0, 1002.0, 1003.0, 1004.0, ..., 1007.0, 1008.0, 1009.0, 1010.0, 1011.0], [1000.7, 1001.7, 1002.7, 1003.7, 1004.7, ..., 1007.7, 1008.7, 1009.7, 1010.7, 1011.7], [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7], [1002.0, 1003.0, 1004.0, 1005.0, 1006.0, ..., 1009.0, 1010.0, 1011.0, 1012.0, 1013.0], [1001.4, 1002.4, 1003.4, 1004.4, 1005.4, ..., 1008.4, 1009.4, 1010.4, 1011.4, 1012.4], [1000.5, 1001.5, 1002.5, 1003.5, 1004.5, ..., 1007.5, 1008.5, 1009.5, 1010.5, 1011.5]], [[1001.4, 1002.4, 1003.4, 1004.4, 1005.4, ..., 1008.4, 1009.4, 1010.4, 1011.4, 1012.4], [1002.3, 1003.3, 1004.3, 1005.3, 1006.3, ..., 1009.3, 1010.3, 1011.3, 1012.3, 1013.3], [1002.3, 1003.3, 1004.3, 1005.3, 1006.3, ..., 1009.3, 1010.3, 1011.3, 1012.3, 1013.3], [1001.6, 1002.6, 1003.6, 1004.6, 1005.6, ..., 1008.6, 1009.6, 1010.6, 1011.6, 1012.6], [1000.7, 1001.7, 1002.7, 1003.7, 1004.7, ..., 1007.7, 1008.7, 1009.7, 1010.7, 1011.7], [1000.5, 1001.5, 1002.5, 1003.5, 1004.5, ..., 1007.5, 1008.5, 1009.5, 1010.5, 1011.5], [1001.1, 1002.1, 1003.1, 1004.1, 1005.1, ..., 1008.1, 1009.1, 1010.1, 1011.1, 1012.1], [1002.1, 1003.1, 1004.1, 1005.1, 1006.1, ..., 1009.1, 1010.1, 1011.1, 1012.1, 1013.1], [1002.4, 1003.4, 1004.4, 1005.4, 1006.4, ..., 1009.4, 1010.4, 1011.4, 1012.4, 1013.4], [1001.8, 1002.8, 1003.8, 1004.8, 1005.8, ..., 1008.8, 1009.8, 1010.8, 1011.8, 1012.8], [1000.9, 1001.9, 1002.9, 1003.9, 1004.9, ..., 1007.9, 1008.9, 1009.9, 1010.9, 1011.9]], ..., [[1002.0, 1003.0, 1004.0, 1005.0, 1006.0, ..., 1009.0, 1010.0, 1011.0, 1012.0, 1013.0], [1002.8, 1003.8, 1004.8, 1005.8, 1006.8, ..., 1009.8, 1010.8, 1011.8, 1012.8, 1013.8], [1002.9, 1003.9, 1004.9, 1005.9, 1006.9, ..., 1009.9, 1010.9, 1011.9, 1012.9, 1013.9], [1002.1, 1003.1, 1004.1, 1005.1, 1006.1, ..., 1009.1, 1010.1, 1011.1, 1012.1, 1013.1], [1001.2, 1002.2, 1003.2, 1004.2, 1005.2, ..., 1008.2, 1009.2, 1010.2, 1011.2, 1012.2], [1001.0, 1002.0, 1003.0, 1004.0, 1005.0, ..., 1008.0, 1009.0, 1010.0, 1011.0, 1012.0], [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7], [1002.7, 1003.7, 1004.7, 1005.7, 1006.7, ..., 1009.7, 1010.7, 1011.7, 1012.7, 1013.7], [1003.0, 1004.0, 1005.0, 1006.0, 1007.0, ..., 1010.0, 1011.0, 1012.0, 1013.0, 1014.0], [1002.4, 1003.4, 1004.4, 1005.4, 1006.4, ..., 1009.4, 1010.4, 1011.4, 1012.4, 1013.4], [1001.5, 1002.5, 1003.5, 1004.5, 1005.5, ..., 1008.5, 1009.5, 1010.5, 1011.5, 1012.5]], [[1002.2, 1003.2, 1004.2, 1005.2, 1006.2, ..., 1009.2, 1010.2, 1011.2, 1012.2, 1013.2], [1003.1, 1004.1, 1005.1, 1006.1, 1007.1, ..., 1010.1, 1011.1, 1012.1, 1013.1, 1014.1], [1003.1, 1004.1, 1005.1, 1006.1, 1007.1, ..., 1010.1, 1011.1, 1012.1, 1013.1, 1014.1], [1002.4, 1003.4, 1004.4, 1005.4, 1006.4, ..., 1009.4, 1010.4, 1011.4, 1012.4, 1013.4], [1001.5, 1002.5, 1003.5, 1004.5, 1005.5, ..., 1008.5, 1009.5, 1010.5, 1011.5, 1012.5], [1001.3, 1002.3, 1003.3, 1004.3, 1005.3, ..., 1008.3, 1009.3, 1010.3, 1011.3, 1012.3], [1002.0, 1003.0, 1004.0, 1005.0, 1006.0, ..., 1009.0, 1010.0, 1011.0, 1012.0, 1013.0], [1002.9, 1003.9, 1004.9, 1005.9, 1006.9, ..., 1009.9, 1010.9, 1011.9, 1012.9, 1013.9], [1003.2, 1004.2, 1005.2, 1006.2, 1007.2, ..., 1010.2, 1011.2, 1012.2, 1013.2, 1014.2], [1002.6, 1003.6, 1004.6, 1005.6, 1006.6, ..., 1009.6, 1010.6, 1011.6, 1012.6, 1013.6], [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7]], [[1002.5, 1003.5, 1004.5, 1005.5, 1006.5, ..., 1009.5, 1010.5, 1011.5, 1012.5, 1013.5], [1003.3, 1004.3, 1005.3, 1006.3, 1007.3, ..., 1010.3, 1011.3, 1012.3, 1013.3, 1014.3], [1003.4, 1004.4, 1005.4, 1006.4, 1007.4, ..., 1010.4, 1011.4, 1012.4, 1013.4, 1014.4], [1002.6, 1003.6, 1004.6, 1005.6, 1006.6, ..., 1009.6, 1010.6, 1011.6, 1012.6, 1013.6], [1001.7, 1002.7, 1003.7, 1004.7, 1005.7, ..., 1008.7, 1009.7, 1010.7, 1011.7, 1012.7], [1001.5, 1002.5, 1003.5, 1004.5, 1005.5, ..., 1008.5, 1009.5, 1010.5, 1011.5, 1012.5], [1002.2, 1003.2, 1004.2, 1005.2, 1006.2, ..., 1009.2, 1010.2, 1011.2, 1012.2, 1013.2], [1003.1, 1004.1, 1005.1, 1006.1, 1007.1, ..., 1010.1, 1011.1, 1012.1, 1013.1, 1014.1], [1003.4, 1004.4, 1005.4, 1006.4, 1007.4, ..., 1010.4, 1011.4, 1012.4, 1013.4, 1014.4], [1002.9, 1003.9, 1004.9, 1005.9, 1006.9, ..., 1009.9, 1010.9, 1011.9, 1012.9, 1013.9], [1001.9, 1002.9, 1003.9, 1004.9, 1005.9, ..., 1008.9, 1009.9, 1010.9, 1011.9, 1012.9]]]"; assert_str_eq(expected, &actual); } #[test] fn dim_4_overflow_outer() { let a = Array4::from_shape_fn((10, 10, 3, 3), |(i, j, k, l)| i + j + k + l); let actual = format!("{:2}", a); // Generated using NumPy with: // np.set_printoptions(threshold=500, edgeitems=3) // np.fromfunction(lambda i, j, k, l: i + j + k + l, (10, 10, 3, 3), dtype=int) // let expected = "\ [[[[ 0, 1, 2], [ 1, 2, 3], [ 2, 3, 4]], [[ 1, 2, 3], [ 2, 3, 4], [ 3, 4, 5]], [[ 2, 3, 4], [ 3, 4, 5], [ 4, 5, 6]], ..., [[ 7, 8, 9], [ 8, 9, 10], [ 9, 10, 11]], [[ 8, 9, 10], [ 9, 10, 11], [10, 11, 12]], [[ 9, 10, 11], [10, 11, 12], [11, 12, 13]]], [[[ 1, 2, 3], [ 2, 3, 4], [ 3, 4, 5]], [[ 2, 3, 4], [ 3, 4, 5], [ 4, 5, 6]], [[ 3, 4, 5], [ 4, 5, 6], [ 5, 6, 7]], ..., [[ 8, 9, 10], [ 9, 10, 11], [10, 11, 12]], [[ 9, 10, 11], [10, 11, 12], [11, 12, 13]], [[10, 11, 12], [11, 12, 13], [12, 13, 14]]], [[[ 2, 3, 4], [ 3, 4, 5], [ 4, 5, 6]], [[ 3, 4, 5], [ 4, 5, 6], [ 5, 6, 7]], [[ 4, 5, 6], [ 5, 6, 7], [ 6, 7, 8]], ..., [[ 9, 10, 11], [10, 11, 12], [11, 12, 13]], [[10, 11, 12], [11, 12, 13], [12, 13, 14]], [[11, 12, 13], [12, 13, 14], [13, 14, 15]]], ..., [[[ 7, 8, 9], [ 8, 9, 10], [ 9, 10, 11]], [[ 8, 9, 10], [ 9, 10, 11], [10, 11, 12]], [[ 9, 10, 11], [10, 11, 12], [11, 12, 13]], ..., [[14, 15, 16], [15, 16, 17], [16, 17, 18]], [[15, 16, 17], [16, 17, 18], [17, 18, 19]], [[16, 17, 18], [17, 18, 19], [18, 19, 20]]], [[[ 8, 9, 10], [ 9, 10, 11], [10, 11, 12]], [[ 9, 10, 11], [10, 11, 12], [11, 12, 13]], [[10, 11, 12], [11, 12, 13], [12, 13, 14]], ..., [[15, 16, 17], [16, 17, 18], [17, 18, 19]], [[16, 17, 18], [17, 18, 19], [18, 19, 20]], [[17, 18, 19], [18, 19, 20], [19, 20, 21]]], [[[ 9, 10, 11], [10, 11, 12], [11, 12, 13]], [[10, 11, 12], [11, 12, 13], [12, 13, 14]], [[11, 12, 13], [12, 13, 14], [13, 14, 15]], ..., [[16, 17, 18], [17, 18, 19], [18, 19, 20]], [[17, 18, 19], [18, 19, 20], [19, 20, 21]], [[18, 19, 20], [19, 20, 21], [20, 21, 22]]]]"; assert_str_eq(expected, &actual); } } ndarray-0.16.1/src/arraytraits.rs000064400000000000000000000341311046102023000150550ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[cfg(not(feature = "std"))] use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::hash; use std::mem; use std::mem::size_of; use std::ops::{Index, IndexMut}; use std::{iter::FromIterator, slice}; use crate::imp_prelude::*; use crate::Arc; use crate::{ dimension, iter::{Iter, IterMut}, numeric_util, FoldWhile, NdIndex, OwnedArcRepr, Zip, }; #[cold] #[inline(never)] pub(crate) fn array_out_of_bounds() -> ! { panic!("ndarray: index out of bounds"); } #[inline(always)] pub fn debug_bounds_check(_a: &ArrayBase, _index: &I) where D: Dimension, I: NdIndex, S: Data, { debug_bounds_check!(_a, *_index); } /// Access the element at **index**. /// /// **Panics** if index is out of bounds. impl Index for ArrayBase where D: Dimension, I: NdIndex, S: Data, { type Output = S::Elem; #[inline] fn index(&self, index: I) -> &S::Elem { debug_bounds_check!(self, index); unsafe { &*self.ptr.as_ptr().offset( index .index_checked(&self.dim, &self.strides) .unwrap_or_else(|| array_out_of_bounds()), ) } } } /// Access the element at **index** mutably. /// /// **Panics** if index is out of bounds. impl IndexMut for ArrayBase where D: Dimension, I: NdIndex, S: DataMut, { #[inline] fn index_mut(&mut self, index: I) -> &mut S::Elem { debug_bounds_check!(self, index); unsafe { &mut *self.as_mut_ptr().offset( index .index_checked(&self.dim, &self.strides) .unwrap_or_else(|| array_out_of_bounds()), ) } } } /// Return `true` if the array shapes and all elements of `self` and /// `rhs` are equal. Return `false` otherwise. impl PartialEq> for ArrayBase where A: PartialEq, S: Data, S2: Data, D: Dimension, { fn eq(&self, rhs: &ArrayBase) -> bool { if self.shape() != rhs.shape() { return false; } if let Some(self_s) = self.as_slice() { if let Some(rhs_s) = rhs.as_slice() { return numeric_util::unrolled_eq(self_s, rhs_s); } } Zip::from(self) .and(rhs) .fold_while(true, |_, a, b| { if a != b { FoldWhile::Done(false) } else { FoldWhile::Continue(true) } }) .into_inner() } } /// Return `true` if the array shapes and all elements of `self` and /// `rhs` are equal. Return `false` otherwise. #[allow(clippy::unconditional_recursion)] // false positive impl<'a, A, B, S, S2, D> PartialEq<&'a ArrayBase> for ArrayBase where A: PartialEq, S: Data, S2: Data, D: Dimension, { fn eq(&self, rhs: &&ArrayBase) -> bool { *self == **rhs } } /// Return `true` if the array shapes and all elements of `self` and /// `rhs` are equal. Return `false` otherwise. #[allow(clippy::unconditional_recursion)] // false positive impl<'a, A, B, S, S2, D> PartialEq> for &'a ArrayBase where A: PartialEq, S: Data, S2: Data, D: Dimension, { fn eq(&self, rhs: &ArrayBase) -> bool { **self == *rhs } } impl Eq for ArrayBase where D: Dimension, S: Data, S::Elem: Eq, { } impl From> for ArrayBase where S: DataOwned { /// Create a one-dimensional array from a boxed slice (no copying needed). /// /// **Panics** if the length is greater than `isize::MAX`. fn from(b: Box<[A]>) -> Self { Self::from_vec(b.into_vec()) } } impl From> for ArrayBase where S: DataOwned { /// Create a one-dimensional array from a vector (no copying needed). /// /// **Panics** if the length is greater than `isize::MAX`. /// /// ```rust /// use ndarray::Array; /// /// let array = Array::from(vec![1., 2., 3., 4.]); /// ``` fn from(v: Vec) -> Self { Self::from_vec(v) } } impl FromIterator for ArrayBase where S: DataOwned { /// Create a one-dimensional array from an iterable. /// /// **Panics** if the length is greater than `isize::MAX`. /// /// ```rust /// use ndarray::{Array, arr1}; /// /// // Either use `from_iter` directly or use `Iterator::collect`. /// let array = Array::from_iter((0..5).map(|x| x * x)); /// assert!(array == arr1(&[0, 1, 4, 9, 16])) /// ``` fn from_iter(iterable: I) -> ArrayBase where I: IntoIterator { Self::from_iter(iterable) } } impl<'a, S, D> IntoIterator for &'a ArrayBase where D: Dimension, S: Data, { type Item = &'a S::Elem; type IntoIter = Iter<'a, S::Elem, D>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a, S, D> IntoIterator for &'a mut ArrayBase where D: Dimension, S: DataMut, { type Item = &'a mut S::Elem; type IntoIter = IterMut<'a, S::Elem, D>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl<'a, A, D> IntoIterator for ArrayView<'a, A, D> where D: Dimension { type Item = &'a A; type IntoIter = Iter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { self.into_iter_() } } impl<'a, A, D> IntoIterator for ArrayViewMut<'a, A, D> where D: Dimension { type Item = &'a mut A; type IntoIter = IterMut<'a, A, D>; fn into_iter(self) -> Self::IntoIter { self.into_iter_() } } impl hash::Hash for ArrayBase where D: Dimension, S: Data, S::Elem: hash::Hash, { // Note: elements are hashed in the logical order fn hash(&self, state: &mut H) { self.shape().hash(state); if let Some(self_s) = self.as_slice() { hash::Hash::hash_slice(self_s, state); } else { for row in self.rows() { if let Some(row_s) = row.as_slice() { hash::Hash::hash_slice(row_s, state); } else { for elt in row { elt.hash(state) } } } } } } // NOTE: ArrayBase keeps an internal raw pointer that always // points into the storage. This is Sync & Send as long as we // follow the usual inherited mutability rules, as we do with // Vec, &[] and &mut [] /// `ArrayBase` is `Sync` when the storage type is. unsafe impl Sync for ArrayBase where S: Sync + Data, D: Sync, { } /// `ArrayBase` is `Send` when the storage type is. unsafe impl Send for ArrayBase where S: Send + Data, D: Send, { } #[cfg(feature = "serde")] // Use version number so we can add a packed format later. pub const ARRAY_FORMAT_VERSION: u8 = 1u8; // use "raw" form instead of type aliases here so that they show up in docs /// Implementation of `ArrayView::from(&S)` where `S` is a slice or sliceable. /// /// **Panics** if the length of the slice overflows `isize`. (This can only /// occur if `A` is zero-sized, because slices cannot contain more than /// `isize::MAX` number of bytes.) impl<'a, A, Slice: ?Sized> From<&'a Slice> for ArrayView<'a, A, Ix1> where Slice: AsRef<[A]> { /// Create a one-dimensional read-only array view of the data in `slice`. /// /// **Panics** if the slice length is greater than `isize::MAX`. fn from(slice: &'a Slice) -> Self { aview1(slice.as_ref()) } } /// Implementation of ArrayView2::from(&[[A; N]; M]) /// /// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A /// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). /// **Panics** if N == 0 and the number of rows is greater than isize::MAX. impl<'a, A, const M: usize, const N: usize> From<&'a [[A; N]; M]> for ArrayView<'a, A, Ix2> { /// Create a two-dimensional read-only array view of the data in `slice` fn from(xs: &'a [[A; N]; M]) -> Self { Self::from(&xs[..]) } } /// Implementation of ArrayView2::from(&[[A; N]]) /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. (This /// can only occur if A is zero-sized or if `N` is zero, because slices cannot /// contain more than `isize::MAX` number of bytes.) impl<'a, A, const N: usize> From<&'a [[A; N]]> for ArrayView<'a, A, Ix2> { /// Create a two-dimensional read-only array view of the data in `slice` fn from(xs: &'a [[A; N]]) -> Self { aview2(xs) } } /// Implementation of `ArrayView::from(&A)` where `A` is an array. impl<'a, A, S, D> From<&'a ArrayBase> for ArrayView<'a, A, D> where S: Data, D: Dimension, { /// Create a read-only array view of the array. fn from(array: &'a ArrayBase) -> Self { array.view() } } /// Implementation of `ArrayViewMut::from(&mut S)` where `S` is a slice or sliceable. impl<'a, A, Slice: ?Sized> From<&'a mut Slice> for ArrayViewMut<'a, A, Ix1> where Slice: AsMut<[A]> { /// Create a one-dimensional read-write array view of the data in `slice`. /// /// **Panics** if the slice length is greater than `isize::MAX`. fn from(slice: &'a mut Slice) -> Self { let xs = slice.as_mut(); if mem::size_of::() == 0 { assert!( xs.len() <= isize::MAX as usize, "Slice length must fit in `isize`.", ); } unsafe { Self::from_shape_ptr(xs.len(), xs.as_mut_ptr()) } } } /// Implementation of ArrayViewMut2::from(&mut [[A; N]; M]) /// /// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A /// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). /// **Panics** if N == 0 and the number of rows is greater than isize::MAX. impl<'a, A, const M: usize, const N: usize> From<&'a mut [[A; N]; M]> for ArrayViewMut<'a, A, Ix2> { /// Create a two-dimensional read-write array view of the data in `slice` fn from(xs: &'a mut [[A; N]; M]) -> Self { Self::from(&mut xs[..]) } } /// Implementation of ArrayViewMut2::from(&mut [[A; N]]) /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. (This /// can only occur if `A` is zero-sized or if `N` is zero, because slices /// cannot contain more than `isize::MAX` number of bytes.) impl<'a, A, const N: usize> From<&'a mut [[A; N]]> for ArrayViewMut<'a, A, Ix2> { /// Create a two-dimensional read-write array view of the data in `slice` fn from(xs: &'a mut [[A; N]]) -> Self { let cols = N; let rows = xs.len(); let dim = Ix2(rows, cols); if size_of::() == 0 { dimension::size_of_shape_checked(&dim).expect("Product of non-zero axis lengths must not overflow isize."); } else if N == 0 { assert!( xs.len() <= isize::MAX as usize, "Product of non-zero axis lengths must not overflow isize.", ); } // `cols * rows` is guaranteed to fit in `isize` because we checked that it fits in // `isize::MAX` unsafe { let data = slice::from_raw_parts_mut(xs.as_mut_ptr() as *mut A, cols * rows); ArrayViewMut::from_shape_ptr(dim, data.as_mut_ptr()) } } } /// Implementation of `ArrayViewMut::from(&mut A)` where `A` is an array. impl<'a, A, S, D> From<&'a mut ArrayBase> for ArrayViewMut<'a, A, D> where S: DataMut, D: Dimension, { /// Create a read-write array view of the array. fn from(array: &'a mut ArrayBase) -> Self { array.view_mut() } } impl From> for ArcArray where D: Dimension { fn from(arr: Array) -> ArcArray { let data = OwnedArcRepr(Arc::new(arr.data)); // safe because: equivalent unmoved data, ptr and dims remain valid unsafe { ArrayBase::from_data_ptr(data, arr.ptr).with_strides_dim(arr.strides, arr.dim) } } } /// Argument conversion into an array view /// /// The trait is parameterized over `A`, the element type, and `D`, the /// dimensionality of the array. `D` defaults to one-dimensional. /// /// Use `.into()` to do the conversion. /// /// ``` /// use ndarray::AsArray; /// /// fn sum<'a, V: AsArray<'a, f64>>(data: V) -> f64 { /// let array_view = data.into(); /// array_view.sum() /// } /// /// assert_eq!( /// sum(&[1., 2., 3.]), /// 6. /// ); /// /// ``` pub trait AsArray<'a, A: 'a, D = Ix1>: Into> where D: Dimension { } impl<'a, A: 'a, D, T> AsArray<'a, A, D> for T where T: Into>, D: Dimension, { } /// Create an owned array with a default state. /// /// The array is created with dimension `D::default()`, which results /// in for example dimensions `0` and `(0, 0)` with zero elements for the /// one-dimensional and two-dimensional cases respectively. /// /// The default dimension for `IxDyn` is `IxDyn(&[0])` (array has zero /// elements). And the default for the dimension `()` is `()` (array has /// one element). /// /// Since arrays cannot grow, the intention is to use the default value as /// placeholder. impl Default for ArrayBase where S: DataOwned, D: Dimension, A: Default, { // NOTE: We can implement Default for non-zero dimensional array views by // using an empty slice, however we need a trait for nonzero Dimension. fn default() -> Self { ArrayBase::default(D::default()) } } ndarray-0.16.1/src/data_repr.rs000064400000000000000000000115351046102023000144540ustar 00000000000000use crate::extension::nonnull; #[cfg(not(feature = "std"))] use alloc::borrow::ToOwned; use alloc::slice; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem; use std::mem::ManuallyDrop; use std::ptr::NonNull; #[allow(unused_imports)] use rawpointer::PointerExt; /// Array's representation. /// /// *Don’t use this type directly—use the type alias /// [`Array`](crate::Array) for the array type!* // Like a Vec, but with non-unique ownership semantics // // repr(C) to make it transmutable OwnedRepr -> OwnedRepr if // transmutable A -> B. #[derive(Debug)] #[repr(C)] pub struct OwnedRepr { ptr: NonNull, len: usize, capacity: usize, } impl OwnedRepr { pub(crate) fn from(v: Vec) -> Self { let mut v = ManuallyDrop::new(v); let len = v.len(); let capacity = v.capacity(); let ptr = nonnull::nonnull_from_vec_data(&mut v); Self { ptr, len, capacity } } pub(crate) fn into_vec(self) -> Vec { ManuallyDrop::new(self).take_as_vec() } pub(crate) fn as_slice(&self) -> &[A] { unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } } pub(crate) fn len(&self) -> usize { self.len } pub(crate) fn as_ptr(&self) -> *const A { self.ptr.as_ptr() } pub(crate) fn as_nonnull_mut(&mut self) -> NonNull { self.ptr } /// Return end pointer pub(crate) fn as_end_nonnull(&self) -> NonNull { unsafe { self.ptr.add(self.len) } } /// Reserve `additional` elements; return the new pointer /// /// ## Safety /// /// Note that existing pointers into the data are invalidated #[must_use = "must use new pointer to update existing pointers"] pub(crate) fn reserve(&mut self, additional: usize) -> NonNull { self.modify_as_vec(|mut v| { v.reserve(additional); v }); self.as_nonnull_mut() } /// Set the valid length of the data /// /// ## Safety /// /// The first `new_len` elements of the data should be valid. pub(crate) unsafe fn set_len(&mut self, new_len: usize) { debug_assert!(new_len <= self.capacity); self.len = new_len; } /// Return the length (number of elements in total) pub(crate) fn release_all_elements(&mut self) -> usize { let ret = self.len; self.len = 0; ret } /// Cast self into equivalent repr of other element type /// /// ## Safety /// /// Caller must ensure the two types have the same representation. /// **Panics** if sizes don't match (which is not a sufficient check). pub(crate) unsafe fn data_subst(self) -> OwnedRepr { // necessary but not sufficient check assert_eq!(mem::size_of::(), mem::size_of::()); let self_ = ManuallyDrop::new(self); OwnedRepr { ptr: self_.ptr.cast::(), len: self_.len, capacity: self_.capacity, } } fn modify_as_vec(&mut self, f: impl FnOnce(Vec) -> Vec) { let v = self.take_as_vec(); *self = Self::from(f(v)); } fn take_as_vec(&mut self) -> Vec { let capacity = self.capacity; let len = self.len; self.len = 0; self.capacity = 0; unsafe { Vec::from_raw_parts(self.ptr.as_ptr(), len, capacity) } } } impl Clone for OwnedRepr where A: Clone { fn clone(&self) -> Self { Self::from(self.as_slice().to_owned()) } fn clone_from(&mut self, other: &Self) { let mut v = self.take_as_vec(); let other = other.as_slice(); if v.len() > other.len() { v.truncate(other.len()); } let (front, back) = other.split_at(v.len()); v.clone_from_slice(front); v.extend_from_slice(back); *self = Self::from(v); } } impl Drop for OwnedRepr { fn drop(&mut self) { if self.capacity > 0 { // correct because: If the elements don't need dropping, an // empty Vec is ok. Only the Vec's allocation needs dropping. // // implemented because: in some places in ndarray // where A: Copy (hence does not need drop) we use uninitialized elements in // vectors. Setting the length to 0 avoids that the vector tries to // drop, slice or otherwise produce values of these elements. // (The details of the validity letting this happen with nonzero len, are // under discussion as of this writing.) if !mem::needs_drop::() { self.len = 0; } // drop as a Vec. self.take_as_vec(); } } } unsafe impl Sync for OwnedRepr where A: Sync {} unsafe impl Send for OwnedRepr where A: Send {} ndarray-0.16.1/src/data_traits.rs000064400000000000000000000516271046102023000150200ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! The data (inner representation) traits for ndarray #[allow(unused_imports)] use rawpointer::PointerExt; #[cfg(target_has_atomic = "ptr")] use alloc::sync::Arc; #[cfg(not(target_has_atomic = "ptr"))] use portable_atomic_util::Arc; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem::MaybeUninit; use std::mem::{self, size_of}; use std::ptr::NonNull; use crate::{ArcArray, Array, ArrayBase, CowRepr, Dimension, OwnedArcRepr, OwnedRepr, RawViewRepr, ViewRepr}; /// Array representation trait. /// /// For an array that meets the invariants of the `ArrayBase` type. This trait /// does not imply any ownership or lifetime; pointers to elements in the array /// may not be safe to dereference. /// /// ***Note:*** `RawData` is not an extension interface at this point. /// Traits in Rust can serve many different roles. This trait is public because /// it is used as a bound on public methods. #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait RawData: Sized { /// The array element type. type Elem; #[doc(hidden)] fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool; private_decl! {} } /// Array representation trait. /// /// For an array with writable elements. /// /// ***Internal trait, see `RawData`.*** #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait RawDataMut: RawData { /// If possible, ensures that the array has unique access to its data. /// /// The implementer must ensure that if the input is contiguous, then the /// output has the same strides as input. /// /// Additionally, if `Self` provides safe mutable access to array elements, /// then this method **must** panic or ensure that the data is unique. #[doc(hidden)] fn try_ensure_unique(_: &mut ArrayBase) where Self: Sized, D: Dimension; /// If possible, returns whether the array has unique access to its data. /// /// If `Self` provides safe mutable access to array elements, then it /// **must** return `Some(_)`. #[doc(hidden)] fn try_is_unique(&mut self) -> Option; } /// Array representation trait. /// /// An array representation that can be cloned. /// /// ***Internal trait, see `RawData`.*** #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait RawDataClone: RawData { #[doc(hidden)] /// Unsafe because, `ptr` must point inside the current storage. unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull); #[doc(hidden)] unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: NonNull) -> NonNull { let (data, ptr) = other.clone_with_ptr(ptr); *self = data; ptr } } /// Array representation trait. /// /// For an array with elements that can be accessed with safe code. /// /// ***Internal trait, see `RawData`.*** #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait Data: RawData { /// Converts the array to a uniquely owned array, cloning elements if necessary. #[doc(hidden)] #[allow(clippy::wrong_self_convention)] fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, D: Dimension; /// Converts the array into `Array` if this is possible without /// cloning the array elements. Otherwise, returns `self_` unchanged. #[doc(hidden)] fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> where D: Dimension; /// Return a shared ownership (copy on write) array based on the existing one, /// cloning elements if necessary. #[doc(hidden)] #[allow(clippy::wrong_self_convention)] fn to_shared(self_: &ArrayBase) -> ArcArray where Self::Elem: Clone, D: Dimension, { // clone to shared self_.to_owned().into_shared() } } /// Array representation trait. /// /// For an array with writable elements that can be accessed with safe code. /// /// ***Internal trait, see `Data`.*** // // # For implementers // // If you implement the `DataMut` trait, you are guaranteeing that the // `RawDataMut::try_ensure_unique` implementation always panics or ensures that // the data is unique. You are also guaranteeing that `try_is_unique` always // returns `Some(_)`. #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait DataMut: Data + RawDataMut { /// Ensures that the array has unique access to its data. #[doc(hidden)] #[inline] fn ensure_unique(self_: &mut ArrayBase) where Self: Sized, D: Dimension, { Self::try_ensure_unique(self_) } /// Returns whether the array has unique access to its data. #[doc(hidden)] #[inline] #[allow(clippy::wrong_self_convention)] // mut needed for Arc types fn is_unique(&mut self) -> bool { self.try_is_unique().unwrap() } } unsafe impl RawData for RawViewRepr<*const A> { type Elem = A; #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } private_impl! {} } unsafe impl RawDataClone for RawViewRepr<*const A> { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { (*self, ptr) } } unsafe impl RawData for RawViewRepr<*mut A> { type Elem = A; #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } private_impl! {} } unsafe impl RawDataMut for RawViewRepr<*mut A> { #[inline] fn try_ensure_unique(_: &mut ArrayBase) where Self: Sized, D: Dimension, { } #[inline] fn try_is_unique(&mut self) -> Option { None } } unsafe impl RawDataClone for RawViewRepr<*mut A> { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { (*self, ptr) } } unsafe impl RawData for OwnedArcRepr { type Elem = A; fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { self.0._is_pointer_inbounds(self_ptr) } private_impl! {} } // NOTE: Copy on write unsafe impl RawDataMut for OwnedArcRepr where A: Clone { fn try_ensure_unique(self_: &mut ArrayBase) where Self: Sized, D: Dimension, { if Arc::get_mut(&mut self_.data.0).is_some() { return; } if self_.dim.size() <= self_.data.0.len() / 2 { // Clone only the visible elements if the current view is less than // half of backing data. *self_ = self_.to_owned().into_shared(); return; } let rcvec = &mut self_.data.0; let a_size = mem::size_of::() as isize; let our_off = if a_size != 0 { (self_.ptr.as_ptr() as isize - rcvec.as_ptr() as isize) / a_size } else { 0 }; let rvec = Arc::make_mut(rcvec); unsafe { self_.ptr = rvec.as_nonnull_mut().offset(our_off); } } fn try_is_unique(&mut self) -> Option { Some(Arc::get_mut(&mut self.0).is_some()) } } unsafe impl Data for OwnedArcRepr { fn into_owned(mut self_: ArrayBase) -> Array where A: Clone, D: Dimension, { Self::ensure_unique(&mut self_); let data = Arc::try_unwrap(self_.data.0).ok().unwrap(); // safe because data is equivalent unsafe { ArrayBase::from_data_ptr(data, self_.ptr).with_strides_dim(self_.strides, self_.dim) } } fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> where D: Dimension { match Arc::try_unwrap(self_.data.0) { Ok(owned_data) => unsafe { // Safe because the data is equivalent. Ok(ArrayBase::from_data_ptr(owned_data, self_.ptr).with_strides_dim(self_.strides, self_.dim)) }, Err(arc_data) => unsafe { // Safe because the data is equivalent; we're just // reconstructing `self_`. Err(ArrayBase::from_data_ptr(OwnedArcRepr(arc_data), self_.ptr) .with_strides_dim(self_.strides, self_.dim)) }, } } #[allow(clippy::wrong_self_convention)] fn to_shared(self_: &ArrayBase) -> ArcArray where Self::Elem: Clone, D: Dimension, { // to shared using clone of OwnedArcRepr without clone of raw data. self_.clone() } } unsafe impl DataMut for OwnedArcRepr where A: Clone {} unsafe impl RawDataClone for OwnedArcRepr { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { // pointer is preserved (self.clone(), ptr) } } unsafe impl RawData for OwnedRepr { type Elem = A; fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { let slc = self.as_slice(); let ptr = slc.as_ptr() as *mut A; let end = unsafe { ptr.add(slc.len()) }; self_ptr >= ptr && self_ptr <= end } private_impl! {} } unsafe impl RawDataMut for OwnedRepr { #[inline] fn try_ensure_unique(_: &mut ArrayBase) where Self: Sized, D: Dimension, { } #[inline] fn try_is_unique(&mut self) -> Option { Some(true) } } unsafe impl Data for OwnedRepr { #[inline] fn into_owned(self_: ArrayBase) -> Array where A: Clone, D: Dimension, { self_ } #[inline] fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> where D: Dimension { Ok(self_) } } unsafe impl DataMut for OwnedRepr {} unsafe impl RawDataClone for OwnedRepr where A: Clone { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { let mut u = self.clone(); let mut new_ptr = u.as_nonnull_mut(); if size_of::() != 0 { let our_off = (ptr.as_ptr() as isize - self.as_ptr() as isize) / mem::size_of::() as isize; new_ptr = new_ptr.offset(our_off); } (u, new_ptr) } unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: NonNull) -> NonNull { let our_off = if size_of::() != 0 { (ptr.as_ptr() as isize - other.as_ptr() as isize) / mem::size_of::() as isize } else { 0 }; self.clone_from(other); self.as_nonnull_mut().offset(our_off) } } unsafe impl<'a, A> RawData for ViewRepr<&'a A> { type Elem = A; #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } private_impl! {} } unsafe impl<'a, A> Data for ViewRepr<&'a A> { fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, D: Dimension, { self_.to_owned() } fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> where D: Dimension { Err(self_) } } unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { (*self, ptr) } } unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> { type Elem = A; #[inline(always)] fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } private_impl! {} } unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> { #[inline] fn try_ensure_unique(_: &mut ArrayBase) where Self: Sized, D: Dimension, { } #[inline] fn try_is_unique(&mut self) -> Option { Some(true) } } unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { fn into_owned(self_: ArrayBase) -> Array where Self::Elem: Clone, D: Dimension, { self_.to_owned() } fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> where D: Dimension { Err(self_) } } unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} /// Array representation trait. /// /// A representation which can be the owner of its data. /// /// ***Internal trait, see `Data`.*** // The owned storage represents the ownership and allocation of the array's elements. // The storage may be unique or shared ownership style; it must be an aliasable owner // (permit aliasing pointers, such as our separate array head pointer). // // The array storage must be initially mutable - copy on write arrays may require copying for // unsharing storage before mutating it. The initially allocated storage must be mutable so // that it can be mutated directly - through .raw_view_mut_unchecked() - for initialization. #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait DataOwned: Data { /// Corresponding owned data with MaybeUninit elements type MaybeUninit: DataOwned> + RawDataSubst; #[doc(hidden)] fn new(elements: Vec) -> Self; /// Converts the data representation to a shared (copy on write) /// representation, cloning the array elements if necessary. #[doc(hidden)] #[allow(clippy::wrong_self_convention)] fn into_shared(self_: ArrayBase) -> ArcArray where Self::Elem: Clone, D: Dimension; } /// Array representation trait. /// /// A representation that is a lightweight view. /// /// ***Internal trait, see `Data`.*** #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait DataShared: Clone + Data + RawDataClone {} unsafe impl DataShared for OwnedArcRepr {} unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {} unsafe impl DataOwned for OwnedRepr { type MaybeUninit = OwnedRepr>; fn new(elements: Vec) -> Self { OwnedRepr::from(elements) } fn into_shared(self_: ArrayBase) -> ArcArray where A: Clone, D: Dimension, { ArcArray::from(self_) } } unsafe impl DataOwned for OwnedArcRepr { type MaybeUninit = OwnedArcRepr>; fn new(elements: Vec) -> Self { OwnedArcRepr(Arc::new(OwnedRepr::from(elements))) } fn into_shared(self_: ArrayBase) -> ArcArray where A: Clone, D: Dimension, { self_ } } unsafe impl<'a, A> RawData for CowRepr<'a, A> { type Elem = A; #[inline] fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool { match self { CowRepr::View(view) => view._is_pointer_inbounds(ptr), CowRepr::Owned(data) => data._is_pointer_inbounds(ptr), } } private_impl! {} } unsafe impl<'a, A> RawDataMut for CowRepr<'a, A> where A: Clone { #[inline] fn try_ensure_unique(array: &mut ArrayBase) where Self: Sized, D: Dimension, { match array.data { CowRepr::View(_) => { let owned = array.to_owned(); array.data = CowRepr::Owned(owned.data); array.ptr = owned.ptr; array.dim = owned.dim; array.strides = owned.strides; } CowRepr::Owned(_) => {} } } #[inline] fn try_is_unique(&mut self) -> Option { Some(self.is_owned()) } } unsafe impl<'a, A> RawDataClone for CowRepr<'a, A> where A: Clone { unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { match self { CowRepr::View(view) => { let (new_view, ptr) = view.clone_with_ptr(ptr); (CowRepr::View(new_view), ptr) } CowRepr::Owned(data) => { let (new_data, ptr) = data.clone_with_ptr(ptr); (CowRepr::Owned(new_data), ptr) } } } unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: NonNull) -> NonNull { match (&mut *self, other) { (CowRepr::View(self_), CowRepr::View(other)) => self_.clone_from_with_ptr(other, ptr), (CowRepr::Owned(self_), CowRepr::Owned(other)) => self_.clone_from_with_ptr(other, ptr), (_, CowRepr::Owned(other)) => { let (cloned, ptr) = other.clone_with_ptr(ptr); *self = CowRepr::Owned(cloned); ptr } (_, CowRepr::View(other)) => { let (cloned, ptr) = other.clone_with_ptr(ptr); *self = CowRepr::View(cloned); ptr } } } } unsafe impl<'a, A> Data for CowRepr<'a, A> { #[inline] fn into_owned(self_: ArrayBase, D>) -> Array where A: Clone, D: Dimension, { match self_.data { CowRepr::View(_) => self_.to_owned(), CowRepr::Owned(data) => unsafe { // safe because the data is equivalent so ptr, dims remain valid ArrayBase::from_data_ptr(data, self_.ptr).with_strides_dim(self_.strides, self_.dim) }, } } fn try_into_owned_nocopy(self_: ArrayBase) -> Result, ArrayBase> where D: Dimension { match self_.data { CowRepr::View(_) => Err(self_), CowRepr::Owned(data) => unsafe { // safe because the data is equivalent so ptr, dims remain valid Ok(ArrayBase::from_data_ptr(data, self_.ptr).with_strides_dim(self_.strides, self_.dim)) }, } } } unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} unsafe impl<'a, A> DataOwned for CowRepr<'a, A> { type MaybeUninit = CowRepr<'a, MaybeUninit>; fn new(elements: Vec) -> Self { CowRepr::Owned(OwnedRepr::new(elements)) } fn into_shared(self_: ArrayBase) -> ArcArray where A: Clone, D: Dimension, { self_.into_owned().into_shared() } } /// Array representation trait. /// /// The RawDataSubst trait maps the element type of array storage, while /// keeping the same kind of storage. /// /// For example, `RawDataSubst` can map the type `OwnedRepr` to `OwnedRepr`. pub trait RawDataSubst: RawData { /// The resulting array storage of the same kind but substituted element type type Output: RawData; /// Unsafely translate the data representation from one element /// representation to another. /// /// ## Safety /// /// Caller must ensure the two types have the same representation. unsafe fn data_subst(self) -> Self::Output; } impl RawDataSubst for OwnedRepr { type Output = OwnedRepr; unsafe fn data_subst(self) -> Self::Output { self.data_subst() } } impl RawDataSubst for OwnedArcRepr { type Output = OwnedArcRepr; unsafe fn data_subst(self) -> Self::Output { OwnedArcRepr(Arc::from_raw(Arc::into_raw(self.0) as *const OwnedRepr)) } } impl RawDataSubst for RawViewRepr<*const A> { type Output = RawViewRepr<*const B>; unsafe fn data_subst(self) -> Self::Output { RawViewRepr::new() } } impl RawDataSubst for RawViewRepr<*mut A> { type Output = RawViewRepr<*mut B>; unsafe fn data_subst(self) -> Self::Output { RawViewRepr::new() } } impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a A> { type Output = ViewRepr<&'a B>; unsafe fn data_subst(self) -> Self::Output { ViewRepr::new() } } impl<'a, A: 'a, B: 'a> RawDataSubst for ViewRepr<&'a mut A> { type Output = ViewRepr<&'a mut B>; unsafe fn data_subst(self) -> Self::Output { ViewRepr::new() } } impl<'a, A: 'a, B: 'a> RawDataSubst for CowRepr<'a, A> { type Output = CowRepr<'a, B>; unsafe fn data_subst(self) -> Self::Output { match self { CowRepr::View(view) => CowRepr::View(view.data_subst()), CowRepr::Owned(owned) => CowRepr::Owned(owned.data_subst()), } } } ndarray-0.16.1/src/dimension/axes.rs000064400000000000000000000060551046102023000154410ustar 00000000000000use crate::{Axis, Dimension, Ixs}; /// Create a new Axes iterator pub(crate) fn axes_of<'a, D>(d: &'a D, strides: &'a D) -> Axes<'a, D> where D: Dimension { Axes { dim: d, strides, start: 0, end: d.ndim(), } } /// An iterator over the length and stride of each axis of an array. /// /// This iterator is created from the array method /// [`.axes()`](crate::ArrayBase::axes). /// /// Iterator element type is [`AxisDescription`]. /// /// # Examples /// /// ``` /// use ndarray::Array3; /// use ndarray::Axis; /// /// let a = Array3::::zeros((3, 5, 4)); /// /// // find the largest axis in the array /// // check the axis index and its length /// /// let largest_axis = a.axes() /// .max_by_key(|ax| ax.len) /// .unwrap(); /// assert_eq!(largest_axis.axis, Axis(1)); /// assert_eq!(largest_axis.len, 5); /// ``` #[derive(Debug)] pub struct Axes<'a, D> { dim: &'a D, strides: &'a D, start: usize, end: usize, } /// Description of the axis, its length and its stride. #[derive(Debug)] pub struct AxisDescription { /// Axis identifier (index) pub axis: Axis, /// Length in count of elements of the current axis pub len: usize, /// Stride in count of elements of the current axis pub stride: isize, } copy_and_clone!(AxisDescription); copy_and_clone!(['a, D] Axes<'a, D>); impl<'a, D> Iterator for Axes<'a, D> where D: Dimension { /// Description of the axis, its length and its stride. type Item = AxisDescription; fn next(&mut self) -> Option { if self.start < self.end { let i = self.start.post_inc(); Some(AxisDescription { axis: Axis(i), len: self.dim[i], stride: self.strides[i] as Ixs, }) } else { None } } fn fold(self, init: B, f: F) -> B where F: FnMut(B, AxisDescription) -> B { (self.start..self.end) .map(move |i| AxisDescription { axis: Axis(i), len: self.dim[i], stride: self.strides[i] as isize, }) .fold(init, f) } fn size_hint(&self) -> (usize, Option) { let len = self.end - self.start; (len, Some(len)) } } impl<'a, D> DoubleEndedIterator for Axes<'a, D> where D: Dimension { fn next_back(&mut self) -> Option { if self.start < self.end { let i = self.end.pre_dec(); Some(AxisDescription { axis: Axis(i), len: self.dim[i], stride: self.strides[i] as Ixs, }) } else { None } } } trait IncOps: Copy { fn post_inc(&mut self) -> Self; fn pre_dec(&mut self) -> Self; } impl IncOps for usize { #[inline(always)] fn post_inc(&mut self) -> Self { let x = *self; *self += 1; x } #[inline(always)] fn pre_dec(&mut self) -> Self { *self -= 1; *self } } ndarray-0.16.1/src/dimension/axis.rs000064400000000000000000000025641046102023000154460ustar 00000000000000// Copyright 2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /// An axis index. /// /// An axis one of an array’s “dimensions”; an *n*-dimensional array has *n* /// axes. Axis *0* is the array’s outermost axis and *n*-1 is the innermost. /// /// All array axis arguments use this type to make the code easier to write /// correctly and easier to understand. /// /// For example: in a method like `index_axis(axis, index)` the code becomes /// self-explanatory when it's called like `.index_axis(Axis(1), i)`; it's /// evident which integer is the axis number and which is the index. /// /// Note: This type does **not** implement From/Into usize and similar trait /// based conversions, because we want to preserve code readability and quality. /// /// `Axis(1)` in itself is a very clear code style and the style that should be /// avoided is code like `1.into()`. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Axis(pub usize); impl Axis { /// Return the index of the axis. #[inline(always)] pub fn index(self) -> usize { self.0 } } ndarray-0.16.1/src/dimension/broadcast.rs000064400000000000000000000105751046102023000164450ustar 00000000000000use crate::error::*; use crate::{Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; /// Calculate the common shape for a pair of array shapes, that they can be broadcasted /// to. Return an error if the shapes are not compatible. /// /// Uses the [NumPy broadcasting rules] // (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules). pub(crate) fn co_broadcast(shape1: &D1, shape2: &D2) -> Result where D1: Dimension, D2: Dimension, Output: Dimension, { let (k, overflow) = shape1.ndim().overflowing_sub(shape2.ndim()); // Swap the order if d2 is longer. if overflow { return co_broadcast::(shape2, shape1); } // The output should be the same length as shape1. let mut out = Output::zeros(shape1.ndim()); for (out, s) in izip!(out.slice_mut(), shape1.slice()) { *out = *s; } for (out, s2) in izip!(&mut out.slice_mut()[k..], shape2.slice()) { if *out != *s2 { if *out == 1 { *out = *s2 } else if *s2 != 1 { return Err(from_kind(ErrorKind::IncompatibleShape)); } } } Ok(out) } pub trait DimMax { /// The resulting dimension type after broadcasting. type Output: Dimension; } /// Dimensions of the same type remain unchanged when co_broadcast. /// So you can directly use `D` as the resulting type. /// (Instead of `>::BroadcastOutput`) impl DimMax for D { type Output = D; } macro_rules! impl_broadcast_distinct_fixed { ($smaller:ty, $larger:ty) => { impl DimMax<$larger> for $smaller { type Output = $larger; } impl DimMax<$smaller> for $larger { type Output = $larger; } }; } impl_broadcast_distinct_fixed!(Ix0, Ix1); impl_broadcast_distinct_fixed!(Ix0, Ix2); impl_broadcast_distinct_fixed!(Ix0, Ix3); impl_broadcast_distinct_fixed!(Ix0, Ix4); impl_broadcast_distinct_fixed!(Ix0, Ix5); impl_broadcast_distinct_fixed!(Ix0, Ix6); impl_broadcast_distinct_fixed!(Ix1, Ix2); impl_broadcast_distinct_fixed!(Ix1, Ix3); impl_broadcast_distinct_fixed!(Ix1, Ix4); impl_broadcast_distinct_fixed!(Ix1, Ix5); impl_broadcast_distinct_fixed!(Ix1, Ix6); impl_broadcast_distinct_fixed!(Ix2, Ix3); impl_broadcast_distinct_fixed!(Ix2, Ix4); impl_broadcast_distinct_fixed!(Ix2, Ix5); impl_broadcast_distinct_fixed!(Ix2, Ix6); impl_broadcast_distinct_fixed!(Ix3, Ix4); impl_broadcast_distinct_fixed!(Ix3, Ix5); impl_broadcast_distinct_fixed!(Ix3, Ix6); impl_broadcast_distinct_fixed!(Ix4, Ix5); impl_broadcast_distinct_fixed!(Ix4, Ix6); impl_broadcast_distinct_fixed!(Ix5, Ix6); impl_broadcast_distinct_fixed!(Ix0, IxDyn); impl_broadcast_distinct_fixed!(Ix1, IxDyn); impl_broadcast_distinct_fixed!(Ix2, IxDyn); impl_broadcast_distinct_fixed!(Ix3, IxDyn); impl_broadcast_distinct_fixed!(Ix4, IxDyn); impl_broadcast_distinct_fixed!(Ix5, IxDyn); impl_broadcast_distinct_fixed!(Ix6, IxDyn); #[cfg(test)] #[cfg(feature = "std")] mod tests { use super::co_broadcast; use crate::{Dim, DimMax, Dimension, ErrorKind, Ix0, IxDynImpl, ShapeError}; #[test] fn test_broadcast_shape() { fn test_co(d1: &D1, d2: &D2, r: Result<>::Output, ShapeError>) where D1: Dimension + DimMax, D2: Dimension, { let d = co_broadcast::>::Output>(&d1, d2); assert_eq!(d, r); } test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3]))); test_co(&Dim([1, 2, 2]), &Dim([1, 3, 4]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5]))); let v = vec![1, 2, 3, 4, 5, 6, 7]; 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())))); let d = Dim([1, 2, 1, 3]); test_co(&d, &d, Ok(d)); test_co(&Dim([2, 1, 2]).into_dyn(), &Dim(0), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); test_co(&Dim([2, 1, 1]), &Dim([0, 0, 1, 3, 4]), Ok(Dim([0, 0, 2, 3, 4]))); test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0]))); test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0]))); test_co(&Dim([1, 3, 0, 1, 1]), &Dim([1, 2, 3, 1]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); } } ndarray-0.16.1/src/dimension/conversion.rs000064400000000000000000000104011046102023000166540ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Tuple to array conversion, IntoDimension, and related things #[cfg(not(feature = "std"))] use alloc::vec::Vec; use num_traits::Zero; use std::ops::{Index, IndexMut}; use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl}; /// $m: macro callback /// $m is called with $arg and then the indices corresponding to the size argument macro_rules! index { ($m:ident $arg:tt 0) => ($m!($arg)); ($m:ident $arg:tt 1) => ($m!($arg 0)); ($m:ident $arg:tt 2) => ($m!($arg 0 1)); ($m:ident $arg:tt 3) => ($m!($arg 0 1 2)); ($m:ident $arg:tt 4) => ($m!($arg 0 1 2 3)); ($m:ident $arg:tt 5) => ($m!($arg 0 1 2 3 4)); ($m:ident $arg:tt 6) => ($m!($arg 0 1 2 3 4 5)); ($m:ident $arg:tt 7) => ($m!($arg 0 1 2 3 4 5 6)); } macro_rules! index_item { ($m:ident $arg:tt 0) => (); ($m:ident $arg:tt 1) => ($m!($arg 0);); ($m:ident $arg:tt 2) => ($m!($arg 0 1);); ($m:ident $arg:tt 3) => ($m!($arg 0 1 2);); ($m:ident $arg:tt 4) => ($m!($arg 0 1 2 3);); ($m:ident $arg:tt 5) => ($m!($arg 0 1 2 3 4);); ($m:ident $arg:tt 6) => ($m!($arg 0 1 2 3 4 5);); ($m:ident $arg:tt 7) => ($m!($arg 0 1 2 3 4 5 6);); } /// Argument conversion a dimension. pub trait IntoDimension { type Dim: Dimension; fn into_dimension(self) -> Self::Dim; } impl IntoDimension for Ix { type Dim = Ix1; #[inline(always)] fn into_dimension(self) -> Ix1 { Ix1(self) } } impl IntoDimension for D where D: Dimension { type Dim = D; #[inline(always)] fn into_dimension(self) -> Self { self } } impl IntoDimension for IxDynImpl { type Dim = IxDyn; #[inline(always)] fn into_dimension(self) -> Self::Dim { Dim::new(self) } } impl IntoDimension for Vec { type Dim = IxDyn; #[inline(always)] fn into_dimension(self) -> Self::Dim { Dim::new(IxDynImpl::from(self)) } } pub trait Convert { type To; fn convert(self) -> Self::To; } macro_rules! sub { ($_x:tt $y:tt) => { $y }; } macro_rules! tuple_type { ([$T:ident] $($index:tt)*) => ( ( $(sub!($index $T), )* ) ); } macro_rules! tuple_expr { ([$self_:expr] $($index:tt)*) => ( ( $($self_[$index], )* ) ); } macro_rules! array_expr { ([$self_:expr] $($index:tt)*) => ( [$($self_ . $index, )*] ); } macro_rules! array_zero { ([] $($index:tt)*) => ( [$(sub!($index 0), )*] ); } macro_rules! tuple_to_array { ([] $($n:tt)*) => { $( impl Convert for [Ix; $n] { type To = index!(tuple_type [Ix] $n); #[inline] fn convert(self) -> Self::To { index!(tuple_expr [self] $n) } } impl IntoDimension for [Ix; $n] { type Dim = Dim<[Ix; $n]>; #[inline(always)] fn into_dimension(self) -> Self::Dim { Dim::new(self) } } impl IntoDimension for index!(tuple_type [Ix] $n) { type Dim = Dim<[Ix; $n]>; #[inline(always)] fn into_dimension(self) -> Self::Dim { Dim::new(index!(array_expr [self] $n)) } } impl Index for Dim<[Ix; $n]> { type Output = usize; #[inline(always)] fn index(&self, index: usize) -> &Self::Output { &self.ix()[index] } } impl IndexMut for Dim<[Ix; $n]> { #[inline(always)] fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.ixm()[index] } } impl Zero for Dim<[Ix; $n]> { #[inline] fn zero() -> Self { Dim::new(index!(array_zero [] $n)) } fn is_zero(&self) -> bool { self.slice().iter().all(|x| *x == 0) } } )* }; } index_item!(tuple_to_array [] 7); ndarray-0.16.1/src/dimension/dim.rs000064400000000000000000000107701046102023000152510ustar 00000000000000// Copyright 2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::Dimension; use super::IntoDimension; use crate::itertools::zip; use crate::Ix; use std::fmt; /// Dimension description. /// /// `Dim` describes the number of axes and the length of each axis /// in an array. It is also used as an index type. /// /// See also the [`Dimension`] trait for its methods and /// operations. /// /// # Examples /// /// To create an array with a particular dimension, you'd just pass /// a tuple (in this example (3, 2) is used), which is converted to /// `Dim` by the array constructor. /// /// ``` /// use ndarray::Array2; /// use ndarray::Dim; /// /// let mut array = Array2::zeros((3, 2)); /// array[[0, 0]] = 1.; /// assert_eq!(array.raw_dim(), Dim([3, 2])); /// ``` #[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] pub struct Dim { index: I, } impl Dim { /// Private constructor and accessors for Dim pub(crate) const fn new(index: I) -> Dim { Dim { index } } #[inline(always)] pub(crate) fn ix(&self) -> &I { &self.index } #[inline(always)] pub(crate) fn ixm(&mut self) -> &mut I { &mut self.index } } /// Create a new dimension value. #[allow(non_snake_case)] pub fn Dim(index: T) -> T::Dim where T: IntoDimension { index.into_dimension() } impl PartialEq for Dim where I: PartialEq { fn eq(&self, rhs: &I) -> bool { self.index == *rhs } } impl fmt::Debug for Dim where I: fmt::Debug { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.index) } } use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; macro_rules! impl_op { ($op:ident, $op_m:ident, $opassign:ident, $opassign_m:ident, $expr:ident) => { impl $op for Dim where Dim: Dimension, { type Output = Self; fn $op_m(mut self, rhs: Self) -> Self { $expr!(self, &rhs); self } } impl $opassign for Dim where Dim: Dimension, { fn $opassign_m(&mut self, rhs: Self) { $expr!(*self, &rhs); } } impl<'a, I> $opassign<&'a Dim> for Dim where Dim: Dimension, { fn $opassign_m(&mut self, rhs: &Self) { for (x, &y) in zip(self.slice_mut(), rhs.slice()) { $expr!(*x, y); } } } }; } macro_rules! impl_single_op { ($op:ident, $op_m:ident, $opassign:ident, $opassign_m:ident, $expr:ident) => { impl $op for Dim<[Ix; 1]> { type Output = Self; #[inline] fn $op_m(mut self, rhs: Ix) -> Self { $expr!(self, rhs); self } } impl $opassign for Dim<[Ix; 1]> { #[inline] fn $opassign_m(&mut self, rhs: Ix) { $expr!((*self)[0], rhs); } } }; } macro_rules! impl_scalar_op { ($op:ident, $op_m:ident, $opassign:ident, $opassign_m:ident, $expr:ident) => { impl $op for Dim where Dim: Dimension, { type Output = Self; fn $op_m(mut self, rhs: Ix) -> Self { $expr!(self, rhs); self } } impl $opassign for Dim where Dim: Dimension, { fn $opassign_m(&mut self, rhs: Ix) { for x in self.slice_mut() { $expr!(*x, rhs); } } } }; } macro_rules! add { ($x:expr, $y:expr) => { $x += $y; }; } macro_rules! sub { ($x:expr, $y:expr) => { $x -= $y; }; } macro_rules! mul { ($x:expr, $y:expr) => { $x *= $y; }; } impl_op!(Add, add, AddAssign, add_assign, add); impl_single_op!(Add, add, AddAssign, add_assign, add); impl_op!(Sub, sub, SubAssign, sub_assign, sub); impl_single_op!(Sub, sub, SubAssign, sub_assign, sub); impl_op!(Mul, mul, MulAssign, mul_assign, mul); impl_scalar_op!(Mul, mul, MulAssign, mul_assign, mul); ndarray-0.16.1/src/dimension/dimension_trait.rs000064400000000000000000000631231046102023000176700ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::fmt::Debug; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use std::ops::{Index, IndexMut}; use super::axes_of; use super::conversion::Convert; use super::ops::DimAdd; use super::{stride_offset, stride_offset_checked}; use crate::itertools::{enumerate, zip}; use crate::IntoDimension; use crate::RemoveAxis; use crate::{ArrayView1, ArrayViewMut1}; use crate::{Axis, DimMax}; use crate::{Dim, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl, Ixs}; /// Array shape and index trait. /// /// This trait defines a number of methods and operations that can be used on /// dimensions and indices. /// /// **Note:** *This trait can not be implemented outside the crate* pub trait Dimension: Clone + Eq + Debug + Send + Sync + Default + IndexMut + Add + AddAssign + for<'x> AddAssign<&'x Self> + Sub + SubAssign + for<'x> SubAssign<&'x Self> + Mul + Mul + MulAssign + for<'x> MulAssign<&'x Self> + MulAssign + DimMax + DimMax + DimMax + DimMax<::Smaller, Output = Self> + DimMax<::Larger, Output = ::Larger> + DimAdd + DimAdd<::Smaller> + DimAdd<::Larger> + DimAdd + DimAdd::Larger> + DimAdd { /// For fixed-size dimension representations (e.g. `Ix2`), this should be /// `Some(ndim)`, and for variable-size dimension representations (e.g. /// `IxDyn`), this should be `None`. const NDIM: Option; /// Pattern matching friendly form of the dimension value. /// /// - For `Ix1`: `usize`, /// - For `Ix2`: `(usize, usize)` /// - and so on.. /// - For `IxDyn`: `IxDyn` type Pattern: IntoDimension + Clone + Debug + PartialEq + Eq + Default; /// Next smaller dimension (if applicable) type Smaller: Dimension; /// Next larger dimension type Larger: Dimension + RemoveAxis; /// Returns the number of dimensions (number of axes). fn ndim(&self) -> usize; /// Convert the dimension into a pattern matching friendly value. fn into_pattern(self) -> Self::Pattern; /// Compute the size of the dimension (number of elements) fn size(&self) -> usize { self.slice().iter().product() } /// Compute the size while checking for overflow. fn size_checked(&self) -> Option { self.slice() .iter() .try_fold(1_usize, |s, &a| s.checked_mul(a)) } #[doc(hidden)] fn slice(&self) -> &[Ix]; #[doc(hidden)] fn slice_mut(&mut self) -> &mut [Ix]; /// Borrow as a read-only array view. fn as_array_view(&self) -> ArrayView1<'_, Ix> { ArrayView1::from(self.slice()) } /// Borrow as a read-write array view. fn as_array_view_mut(&mut self) -> ArrayViewMut1<'_, Ix> { ArrayViewMut1::from(self.slice_mut()) } #[doc(hidden)] fn equal(&self, rhs: &Self) -> bool { self.slice() == rhs.slice() } /// Returns the strides for a standard layout array with the given shape. /// /// If the array is non-empty, the strides result in contiguous layout; if /// the array is empty, the strides are all zeros. #[doc(hidden)] fn default_strides(&self) -> Self { // Compute default array strides // Shape (a, b, c) => Give strides (b * c, c, 1) let mut strides = Self::zeros(self.ndim()); // For empty arrays, use all zero strides. if self.slice().iter().all(|&d| d != 0) { let mut it = strides.slice_mut().iter_mut().rev(); // Set first element to 1 if let Some(rs) = it.next() { *rs = 1; } let mut cum_prod = 1; for (rs, dim) in it.zip(self.slice().iter().rev()) { cum_prod *= *dim; *rs = cum_prod; } } strides } /// Returns the strides for a Fortran layout array with the given shape. /// /// If the array is non-empty, the strides result in contiguous layout; if /// the array is empty, the strides are all zeros. #[doc(hidden)] fn fortran_strides(&self) -> Self { // Compute fortran array strides // Shape (a, b, c) => Give strides (1, a, a * b) let mut strides = Self::zeros(self.ndim()); // For empty arrays, use all zero strides. if self.slice().iter().all(|&d| d != 0) { let mut it = strides.slice_mut().iter_mut(); // Set first element to 1 if let Some(rs) = it.next() { *rs = 1; } let mut cum_prod = 1; for (rs, dim) in it.zip(self.slice()) { cum_prod *= *dim; *rs = cum_prod; } } strides } /// Creates a dimension of all zeros with the specified ndim. /// /// This method is useful for generalizing over fixed-size and /// variable-size dimension representations. /// /// **Panics** if `Self` has a fixed size that is not `ndim`. fn zeros(ndim: usize) -> Self; #[doc(hidden)] #[inline] fn first_index(&self) -> Option { for ax in self.slice().iter() { if *ax == 0 { return None; } } Some(Self::zeros(self.ndim())) } #[doc(hidden)] /// Iteration -- Use self as size, and return next index after `index` /// or None if there are no more. // FIXME: use &Self for index or even &mut? #[inline] fn next_for(&self, index: Self) -> Option { let mut index = index; let mut done = false; for (&dim, ix) in zip(self.slice(), index.slice_mut()).rev() { *ix += 1; if *ix == dim { *ix = 0; } else { done = true; break; } } if done { Some(index) } else { None } } #[doc(hidden)] /// Iteration -- Use self as size, and create the next index after `index` /// Return false if iteration is done /// /// Next in f-order #[inline] fn next_for_f(&self, index: &mut Self) -> bool { let mut end_iteration = true; for (&dim, ix) in zip(self.slice(), index.slice_mut()) { *ix += 1; if *ix == dim { *ix = 0; } else { end_iteration = false; break; } } !end_iteration } /// Returns `true` iff `strides1` and `strides2` are equivalent for the /// shape `self`. /// /// The strides are equivalent if, for each axis with length > 1, the /// strides are equal. /// /// Note: Returns `false` if any of the ndims don't match. #[doc(hidden)] fn strides_equivalent(&self, strides1: &Self, strides2: &D) -> bool where D: Dimension { let shape_ndim = self.ndim(); shape_ndim == strides1.ndim() && shape_ndim == strides2.ndim() && izip!(self.slice(), strides1.slice(), strides2.slice()) .all(|(&d, &s1, &s2)| d <= 1 || s1 as isize == s2 as isize) } #[doc(hidden)] /// Return stride offset for index. fn stride_offset(index: &Self, strides: &Self) -> isize { let mut offset = 0; for (&i, &s) in izip!(index.slice(), strides.slice()) { offset += stride_offset(i, s); } offset } #[doc(hidden)] /// Return stride offset for this dimension and index. fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { stride_offset_checked(self.slice(), strides.slice(), index.slice()) } #[doc(hidden)] fn last_elem(&self) -> usize { if self.ndim() == 0 { 0 } else { self.slice()[self.ndim() - 1] } } #[doc(hidden)] fn set_last_elem(&mut self, i: usize) { let nd = self.ndim(); self.slice_mut()[nd - 1] = i; } #[doc(hidden)] fn is_contiguous(dim: &Self, strides: &Self) -> bool { let defaults = dim.default_strides(); if strides.equal(&defaults) { return true; } if dim.ndim() == 1 { // fast case for ndim == 1: // Either we have length <= 1, then stride is arbitrary, // or we have stride == 1 or stride == -1, but +1 case is already handled above. dim[0] <= 1 || strides[0] as isize == -1 } else { let order = strides._fastest_varying_stride_order(); let strides = strides.slice(); let dim_slice = dim.slice(); let mut cstride = 1; for &i in order.slice() { // a dimension of length 1 can have unequal strides if dim_slice[i] != 1 && (strides[i] as isize).unsigned_abs() != cstride { return false; } cstride *= dim_slice[i]; } true } } /// Return the axis ordering corresponding to the fastest variation /// (in ascending order). /// /// Assumes that no stride value appears twice. #[doc(hidden)] fn _fastest_varying_stride_order(&self) -> Self { let mut indices = self.clone(); for (i, elt) in enumerate(indices.slice_mut()) { *elt = i; } let strides = self.slice(); indices .slice_mut() .sort_by_key(|&i| (strides[i] as isize).abs()); indices } /// Compute the minimum stride axis (absolute value), under the constraint /// that the length of the axis is > 1; #[doc(hidden)] fn min_stride_axis(&self, strides: &Self) -> Axis { let n = match self.ndim() { 0 => panic!("min_stride_axis: Array must have ndim > 0"), 1 => return Axis(0), n => n, }; axes_of(self, strides) .rev() .min_by_key(|ax| ax.stride.abs()) .map_or(Axis(n - 1), |ax| ax.axis) } /// Compute the maximum stride axis (absolute value), under the constraint /// that the length of the axis is > 1; #[doc(hidden)] fn max_stride_axis(&self, strides: &Self) -> Axis { match self.ndim() { 0 => panic!("max_stride_axis: Array must have ndim > 0"), 1 => return Axis(0), _ => {} } axes_of(self, strides) .filter(|ax| ax.len > 1) .max_by_key(|ax| ax.stride.abs()) .map_or(Axis(0), |ax| ax.axis) } /// Convert the dimensional into a dynamic dimensional (IxDyn). fn into_dyn(self) -> IxDyn { IxDyn(self.slice()) } #[doc(hidden)] fn from_dimension(d: &D2) -> Option { let mut s = Self::default(); if s.ndim() == d.ndim() { for i in 0..d.ndim() { s[i] = d[i]; } Some(s) } else { None } } #[doc(hidden)] fn insert_axis(&self, axis: Axis) -> Self::Larger; #[doc(hidden)] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller; private_decl! {} } // Dimension impls macro_rules! impl_insert_axis_array( ($n:expr) => ( #[inline] fn insert_axis(&self, axis: Axis) -> Self::Larger { debug_assert!(axis.index() <= $n); let mut out = [1; $n + 1]; out[0..axis.index()].copy_from_slice(&self.slice()[0..axis.index()]); out[axis.index()+1..=$n].copy_from_slice(&self.slice()[axis.index()..$n]); Dim(out) } ); ); impl Dimension for Dim<[Ix; 0]> { const NDIM: Option = Some(0); type Pattern = (); type Smaller = Self; type Larger = Ix1; // empty product is 1 -> size is 1 #[inline] fn ndim(&self) -> usize { 0 } #[inline] fn slice(&self) -> &[Ix] { &[] } #[inline] fn slice_mut(&mut self) -> &mut [Ix] { &mut [] } #[inline] fn _fastest_varying_stride_order(&self) -> Self { Ix0() } #[inline] fn into_pattern(self) -> Self::Pattern {} #[inline] fn zeros(ndim: usize) -> Self { assert_eq!(ndim, 0); Self::default() } #[inline] fn next_for(&self, _index: Self) -> Option { None } impl_insert_axis_array!(0); #[inline] fn try_remove_axis(&self, _ignore: Axis) -> Self::Smaller { *self } private_impl! {} } impl Dimension for Dim<[Ix; 1]> { const NDIM: Option = Some(1); type Pattern = Ix; type Smaller = Ix0; type Larger = Ix2; #[inline] fn ndim(&self) -> usize { 1 } #[inline] fn slice(&self) -> &[Ix] { self.ix() } #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] fn into_pattern(self) -> Self::Pattern { get!(&self, 0) } #[inline] fn zeros(ndim: usize) -> Self { assert_eq!(ndim, 1); Self::default() } #[inline] fn next_for(&self, mut index: Self) -> Option { getm!(index, 0) += 1; if get!(&index, 0) < get!(self, 0) { Some(index) } else { None } } #[inline] fn equal(&self, rhs: &Self) -> bool { get!(self, 0) == get!(rhs, 0) } #[inline] fn size(&self) -> usize { get!(self, 0) } #[inline] fn size_checked(&self) -> Option { Some(get!(self, 0)) } #[inline] fn default_strides(&self) -> Self { if get!(self, 0) == 0 { Ix1(0) } else { Ix1(1) } } #[inline] fn _fastest_varying_stride_order(&self) -> Self { Ix1(0) } #[inline(always)] fn min_stride_axis(&self, _: &Self) -> Axis { Axis(0) } #[inline(always)] fn max_stride_axis(&self, _: &Self) -> Axis { Axis(0) } #[inline] fn first_index(&self) -> Option { if get!(self, 0) != 0 { Some(Ix1(0)) } else { None } } /// Self is an index, return the stride offset #[inline(always)] fn stride_offset(index: &Self, stride: &Self) -> isize { stride_offset(get!(index, 0), get!(stride, 0)) } /// Return stride offset for this dimension and index. #[inline] fn stride_offset_checked(&self, stride: &Self, index: &Self) -> Option { if get!(index, 0) < get!(self, 0) { Some(stride_offset(get!(index, 0), get!(stride, 0))) } else { None } } impl_insert_axis_array!(1); #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { self.remove_axis(axis) } fn from_dimension(d: &D2) -> Option { if 1 == d.ndim() { Some(Ix1(d[0])) } else { None } } private_impl! {} } impl Dimension for Dim<[Ix; 2]> { const NDIM: Option = Some(2); type Pattern = (Ix, Ix); type Smaller = Ix1; type Larger = Ix3; #[inline] fn ndim(&self) -> usize { 2 } #[inline] fn into_pattern(self) -> Self::Pattern { self.ix().convert() } #[inline] fn slice(&self) -> &[Ix] { self.ix() } #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] fn zeros(ndim: usize) -> Self { assert_eq!(ndim, 2); Self::default() } #[inline] fn next_for(&self, index: Self) -> Option { let mut i = get!(&index, 0); let mut j = get!(&index, 1); let imax = get!(self, 0); let jmax = get!(self, 1); j += 1; if j >= jmax { j = 0; i += 1; if i >= imax { return None; } } Some(Ix2(i, j)) } #[inline] fn equal(&self, rhs: &Self) -> bool { get!(self, 0) == get!(rhs, 0) && get!(self, 1) == get!(rhs, 1) } #[inline] fn size(&self) -> usize { get!(self, 0) * get!(self, 1) } #[inline] fn size_checked(&self) -> Option { let m = get!(self, 0); let n = get!(self, 1); m.checked_mul(n) } #[inline] fn last_elem(&self) -> usize { get!(self, 1) } #[inline] fn set_last_elem(&mut self, i: usize) { getm!(self, 1) = i; } #[inline] fn default_strides(&self) -> Self { let m = get!(self, 0); let n = get!(self, 1); if m == 0 || n == 0 { Ix2(0, 0) } else { Ix2(n, 1) } } #[inline] fn fortran_strides(&self) -> Self { let m = get!(self, 0); let n = get!(self, 1); if m == 0 || n == 0 { Ix2(0, 0) } else { Ix2(1, m) } } #[inline] fn _fastest_varying_stride_order(&self) -> Self { if (get!(self, 0) as Ixs).abs() <= (get!(self, 1) as Ixs).abs() { Ix2(0, 1) } else { Ix2(1, 0) } } #[inline] fn min_stride_axis(&self, strides: &Self) -> Axis { let s = get!(strides, 0) as Ixs; let t = get!(strides, 1) as Ixs; if s.abs() < t.abs() { Axis(0) } else { Axis(1) } } #[inline] fn first_index(&self) -> Option { let m = get!(self, 0); let n = get!(self, 1); if m != 0 && n != 0 { Some(Ix2(0, 0)) } else { None } } /// Self is an index, return the stride offset #[inline(always)] fn stride_offset(index: &Self, strides: &Self) -> isize { let i = get!(index, 0); let j = get!(index, 1); let s = get!(strides, 0); let t = get!(strides, 1); stride_offset(i, s) + stride_offset(j, t) } /// Return stride offset for this dimension and index. #[inline] fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { let m = get!(self, 0); let n = get!(self, 1); let i = get!(index, 0); let j = get!(index, 1); let s = get!(strides, 0); let t = get!(strides, 1); if i < m && j < n { Some(stride_offset(i, s) + stride_offset(j, t)) } else { None } } impl_insert_axis_array!(2); #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { self.remove_axis(axis) } private_impl! {} } impl Dimension for Dim<[Ix; 3]> { const NDIM: Option = Some(3); type Pattern = (Ix, Ix, Ix); type Smaller = Ix2; type Larger = Ix4; #[inline] fn ndim(&self) -> usize { 3 } #[inline] fn into_pattern(self) -> Self::Pattern { self.ix().convert() } #[inline] fn slice(&self) -> &[Ix] { self.ix() } #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] fn size(&self) -> usize { let m = get!(self, 0); let n = get!(self, 1); let o = get!(self, 2); m * n * o } #[inline] fn zeros(ndim: usize) -> Self { assert_eq!(ndim, 3); Self::default() } #[inline] fn next_for(&self, index: Self) -> Option { let mut i = get!(&index, 0); let mut j = get!(&index, 1); let mut k = get!(&index, 2); let imax = get!(self, 0); let jmax = get!(self, 1); let kmax = get!(self, 2); k += 1; if k == kmax { k = 0; j += 1; if j == jmax { j = 0; i += 1; if i == imax { return None; } } } Some(Ix3(i, j, k)) } /// Self is an index, return the stride offset #[inline] fn stride_offset(index: &Self, strides: &Self) -> isize { let i = get!(index, 0); let j = get!(index, 1); let k = get!(index, 2); let s = get!(strides, 0); let t = get!(strides, 1); let u = get!(strides, 2); stride_offset(i, s) + stride_offset(j, t) + stride_offset(k, u) } /// Return stride offset for this dimension and index. #[inline] fn stride_offset_checked(&self, strides: &Self, index: &Self) -> Option { let m = get!(self, 0); let n = get!(self, 1); let l = get!(self, 2); let i = get!(index, 0); let j = get!(index, 1); let k = get!(index, 2); let s = get!(strides, 0); let t = get!(strides, 1); let u = get!(strides, 2); if i < m && j < n && k < l { Some(stride_offset(i, s) + stride_offset(j, t) + stride_offset(k, u)) } else { None } } #[inline] fn _fastest_varying_stride_order(&self) -> Self { let mut stride = *self; let mut order = Ix3(0, 1, 2); macro_rules! swap { ($stride:expr, $order:expr, $x:expr, $y:expr) => { if ($stride[$x] as isize).abs() > ($stride[$y] as isize).abs() { $stride.swap($x, $y); $order.ixm().swap($x, $y); } }; } { // stable sorting network for 3 elements let strides = stride.slice_mut(); swap![strides, order, 1, 2]; swap![strides, order, 0, 1]; swap![strides, order, 1, 2]; } order } impl_insert_axis_array!(3); #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { self.remove_axis(axis) } private_impl! {} } macro_rules! large_dim { ($n:expr, $name:ident, $pattern:ty, $larger:ty, { $($insert_axis:tt)* }) => ( impl Dimension for Dim<[Ix; $n]> { const NDIM: Option = Some($n); type Pattern = $pattern; type Smaller = Dim<[Ix; $n - 1]>; type Larger = $larger; #[inline] fn ndim(&self) -> usize { $n } #[inline] fn into_pattern(self) -> Self::Pattern { self.ix().convert() } #[inline] fn slice(&self) -> &[Ix] { self.ix() } #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] fn zeros(ndim: usize) -> Self { assert_eq!(ndim, $n); Self::default() } $($insert_axis)* #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { self.remove_axis(axis) } private_impl!{} } ); } large_dim!(4, Ix4, (Ix, Ix, Ix, Ix), Ix5, { impl_insert_axis_array!(4); }); large_dim!(5, Ix5, (Ix, Ix, Ix, Ix, Ix), Ix6, { impl_insert_axis_array!(5); }); large_dim!(6, Ix6, (Ix, Ix, Ix, Ix, Ix, Ix), IxDyn, { fn insert_axis(&self, axis: Axis) -> Self::Larger { debug_assert!(axis.index() <= self.ndim()); let mut out = Vec::with_capacity(self.ndim() + 1); out.extend_from_slice(&self.slice()[0..axis.index()]); out.push(1); out.extend_from_slice(&self.slice()[axis.index()..self.ndim()]); Dim(out) } }); /// IxDyn is a "dynamic" index, pretty hard to use when indexing, /// and memory wasteful, but it allows an arbitrary and dynamic number of axes. impl Dimension for IxDyn { const NDIM: Option = None; type Pattern = Self; type Smaller = Self; type Larger = Self; #[inline] fn ndim(&self) -> usize { self.ix().len() } #[inline] fn slice(&self) -> &[Ix] { self.ix() } #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] fn into_pattern(self) -> Self::Pattern { self } #[inline] fn zeros(ndim: usize) -> Self { IxDyn::zeros(ndim) } #[inline] fn insert_axis(&self, axis: Axis) -> Self::Larger { debug_assert!(axis.index() <= self.ndim()); Dim::new(self.ix().insert(axis.index())) } #[inline] fn try_remove_axis(&self, axis: Axis) -> Self::Smaller { if self.ndim() > 0 { self.remove_axis(axis) } else { self.clone() } } fn from_dimension(d: &D2) -> Option { Some(IxDyn(d.slice())) } fn into_dyn(self) -> IxDyn { self } private_impl! {} } impl Index for Dim { type Output = >::Output; fn index(&self, index: usize) -> &Self::Output { &self.ix()[index] } } impl IndexMut for Dim { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.ixm()[index] } } ndarray-0.16.1/src/dimension/dynindeximpl.rs000064400000000000000000000137031046102023000172030ustar 00000000000000use crate::imp_prelude::*; #[cfg(not(feature = "std"))] use alloc::boxed::Box; use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut, Index, IndexMut}; const CAP: usize = 4; /// T is usize or isize #[derive(Debug)] enum IxDynRepr { Inline(u32, [T; CAP]), Alloc(Box<[T]>), } impl Deref for IxDynRepr { type Target = [T]; fn deref(&self) -> &[T] { match *self { IxDynRepr::Inline(len, ref ar) => { debug_assert!(len as usize <= ar.len()); unsafe { ar.get_unchecked(..len as usize) } } IxDynRepr::Alloc(ref ar) => ar, } } } impl DerefMut for IxDynRepr { fn deref_mut(&mut self) -> &mut [T] { match *self { IxDynRepr::Inline(len, ref mut ar) => { debug_assert!(len as usize <= ar.len()); unsafe { ar.get_unchecked_mut(..len as usize) } } IxDynRepr::Alloc(ref mut ar) => ar, } } } /// The default is equivalent to `Self::from(&[0])`. impl Default for IxDynRepr { fn default() -> Self { Self::copy_from(&[0]) } } use num_traits::Zero; impl IxDynRepr { pub fn copy_from(x: &[T]) -> Self { if x.len() <= CAP { let mut arr = [T::zero(); CAP]; arr[..x.len()].copy_from_slice(x); IxDynRepr::Inline(x.len() as _, arr) } else { Self::from(x) } } } impl IxDynRepr { // make an Inline or Alloc version as appropriate fn from_vec_auto(v: Vec) -> Self { if v.len() <= CAP { Self::copy_from(&v) } else { Self::from_vec(v) } } } impl IxDynRepr { fn from_vec(v: Vec) -> Self { IxDynRepr::Alloc(v.into_boxed_slice()) } fn from(x: &[T]) -> Self { Self::from_vec(x.to_vec()) } } impl Clone for IxDynRepr { fn clone(&self) -> Self { match *self { IxDynRepr::Inline(len, arr) => IxDynRepr::Inline(len, arr), _ => Self::from(&self[..]), } } } impl Eq for IxDynRepr {} impl PartialEq for IxDynRepr { fn eq(&self, rhs: &Self) -> bool { match (self, rhs) { (&IxDynRepr::Inline(slen, ref sarr), &IxDynRepr::Inline(rlen, ref rarr)) => slen == rlen && (0..CAP) .filter(|&i| i < slen as usize) .all(|i| sarr[i] == rarr[i]), _ => self[..] == rhs[..], } } } impl Hash for IxDynRepr { fn hash(&self, state: &mut H) { Hash::hash(&self[..], state) } } /// Dynamic dimension or index type. /// /// Use `IxDyn` directly. This type implements a dynamic number of /// dimensions or indices. Short dimensions are stored inline and don't need /// any dynamic memory allocation. #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub struct IxDynImpl(IxDynRepr); impl IxDynImpl { pub(crate) fn insert(&self, i: usize) -> Self { let len = self.len(); debug_assert!(i <= len); IxDynImpl(if len < CAP { let mut out = [1; CAP]; out[0..i].copy_from_slice(&self[0..i]); out[i + 1..=len].copy_from_slice(&self[i..len]); IxDynRepr::Inline((len + 1) as u32, out) } else { let mut out = Vec::with_capacity(len + 1); out.extend_from_slice(&self[0..i]); out.push(1); out.extend_from_slice(&self[i..len]); IxDynRepr::from_vec(out) }) } fn remove(&self, i: usize) -> Self { IxDynImpl(match self.0 { IxDynRepr::Inline(0, _) => IxDynRepr::Inline(0, [0; CAP]), IxDynRepr::Inline(1, _) => IxDynRepr::Inline(0, [0; CAP]), IxDynRepr::Inline(2, ref arr) => { let mut out = [0; CAP]; out[0] = arr[1 - i]; IxDynRepr::Inline(1, out) } ref ixdyn => { let len = ixdyn.len(); let mut result = IxDynRepr::copy_from(&ixdyn[..len - 1]); for j in i..len - 1 { result[j] = ixdyn[j + 1] } result } }) } } impl<'a> From<&'a [Ix]> for IxDynImpl { #[inline] fn from(ix: &'a [Ix]) -> Self { IxDynImpl(IxDynRepr::copy_from(ix)) } } impl From> for IxDynImpl { #[inline] fn from(ix: Vec) -> Self { IxDynImpl(IxDynRepr::from_vec_auto(ix)) } } impl Index for IxDynImpl where [Ix]: Index { type Output = <[Ix] as Index>::Output; fn index(&self, index: J) -> &Self::Output { &self.0[index] } } impl IndexMut for IxDynImpl where [Ix]: IndexMut { fn index_mut(&mut self, index: J) -> &mut Self::Output { &mut self.0[index] } } impl Deref for IxDynImpl { type Target = [Ix]; #[inline] fn deref(&self) -> &[Ix] { &self.0 } } impl DerefMut for IxDynImpl { #[inline] fn deref_mut(&mut self) -> &mut [Ix] { &mut self.0 } } impl<'a> IntoIterator for &'a IxDynImpl { type Item = &'a Ix; type IntoIter = <&'a [Ix] as IntoIterator>::IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { self[..].iter() } } impl RemoveAxis for Dim { fn remove_axis(&self, axis: Axis) -> Self { debug_assert!(axis.index() < self.ndim()); Dim::new(self.ix().remove(axis.index())) } } impl IxDyn { /// Create a new dimension value with `n` axes, all zeros #[inline] pub fn zeros(n: usize) -> IxDyn { const ZEROS: &[usize] = &[0; 4]; if n <= ZEROS.len() { Dim(&ZEROS[..n]) } else { Dim(vec![0; n]) } } } ndarray-0.16.1/src/dimension/macros.rs000064400000000000000000000004071046102023000157600ustar 00000000000000/// Indexing macro for Dim<[usize; N]> this /// gets the index at `$i` in the underlying array macro_rules! get { ($dim:expr, $i:expr) => { (*$dim.ix())[$i] }; } macro_rules! getm { ($dim:expr, $i:expr) => { (*$dim.ixm())[$i] }; } ndarray-0.16.1/src/dimension/mod.rs000064400000000000000000001226001046102023000152530ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::shape_builder::Strides; use crate::slice::SliceArg; use crate::{Ix, Ixs, Slice, SliceInfoElem}; use num_integer::div_floor; pub use self::axes::{Axes, AxisDescription}; pub use self::axis::Axis; pub use self::broadcast::DimMax; pub use self::conversion::IntoDimension; pub use self::dim::*; pub use self::dimension_trait::Dimension; pub use self::dynindeximpl::IxDynImpl; pub use self::ndindex::NdIndex; pub use self::ops::DimAdd; pub use self::remove_axis::RemoveAxis; pub(crate) use self::axes::axes_of; pub(crate) use self::reshape::reshape_dim; use std::mem; #[macro_use] mod macros; mod axes; mod axis; pub(crate) mod broadcast; mod conversion; pub mod dim; mod dimension_trait; mod dynindeximpl; mod ndindex; mod ops; mod remove_axis; pub(crate) mod reshape; mod sequence; /// Calculate offset from `Ix` stride converting sign properly #[inline(always)] pub fn stride_offset(n: Ix, stride: Ix) -> isize { (n as isize) * (stride as Ixs) } /// Check whether the given `dim` and `stride` lead to overlapping indices /// /// There is overlap if, when iterating through the dimensions in order of /// increasing stride, the current stride is less than or equal to the maximum /// possible offset along the preceding axes. (Axes of length ≤1 are ignored.) pub(crate) fn dim_stride_overlap(dim: &D, strides: &D) -> bool { let order = strides._fastest_varying_stride_order(); let mut sum_prev_offsets = 0; for &index in order.slice() { let d = dim[index]; let s = (strides[index] as isize).abs(); match d { 0 => return false, 1 => {} _ => { if s <= sum_prev_offsets { return true; } sum_prev_offsets += (d - 1) as isize * s; } } } false } /// Returns the `size` of the `dim`, checking that the product of non-zero axis /// lengths does not exceed `isize::MAX`. /// /// If `size_of_checked_shape(dim)` returns `Ok(size)`, the data buffer is a /// slice or `Vec` of length `size`, and `strides` are created with /// `self.default_strides()` or `self.fortran_strides()`, then the invariants /// are met to construct an array from the data buffer, `dim`, and `strides`. /// (The data buffer being a slice or `Vec` guarantees that it contains no more /// than `isize::MAX` bytes.) pub fn size_of_shape_checked(dim: &D) -> Result { let size_nonzero = dim .slice() .iter() .filter(|&&d| d != 0) .try_fold(1usize, |acc, &d| acc.checked_mul(d)) .ok_or_else(|| from_kind(ErrorKind::Overflow))?; if size_nonzero > isize::MAX as usize { Err(from_kind(ErrorKind::Overflow)) } else { Ok(dim.size()) } } /// Select how aliasing is checked /// /// For owned or mutable data: /// /// The strides must not allow any element to be referenced by two different indices. /// #[derive(Copy, Clone, PartialEq)] pub(crate) enum CanIndexCheckMode { /// Owned or mutable: No aliasing OwnedMutable, /// Aliasing ReadOnly, } /// Checks whether the given data and dimension meet the invariants of the /// `ArrayBase` type, assuming the strides are created using /// `dim.default_strides()` or `dim.fortran_strides()`. /// /// To meet the invariants, /// /// 1. The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// 2. The result of `dim.size()` (assuming no overflow) must be less than or /// equal to the length of the slice. /// /// (Since `dim.default_strides()` and `dim.fortran_strides()` always return /// contiguous strides for non-empty arrays, this ensures that for non-empty /// arrays the difference between the least address and greatest address /// accessible by moving along all axes is < the length of the slice. Since /// `dim.default_strides()` and `dim.fortran_strides()` always return all /// zero strides for empty arrays, this ensures that for empty arrays the /// difference between the least address and greatest address accessible by /// moving along all axes is ≤ the length of the slice.) /// /// Note that since slices cannot contain more than `isize::MAX` bytes, /// conditions 1 and 2 are sufficient to guarantee that the offset in units of /// `A` and in units of bytes between the least address and greatest address /// accessible by moving along all axes does not exceed `isize::MAX`. pub(crate) fn can_index_slice_with_strides( data: &[A], dim: &D, strides: &Strides, mode: CanIndexCheckMode, ) -> Result<(), ShapeError> { if let Strides::Custom(strides) = strides { can_index_slice(data, dim, strides, mode) } else { // contiguous shapes: never aliasing, mode does not matter can_index_slice_not_custom(data.len(), dim) } } pub(crate) fn can_index_slice_not_custom(data_len: usize, dim: &D) -> Result<(), ShapeError> { // Condition 1. let len = size_of_shape_checked(dim)?; // Condition 2. if len > data_len { return Err(from_kind(ErrorKind::OutOfBounds)); } Ok(()) } /// Returns the absolute difference in units of `A` between least and greatest /// address accessible by moving along all axes. /// /// Returns `Ok` only if /// /// 1. The ndim of `dim` and `strides` is the same. /// /// 2. The absolute difference in units of `A` and in units of bytes between /// the least address and greatest address accessible by moving along all axes /// does not exceed `isize::MAX`. /// /// 3. The product of non-zero axis lengths does not exceed `isize::MAX`. (This /// also implies that the length of any individual axis does not exceed /// `isize::MAX`.) pub fn max_abs_offset_check_overflow(dim: &D, strides: &D) -> Result where D: Dimension { max_abs_offset_check_overflow_impl(mem::size_of::(), dim, strides) } fn max_abs_offset_check_overflow_impl(elem_size: usize, dim: &D, strides: &D) -> Result where D: Dimension { // Condition 1. if dim.ndim() != strides.ndim() { return Err(from_kind(ErrorKind::IncompatibleLayout)); } // Condition 3. let _ = size_of_shape_checked(dim)?; // Determine absolute difference in units of `A` between least and greatest // address accessible by moving along all axes. let max_offset: usize = izip!(dim.slice(), strides.slice()) .try_fold(0usize, |acc, (&d, &s)| { let s = s as isize; // Calculate maximum possible absolute movement along this axis. let off = d.saturating_sub(1).checked_mul(s.unsigned_abs())?; acc.checked_add(off) }) .ok_or_else(|| from_kind(ErrorKind::Overflow))?; // Condition 2a. if max_offset > isize::MAX as usize { return Err(from_kind(ErrorKind::Overflow)); } // Determine absolute difference in units of bytes between least and // greatest address accessible by moving along all axes let max_offset_bytes = max_offset .checked_mul(elem_size) .ok_or_else(|| from_kind(ErrorKind::Overflow))?; // Condition 2b. if max_offset_bytes > isize::MAX as usize { return Err(from_kind(ErrorKind::Overflow)); } Ok(max_offset) } /// Checks whether the given data, dimension, and strides meet the invariants /// of the `ArrayBase` type (except for checking ownership of the data). /// /// To meet the invariants, /// /// 1. The ndim of `dim` and `strides` must be the same. /// /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// 3. If the array will be empty (any axes are zero-length), the difference /// between the least address and greatest address accessible by moving /// along all axes must be ≤ `data.len()`. (It's fine in this case to move /// one byte past the end of the slice since the pointers will be offset but /// never dereferenced.) /// /// If the array will not be empty, the difference between the least address /// and greatest address accessible by moving along all axes must be < /// `data.len()`. This and #3 ensure that all dereferenceable pointers point /// to elements within the slice. /// /// 4. The strides must not allow any element to be referenced by two different /// indices. /// /// Note that since slices cannot contain more than `isize::MAX` bytes, /// condition 4 is sufficient to guarantee that the absolute difference in /// units of `A` and in units of bytes between the least address and greatest /// address accessible by moving along all axes does not exceed `isize::MAX`. /// /// Warning: This function is sufficient to check the invariants of ArrayBase /// only if the pointer to the first element of the array is chosen such that /// the element with the smallest memory address is at the start of the /// allocation. (In other words, the pointer to the first element of the array /// must be computed using `offset_from_low_addr_ptr_to_logical_ptr` so that /// negative strides are correctly handled.) pub(crate) fn can_index_slice( data: &[A], dim: &D, strides: &D, mode: CanIndexCheckMode, ) -> Result<(), ShapeError> { // Check conditions 1 and 2 and calculate `max_offset`. let max_offset = max_abs_offset_check_overflow::(dim, strides)?; can_index_slice_impl(max_offset, data.len(), dim, strides, mode) } fn can_index_slice_impl( max_offset: usize, data_len: usize, dim: &D, strides: &D, mode: CanIndexCheckMode, ) -> Result<(), ShapeError> { // Check condition 3. let is_empty = dim.slice().iter().any(|&d| d == 0); if is_empty && max_offset > data_len { return Err(from_kind(ErrorKind::OutOfBounds)); } if !is_empty && max_offset >= data_len { return Err(from_kind(ErrorKind::OutOfBounds)); } // Check condition 4. if !is_empty && mode != CanIndexCheckMode::ReadOnly && dim_stride_overlap(dim, strides) { return Err(from_kind(ErrorKind::Unsupported)); } Ok(()) } /// Stride offset checked general version (slices) #[inline] pub fn stride_offset_checked(dim: &[Ix], strides: &[Ix], index: &[Ix]) -> Option { if index.len() != dim.len() { return None; } let mut offset = 0; for (&d, &i, &s) in izip!(dim, index, strides) { if i >= d { return None; } offset += stride_offset(i, s); } Some(offset) } /// Checks if strides are non-negative. pub fn strides_non_negative(strides: &D) -> Result<(), ShapeError> where D: Dimension { for &stride in strides.slice() { if (stride as isize) < 0 { return Err(from_kind(ErrorKind::Unsupported)); } } Ok(()) } /// Implementation-specific extensions to `Dimension` pub trait DimensionExt { // note: many extensions go in the main trait if they need to be special- // cased per dimension /// Get the dimension at `axis`. /// /// *Panics* if `axis` is out of bounds. #[track_caller] fn axis(&self, axis: Axis) -> Ix; /// Set the dimension at `axis`. /// /// *Panics* if `axis` is out of bounds. #[track_caller] fn set_axis(&mut self, axis: Axis, value: Ix); } impl DimensionExt for D where D: Dimension { #[inline] fn axis(&self, axis: Axis) -> Ix { self[axis.index()] } #[inline] fn set_axis(&mut self, axis: Axis, value: Ix) { self[axis.index()] = value; } } impl DimensionExt for [Ix] { #[inline] fn axis(&self, axis: Axis) -> Ix { self[axis.index()] } #[inline] fn set_axis(&mut self, axis: Axis, value: Ix) { self[axis.index()] = value; } } /// Collapse axis `axis` and shift so that only subarray `index` is /// available. /// /// **Panics** if `index` is larger than the size of the axis #[track_caller] // FIXME: Move to Dimension trait pub fn do_collapse_axis(dims: &mut D, strides: &D, axis: usize, index: usize) -> isize { let dim = dims.slice()[axis]; let stride = strides.slice()[axis]; ndassert!( index < dim, "collapse_axis: Index {} must be less than axis length {} for \ array with shape {:?}", index, dim, *dims ); dims.slice_mut()[axis] = 1; stride_offset(index, stride) } /// Compute the equivalent unsigned index given the axis length and signed index. #[inline] pub fn abs_index(len: Ix, index: Ixs) -> Ix { if index < 0 { len - (-index as Ix) } else { index as Ix } } /// Determines nonnegative start and end indices, and performs sanity checks. /// /// The return value is (start, end, step). /// /// **Panics** if stride is 0 or if any index is out of bounds. #[track_caller] fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) { let Slice { start, end, step } = slice; let start = abs_index(axis_len, start); let mut end = abs_index(axis_len, end.unwrap_or(axis_len as isize)); if end < start { end = start; } ndassert!( start <= axis_len, "Slice begin {} is past end of axis of length {}", start, axis_len, ); ndassert!( end <= axis_len, "Slice end {} is past end of axis of length {}", end, axis_len, ); ndassert!(step != 0, "Slice stride must not be zero"); (start, end, step) } /// This function computes the offset from the lowest address element to the /// logically first element. pub fn offset_from_low_addr_ptr_to_logical_ptr(dim: &D, strides: &D) -> usize { let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (&d, &s)| { let s = s as isize; if s < 0 && d > 1 { _offset - s * (d as isize - 1) } else { _offset } }); debug_assert!(offset >= 0); offset as usize } /// Modify dimension, stride and return data pointer offset /// /// **Panics** if stride is 0 or if any index is out of bounds. #[track_caller] pub fn do_slice(dim: &mut usize, stride: &mut usize, slice: Slice) -> isize { let (start, end, step) = to_abs_slice(*dim, slice); let m = end - start; let s = (*stride) as isize; // Compute data pointer offset. let offset = if m == 0 { // In this case, the resulting array is empty, so we *can* avoid performing a nonzero // offset. // // In two special cases (which are the true reason for this `m == 0` check), we *must* avoid // the nonzero offset corresponding to the general case. // // * When `end == 0 && step < 0`. (These conditions imply that `m == 0` since `to_abs_slice` // ensures that `0 <= start <= end`.) We cannot execute `stride_offset(end - 1, *stride)` // because the `end - 1` would underflow. // // * When `start == *dim && step > 0`. (These conditions imply that `m == 0` since // `to_abs_slice` ensures that `start <= end <= *dim`.) We cannot use the offset returned // by `stride_offset(start, *stride)` because that would be past the end of the axis. 0 } else if step < 0 { // When the step is negative, the new first element is `end - 1`, not `start`, since the // direction is reversed. stride_offset(end - 1, *stride) } else { stride_offset(start, *stride) }; // Update dimension. let abs_step = step.unsigned_abs(); *dim = if abs_step == 1 { m } else { let d = m / abs_step; let r = m % abs_step; d + if r > 0 { 1 } else { 0 } }; // Update stride. The additional check is necessary to avoid possible // overflow in the multiplication. *stride = if *dim <= 1 { 0 } else { (s * step) as usize }; offset } /// Solves `a * x + b * y = gcd(a, b)` for `x`, `y`, and `gcd(a, b)`. /// /// Returns `(g, (x, y))`, where `g` is `gcd(a, b)`, and `g` is always /// nonnegative. /// /// See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm fn extended_gcd(a: isize, b: isize) -> (isize, (isize, isize)) { if a == 0 { (b.abs(), (0, b.signum())) } else if b == 0 { (a.abs(), (a.signum(), 0)) } else { let mut r = (a, b); let mut s = (1, 0); let mut t = (0, 1); while r.1 != 0 { let q = r.0 / r.1; r = (r.1, r.0 - q * r.1); s = (s.1, s.0 - q * s.1); t = (t.1, t.0 - q * t.1); } if r.0 > 0 { (r.0, (s.0, t.0)) } else { (-r.0, (-s.0, -t.0)) } } } /// Solves `a * x + b * y = c` for `x` where `a`, `b`, `c`, `x`, and `y` are /// integers. /// /// If the return value is `Some((x0, xd))`, there is a solution. `xd` is /// always positive. Solutions `x` are given by `x0 + xd * t` where `t` is any /// integer. The value of `y` for any `x` is then `y = (c - a * x) / b`. /// /// If the return value is `None`, no solutions exist. /// /// **Note** `a` and `b` must be nonzero. /// /// See https://en.wikipedia.org/wiki/Diophantine_equation#One_equation /// and https://math.stackexchange.com/questions/1656120#1656138 fn solve_linear_diophantine_eq(a: isize, b: isize, c: isize) -> Option<(isize, isize)> { debug_assert_ne!(a, 0); debug_assert_ne!(b, 0); let (g, (u, _)) = extended_gcd(a, b); if c % g == 0 { Some((c / g * u, (b / g).abs())) } else { None } } /// Returns `true` if two (finite length) arithmetic sequences intersect. /// /// `min*` and `max*` are the (inclusive) bounds of the sequences, and they /// must be elements in the sequences. `step*` are the steps between /// consecutive elements (the sign is irrelevant). /// /// **Note** `step1` and `step2` must be nonzero. fn arith_seq_intersect((min1, max1, step1): (isize, isize, isize), (min2, max2, step2): (isize, isize, isize)) -> bool { debug_assert!(max1 >= min1); debug_assert!(max2 >= min2); debug_assert_eq!((max1 - min1) % step1, 0); debug_assert_eq!((max2 - min2) % step2, 0); // Handle the easy case where we don't have to solve anything. if min1 > max2 || min2 > max1 { false } else { // The sign doesn't matter semantically, and it's mathematically convenient // for `step1` and `step2` to be positive. let step1 = step1.abs(); let step2 = step2.abs(); // Ignoring the min/max bounds, the sequences are // a(x) = min1 + step1 * x // b(y) = min2 + step2 * y // // For intersections a(x) = b(y), we have: // min1 + step1 * x = min2 + step2 * y // ⇒ -step1 * x + step2 * y = min1 - min2 // which is a linear Diophantine equation. if let Some((x0, xd)) = solve_linear_diophantine_eq(-step1, step2, min1 - min2) { // Minimum of [min1, max1] ∩ [min2, max2] let min = ::std::cmp::max(min1, min2); // Maximum of [min1, max1] ∩ [min2, max2] let max = ::std::cmp::min(max1, max2); // The potential intersections are // a(x) = min1 + step1 * (x0 + xd * t) // where `t` is any integer. // // There is an intersection in `[min, max]` if there exists an // integer `t` such that // min ≤ a(x) ≤ max // ⇒ min ≤ min1 + step1 * (x0 + xd * t) ≤ max // ⇒ min ≤ min1 + step1 * x0 + step1 * xd * t ≤ max // ⇒ min - min1 - step1 * x0 ≤ (step1 * xd) * t ≤ max - min1 - step1 * x0 // // Therefore, the least possible intersection `a(x)` that is ≥ `min` has // t = ⌈(min - min1 - step1 * x0) / (step1 * xd)⌉ // If this `a(x) is also ≤ `max`, then there is an intersection in `[min, max]`. // // The greatest possible intersection `a(x)` that is ≤ `max` has // t = ⌊(max - min1 - step1 * x0) / (step1 * xd)⌋ // If this `a(x) is also ≥ `min`, then there is an intersection in `[min, max]`. min1 + step1 * (x0 - xd * div_floor(min - min1 - step1 * x0, -step1 * xd)) <= max || min1 + step1 * (x0 + xd * div_floor(max - min1 - step1 * x0, step1 * xd)) >= min } else { false } } } /// Returns the minimum and maximum values of the indices (inclusive). /// /// If the slice is empty, then returns `None`, otherwise returns `Some((min, max))`. fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> { let (start, end, step) = to_abs_slice(axis_len, slice); if start == end { None } else if step > 0 { Some((start, end - 1 - (end - start - 1) % (step as usize))) } else { Some((start + (end - start - 1) % (-step as usize), end - 1)) } } /// Returns `true` iff the slices intersect. pub fn slices_intersect(dim: &D, indices1: impl SliceArg, indices2: impl SliceArg) -> bool { debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim()); for (&axis_len, &si1, &si2) in izip!( dim.slice(), indices1.as_ref().iter().filter(|si| !si.is_new_axis()), indices2.as_ref().iter().filter(|si| !si.is_new_axis()), ) { // The slices do not intersect iff any pair of `SliceInfoElem` does not intersect. match (si1, si2) { ( SliceInfoElem::Slice { start: start1, end: end1, step: step1, }, SliceInfoElem::Slice { start: start2, end: end2, step: step2, }, ) => { let (min1, max1) = match slice_min_max(axis_len, Slice::new(start1, end1, step1)) { Some(m) => m, None => return false, }; let (min2, max2) = match slice_min_max(axis_len, Slice::new(start2, end2, step2)) { Some(m) => m, None => return false, }; if !arith_seq_intersect((min1 as isize, max1 as isize, step1), (min2 as isize, max2 as isize, step2)) { return false; } } (SliceInfoElem::Slice { start, end, step }, SliceInfoElem::Index(ind)) | (SliceInfoElem::Index(ind), SliceInfoElem::Slice { start, end, step }) => { let ind = abs_index(axis_len, ind); let (min, max) = match slice_min_max(axis_len, Slice::new(start, end, step)) { Some(m) => m, None => return false, }; if ind < min || ind > max || (ind - min) % step.unsigned_abs() != 0 { return false; } } (SliceInfoElem::Index(ind1), SliceInfoElem::Index(ind2)) => { let ind1 = abs_index(axis_len, ind1); let ind2 = abs_index(axis_len, ind2); if ind1 != ind2 { return false; } } (SliceInfoElem::NewAxis, _) | (_, SliceInfoElem::NewAxis) => unreachable!(), } } true } pub(crate) fn is_layout_c(dim: &D, strides: &D) -> bool { if let Some(1) = D::NDIM { return strides[0] == 1 || dim[0] <= 1; } for &d in dim.slice() { if d == 0 { return true; } } let mut contig_stride = 1_isize; // check all dimensions -- a dimension of length 1 can have unequal strides for (&dim, &s) in izip!(dim.slice().iter().rev(), strides.slice().iter().rev()) { if dim != 1 { let s = s as isize; if s != contig_stride { return false; } contig_stride *= dim as isize; } } true } pub(crate) fn is_layout_f(dim: &D, strides: &D) -> bool { if let Some(1) = D::NDIM { return strides[0] == 1 || dim[0] <= 1; } for &d in dim.slice() { if d == 0 { return true; } } let mut contig_stride = 1_isize; // check all dimensions -- a dimension of length 1 can have unequal strides for (&dim, &s) in izip!(dim.slice(), strides.slice()) { if dim != 1 { let s = s as isize; if s != contig_stride { return false; } contig_stride *= dim as isize; } } true } pub fn merge_axes(dim: &mut D, strides: &mut D, take: Axis, into: Axis) -> bool where D: Dimension { let into_len = dim.axis(into); let into_stride = strides.axis(into) as isize; let take_len = dim.axis(take); let take_stride = strides.axis(take) as isize; let merged_len = into_len * take_len; if take_len <= 1 { dim.set_axis(into, merged_len); dim.set_axis(take, if merged_len == 0 { 0 } else { 1 }); true } else if into_len <= 1 { strides.set_axis(into, take_stride as usize); dim.set_axis(into, merged_len); dim.set_axis(take, if merged_len == 0 { 0 } else { 1 }); true } else if take_stride == into_len as isize * into_stride { dim.set_axis(into, merged_len); dim.set_axis(take, 1); true } else { false } } /// Move the axis which has the smallest absolute stride and a length /// greater than one to be the last axis. pub fn move_min_stride_axis_to_last(dim: &mut D, strides: &mut D) where D: Dimension { debug_assert_eq!(dim.ndim(), strides.ndim()); match dim.ndim() { 0 | 1 => {} 2 => if dim[1] <= 1 || dim[0] > 1 && (strides[0] as isize).abs() < (strides[1] as isize).abs() { dim.slice_mut().swap(0, 1); strides.slice_mut().swap(0, 1); }, n => { if let Some(min_stride_axis) = (0..n) .filter(|&ax| dim[ax] > 1) .min_by_key(|&ax| (strides[ax] as isize).abs()) { let last = n - 1; dim.slice_mut().swap(last, min_stride_axis); strides.slice_mut().swap(last, min_stride_axis); } } } } #[cfg(test)] mod test { use super::{ arith_seq_intersect, can_index_slice, can_index_slice_not_custom, extended_gcd, max_abs_offset_check_overflow, slice_min_max, slices_intersect, solve_linear_diophantine_eq, CanIndexCheckMode, IntoDimension, }; use crate::error::{from_kind, ErrorKind}; use crate::slice::Slice; use crate::{Dim, Dimension, Ix0, Ix1, Ix2, Ix3, IxDyn, NewAxis}; use num_integer::gcd; use quickcheck::{quickcheck, TestResult}; #[test] fn slice_indexing_uncommon_strides() { let v: alloc::vec::Vec<_> = (0..12).collect(); let dim = (2, 3, 2).into_dimension(); let strides = (1, 2, 6).into_dimension(); assert!(super::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable).is_ok()); let strides = (2, 4, 12).into_dimension(); assert_eq!( super::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable), Err(from_kind(ErrorKind::OutOfBounds)) ); } #[test] fn overlapping_strides_dim() { let dim = (2, 3, 2).into_dimension(); let strides = (5, 2, 1).into_dimension(); assert!(super::dim_stride_overlap(&dim, &strides)); let strides = (-5isize as usize, 2, -1isize as usize).into_dimension(); assert!(super::dim_stride_overlap(&dim, &strides)); let strides = (6, 2, 1).into_dimension(); assert!(!super::dim_stride_overlap(&dim, &strides)); let strides = (6, -2isize as usize, 1).into_dimension(); assert!(!super::dim_stride_overlap(&dim, &strides)); let strides = (6, 0, 1).into_dimension(); assert!(super::dim_stride_overlap(&dim, &strides)); let strides = (-6isize as usize, 0, 1).into_dimension(); assert!(super::dim_stride_overlap(&dim, &strides)); let dim = (2, 2).into_dimension(); let strides = (3, 2).into_dimension(); assert!(!super::dim_stride_overlap(&dim, &strides)); let strides = (3, -2isize as usize).into_dimension(); assert!(!super::dim_stride_overlap(&dim, &strides)); } #[test] fn max_abs_offset_check_overflow_examples() { let dim = (1, ::std::isize::MAX as usize, 1).into_dimension(); let strides = (1, 1, 1).into_dimension(); max_abs_offset_check_overflow::(&dim, &strides).unwrap(); let dim = (1, ::std::isize::MAX as usize, 2).into_dimension(); let strides = (1, 1, 1).into_dimension(); max_abs_offset_check_overflow::(&dim, &strides).unwrap_err(); let dim = (0, 2, 2).into_dimension(); let strides = (1, ::std::isize::MAX as usize, 1).into_dimension(); max_abs_offset_check_overflow::(&dim, &strides).unwrap_err(); let dim = (0, 2, 2).into_dimension(); let strides = (1, ::std::isize::MAX as usize / 4, 1).into_dimension(); max_abs_offset_check_overflow::(&dim, &strides).unwrap_err(); } #[test] fn can_index_slice_ix0() { can_index_slice::(&[1], &Ix0(), &Ix0(), CanIndexCheckMode::OwnedMutable).unwrap(); can_index_slice::(&[], &Ix0(), &Ix0(), CanIndexCheckMode::OwnedMutable).unwrap_err(); } #[test] fn can_index_slice_ix1() { let mode = CanIndexCheckMode::OwnedMutable; can_index_slice::(&[], &Ix1(0), &Ix1(0), mode).unwrap(); can_index_slice::(&[], &Ix1(0), &Ix1(1), mode).unwrap(); can_index_slice::(&[], &Ix1(1), &Ix1(0), mode).unwrap_err(); can_index_slice::(&[], &Ix1(1), &Ix1(1), mode).unwrap_err(); can_index_slice::(&[1], &Ix1(1), &Ix1(0), mode).unwrap(); can_index_slice::(&[1], &Ix1(1), &Ix1(2), mode).unwrap(); can_index_slice::(&[1], &Ix1(1), &Ix1(-1isize as usize), mode).unwrap(); can_index_slice::(&[1], &Ix1(2), &Ix1(1), mode).unwrap_err(); can_index_slice::(&[1, 2], &Ix1(2), &Ix1(0), mode).unwrap_err(); can_index_slice::(&[1, 2], &Ix1(2), &Ix1(1), mode).unwrap(); can_index_slice::(&[1, 2], &Ix1(2), &Ix1(-1isize as usize), mode).unwrap(); } #[test] fn can_index_slice_ix2() { let mode = CanIndexCheckMode::OwnedMutable; can_index_slice::(&[], &Ix2(0, 0), &Ix2(0, 0), mode).unwrap(); can_index_slice::(&[], &Ix2(0, 0), &Ix2(2, 1), mode).unwrap(); can_index_slice::(&[], &Ix2(0, 1), &Ix2(0, 0), mode).unwrap(); can_index_slice::(&[], &Ix2(0, 1), &Ix2(2, 1), mode).unwrap(); can_index_slice::(&[], &Ix2(0, 2), &Ix2(0, 0), mode).unwrap(); can_index_slice::(&[], &Ix2(0, 2), &Ix2(2, 1), mode).unwrap_err(); can_index_slice::(&[1], &Ix2(1, 2), &Ix2(5, 1), mode).unwrap_err(); can_index_slice::(&[1, 2], &Ix2(1, 2), &Ix2(5, 1), mode).unwrap(); can_index_slice::(&[1, 2], &Ix2(1, 2), &Ix2(5, 2), mode).unwrap_err(); can_index_slice::(&[1, 2, 3, 4, 5], &Ix2(2, 2), &Ix2(3, 1), mode).unwrap(); can_index_slice::(&[1, 2, 3, 4], &Ix2(2, 2), &Ix2(3, 1), mode).unwrap_err(); // aliasing strides: ok when readonly can_index_slice::(&[0; 4], &Ix2(2, 2), &Ix2(1, 1), CanIndexCheckMode::OwnedMutable).unwrap_err(); can_index_slice::(&[0; 4], &Ix2(2, 2), &Ix2(1, 1), CanIndexCheckMode::ReadOnly).unwrap(); } #[test] fn can_index_slice_ix3() { let mode = CanIndexCheckMode::OwnedMutable; can_index_slice::(&[], &Ix3(0, 0, 1), &Ix3(2, 1, 3), mode).unwrap(); can_index_slice::(&[], &Ix3(1, 1, 1), &Ix3(2, 1, 3), mode).unwrap_err(); can_index_slice::(&[1], &Ix3(1, 1, 1), &Ix3(2, 1, 3), mode).unwrap(); can_index_slice::(&[1; 11], &Ix3(2, 2, 3), &Ix3(6, 3, 1), mode).unwrap_err(); can_index_slice::(&[1; 12], &Ix3(2, 2, 3), &Ix3(6, 3, 1), mode).unwrap(); } #[test] fn can_index_slice_zero_size_elem() { let mode = CanIndexCheckMode::OwnedMutable; can_index_slice::<(), _>(&[], &Ix1(0), &Ix1(1), mode).unwrap(); can_index_slice::<(), _>(&[()], &Ix1(1), &Ix1(1), mode).unwrap(); can_index_slice::<(), _>(&[(), ()], &Ix1(2), &Ix1(1), mode).unwrap(); // These might seem okay because the element type is zero-sized, but // there could be a zero-sized type such that the number of instances // in existence are carefully controlled. can_index_slice::<(), _>(&[], &Ix1(1), &Ix1(1), mode).unwrap_err(); can_index_slice::<(), _>(&[()], &Ix1(2), &Ix1(1), mode).unwrap_err(); can_index_slice::<(), _>(&[(), ()], &Ix2(2, 1), &Ix2(1, 0), mode).unwrap(); can_index_slice::<(), _>(&[], &Ix2(0, 2), &Ix2(0, 0), mode).unwrap(); // This case would be probably be sound, but that's not entirely clear // and it's not worth the special case code. can_index_slice::<(), _>(&[], &Ix2(0, 2), &Ix2(2, 1), mode).unwrap_err(); } quickcheck! { fn can_index_slice_not_custom_same_as_can_index_slice(data: alloc::vec::Vec, dim: alloc::vec::Vec) -> bool { let dim = IxDyn(&dim); let result = can_index_slice_not_custom(data.len(), &dim); if dim.size_checked().is_none() { // Avoid overflow `dim.default_strides()` or `dim.fortran_strides()`. result.is_err() } else { result == can_index_slice(&data, &dim, &dim.default_strides(), CanIndexCheckMode::OwnedMutable) && result == can_index_slice(&data, &dim, &dim.fortran_strides(), CanIndexCheckMode::OwnedMutable) } } } quickcheck! { // FIXME: This test can't handle larger values at the moment fn extended_gcd_solves_eq(a: i16, b: i16) -> bool { let (a, b) = (a as isize, b as isize); let (g, (x, y)) = extended_gcd(a, b); a * x + b * y == g } // FIXME: This test can't handle larger values at the moment fn extended_gcd_correct_gcd(a: i16, b: i16) -> bool { let (a, b) = (a as isize, b as isize); let (g, _) = extended_gcd(a, b); g == gcd(a, b) } } #[test] fn extended_gcd_zero() { assert_eq!(extended_gcd(0, 0), (0, (0, 0))); assert_eq!(extended_gcd(0, 5), (5, (0, 1))); assert_eq!(extended_gcd(5, 0), (5, (1, 0))); assert_eq!(extended_gcd(0, -5), (5, (0, -1))); assert_eq!(extended_gcd(-5, 0), (5, (-1, 0))); } quickcheck! { // FIXME: This test can't handle larger values at the moment fn solve_linear_diophantine_eq_solution_existence( a: i16, b: i16, c: i16 ) -> TestResult { let (a, b, c) = (a as isize, b as isize, c as isize); if a == 0 || b == 0 { TestResult::discard() } else { TestResult::from_bool( (c % gcd(a, b) == 0) == solve_linear_diophantine_eq(a, b, c).is_some() ) } } // FIXME: This test can't handle larger values at the moment fn solve_linear_diophantine_eq_correct_solution( a: i8, b: i8, c: i8, t: i8 ) -> TestResult { let (a, b, c, t) = (a as isize, b as isize, c as isize, t as isize); if a == 0 || b == 0 { TestResult::discard() } else { match solve_linear_diophantine_eq(a, b, c) { Some((x0, xd)) => { let x = x0 + xd * t; let y = (c - a * x) / b; TestResult::from_bool(a * x + b * y == c) } None => TestResult::discard(), } } } } quickcheck! { // FIXME: This test is extremely slow, even with i16 values, investigate fn arith_seq_intersect_correct( first1: i8, len1: i8, step1: i8, first2: i8, len2: i8, step2: i8 ) -> TestResult { use std::cmp; let (len1, len2) = (len1 as isize, len2 as isize); let (first1, step1) = (first1 as isize, step1 as isize); let (first2, step2) = (first2 as isize, step2 as isize); if len1 == 0 || len2 == 0 { // This case is impossible to reach in `arith_seq_intersect()` // because the `min*` and `max*` arguments are inclusive. return TestResult::discard(); } let len1 = len1.abs(); let len2 = len2.abs(); // Convert to `min*` and `max*` arguments for `arith_seq_intersect()`. let last1 = first1 + step1 * (len1 - 1); let (min1, max1) = (cmp::min(first1, last1), cmp::max(first1, last1)); let last2 = first2 + step2 * (len2 - 1); let (min2, max2) = (cmp::min(first2, last2), cmp::max(first2, last2)); // Naively determine if the sequences intersect. let seq1: alloc::vec::Vec<_> = (0..len1) .map(|n| first1 + step1 * n) .collect(); let intersects = (0..len2) .map(|n| first2 + step2 * n) .any(|elem2| seq1.contains(&elem2)); TestResult::from_bool( arith_seq_intersect( (min1, max1, if step1 == 0 { 1 } else { step1 }), (min2, max2, if step2 == 0 { 1 } else { step2 }) ) == intersects ) } } #[test] fn slice_min_max_empty() { assert_eq!(slice_min_max(0, Slice::new(0, None, 3)), None); assert_eq!(slice_min_max(10, Slice::new(1, Some(1), 3)), None); assert_eq!(slice_min_max(10, Slice::new(-1, Some(-1), 3)), None); assert_eq!(slice_min_max(10, Slice::new(1, Some(1), -3)), None); assert_eq!(slice_min_max(10, Slice::new(-1, Some(-1), -3)), None); } #[test] fn slice_min_max_pos_step() { assert_eq!(slice_min_max(10, Slice::new(1, Some(8), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(1, Some(9), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, Some(8), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, Some(9), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(1, Some(-2), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(1, Some(-1), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, Some(-2), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, Some(-1), 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(1, None, 3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, None, 3)), Some((1, 7))); assert_eq!(slice_min_max(11, Slice::new(1, None, 3)), Some((1, 10))); assert_eq!(slice_min_max(11, Slice::new(-10, None, 3)), Some((1, 10))); } #[test] fn slice_min_max_neg_step() { assert_eq!(slice_min_max(10, Slice::new(1, Some(8), -3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(2, Some(8), -3)), Some((4, 7))); assert_eq!(slice_min_max(10, Slice::new(-9, Some(8), -3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(-8, Some(8), -3)), Some((4, 7))); assert_eq!(slice_min_max(10, Slice::new(1, Some(-2), -3)), Some((1, 7))); assert_eq!(slice_min_max(10, Slice::new(2, Some(-2), -3)), Some((4, 7))); assert_eq!( slice_min_max(10, Slice::new(-9, Some(-2), -3)), Some((1, 7)) ); assert_eq!( slice_min_max(10, Slice::new(-8, Some(-2), -3)), Some((4, 7)) ); assert_eq!(slice_min_max(9, Slice::new(2, None, -3)), Some((2, 8))); assert_eq!(slice_min_max(9, Slice::new(-7, None, -3)), Some((2, 8))); assert_eq!(slice_min_max(9, Slice::new(3, None, -3)), Some((5, 8))); assert_eq!(slice_min_max(9, Slice::new(-6, None, -3)), Some((5, 8))); } #[test] fn slices_intersect_true() { assert!(slices_intersect( &Dim([4, 5]), s![NewAxis, .., NewAxis, ..], s![.., NewAxis, .., NewAxis] )); assert!(slices_intersect( &Dim([4, 5]), s![NewAxis, 0, ..], s![0, ..] )); assert!(slices_intersect( &Dim([4, 5]), s![..;2, ..], s![..;3, NewAxis, ..] )); assert!(slices_intersect( &Dim([4, 5]), s![.., ..;2], s![.., 1..;3, NewAxis] )); assert!(slices_intersect(&Dim([4, 10]), s![.., ..;9], s![.., 3..;6])); } #[test] fn slices_intersect_false() { assert!(!slices_intersect( &Dim([4, 5]), s![..;2, ..], s![NewAxis, 1..;2, ..] )); assert!(!slices_intersect( &Dim([4, 5]), s![..;2, NewAxis, ..], s![1..;3, ..] )); assert!(!slices_intersect( &Dim([4, 5]), s![.., ..;9], s![.., 3..;6, NewAxis] )); } } ndarray-0.16.1/src/dimension/ndindex.rs000064400000000000000000000164471046102023000161400ustar 00000000000000use std::fmt::Debug; use super::{stride_offset, stride_offset_checked}; use crate::itertools::zip; use crate::{Dim, Dimension, IntoDimension, Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn, IxDynImpl}; /// Tuple or fixed size arrays that can be used to index an array. /// /// ``` /// use ndarray::arr2; /// /// let mut a = arr2(&[[0, 1], /// [2, 3]]); /// assert_eq!(a[[0, 1]], 1); /// assert_eq!(a[[1, 1]], 3); /// a[[1, 1]] += 1; /// assert_eq!(a[(1, 1)], 4); /// ``` #[allow(clippy::missing_safety_doc)] // TODO: Add doc pub unsafe trait NdIndex: Debug { #[doc(hidden)] fn index_checked(&self, dim: &E, strides: &E) -> Option; #[doc(hidden)] fn index_unchecked(&self, strides: &E) -> isize; } unsafe impl NdIndex for D where D: Dimension { fn index_checked(&self, dim: &D, strides: &D) -> Option { dim.stride_offset_checked(strides, self) } fn index_unchecked(&self, strides: &D) -> isize { D::stride_offset(self, strides) } } unsafe impl NdIndex for () { #[inline] fn index_checked(&self, dim: &Ix0, strides: &Ix0) -> Option { dim.stride_offset_checked(strides, &Ix0()) } #[inline(always)] fn index_unchecked(&self, _strides: &Ix0) -> isize { 0 } } unsafe impl NdIndex for (Ix, Ix) { #[inline] fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option { dim.stride_offset_checked(strides, &Ix2(self.0, self.1)) } #[inline] fn index_unchecked(&self, strides: &Ix2) -> isize { stride_offset(self.0, get!(strides, 0)) + stride_offset(self.1, get!(strides, 1)) } } unsafe impl NdIndex for (Ix, Ix, Ix) { #[inline] fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] fn index_unchecked(&self, strides: &Ix3) -> isize { stride_offset(self.0, get!(strides, 0)) + stride_offset(self.1, get!(strides, 1)) + stride_offset(self.2, get!(strides, 2)) } } unsafe impl NdIndex for (Ix, Ix, Ix, Ix) { #[inline] fn index_checked(&self, dim: &Ix4, strides: &Ix4) -> Option { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] fn index_unchecked(&self, strides: &Ix4) -> isize { zip(strides.ix(), self.into_dimension().ix()) .map(|(&s, &i)| stride_offset(i, s)) .sum() } } unsafe impl NdIndex for (Ix, Ix, Ix, Ix, Ix) { #[inline] fn index_checked(&self, dim: &Ix5, strides: &Ix5) -> Option { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] fn index_unchecked(&self, strides: &Ix5) -> isize { zip(strides.ix(), self.into_dimension().ix()) .map(|(&s, &i)| stride_offset(i, s)) .sum() } } unsafe impl NdIndex for (Ix, Ix, Ix, Ix, Ix, Ix) { #[inline] fn index_checked(&self, dim: &Ix6, strides: &Ix6) -> Option { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] fn index_unchecked(&self, strides: &Ix6) -> isize { zip(strides.ix(), self.into_dimension().ix()) .map(|(&s, &i)| stride_offset(i, s)) .sum() } } unsafe impl NdIndex for Ix { #[inline] fn index_checked(&self, dim: &Ix1, strides: &Ix1) -> Option { dim.stride_offset_checked(strides, &Ix1(*self)) } #[inline(always)] fn index_unchecked(&self, strides: &Ix1) -> isize { stride_offset(*self, get!(strides, 0)) } } unsafe impl NdIndex for Ix { #[inline] fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { debug_assert_eq!(dim.ndim(), 1); stride_offset_checked(dim.ix(), strides.ix(), &[*self]) } #[inline(always)] fn index_unchecked(&self, strides: &IxDyn) -> isize { debug_assert_eq!(strides.ndim(), 1); stride_offset(*self, get!(strides, 0)) } } macro_rules! ndindex_with_array { ($([$n:expr, $ix_n:ident $($index:tt)*])+) => { $( // implement NdIndex for [Ix; 2] and so on unsafe impl NdIndex<$ix_n> for [Ix; $n] { #[inline] fn index_checked(&self, dim: &$ix_n, strides: &$ix_n) -> Option { dim.stride_offset_checked(strides, &self.into_dimension()) } #[inline] fn index_unchecked(&self, _strides: &$ix_n) -> isize { $( stride_offset(self[$index], get!(_strides, $index)) + )* 0 } } )+ }; } ndindex_with_array! { [0, Ix0] [1, Ix1 0] [2, Ix2 0 1] [3, Ix3 0 1 2] [4, Ix4 0 1 2 3] [5, Ix5 0 1 2 3 4] [6, Ix6 0 1 2 3 4 5] } // implement NdIndex for Dim<[Ix; 2]> and so on unsafe impl NdIndex for Dim<[Ix; N]> { #[inline] fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { debug_assert_eq!( strides.ndim(), N, "Attempted to index with {:?} in array with {} axes", self, strides.ndim() ); stride_offset_checked(dim.ix(), strides.ix(), self.ix()) } #[inline] fn index_unchecked(&self, strides: &IxDyn) -> isize { debug_assert_eq!( strides.ndim(), N, "Attempted to index with {:?} in array with {} axes", self, strides.ndim() ); (0..N) .map(|i| stride_offset(get!(self, i), get!(strides, i))) .sum() } } // implement NdIndex for [Ix; 2] and so on unsafe impl NdIndex for [Ix; N] { #[inline] fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { debug_assert_eq!( strides.ndim(), N, "Attempted to index with {:?} in array with {} axes", self, strides.ndim() ); stride_offset_checked(dim.ix(), strides.ix(), self) } #[inline] fn index_unchecked(&self, strides: &IxDyn) -> isize { debug_assert_eq!( strides.ndim(), N, "Attempted to index with {:?} in array with {} axes", self, strides.ndim() ); (0..N) .map(|i| stride_offset(self[i], get!(strides, i))) .sum() } } impl<'a> IntoDimension for &'a [Ix] { type Dim = IxDyn; fn into_dimension(self) -> Self::Dim { Dim(IxDynImpl::from(self)) } } unsafe impl<'a> NdIndex for &'a IxDyn { fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { (**self).index_checked(dim, strides) } fn index_unchecked(&self, strides: &IxDyn) -> isize { (**self).index_unchecked(strides) } } unsafe impl<'a> NdIndex for &'a [Ix] { fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option { stride_offset_checked(dim.ix(), strides.ix(), self) } fn index_unchecked(&self, strides: &IxDyn) -> isize { zip(strides.ix(), *self) .map(|(&s, &i)| stride_offset(i, s)) .sum() } } ndarray-0.16.1/src/dimension/ops.rs000064400000000000000000000047611046102023000153040ustar 00000000000000use crate::imp_prelude::*; /// Adds the two dimensions at compile time. pub trait DimAdd { /// The sum of the two dimensions. type Output: Dimension; } macro_rules! impl_dimadd_const_out_const { ($lhs:expr, $rhs:expr) => { impl DimAdd> for Dim<[usize; $lhs]> { type Output = Dim<[usize; $lhs + $rhs]>; } }; } macro_rules! impl_dimadd_const_out_dyn { ($lhs:expr, IxDyn) => { impl DimAdd for Dim<[usize; $lhs]> { type Output = IxDyn; } }; ($lhs:expr, $rhs:expr) => { impl DimAdd> for Dim<[usize; $lhs]> { type Output = IxDyn; } }; } impl DimAdd for Ix0 { type Output = D; } impl_dimadd_const_out_const!(1, 0); impl_dimadd_const_out_const!(1, 1); impl_dimadd_const_out_const!(1, 2); impl_dimadd_const_out_const!(1, 3); impl_dimadd_const_out_const!(1, 4); impl_dimadd_const_out_const!(1, 5); impl_dimadd_const_out_dyn!(1, 6); impl_dimadd_const_out_dyn!(1, IxDyn); impl_dimadd_const_out_const!(2, 0); impl_dimadd_const_out_const!(2, 1); impl_dimadd_const_out_const!(2, 2); impl_dimadd_const_out_const!(2, 3); impl_dimadd_const_out_const!(2, 4); impl_dimadd_const_out_dyn!(2, 5); impl_dimadd_const_out_dyn!(2, 6); impl_dimadd_const_out_dyn!(2, IxDyn); impl_dimadd_const_out_const!(3, 0); impl_dimadd_const_out_const!(3, 1); impl_dimadd_const_out_const!(3, 2); impl_dimadd_const_out_const!(3, 3); impl_dimadd_const_out_dyn!(3, 4); impl_dimadd_const_out_dyn!(3, 5); impl_dimadd_const_out_dyn!(3, 6); impl_dimadd_const_out_dyn!(3, IxDyn); impl_dimadd_const_out_const!(4, 0); impl_dimadd_const_out_const!(4, 1); impl_dimadd_const_out_const!(4, 2); impl_dimadd_const_out_dyn!(4, 3); impl_dimadd_const_out_dyn!(4, 4); impl_dimadd_const_out_dyn!(4, 5); impl_dimadd_const_out_dyn!(4, 6); impl_dimadd_const_out_dyn!(4, IxDyn); impl_dimadd_const_out_const!(5, 0); impl_dimadd_const_out_const!(5, 1); impl_dimadd_const_out_dyn!(5, 2); impl_dimadd_const_out_dyn!(5, 3); impl_dimadd_const_out_dyn!(5, 4); impl_dimadd_const_out_dyn!(5, 5); impl_dimadd_const_out_dyn!(5, 6); impl_dimadd_const_out_dyn!(5, IxDyn); impl_dimadd_const_out_const!(6, 0); impl_dimadd_const_out_dyn!(6, 1); impl_dimadd_const_out_dyn!(6, 2); impl_dimadd_const_out_dyn!(6, 3); impl_dimadd_const_out_dyn!(6, 4); impl_dimadd_const_out_dyn!(6, 5); impl_dimadd_const_out_dyn!(6, 6); impl_dimadd_const_out_dyn!(6, IxDyn); impl DimAdd for IxDyn { type Output = IxDyn; } ndarray-0.16.1/src/dimension/remove_axis.rs000064400000000000000000000035141046102023000170170ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::{Axis, Dim, Dimension, Ix, Ix0, Ix1}; /// Array shape with a next smaller dimension. /// /// `RemoveAxis` defines a larger-than relation for array shapes: /// removing one axis from *Self* gives smaller dimension *Smaller*. pub trait RemoveAxis: Dimension { fn remove_axis(&self, axis: Axis) -> Self::Smaller; } impl RemoveAxis for Dim<[Ix; 1]> { #[inline] fn remove_axis(&self, axis: Axis) -> Ix0 { debug_assert!(axis.index() < self.ndim()); Ix0() } } impl RemoveAxis for Dim<[Ix; 2]> { #[inline] fn remove_axis(&self, axis: Axis) -> Ix1 { let axis = axis.index(); debug_assert!(axis < self.ndim()); if axis == 0 { Ix1(get!(self, 1)) } else { Ix1(get!(self, 0)) } } } macro_rules! impl_remove_axis_array( ($($n:expr),*) => ( $( impl RemoveAxis for Dim<[Ix; $n]> { #[inline] fn remove_axis(&self, axis: Axis) -> Self::Smaller { debug_assert!(axis.index() < self.ndim()); let mut result = Dim([0; $n - 1]); { let src = self.slice(); let dst = result.slice_mut(); dst[..axis.index()].copy_from_slice(&src[..axis.index()]); dst[axis.index()..].copy_from_slice(&src[axis.index() + 1..]); } result } } )* ); ); impl_remove_axis_array!(3, 4, 5, 6); ndarray-0.16.1/src/dimension/reshape.rs000064400000000000000000000234251046102023000161300ustar 00000000000000use crate::dimension::sequence::{Forward, Reverse, Sequence, SequenceMut}; use crate::{Dimension, ErrorKind, Order, ShapeError}; #[inline] pub(crate) fn reshape_dim(from: &D, strides: &D, to: &E, order: Order) -> Result where D: Dimension, E: Dimension, { debug_assert_eq!(from.ndim(), strides.ndim()); let mut to_strides = E::zeros(to.ndim()); match order { Order::RowMajor => { reshape_dim_c(&Forward(from), &Forward(strides), &Forward(to), Forward(&mut to_strides))?; } Order::ColumnMajor => { reshape_dim_c(&Reverse(from), &Reverse(strides), &Reverse(to), Reverse(&mut to_strides))?; } } Ok(to_strides) } /// Try to reshape an array with dimensions `from_dim` and strides `from_strides` to the new /// dimension `to_dim`, while keeping the same layout of elements in memory. The strides needed /// if this is possible are stored into `to_strides`. /// /// This function uses RowMajor index ordering if the inputs are read in the forward direction /// (index 0 is axis 0 etc) and ColumnMajor index ordering if the inputs are read in reversed /// direction (as made possible with the Sequence trait). /// /// Preconditions: /// /// 1. from_dim and to_dim are valid dimensions (product of all non-zero axes /// fits in isize::MAX). /// 2. from_dim and to_dim are don't have any axes that are zero (that should be handled before /// this function). /// 3. `to_strides` should be an all-zeros or all-ones dimension of the right dimensionality /// (but it will be overwritten after successful exit of this function). /// /// This function returns: /// /// - IncompatibleShape if the two shapes are not of matching number of elements /// - IncompatibleLayout if the input shape and stride can not be remapped to the output shape /// without moving the array data into a new memory layout. /// - Ok if the from dim could be mapped to the new to dim. fn reshape_dim_c(from_dim: &D, from_strides: &D, to_dim: &E, mut to_strides: E2) -> Result<(), ShapeError> where D: Sequence, E: Sequence, E2: SequenceMut, { // cursor indexes into the from and to dimensions let mut fi = 0; // index into `from_dim` let mut ti = 0; // index into `to_dim`. while fi < from_dim.len() && ti < to_dim.len() { let mut fd = from_dim[fi]; let mut fs = from_strides[fi] as isize; let mut td = to_dim[ti]; if fd == td { to_strides[ti] = from_strides[fi]; fi += 1; ti += 1; continue; } if fd == 1 { fi += 1; continue; } if td == 1 { to_strides[ti] = 1; ti += 1; continue; } if fd == 0 || td == 0 { debug_assert!(false, "zero dim not handled by this function"); return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } // stride times element count is to be distributed out over a combination of axes. let mut fstride_whole = fs * (fd as isize); let mut fd_product = fd; // cumulative product of axis lengths in the combination (from) let mut td_product = td; // cumulative product of axis lengths in the combination (to) // The two axis lengths are not a match, so try to combine multiple axes // to get it to match up. while fd_product != td_product { if fd_product < td_product { // Take another axis on the from side fi += 1; if fi >= from_dim.len() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } fd = from_dim[fi]; fd_product *= fd; if fd > 1 { let fs_old = fs; fs = from_strides[fi] as isize; // check if this axis and the next are contiguous together if fs_old != fd as isize * fs { return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout)); } } } else { // Take another axis on the `to` side // First assign the stride to the axis we leave behind fstride_whole /= td as isize; to_strides[ti] = fstride_whole as usize; ti += 1; if ti >= to_dim.len() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } td = to_dim[ti]; td_product *= td; } } fstride_whole /= td as isize; to_strides[ti] = fstride_whole as usize; fi += 1; ti += 1; } // skip past 1-dims at the end while fi < from_dim.len() && from_dim[fi] == 1 { fi += 1; } while ti < to_dim.len() && to_dim[ti] == 1 { to_strides[ti] = 1; ti += 1; } if fi < from_dim.len() || ti < to_dim.len() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } Ok(()) } #[cfg(feature = "std")] #[test] fn test_reshape() { use crate::Dim; macro_rules! test_reshape { (fail $order:ident from $from:expr, $stride:expr, to $to:expr) => { let res = reshape_dim(&Dim($from), &Dim($stride), &Dim($to), Order::$order); println!("Reshape {:?} {:?} to {:?}, order {:?}\n => {:?}", $from, $stride, $to, Order::$order, res); let _res = res.expect_err("Expected failed reshape"); }; (ok $order:ident from $from:expr, $stride:expr, to $to:expr, $to_stride:expr) => {{ let res = reshape_dim(&Dim($from), &Dim($stride), &Dim($to), Order::$order); println!("Reshape {:?} {:?} to {:?}, order {:?}\n => {:?}", $from, $stride, $to, Order::$order, res); println!("default stride for from dim: {:?}", Dim($from).default_strides()); println!("default stride for to dim: {:?}", Dim($to).default_strides()); let res = res.expect("Expected successful reshape"); assert_eq!(res, Dim($to_stride), "mismatch in strides"); }}; } test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [1, 2, 3], [6, 3, 1]); test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [2, 3], [3, 1]); test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [6], [1]); test_reshape!(fail C from [1, 2, 3], [6, 3, 1], to [1]); test_reshape!(fail F from [1, 2, 3], [6, 3, 1], to [1]); test_reshape!(ok C from [6], [1], to [3, 2], [2, 1]); test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [4, 15], [15, 1]); test_reshape!(ok C from [4, 4, 4], [16, 4, 1], to [16, 4], [4, 1]); test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 4, 1], [8, 4, 1, 1]); test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 4], [8, 4, 1]); test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 2, 2], [8, 4, 2, 1]); test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 1, 4], [8, 4, 1, 1]); test_reshape!(ok C from [4, 4, 4], [16, 4, 1], to [16, 4], [4, 1]); test_reshape!(ok C from [3, 4, 4], [16, 4, 1], to [3, 16], [16, 1]); test_reshape!(ok C from [4, 4], [8, 1], to [2, 2, 2, 2], [16, 8, 2, 1]); test_reshape!(fail C from [4, 4], [8, 1], to [2, 1, 4, 2]); test_reshape!(ok C from [16], [4], to [2, 2, 4], [32, 16, 4]); test_reshape!(ok C from [16], [-4isize as usize], to [2, 2, 4], [-32isize as usize, -16isize as usize, -4isize as usize]); test_reshape!(ok F from [16], [4], to [2, 2, 4], [4, 8, 16]); test_reshape!(ok F from [16], [-4isize as usize], to [2, 2, 4], [-4isize as usize, -8isize as usize, -16isize as usize]); test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [12, 5], [5, 1]); test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [4, 15], [15, 1]); test_reshape!(fail F from [3, 4, 5], [20, 5, 1], to [4, 15]); test_reshape!(ok C from [3, 4, 5, 7], [140, 35, 7, 1], to [28, 15], [15, 1]); // preserve stride if shape matches test_reshape!(ok C from [10], [2], to [10], [2]); test_reshape!(ok F from [10], [2], to [10], [2]); test_reshape!(ok C from [2, 10], [1, 2], to [2, 10], [1, 2]); test_reshape!(ok F from [2, 10], [1, 2], to [2, 10], [1, 2]); test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [3, 4, 5], [20, 5, 1]); test_reshape!(ok F from [3, 4, 5], [20, 5, 1], to [3, 4, 5], [20, 5, 1]); test_reshape!(ok C from [3, 4, 5], [4, 1, 1], to [12, 5], [1, 1]); test_reshape!(ok F from [3, 4, 5], [1, 3, 12], to [12, 5], [1, 12]); test_reshape!(ok F from [3, 4, 5], [1, 3, 1], to [12, 5], [1, 1]); // broadcast shapes test_reshape!(ok C from [3, 4, 5, 7], [0, 0, 7, 1], to [12, 35], [0, 1]); test_reshape!(fail C from [3, 4, 5, 7], [0, 0, 7, 1], to [28, 15]); // one-filled shapes test_reshape!(ok C from [10], [1], to [1, 10, 1, 1, 1], [1, 1, 1, 1, 1]); test_reshape!(ok F from [10], [1], to [1, 10, 1, 1, 1], [1, 1, 1, 1, 1]); test_reshape!(ok C from [1, 10], [10, 1], to [1, 10, 1, 1, 1], [10, 1, 1, 1, 1]); test_reshape!(ok F from [1, 10], [10, 1], to [1, 10, 1, 1, 1], [10, 1, 1, 1, 1]); test_reshape!(ok C from [1, 10], [1, 1], to [1, 5, 1, 1, 2], [1, 2, 2, 2, 1]); test_reshape!(ok F from [1, 10], [1, 1], to [1, 5, 1, 1, 2], [1, 1, 5, 5, 5]); test_reshape!(ok C from [10, 1, 1, 1, 1], [1, 1, 1, 1, 1], to [10], [1]); test_reshape!(ok F from [10, 1, 1, 1, 1], [1, 1, 1, 1, 1], to [10], [1]); test_reshape!(ok C from [1, 5, 1, 2, 1], [1, 2, 1, 1, 1], to [10], [1]); test_reshape!(fail F from [1, 5, 1, 2, 1], [1, 2, 1, 1, 1], to [10]); test_reshape!(ok F from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10], [1]); test_reshape!(fail C from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10]); } ndarray-0.16.1/src/dimension/sequence.rs000064400000000000000000000043141046102023000163050ustar 00000000000000use std::ops::Index; use std::ops::IndexMut; use crate::dimension::Dimension; pub(in crate::dimension) struct Forward(pub(crate) D); pub(in crate::dimension) struct Reverse(pub(crate) D); impl Index for Forward<&D> where D: Dimension { type Output = usize; #[inline] fn index(&self, index: usize) -> &usize { &self.0[index] } } impl Index for Forward<&mut D> where D: Dimension { type Output = usize; #[inline] fn index(&self, index: usize) -> &usize { &self.0[index] } } impl IndexMut for Forward<&mut D> where D: Dimension { #[inline] fn index_mut(&mut self, index: usize) -> &mut usize { &mut self.0[index] } } impl Index for Reverse<&D> where D: Dimension { type Output = usize; #[inline] fn index(&self, index: usize) -> &usize { &self.0[self.len() - index - 1] } } impl Index for Reverse<&mut D> where D: Dimension { type Output = usize; #[inline] fn index(&self, index: usize) -> &usize { &self.0[self.len() - index - 1] } } impl IndexMut for Reverse<&mut D> where D: Dimension { #[inline] fn index_mut(&mut self, index: usize) -> &mut usize { let len = self.len(); &mut self.0[len - index - 1] } } /// Indexable sequence with length pub(in crate::dimension) trait Sequence: Index { fn len(&self) -> usize; } /// Indexable sequence with length (mut) pub(in crate::dimension) trait SequenceMut: Sequence + IndexMut {} impl Sequence for Forward<&D> where D: Dimension { #[inline] fn len(&self) -> usize { self.0.ndim() } } impl Sequence for Forward<&mut D> where D: Dimension { #[inline] fn len(&self) -> usize { self.0.ndim() } } impl SequenceMut for Forward<&mut D> where D: Dimension {} impl Sequence for Reverse<&D> where D: Dimension { #[inline] fn len(&self) -> usize { self.0.ndim() } } impl Sequence for Reverse<&mut D> where D: Dimension { #[inline] fn len(&self) -> usize { self.0.ndim() } } impl SequenceMut for Reverse<&mut D> where D: Dimension {} ndarray-0.16.1/src/doc/crate_feature_flags.rs000064400000000000000000000023521046102023000172420ustar 00000000000000//! Crate Feature Flags //! //! The following crate feature flags are available. They are configured in your //! `Cargo.toml` where the dependency on `ndarray` is defined. //! //! ## `std` //! - Rust standard library (enabled by default) //! - This crate can be used without the standard library by disabling the //! default `std` feature. To do so, use `default-features = false` in //! your `Cargo.toml`. //! - The `geomspace` `linspace` `logspace` `range` `std` `var` `var_axis` //! and `std_axis` methods are only available when `std` is enabled. //! //! ## `serde` //! - Enables serialization support for serde 1.x //! //! ## `rayon` //! - Enables parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. //! - Implies std //! //! ## `approx` //! - Enables implementations of traits of the [`approx`] crate. //! //! ## `blas` //! - Enable transparent BLAS support for matrix multiplication. //! Uses ``blas-src`` for pluggable backend, which needs to be configured //! separately (see the README). //! //! ## `matrixmultiply-threading` //! - Enable the ``threading`` feature in the matrixmultiply package //! //! [`parallel`]: crate::parallel #[cfg(doc)] use crate::parallel::par_azip; ndarray-0.16.1/src/doc/mod.rs000064400000000000000000000001431046102023000140300ustar 00000000000000//! Standalone documentation pages. pub mod crate_feature_flags; pub mod ndarray_for_numpy_users; ndarray-0.16.1/src/doc/ndarray_for_numpy_users/coord_transform.rs000064400000000000000000000101741046102023000234360ustar 00000000000000//! Example of rotation with Euler angles. //! //! This is an example of some coordinate transformations (using Euler angles) //! for illustrative purposes. Note that other crates such as //! [`cgmath`](https://crates.io/crates/cgmath) or //! [`nalgebra`](https://crates.io/crates/nalgebra) may be better-suited if //! most of your work is coordinate transformations, since they have built-in //! geometry primitives. //! //! This is the original Python program: //! //! ```python //! # Euler angles (rows) for four coordinate systems (columns). //! nelems = 4 //! bunge = np.ones((3, nelems)) //! //! # Precompute sines and cosines //! s1 = np.sin(bunge[0, :]) //! c1 = np.cos(bunge[0, :]) //! s2 = np.sin(bunge[1, :]) //! c2 = np.cos(bunge[1, :]) //! s3 = np.sin(bunge[2, :]) //! c3 = np.cos(bunge[2, :]) //! //! # Rotation matrices. //! rmat = np.zeros((3, 3, nelems), order='F') //! for i in range(nelems): //! rmat[0, 0, i] = c1[i] * c3[i] - s1[i] * s3[i] * c2[i] //! rmat[0, 1, i] = -c1[i] * s3[i] - s1[i] * c2[i] * c3[i] //! rmat[0, 2, i] = s1[i] * s2[i] //! //! rmat[1, 0, i] = s1[i] * c3[i] + c1[i] * c2[i] * s3[i] //! rmat[1, 1, i] = -s1[i] * s3[i] + c1[i] * c2[i] * c3[i] //! rmat[1, 2, i] = -c1[i] * s2[i] //! //! rmat[2, 0, i] = s2[i] * s3[i] //! rmat[2, 1, i] = s2[i] * c3[i] //! rmat[2, 2, i] = c2[i] //! //! # Unit vectors of coordinate systems to rotate. //! eye2d = np.eye(3) //! //! # Unit vectors after rotation. //! rotated = np.zeros((3, 3, nelems), order='F') //! for i in range(nelems): //! rotated[:,:,i] = rmat[:,:,i].dot(eye2d) //! ``` //! //! This is a direct translation to `ndarray`: //! //! ``` //! use ndarray::prelude::*; //! //! let nelems = 4; //! let bunge = Array::ones((3, nelems)); //! //! let s1 = bunge.slice(s![0, ..]).mapv(f64::sin); //! let c1 = bunge.slice(s![0, ..]).mapv(f64::cos); //! let s2 = bunge.slice(s![1, ..]).mapv(f64::sin); //! let c2 = bunge.slice(s![1, ..]).mapv(f64::cos); //! let s3 = bunge.slice(s![2, ..]).mapv(f64::sin); //! let c3 = bunge.slice(s![2, ..]).mapv(f64::cos); //! //! let mut rmat = Array::zeros((3, 3, nelems).f()); //! for i in 0..nelems { //! rmat[[0, 0, i]] = c1[i] * c3[i] - s1[i] * s3[i] * c2[i]; //! rmat[[0, 1, i]] = -c1[i] * s3[i] - s1[i] * c2[i] * c3[i]; //! rmat[[0, 2, i]] = s1[i] * s2[i]; //! //! rmat[[1, 0, i]] = s1[i] * c3[i] + c1[i] * c2[i] * s3[i]; //! rmat[[1, 1, i]] = -s1[i] * s3[i] + c1[i] * c2[i] * c3[i]; //! rmat[[1, 2, i]] = -c1[i] * s2[i]; //! //! rmat[[2, 0, i]] = s2[i] * s3[i]; //! rmat[[2, 1, i]] = s2[i] * c3[i]; //! rmat[[2, 2, i]] = c2[i]; //! } //! //! let eye2d = Array::eye(3); //! //! let mut rotated = Array::zeros((3, 3, nelems).f()); //! for i in 0..nelems { //! rotated //! .slice_mut(s![.., .., i]) //! .assign(&rmat.slice(s![.., .., i]).dot(&eye2d)); //! } //! ``` //! //! Instead of looping over indices, a cleaner (and usually faster) option is //! to zip arrays together. It's also possible to avoid some of the temporary //! memory allocations in the original program. The improved version looks like //! this: //! //! ``` //! use ndarray::prelude::*; //! //! let nelems = 4; //! let bunge = Array2::::ones((3, nelems)); //! //! let mut rmat = Array::zeros((3, 3, nelems).f()); //! azip!((mut rmat in rmat.axis_iter_mut(Axis(2)), bunge in bunge.axis_iter(Axis(1))) { //! let s1 = bunge[0].sin(); //! let c1 = bunge[0].cos(); //! let s2 = bunge[1].sin(); //! let c2 = bunge[1].cos(); //! let s3 = bunge[2].sin(); //! let c3 = bunge[2].cos(); //! //! rmat[[0, 0]] = c1 * c3 - s1 * s3 * c2; //! rmat[[0, 1]] = -c1 * s3 - s1 * c2 * c3; //! rmat[[0, 2]] = s1 * s2; //! //! rmat[[1, 0]] = s1 * c3 + c1 * c2 * s3; //! rmat[[1, 1]] = -s1 * s3 + c1 * c2 * c3; //! rmat[[1, 2]] = -c1 * s2; //! //! rmat[[2, 0]] = s2 * s3; //! rmat[[2, 1]] = s2 * c3; //! rmat[[2, 2]] = c2; //! }); //! //! let eye2d = Array2::::eye(3); //! //! let mut rotated = Array3::::zeros((3, 3, nelems).f()); //! azip!((mut rotated in rotated.axis_iter_mut(Axis(2)), rmat in rmat.axis_iter(Axis(2))) { //! rotated.assign(&rmat.dot(&eye2d)); //! }); //! ``` ndarray-0.16.1/src/doc/ndarray_for_numpy_users/mod.rs000064400000000000000000000644021046102023000210170ustar 00000000000000//! `ndarray` for NumPy users. //! //! This is an introductory guide to `ndarray` for people with experience using //! NumPy, although it may also be useful to others. For a more general //! introduction to `ndarray`'s array type `ArrayBase`, see the [`ArrayBase` //! docs][ArrayBase]. //! //! # Contents //! //! * [Similarities](#similarities) //! * [Some key differences](#some-key-differences) //! * [The ndarray ecosystem](#the-ndarray-ecosystem) //! * [Other Rust array/matrix crates](#other-rust-arraymatrix-crates) //! * [Rough `ndarray`–NumPy equivalents](#rough-ndarraynumpy-equivalents) //! //! * [Array creation](#array-creation) //! * [Indexing and slicing](#indexing-and-slicing) //! * [Shape and strides](#shape-and-strides) //! * [Mathematics](#mathematics) //! * [Array manipulation](#array-manipulation) //! * [Iteration](#iteration) //! * [Type conversions](#type-conversions) //! * [Convenience methods for 2-D arrays](#convenience-methods-for-2-d-arrays) //! //! # Similarities //! //! `ndarray`'s array type ([`ArrayBase`]), is very similar to //! NumPy's array type (`numpy.ndarray`): //! //! * Arrays have a single element type. //! * Arrays can have arbitrarily many dimensions. //! * Arrays can have arbitrary strides. //! * Indexing starts at zero, not one. //! * The default memory layout is row-major, and the default iterators follow //! row-major order (also called "logical order" in the documentation). //! * Arithmetic operators work elementwise. (For example, `a * b` performs //! elementwise multiplication, not matrix multiplication.) //! * Owned arrays are contiguous in memory. //! * Many operations, such as slicing, are very cheap because they can return //! a view of an array instead of copying the data. //! //! NumPy has many features that `ndarray` doesn't have yet, such as: //! //! * [index arrays](https://docs.scipy.org/doc/numpy/user/basics.indexing.html#index-arrays) //! * [mask index arrays](https://docs.scipy.org/doc/numpy/user/basics.indexing.html#boolean-or-mask-index-arrays) //! * co-broadcasting (`ndarray` only supports broadcasting the right-hand array in a binary operation.) //! //! # Some key differences //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //!
//! //! NumPy //! //! //! //! `ndarray` //! //!
//! //! In NumPy, there is no distinction between owned arrays, views, and mutable //! views. There can be multiple arrays (instances of `numpy.ndarray`) that //! mutably reference the same data. //! //! //! //! In `ndarray`, all arrays are instances of [`ArrayBase`], but //! `ArrayBase` is generic over the ownership of the data. [`Array`] //! owns its data; [`ArrayView`] is a view; //! [`ArrayViewMut`] is a mutable view; [`CowArray`] //! either owns its data or is a view (with copy-on-write mutation of the view //! variant); and [`ArcArray`] has a reference-counted pointer to its //! data (with copy-on-write mutation). Arrays and views follow Rust's aliasing //! rules. //! //!
//! //! In NumPy, all arrays are dynamic-dimensional. //! //! //! //! In `ndarray`, you can create fixed-dimension arrays, such as //! [`Array2`]. This takes advantage of the type system to help you //! write correct code and also avoids small heap allocations for the shape and //! strides. //! //!
//! //! When slicing in NumPy, the indices are `start`, `start + step`, `start + //! 2*step`, … until reaching `end` (exclusive). //! //! //! //! When slicing in `ndarray`, the axis is first sliced with `start..end`. Then if //! `step` is positive, the first index is the front of the slice; if `step` is //! negative, the first index is the back of the slice. This means that the //! behavior is different from NumPy when `step < 0`. See the docs for the //! [`s![]` macro][s!] for more details. //! //!
//! //! # The ndarray ecosystem //! //! `ndarray` does not provide advanced linear algebra routines out of the box (e.g. SVD decomposition). //! Most of the routines that you can find in `scipy.linalg`/`numpy.linalg` are provided by another crate, //! [`ndarray-linalg`](https://crates.io/crates/ndarray-linalg). //! //! The same holds for statistics: `ndarray` provides some basic functionalities (e.g. `mean`) //! but more advanced routines can be found in [`ndarray-stats`](https://crates.io/crates/ndarray-stats). //! //! If you are looking to generate random arrays instead, check out [`ndarray-rand`](https://crates.io/crates/ndarray-rand). //! //! It is also possible to serialize `NumPy` arrays in `.npy`/`.npz` format and deserialize them as `ndarray` arrays (and vice versa) //! using [`ndarray-npy`](https://crates.io/crates/ndarray-npy). //! //! # Other Rust array/matrix crates //! //! Of the array/matrix types in Rust crates, the `ndarray` array type is probably //! the most similar to NumPy's arrays and is the most flexible. However, if your //! use-case is constrained to linear algebra on 1-D and 2-D vectors and matrices, //! it might be worth considering other crates: //! //! * [`nalgebra`](https://crates.io/crates/nalgebra) provides 1-D and 2-D //! column-major vector and matrix types for linear algebra. Vectors and matrices //! can have constant or dynamic shapes, and `nalgebra` uses the type system to //! provide compile-time checking of shapes, not just the number of dimensions. //! `nalgebra` provides convenient functionality for geometry (e.g. coordinate //! transformations) and linear algebra. //! * [`cgmath`](https://crates.io/crates/cgmath) provides 1-D and 2-D column-major //! types of shape 4×4 or smaller. It's primarily designed for computer graphics //! and provides convenient functionality for geometry (e.g. coordinate //! transformations). Similar to `nalgebra`, `cgmath` uses the type system to //! provide compile-time checking of shapes. //! * [`rulinalg`](https://crates.io/crates/rulinalg) provides 1-D and 2-D //! row-major vector and matrix types with dynamic shapes. Similar to `ndarray`, //! `rulinalg` provides compile-time checking of the number of dimensions, but //! not shapes. `rulinalg` provides pure-Rust implementations of linear algebra //! operations. //! * If there's another crate that should be listed here, please let us know. //! //! In contrast to these crates, `ndarray` provides an *n*-dimensional array type, //! so it's not restricted to 1-D and 2-D vectors and matrices. Also, operators //! operate elementwise by default, so the multiplication operator `*` performs //! elementwise multiplication instead of matrix multiplication. (You have to //! specifically call `.dot()` if you want matrix multiplication.) //! //! # Rough `ndarray`–NumPy equivalents //! //! These tables provide some rough equivalents of NumPy operations in `ndarray`. //! There are a variety of other methods that aren't included in these tables, //! including shape-manipulation, array creation, and iteration routines. //! //! It's assumed that you've imported NumPy like this: //! //! ```python //! import numpy as np //! ``` //! //! and `ndarray` like this: //! //! ``` //! use ndarray::prelude::*; //! # //! # fn main() { let _ = arr0(1); } //! ``` //! //! ## Array creation //! //! This table contains ways to create arrays from scratch. For creating arrays by //! operations on other arrays (e.g. arithmetic), see the other tables. Also see //! the [`::from_vec()`][::from_vec()], [`::from_iter()`][::from_iter()], //! [`::default()`][::default()], [`::from_shape_fn()`][::from_shape_fn()], and //! [`::from_shape_vec_unchecked()`][::from_shape_vec_unchecked()] methods. //! //! NumPy | `ndarray` | Notes //! ------|-----------|------ //! `np.array([[1.,2.,3.], [4.,5.,6.]])` | [`array![[1.,2.,3.], [4.,5.,6.]]`][array!] or [`arr2(&[[1.,2.,3.], [4.,5.,6.]])`][arr2()] | 2×3 floating-point array literal //! `np.arange(0., 10., 0.5)` or `np.r_[:10.:0.5]` | [`Array::range(0., 10., 0.5)`][::range()] | create a 1-D array with values `0.`, `0.5`, …, `9.5` //! `np.linspace(0., 10., 11)` or `np.r_[:10.:11j]` | [`Array::linspace(0., 10., 11)`][::linspace()] | create a 1-D array with 11 elements with values `0.`, …, `10.` //! `np.logspace(2.0, 3.0, num=4, base=10.0)` | [`Array::logspace(10.0, 2.0, 3.0, 4)`][::logspace()] | create a 1-D array with 4 elements with values `100.`, `215.4`, `464.1`, `1000.` //! `np.geomspace(1., 1000., num=4)` | [`Array::geomspace(1e0, 1e3, 4)`][::geomspace()] | create a 1-D array with 4 elements with values `1.`, `10.`, `100.`, `1000.` //! `np.ones((3, 4, 5))` | [`Array::ones((3, 4, 5))`][::ones()] | create a 3×4×5 array filled with ones (inferring the element type) //! `np.zeros((3, 4, 5))` | [`Array::zeros((3, 4, 5))`][::zeros()] | create a 3×4×5 array filled with zeros (inferring the element type) //! `np.zeros((3, 4, 5), order='F')` | [`Array::zeros((3, 4, 5).f())`][::zeros()] | create a 3×4×5 array with Fortran (column-major) memory layout filled with zeros (inferring the element type) //! `np.zeros_like(a, order='C')` | [`Array::zeros(a.raw_dim())`][::zeros()] | create an array of zeros of the shape shape as `a`, with row-major memory layout (unlike NumPy, this infers the element type from context instead of duplicating `a`'s element type) //! `np.full((3, 4), 7.)` | [`Array::from_elem((3, 4), 7.)`][::from_elem()] | create a 3×4 array filled with the value `7.` //! `np.eye(3)` | [`Array::eye(3)`][::eye()] | create a 3×3 identity matrix (inferring the element type) //! `np.diag(np.array([1, 2, 3]))` | [`Array2::from_diag(&arr1(&[1, 2, 3]))`][::from_diag()] | create a 3×3 matrix with `[1, 2, 3]` as diagonal and zeros elsewhere (inferring the element type) //! `np.array([1, 2, 3, 4]).reshape((2, 2))` | [`Array::from_shape_vec((2, 2), vec![1, 2, 3, 4])?`][::from_shape_vec()] | create a 2×2 array from the elements in the list/`Vec` //! `np.array([1, 2, 3, 4]).reshape((2, 2), order='F')` | [`Array::from_shape_vec((2, 2).f(), vec![1, 2, 3, 4])?`][::from_shape_vec()] | create a 2×2 array from the elements in the list/`Vec` using Fortran (column-major) order //! `np.random` | See the [`ndarray-rand`](https://crates.io/crates/ndarray-rand) crate. | create arrays of random numbers //! //! Note that the examples in the table rely on the compiler inferring the //! element type and dimensionality from context, which is usually sufficient. //! However, if the compiler cannot infer the types, you can specify them //! manually. These are examples of creating a 3-D Fortran-layout array of //! `f64`s: //! //! ``` //! # use ndarray::prelude::*; //! # //! // This is an example where the compiler can infer the element type //! // because `f64::sin` can only be called on `f64` elements: //! let arr1 = Array::zeros((3, 2, 4).f()); //! arr1.mapv(f64::sin); //! //! // Specify just the element type and infer the dimensionality: //! let arr2 = Array::::zeros((3, 2, 4).f()); //! let arr3: Array = Array::zeros((3, 2, 4).f()); //! //! // Specify both the element type and dimensionality: //! let arr4 = Array3::::zeros((3, 2, 4).f()); //! let arr5: Array3 = Array::zeros((3, 2, 4).f()); //! let arr6 = Array::::zeros((3, 2, 4).f()); //! let arr7: Array = Array::zeros((3, 2, 4).f()); //! ``` //! //! ## Indexing and slicing //! //! A few notes: //! //! * Indices start at 0. For example, "row 1" is the second row in the array. //! //! * Some methods have multiple variants in terms of ownership and mutability. //! Only the non-mutable methods that take the array by reference are listed in //! this table. For example, [`.slice()`][.slice()] also has corresponding //! methods [`.slice_mut()`][.slice_mut()], [`.slice_move()`][.slice_move()], and //! [`.slice_collapse()`][.slice_collapse()]. //! //! * The behavior of slicing is different from NumPy for slices with //! `step < 0`. See the docs for the [`s![]` macro][s!] for more details. //! //! NumPy | `ndarray` | Notes //! ------|-----------|------ //! `a[-1]` | [`a[a.len() - 1]`][.index()] | access the last element in 1-D array `a` //! `a[1, 4]` | [`a[[1, 4]]`][.index()] | access the element in row 1, column 4 //! `a[1]` or `a[1, :, :]` | [`a.slice(s![1, .., ..])`][.slice()] or [`a.index_axis(Axis(0), 1)`][.index_axis()] | get a 2-D subview of a 3-D array at index 1 of axis 0 //! `a[0:5]` or `a[:5]` or `a[0:5, :]` | [`a.slice(s![0..5, ..])`][.slice()] or [`a.slice(s![..5, ..])`][.slice()] or [`a.slice_axis(Axis(0), Slice::from(0..5))`][.slice_axis()] | get the first 5 rows of a 2-D array //! `a[-5:]` or `a[-5:, :]` | [`a.slice(s![-5.., ..])`][.slice()] or [`a.slice_axis(Axis(0), Slice::from(-5..))`][.slice_axis()] | get the last 5 rows of a 2-D array //! `a[:3, 4:9]` | [`a.slice(s![..3, 4..9])`][.slice()] | columns 4, 5, 6, 7, and 8 of the first 3 rows //! `a[1:4:2, ::-1]` | [`a.slice(s![1..4;2, ..;-1])`][.slice()] | rows 1 and 3 with the columns in reverse order //! `a.take([4, 2])` | `a.select(Axis(0), &[4, 2])` | rows 4 and 2 of the array //! //! ## Shape and strides //! //! Note that [`a.shape()`][.shape()], [`a.dim()`][.dim()], and //! [`a.raw_dim()`][.raw_dim()] all return the shape of the array, but as //! different types. `a.shape()` returns the shape as `&[Ix]`, (where //! [`Ix`] is `usize`) which is useful for general operations on the shape. //! `a.dim()` returns the shape as `D::Pattern`, which is useful for //! pattern-matching shapes. `a.raw_dim()` returns the shape as `D`, which is //! useful for creating other arrays of the same shape. //! //! NumPy | `ndarray` | Notes //! ------|-----------|------ //! `np.ndim(a)` or `a.ndim` | [`a.ndim()`][.ndim()] | get the number of dimensions of array `a` //! `np.size(a)` or `a.size` | [`a.len()`][.len()] | get the number of elements in array `a` //! `np.shape(a)` or `a.shape` | [`a.shape()`][.shape()] or [`a.dim()`][.dim()] | get the shape of array `a` //! `a.shape[axis]` | [`a.len_of(Axis(axis))`][.len_of()] | get the length of an axis //! `a.strides` | [`a.strides()`][.strides()] | get the strides of array `a` //! `np.size(a) == 0` or `a.size == 0` | [`a.is_empty()`][.is_empty()] | check if the array has zero elements //! //! ## Mathematics //! //! Note that [`.mapv()`][.mapv()] has corresponding methods [`.map()`][.map()], //! [`.mapv_into()`][.mapv_into()], [`.map_inplace()`][.map_inplace()], and //! [`.mapv_inplace()`][.mapv_inplace()]. Also look at [`.fold()`][.fold()], //! [`.for_each()`][.for_each()], [`.fold_axis()`][.fold_axis()], and //! [`.map_axis()`][.map_axis()]. //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //!
//! //! NumPy //! //! //! //! `ndarray` //! //! //! //! Notes //! //!
//! //! `a.transpose()` or `a.T` //! //! //! //! [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] //! //! //! //! transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) //! //!
//! //! `mat1.dot(mat2)` //! //! //! //! [`mat1.dot(&mat2)`][matrix-* dot] //! //! //! //! 2-D matrix multiply //! //!
//! //! `mat.dot(vec)` //! //! //! //! [`mat.dot(&vec)`][matrix-* dot] //! //! //! //! 2-D matrix dot 1-D column vector //! //!
//! //! `vec.dot(mat)` //! //! //! //! [`vec.dot(&mat)`][vec-* dot] //! //! //! //! 1-D row vector dot 2-D matrix //! //!
//! //! `vec1.dot(vec2)` //! //! //! //! [`vec1.dot(&vec2)`][vec-* dot] //! //! //! //! vector dot product //! //!
//! //! `a * b`, `a + b`, etc. //! //! //! //! [`a * b`, `a + b`, etc.](ArrayBase#arithmetic-operations) //! //! //! //! element-wise arithmetic operations //! //!
//! //! `a**3` //! //! //! //! [`a.mapv(|a| a.powi(3))`][.mapv()] //! //! //! //! element-wise power of 3 //! //!
//! //! `np.sqrt(a)` //! //! //! //! [`a.mapv(f64::sqrt)`][.mapv()] //! //! //! //! element-wise square root for `f64` array //! //!
//! //! `(a>0.5)` //! //! //! //! [`a.mapv(|a| a > 0.5)`][.mapv()] //! //! //! //! array of `bool`s of same shape as `a` with `true` where `a > 0.5` and `false` elsewhere //! //!
//! //! `np.sum(a)` or `a.sum()` //! //! //! //! [`a.sum()`][.sum()] //! //! //! //! sum the elements in `a` //! //!
//! //! `np.sum(a, axis=2)` or `a.sum(axis=2)` //! //! //! //! [`a.sum_axis(Axis(2))`][.sum_axis()] //! //! //! //! sum the elements in `a` along axis 2 //! //!
//! //! `np.mean(a)` or `a.mean()` //! //! //! //! [`a.mean().unwrap()`][.mean()] //! //! //! calculate the mean of the elements in `f64` array `a` //! //!
//! //! `np.mean(a, axis=2)` or `a.mean(axis=2)` //! //! //! //! [`a.mean_axis(Axis(2))`][.mean_axis()] //! //! //! //! calculate the mean of the elements in `a` along axis 2 //! //!
//! //! `np.allclose(a, b, atol=1e-8)` //! //! //! //! [`a.abs_diff_eq(&b, 1e-8)`][.abs_diff_eq()] //! //! //! //! check if the arrays' elementwise differences are within an absolute tolerance (it requires the `approx` feature-flag) //! //!
//! //! `np.diag(a)` //! //! //! //! [`a.diag()`][.diag()] //! //! //! //! view the diagonal of `a` //! //!
//! //! `np.linalg` //! //! //! //! See [`ndarray-linalg`](https://crates.io/crates/ndarray-linalg) //! //! //! //! linear algebra (matrix inverse, solving, decompositions, etc.) //! //!
//! //! ## Array manipulation //! //! NumPy | `ndarray` | Notes //! ------|-----------|------ //! `a[:] = 3.` | [`a.fill(3.)`][.fill()] | set all array elements to the same scalar value //! `a[:] = b` | [`a.assign(&b)`][.assign()] | copy the data from array `b` into array `a` //! `a[:5, 2] = 3.` | [`a.slice_mut(s![..5, 2]).fill(3.)`][.fill()] | set a portion of the array to the same scalar value //! `a[:5, 2] = b` | [`a.slice_mut(s![..5, 2]).assign(&b)`][.assign()] | copy the data from array `b` into part of array `a` //! `np.concatenate((a,b), axis=1)` | [`concatenate![Axis(1), a, b]`][concatenate!] or [`concatenate(Axis(1), &[a.view(), b.view()])`][concatenate()] | concatenate arrays `a` and `b` along axis 1 //! `np.stack((a,b), axis=1)` | [`stack![Axis(1), a, b]`][stack!] or [`stack(Axis(1), vec![a.view(), b.view()])`][stack()] | stack arrays `a` and `b` along axis 1 //! `a[:,np.newaxis]` or `np.expand_dims(a, axis=1)` | [`a.slice(s![.., NewAxis])`][.slice()] or [`a.insert_axis(Axis(1))`][.insert_axis()] | create an view of 1-D array `a`, inserting a new axis 1 //! `a.transpose()` or `a.T` | [`a.t()`][.t()] or [`a.reversed_axes()`][.reversed_axes()] | transpose of array `a` (view for `.t()` or by-move for `.reversed_axes()`) //! `np.diag(a)` | [`a.diag()`][.diag()] | view the diagonal of `a` //! `a.flatten()` | [`use std::iter::FromIterator; Array::from_iter(a.iter().cloned())`][::from_iter()] | create a 1-D array by flattening `a` //! //! ## Iteration //! //! `ndarray` has lots of interesting iterators/producers that implement the //! [`NdProducer`](crate::NdProducer) trait, which is a generalization of `Iterator` //! to multiple dimensions. This makes it possible to correctly and efficiently //! zip together slices/subviews of arrays in multiple dimensions with //! [`Zip`] or [`azip!()`]. The purpose of this is similar to //! [`np.nditer`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.nditer.html), //! but [`Zip`] is implemented and used somewhat differently. //! //! This table lists some of the iterators/producers which have a direct //! equivalent in NumPy. For a more complete introduction to producers and //! iterators, see [*Loops, Producers, and //! Iterators*](ArrayBase#loops-producers-and-iterators). //! Note that there are also variants of these iterators (with a `_mut` suffix) //! that yield `ArrayViewMut` instead of `ArrayView`. //! //! NumPy | `ndarray` | Notes //! ------|-----------|------ //! `a.flat` | [`a.iter()`][.iter()] | iterator over the array elements in logical order //! `np.ndenumerate(a)` | [`a.indexed_iter()`][.indexed_iter()] | flat iterator yielding the index along with each element reference //! `iter(a)` | [`a.outer_iter()`][.outer_iter()] or [`a.axis_iter(Axis(0))`][.axis_iter()] | iterator over the first (outermost) axis, yielding each subview //! //! ## Type conversions //! //! In `ndarray`, conversions between datatypes are done with `mapv()` by //! passing a closure to convert every element independently. //! For the conversion itself, we have several options: //! - `std::convert::From` ensures lossless, safe conversions at compile-time //! and is generally recommended. //! - `std::convert::TryFrom` can be used for potentially unsafe conversions. It //! will return a `Result` which can be handled or `unwrap()`ed to panic if //! any value at runtime cannot be converted losslessly. //! - The `as` keyword compiles to lossless/lossy conversions depending on the //! source and target datatypes. It can be useful when `TryFrom` is a //! performance issue or does not apply. A notable difference to NumPy is that //! `as` performs a [*saturating* cast][sat_conv] when casting //! from floats to integers. Further information can be found in the //! [reference on type cast expressions][as_typecast]. //! //! For details, be sure to check out the type conversion examples. //! //! //! //! //! //! //! //! //! //! //! //!
//! //! NumPy //! //! //! //! `ndarray` //! //! //! //! Notes //! //!
//! //! `a.astype(np.float32)` //! //! //! //! `a.mapv(f32::from)` //! //! //! //! convert `u8` array infallibly to `f32` array with `std::convert::From`, generally recommended //! //!
//! //! `a.astype(np.int32)` //! //! //! //! `a.mapv(i32::from)` //! //! //! //! upcast `u8` array to `i32` array with `std::convert::From`, preferable over `as` because it ensures at compile-time that the conversion is lossless //! //!
//! //! `a.astype(np.uint8)` //! //! //! //! `a.mapv(|x| u8::try_from(x).unwrap())` //! //! //! //! try to convert `i8` array to `u8` array, panic if any value cannot be converted lossless at runtime (e.g. negative value) //! //!
//! //! `a.astype(np.int32)` //! //! //! //! `a.mapv(|x| x as i32)` //! //! //! //! convert `f32` array to `i32` array with ["saturating" conversion][sat_conv]; care needed because it can be a lossy conversion or result in non-finite values! See [the reference for information][as_typecast]. //! //!
//! //! [as_conv]: https://doc.rust-lang.org/rust-by-example/types/cast.html //! [sat_conv]: https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#fixing-unsoundness-in-casts //! [as_typecast]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions //! //! ## Convenience methods for 2-D arrays //! //! NumPy | `ndarray` | Notes //! ------|-----------|------ //! `len(a)` or `a.shape[0]` | [`a.nrows()`][.nrows()] | get the number of rows in a 2-D array //! `a.shape[1]` | [`a.ncols()`][.ncols()] | get the number of columns in a 2-D array //! `a[1]` or `a[1,:]` | [`a.row(1)`][.row()] or [`a.row_mut(1)`][.row_mut()] | view (or mutable view) of row 1 in a 2-D array //! `a[:,4]` | [`a.column(4)`][.column()] or [`a.column_mut(4)`][.column_mut()] | view (or mutable view) of column 4 in a 2-D array //! `a.shape[0] == a.shape[1]` | [`a.is_square()`][.is_square()] | check if the array is square //! //! [.abs_diff_eq()]: ArrayBase#impl-AbsDiffEq> //! [.assign()]: ArrayBase::assign //! [.axis_iter()]: ArrayBase::axis_iter //! [.ncols()]: ArrayBase::ncols //! [.column()]: ArrayBase::column //! [.column_mut()]: ArrayBase::column_mut //! [concatenate()]: crate::concatenate() //! [concatenate!]: crate::concatenate! //! [stack!]: crate::stack! //! [::default()]: ArrayBase::default //! [.diag()]: ArrayBase::diag //! [.dim()]: ArrayBase::dim //! [::eye()]: ArrayBase::eye //! [.fill()]: ArrayBase::fill //! [.fold()]: ArrayBase::fold //! [.fold_axis()]: ArrayBase::fold_axis //! [::from_elem()]: ArrayBase::from_elem //! [::from_iter()]: ArrayBase::from_iter //! [::from_diag()]: ArrayBase::from_diag //! [::from_shape_fn()]: ArrayBase::from_shape_fn //! [::from_shape_vec()]: ArrayBase::from_shape_vec //! [::from_shape_vec_unchecked()]: ArrayBase::from_shape_vec_unchecked //! [::from_vec()]: ArrayBase::from_vec //! [.index()]: ArrayBase#impl-Index //! [.indexed_iter()]: ArrayBase::indexed_iter //! [.insert_axis()]: ArrayBase::insert_axis //! [.is_empty()]: ArrayBase::is_empty //! [.is_square()]: ArrayBase::is_square //! [.iter()]: ArrayBase::iter //! [.len()]: ArrayBase::len //! [.len_of()]: ArrayBase::len_of //! [::linspace()]: ArrayBase::linspace //! [::logspace()]: ArrayBase::logspace //! [::geomspace()]: ArrayBase::geomspace //! [.map()]: ArrayBase::map //! [.map_axis()]: ArrayBase::map_axis //! [.map_inplace()]: ArrayBase::map_inplace //! [.mapv()]: ArrayBase::mapv //! [.mapv_inplace()]: ArrayBase::mapv_inplace //! [.mapv_into()]: ArrayBase::mapv_into //! [matrix-* dot]: ArrayBase::dot-1 //! [.mean()]: ArrayBase::mean //! [.mean_axis()]: ArrayBase::mean_axis //! [.ndim()]: ArrayBase::ndim //! [::ones()]: ArrayBase::ones //! [.outer_iter()]: ArrayBase::outer_iter //! [::range()]: ArrayBase::range //! [.raw_dim()]: ArrayBase::raw_dim //! [.reversed_axes()]: ArrayBase::reversed_axes //! [.row()]: ArrayBase::row //! [.row_mut()]: ArrayBase::row_mut //! [.nrows()]: ArrayBase::nrows //! [.sum()]: ArrayBase::sum //! [.slice()]: ArrayBase::slice //! [.slice_axis()]: ArrayBase::slice_axis //! [.slice_collapse()]: ArrayBase::slice_collapse //! [.slice_move()]: ArrayBase::slice_move //! [.slice_mut()]: ArrayBase::slice_mut //! [.shape()]: ArrayBase::shape //! [stack()]: crate::stack() //! [.strides()]: ArrayBase::strides //! [.index_axis()]: ArrayBase::index_axis //! [.sum_axis()]: ArrayBase::sum_axis //! [.t()]: ArrayBase::t //! [vec-* dot]: ArrayBase::dot //! [.for_each()]: ArrayBase::for_each //! [::zeros()]: ArrayBase::zeros //! [`Zip`]: crate::Zip pub mod coord_transform; pub mod rk_step; pub mod simple_math; // This is to avoid putting `crate::` everywhere #[allow(unused_imports)] use crate::imp_prelude::*; ndarray-0.16.1/src/doc/ndarray_for_numpy_users/rk_step.rs000064400000000000000000000203061046102023000217020ustar 00000000000000//! Example of translating `rk_step` function from SciPy. //! //! This snippet is a [selection of lines from //! `rk.py`](https://github.com/scipy/scipy/blob/v1.0.0/scipy/integrate/_ivp/rk.py#L2-L78). //! See the [license for this snippet](#scipy-license). //! //! ```python //! import numpy as np //! //! def rk_step(fun, t, y, f, h, A, B, C, E, K): //! """Perform a single Runge-Kutta step. //! This function computes a prediction of an explicit Runge-Kutta method and //! also estimates the error of a less accurate method. //! Notation for Butcher tableau is as in [1]_. //! Parameters //! ---------- //! fun : callable //! Right-hand side of the system. //! t : float //! Current time. //! y : ndarray, shape (n,) //! Current state. //! f : ndarray, shape (n,) //! Current value of the derivative, i.e. ``fun(x, y)``. //! h : float //! Step to use. //! A : list of ndarray, length n_stages - 1 //! Coefficients for combining previous RK stages to compute the next //! stage. For explicit methods the coefficients above the main diagonal //! are zeros, so `A` is stored as a list of arrays of increasing lengths. //! The first stage is always just `f`, thus no coefficients for it //! are required. //! B : ndarray, shape (n_stages,) //! Coefficients for combining RK stages for computing the final //! prediction. //! C : ndarray, shape (n_stages - 1,) //! Coefficients for incrementing time for consecutive RK stages. //! The value for the first stage is always zero, thus it is not stored. //! E : ndarray, shape (n_stages + 1,) //! Coefficients for estimating the error of a less accurate method. They //! are computed as the difference between b's in an extended tableau. //! K : ndarray, shape (n_stages + 1, n) //! Storage array for putting RK stages here. Stages are stored in rows. //! Returns //! ------- //! y_new : ndarray, shape (n,) //! Solution at t + h computed with a higher accuracy. //! f_new : ndarray, shape (n,) //! Derivative ``fun(t + h, y_new)``. //! error : ndarray, shape (n,) //! Error estimate of a less accurate method. //! References //! ---------- //! .. [1] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential //! Equations I: Nonstiff Problems", Sec. II.4. //! """ //! K[0] = f //! for s, (a, c) in enumerate(zip(A, C)): //! dy = np.dot(K[:s + 1].T, a) * h //! K[s + 1] = fun(t + c * h, y + dy) //! //! y_new = y + h * np.dot(K[:-1].T, B) //! f_new = fun(t + h, y_new) //! //! K[-1] = f_new //! error = np.dot(K.T, E) * h //! //! return y_new, f_new, error //! ``` //! //! A direct translation to `ndarray` looks like this: //! //! ``` //! use ndarray::prelude::*; //! //! fn rk_step( //! mut fun: F, //! t: f64, //! y: ArrayView1, //! f: ArrayView1, //! h: f64, //! a: &[ArrayView1], //! b: ArrayView1, //! c: ArrayView1, //! e: ArrayView1, //! mut k: ArrayViewMut2, //! ) -> (Array1, Array1, Array1) //! where //! F: FnMut(f64, ArrayView1) -> Array1, //! { //! k.slice_mut(s![0, ..]).assign(&f); //! for (s, (a, c)) in a.iter().zip(c).enumerate() { //! let dy = k.slice(s![..s + 1, ..]).t().dot(a) * h; //! k.slice_mut(s![s + 1, ..]) //! .assign(&(fun(t + c * h, (&y + &dy).view()))); //! } //! //! let y_new = &y + &(h * k.slice(s![..-1, ..]).t().dot(&b)); //! let f_new = fun(t + h, y_new.view()); //! //! k.slice_mut(s![-1, ..]).assign(&f_new); //! let error = k.t().dot(&e) * h; //! //! (y_new, f_new, error) //! } //! # //! # fn main() { let _ = rk_step::) -> _>; } //! ``` //! //! It's possible to improve the efficiency by doing the following: //! //! * Observe that `dy` is a temporary allocation. It's possible to allow the //! add operation to take ownership of `dy` to eliminate an extra allocation //! for the result of the addition. A similar situation occurs when computing //! `y_new`. See the comments in the example below. //! //! * Require the `fun` closure to mutate an existing view instead of //! allocating a new array for the result. //! //! * Don't return a newly allocated `f_new` array. If the caller wants this //! information, they can get it from the last row of `k`. //! //! * Use [`c.mul_add(h, t)`](f64::mul_add) instead of `t + c * h`. This is //! faster and reduces the floating-point error. It might also be beneficial //! to use [`.scaled_add()`] or a combination of //! [`azip!()`] and [`.mul_add()`](f64::mul_add) on the arrays in //! some places, but that's not demonstrated in the example below. //! //! ``` //! use ndarray::prelude::*; //! //! fn rk_step( //! mut fun: F, //! t: f64, //! y: ArrayView1, //! f: ArrayView1, //! h: f64, //! a: &[ArrayView1], //! b: ArrayView1, //! c: ArrayView1, //! e: ArrayView1, //! mut k: ArrayViewMut2, //! ) -> (Array1, Array1) //! where //! F: FnMut(f64, ArrayView1, ArrayViewMut1), //! { //! k.slice_mut(s![0, ..]).assign(&f); //! for (s, (a, c)) in a.iter().zip(c).enumerate() { //! let dy = k.slice(s![..s + 1, ..]).t().dot(a) * h; //! // Note that `dy` comes before `&y` in `dy + &y` in order to reuse the //! // `dy` allocation. (The addition operator will take ownership of `dy` //! // and assign the result to it instead of allocating a new array for the //! // result.) In contrast, you could use `&y + &dy`, but that would perform //! // an unnecessary memory allocation for the result, like NumPy does. //! fun(c.mul_add(h, t), (dy + &y).view(), k.slice_mut(s![s + 1, ..])); //! } //! // Similar case here — moving `&y` to the right hand side allows the addition //! // to reuse the allocated array on the left hand side. //! let y_new = h * k.slice(s![..-1, ..]).t().dot(&b) + &y; //! // Mutate the last row of `k` in-place instead of allocating a new array. //! fun(t + h, y_new.view(), k.slice_mut(s![-1, ..])); //! //! let error = k.t().dot(&e) * h; //! //! (y_new, error) //! } //! # //! # fn main() { let _ = rk_step::, ArrayViewMut1<'_, f64>)>; } //! ``` //! //! [`.scaled_add()`]: crate::ArrayBase::scaled_add //! [`azip!()`]: crate::azip! //! //! ### SciPy license //! //! ```text //! Copyright (c) 2001, 2002 Enthought, Inc. //! All rights reserved. //! //! Copyright (c) 2003-2017 SciPy Developers. //! All rights reserved. //! //! Redistribution and use in source and binary forms, with or without //! modification, are permitted provided that the following conditions are met: //! //! a. Redistributions of source code must retain the above copyright notice, //! this list of conditions and the following disclaimer. //! b. Redistributions in binary form must reproduce the above copyright //! notice, this list of conditions and the following disclaimer in the //! documentation and/or other materials provided with the distribution. //! c. Neither the name of Enthought nor the names of the SciPy Developers //! may be used to endorse or promote products derived from this software //! without specific prior written permission. //! //! //! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" //! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE //! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE //! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS //! BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, //! OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF //! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS //! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN //! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) //! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF //! THE POSSIBILITY OF SUCH DAMAGE. //! ``` ndarray-0.16.1/src/doc/ndarray_for_numpy_users/simple_math.rs000064400000000000000000000025221046102023000225350ustar 00000000000000//! Example of simple math operations on 2-D arrays. //! //! //! //! //! //! //! //! //! //! //! //!
//! //! NumPy //! //! //! //! `ndarray` //! //!
//! //! ```python //! import numpy as np //! //! //! //! //! a = np.full((5, 4), 3.) //! //! //! a[::2, :] = 2. //! //! //! a[:, 1] = np.sin(a[:, 1]) + 1. //! //! //! a[a < 1.5] = 4. //! //! //! odd_sum = a[:, 1::2].sum() //! //! //! b = np.exp(np.arange(4)) //! //! //! c = a + b //! //! //! d = c.T.dot(a) //! ``` //! //! //! //! ``` //! use ndarray::prelude::*; //! //! # fn main() { //! // Create a 5×4 array of threes. //! let mut a = Array2::::from_elem((5, 4), 3.); //! //! // Fill the even-index rows with twos. //! a.slice_mut(s![..;2, ..]).fill(2.); //! //! // Change column 1 to sin(x) + 1. //! a.column_mut(1).mapv_inplace(|x| x.sin() + 1.); //! //! // Change values less than 1.5 to 4. //! a.mapv_inplace(|x| if x < 1.5 { 4. } else { x }); //! //! // Compute the sum of the odd-index columns. //! let odd_sum = a.slice(s![.., 1..;2]).sum(); //! //! // Create a 1-D array of exp(index). //! let b = Array::from_shape_fn(4, |i| (i as f64).exp()); //! //! // Add b to a (broadcasting to rows). //! let c = a + &b; //! //! // Matrix product of c transpose with c. //! let d = c.t().dot(&c); //! # } //! ``` //! //!
ndarray-0.16.1/src/error.rs000064400000000000000000000053771046102023000136530ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::Dimension; #[cfg(feature = "std")] use std::error::Error; use std::fmt; /// An error related to array shape or layout. #[derive(Clone)] pub struct ShapeError { // we want to be able to change this representation later repr: ErrorKind, } impl ShapeError { /// Return the `ErrorKind` of this error. #[inline] pub fn kind(&self) -> ErrorKind { self.repr } /// Create a new `ShapeError` pub fn from_kind(error: ErrorKind) -> Self { from_kind(error) } } /// Error code for an error related to array shape or layout. /// /// This enumeration is not exhaustive. The representation of the enum /// is not guaranteed. #[non_exhaustive] #[derive(Copy, Clone, Debug)] pub enum ErrorKind { /// incompatible shape IncompatibleShape = 1, /// incompatible memory layout IncompatibleLayout, /// the shape does not fit inside type limits RangeLimited, /// out of bounds indexing OutOfBounds, /// aliasing array elements Unsupported, /// overflow when computing offset, length, etc. Overflow, } #[inline(always)] pub fn from_kind(k: ErrorKind) -> ShapeError { ShapeError { repr: k } } impl PartialEq for ErrorKind { #[inline(always)] fn eq(&self, rhs: &Self) -> bool { *self as u8 == *rhs as u8 } } impl PartialEq for ShapeError { #[inline(always)] fn eq(&self, rhs: &Self) -> bool { self.repr == rhs.repr } } #[cfg(feature = "std")] impl Error for ShapeError {} impl fmt::Display for ShapeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let description = match self.kind() { ErrorKind::IncompatibleShape => "incompatible shapes", ErrorKind::IncompatibleLayout => "incompatible memory layout", ErrorKind::RangeLimited => "the shape does not fit in type limits", ErrorKind::OutOfBounds => "out of bounds indexing", ErrorKind::Unsupported => "unsupported operation", ErrorKind::Overflow => "arithmetic overflow", }; write!(f, "ShapeError/{:?}: {}", self.kind(), description) } } impl fmt::Debug for ShapeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self) } } pub fn incompatible_shapes(_a: &D, _b: &E) -> ShapeError where D: Dimension, E: Dimension, { from_kind(ErrorKind::IncompatibleShape) } ndarray-0.16.1/src/extension/nonnull.rs000064400000000000000000000012431046102023000162070ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::ptr::NonNull; /// Return a NonNull pointer to the vector's data pub(crate) fn nonnull_from_vec_data(v: &mut Vec) -> NonNull { // this pointer is guaranteed to be non-null unsafe { NonNull::new_unchecked(v.as_mut_ptr()) } } /// Converts `ptr` to `NonNull` /// /// Safety: `ptr` *must* be non-null. /// This is checked with a debug assertion, and will panic if this is not true, /// but treat this as an unconditional conversion. #[inline] pub(crate) unsafe fn nonnull_debug_checked_from_ptr(ptr: *mut T) -> NonNull { debug_assert!(!ptr.is_null()); NonNull::new_unchecked(ptr) } ndarray-0.16.1/src/extension.rs000064400000000000000000000007031046102023000145220ustar 00000000000000// Copyright 2019 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Extension traits and utility functions for types from outside ndarray pub(crate) mod nonnull; ndarray-0.16.1/src/free_functions.rs000064400000000000000000000246321046102023000155260ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; #[allow(unused_imports)] use std::compile_error; use std::mem::{forget, size_of}; use std::ptr::NonNull; use crate::imp_prelude::*; use crate::{dimension, ArcArray1, ArcArray2}; /// Create an **[`Array`]** with one, two, three, four, five, or six dimensions. /// /// ``` /// use ndarray::array; /// let a1 = array![1, 2, 3, 4]; /// /// let a2 = array![[1, 2], /// [3, 4]]; /// /// let a3 = array![[[1, 2], [3, 4]], /// [[5, 6], [7, 8]]]; /// /// let a4 = array![[[[1, 2, 3, 4]]]]; /// /// let a5 = array![[[[[1, 2, 3, 4, 5]]]]]; /// /// let a6 = array![[[[[[1, 2, 3, 4, 5, 6]]]]]]; /// /// assert_eq!(a1.shape(), &[4]); /// assert_eq!(a2.shape(), &[2, 2]); /// assert_eq!(a3.shape(), &[2, 2, 2]); /// assert_eq!(a4.shape(), &[1, 1, 1, 4]); /// assert_eq!(a5.shape(), &[1, 1, 1, 1, 5]); /// assert_eq!(a6.shape(), &[1, 1, 1, 1, 1, 6]); /// ``` /// /// This macro uses `vec![]`, and has the same ownership semantics; /// elements are moved into the resulting `Array`. /// /// Use `array![...].into_shared()` to create an `ArcArray`. /// /// Attempts to crate 7D+ arrays with this macro will lead to /// a compiler error, since the difference between a 7D array /// of i32 and a 6D array of `[i32; 3]` is ambiguous. Higher-dim /// arrays can be created with [`ArrayD`]. /// /// ```compile_fail /// use ndarray::array; /// let a7 = array![[[[[[[1, 2, 3]]]]]]]; /// // error: Arrays of 7 dimensions or more (or ndarrays of Rust arrays) cannot be constructed with the array! macro. /// ``` #[macro_export] macro_rules! array { ($([$([$([$([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ compile_error!("Arrays of 7 dimensions or more (or ndarrays of Rust arrays) cannot be constructed with the array! macro."); }}; ($([$([$([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ $crate::Array6::from(vec![$([$([$([$([$([$($x,)*],)*],)*],)*],)*],)*]) }}; ($([$([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ $crate::Array5::from(vec![$([$([$([$([$($x,)*],)*],)*],)*],)*]) }}; ($([$([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*]),+ $(,)*) => {{ $crate::Array4::from(vec![$([$([$([$($x,)*],)*],)*],)*]) }}; ($([$([$($x:expr),* $(,)*]),+ $(,)*]),+ $(,)*) => {{ $crate::Array3::from(vec![$([$([$($x,)*],)*],)*]) }}; ($([$($x:expr),* $(,)*]),+ $(,)*) => {{ $crate::Array2::from(vec![$([$($x,)*],)*]) }}; ($($x:expr),* $(,)*) => {{ $crate::Array::from(vec![$($x,)*]) }}; } /// Create a zero-dimensional array with the element `x`. pub fn arr0
(x: A) -> Array0 { unsafe { ArrayBase::from_shape_vec_unchecked((), vec![x]) } } /// Create a one-dimensional array with elements from `xs`. pub fn arr1(xs: &[A]) -> Array1 { ArrayBase::from(xs.to_vec()) } /// Create a one-dimensional array with elements from `xs`. pub fn rcarr1(xs: &[A]) -> ArcArray1 { arr1(xs).into_shared() } /// Create a zero-dimensional array view borrowing `x`. pub const fn aview0(x: &A) -> ArrayView0<'_, A> { ArrayBase { data: ViewRepr::new(), // Safe because references are always non-null. ptr: unsafe { NonNull::new_unchecked(x as *const A as *mut A) }, dim: Ix0(), strides: Ix0(), } } /// Create a one-dimensional array view with elements borrowing `xs`. /// /// **Panics** if the length of the slice overflows `isize`. (This can only /// occur if `A` is zero-sized, because slices cannot contain more than /// `isize::MAX` number of bytes.) /// /// ``` /// use ndarray::{aview1, ArrayView1}; /// /// let data = [1.0; 1024]; /// /// // Create a 2D array view from borrowed data /// let a2d = aview1(&data).into_shape_with_order((32, 32)).unwrap(); /// /// assert_eq!(a2d.sum(), 1024.0); /// /// // Create a const 1D array view /// const C: ArrayView1<'static, f64> = aview1(&[1., 2., 3.]); /// /// assert_eq!(C.sum(), 6.); /// ``` pub const fn aview1(xs: &[A]) -> ArrayView1<'_, A> { if size_of::() == 0 { assert!( xs.len() <= isize::MAX as usize, "Slice length must fit in `isize`.", ); } ArrayBase { data: ViewRepr::new(), // Safe because references are always non-null. ptr: unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) }, dim: Ix1(xs.len()), strides: Ix1(1), } } /// Create a two-dimensional array view with elements borrowing `xs`. /// /// **Panics** if the product of non-zero axis lengths overflows `isize` (This /// can only occur if A is zero-sized or if `N` is zero, because slices cannot /// contain more than `isize::MAX` number of bytes). /// /// ``` /// use ndarray::{aview2, ArrayView2}; /// /// let data = vec![[1., 2., 3.], [4., 5., 6.]]; /// /// let view = aview2(&data); /// assert_eq!(view.sum(), 21.); /// /// // Create a const 2D array view /// const C: ArrayView2<'static, f64> = aview2(&[[1., 2., 3.], [4., 5., 6.]]); /// assert_eq!(C.sum(), 21.); /// ``` pub const fn aview2(xs: &[[A; N]]) -> ArrayView2<'_, A> { let cols = N; let rows = xs.len(); if size_of::() == 0 { if let Some(n_elems) = rows.checked_mul(cols) { assert!( rows <= isize::MAX as usize && cols <= isize::MAX as usize && n_elems <= isize::MAX as usize, "Product of non-zero axis lengths must not overflow isize.", ); } else { panic!("Overflow in number of elements."); } } else if N == 0 { assert!( rows <= isize::MAX as usize, "Product of non-zero axis lengths must not overflow isize.", ); } // Safe because references are always non-null. let ptr = unsafe { NonNull::new_unchecked(xs.as_ptr() as *mut A) }; let dim = Ix2(rows, cols); let strides = if rows == 0 || cols == 0 { Ix2(0, 0) } else { Ix2(cols, 1) }; ArrayBase { data: ViewRepr::new(), ptr, dim, strides, } } /// Create a one-dimensional read-write array view with elements borrowing `xs`. /// /// ``` /// use ndarray::{aview_mut1, s}; /// // Create an array view over some data, then slice it and modify it. /// let mut data = [0; 1024]; /// { /// let mut a = aview_mut1(&mut data).into_shape_with_order((32, 32)).unwrap(); /// a.slice_mut(s![.., ..;3]).fill(5); /// } /// assert_eq!(&data[..10], [5, 0, 0, 5, 0, 0, 5, 0, 0, 5]); /// ``` pub fn aview_mut1(xs: &mut [A]) -> ArrayViewMut1<'_, A> { ArrayViewMut::from(xs) } /// Create a two-dimensional read-write array view with elements borrowing `xs`. /// /// **Panics** if the product of non-zero axis lengths overflows `isize` (This can only occur if A /// is zero-sized because slices cannot contain more than `isize::MAX` number of bytes). /// /// # Example /// /// ``` /// use ndarray::aview_mut2; /// /// // The inner (nested) and outer arrays can be of any length. /// let mut data = [[0.; 2]; 128]; /// { /// // Make a 128 x 2 mut array view then turn it into 2 x 128 /// let mut a = aview_mut2(&mut data).reversed_axes(); /// // Make the first row ones and second row minus ones. /// a.row_mut(0).fill(1.); /// a.row_mut(1).fill(-1.); /// } /// // look at the start of the result /// assert_eq!(&data[..3], [[1., -1.], [1., -1.], [1., -1.]]); /// ``` pub fn aview_mut2(xs: &mut [[A; N]]) -> ArrayViewMut2<'_, A> { ArrayViewMut2::from(xs) } /// Create a two-dimensional array with elements from `xs`. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[1, 2, 3], /// [4, 5, 6]]); /// assert!( /// a.shape() == [2, 3] /// ); /// ``` pub fn arr2(xs: &[[A; N]]) -> Array2 { Array2::from(xs.to_vec()) } macro_rules! impl_from_nested_vec { ($arr_type:ty, $ix_type:tt, $($n:ident),+) => { impl From> for Array { fn from(mut xs: Vec<$arr_type>) -> Self { let dim = $ix_type(xs.len(), $($n),+); let ptr = xs.as_mut_ptr(); let cap = xs.capacity(); let expand_len = dimension::size_of_shape_checked(&dim) .expect("Product of non-zero axis lengths must not overflow isize."); forget(xs); unsafe { let v = if size_of::() == 0 { Vec::from_raw_parts(ptr as *mut A, expand_len, expand_len) } else if $($n == 0 ||)+ false { Vec::new() } else { let expand_cap = cap $(* $n)+; Vec::from_raw_parts(ptr as *mut A, expand_len, expand_cap) }; ArrayBase::from_shape_vec_unchecked(dim, v) } } } }; } impl_from_nested_vec!([A; N], Ix2, N); impl_from_nested_vec!([[A; M]; N], Ix3, N, M); impl_from_nested_vec!([[[A; L]; M]; N], Ix4, N, M, L); impl_from_nested_vec!([[[[A; K]; L]; M]; N], Ix5, N, M, L, K); impl_from_nested_vec!([[[[[A; J]; K]; L]; M]; N], Ix6, N, M, L, K, J); /// Create a two-dimensional array with elements from `xs`. /// pub fn rcarr2(xs: &[[A; N]]) -> ArcArray2 { arr2(xs).into_shared() } /// Create a three-dimensional array with elements from `xs`. /// /// **Panics** if the slices are not all of the same length. /// /// ``` /// use ndarray::arr3; /// /// let a = arr3(&[[[1, 2], /// [3, 4]], /// [[5, 6], /// [7, 8]], /// [[9, 0], /// [1, 2]]]); /// assert!( /// a.shape() == [3, 2, 2] /// ); /// ``` pub fn arr3(xs: &[[[A; M]; N]]) -> Array3 { Array3::from(xs.to_vec()) } /// Create a three-dimensional array with elements from `xs`. pub fn rcarr3(xs: &[[[A; M]; N]]) -> ArcArray { arr3(xs).into_shared() } ndarray-0.16.1/src/geomspace.rs000064400000000000000000000115751046102023000144620ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![cfg(feature = "std")] use num_traits::Float; /// An iterator of a sequence of geometrically spaced floats. /// /// Iterator element type is `F`. pub struct Geomspace { sign: F, start: F, step: F, index: usize, len: usize, } impl Iterator for Geomspace where F: Float { type Item = F; #[inline] fn next(&mut self) -> Option { if self.index >= self.len { None } else { // Calculate the value just like numpy.linspace does let i = self.index; self.index += 1; let exponent = self.start + self.step * F::from(i).unwrap(); Some(self.sign * exponent.exp()) } } #[inline] fn size_hint(&self) -> (usize, Option) { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Geomspace where F: Float { #[inline] fn next_back(&mut self) -> Option { if self.index >= self.len { None } else { // Calculate the value just like numpy.linspace does self.len -= 1; let i = self.len; let exponent = self.start + self.step * F::from(i).unwrap(); Some(self.sign * exponent.exp()) } } } impl ExactSizeIterator for Geomspace where Geomspace: Iterator {} /// An iterator of a sequence of geometrically spaced values. /// /// The `Geomspace` has `n` geometrically spaced elements from `start` to `end` /// (inclusive). /// /// The iterator element type is `F`, where `F` must implement `Float`, e.g. /// `f32` or `f64`. /// /// Returns `None` if `start` and `end` have different signs or if either one /// is zero. Conceptually, this means that in order to obtain a `Some` result, /// `end / start` must be positive. /// /// **Panics** if converting `n - 1` to type `F` fails. #[inline] pub fn geomspace(a: F, b: F, n: usize) -> Option> where F: Float { if a == F::zero() || b == F::zero() || a.is_sign_negative() != b.is_sign_negative() { return None; } let log_a = a.abs().ln(); let log_b = b.abs().ln(); let step = if n > 1 { let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); (log_b - log_a) / num_steps } else { F::zero() }; Some(Geomspace { sign: a.signum(), start: log_a, step, index: 0, len: n, }) } #[cfg(test)] mod tests { use super::geomspace; #[test] #[cfg(feature = "approx")] fn valid() { use crate::{arr1, Array1}; use approx::assert_abs_diff_eq; let array: Array1<_> = geomspace(1e0, 1e3, 4).unwrap().collect(); assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]), epsilon = 1e-12); let array: Array1<_> = geomspace(1e3, 1e0, 4).unwrap().collect(); assert_abs_diff_eq!(array, arr1(&[1e3, 1e2, 1e1, 1e0]), epsilon = 1e-12); let array: Array1<_> = geomspace(-1e3, -1e0, 4).unwrap().collect(); assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]), epsilon = 1e-12); let array: Array1<_> = geomspace(-1e0, -1e3, 4).unwrap().collect(); assert_abs_diff_eq!(array, arr1(&[-1e0, -1e1, -1e2, -1e3]), epsilon = 1e-12); } #[test] fn iter_forward() { let mut iter = geomspace(1.0f64, 1e3, 4).unwrap(); assert!(iter.size_hint() == (4, Some(4))); assert!((iter.next().unwrap() - 1e0).abs() < 1e-5); assert!((iter.next().unwrap() - 1e1).abs() < 1e-5); assert!((iter.next().unwrap() - 1e2).abs() < 1e-5); assert!((iter.next().unwrap() - 1e3).abs() < 1e-5); assert!(iter.next().is_none()); assert!(iter.size_hint() == (0, Some(0))); } #[test] fn iter_backward() { let mut iter = geomspace(1.0f64, 1e3, 4).unwrap(); assert!(iter.size_hint() == (4, Some(4))); assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5); assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5); assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5); assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5); assert!(iter.next_back().is_none()); assert!(iter.size_hint() == (0, Some(0))); } #[test] fn zero_lower() { assert!(geomspace(0.0, 1.0, 4).is_none()); } #[test] fn zero_upper() { assert!(geomspace(1.0, 0.0, 4).is_none()); } #[test] fn zero_included() { assert!(geomspace(-1.0, 1.0, 4).is_none()); } } ndarray-0.16.1/src/impl_1d.rs000064400000000000000000000041261046102023000140360ustar 00000000000000// Copyright 2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Methods for one-dimensional arrays. #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::mem::MaybeUninit; use crate::imp_prelude::*; use crate::low_level_util::AbortIfPanic; /// # Methods For 1-D Arrays impl ArrayBase where S: RawData { /// Return an vector with the elements of the one-dimensional array. pub fn to_vec(&self) -> Vec where A: Clone, S: Data, { if let Some(slc) = self.as_slice() { slc.to_vec() } else { crate::iterators::to_vec(self.iter().cloned()) } } /// Rotate the elements of the array by 1 element towards the front; /// the former first element becomes the last. pub(crate) fn rotate1_front(&mut self) where S: DataMut { // use swapping to keep all elements initialized (as required by owned storage) let mut lane_iter = self.iter_mut(); let mut dst = if let Some(dst) = lane_iter.next() { dst } else { return }; // Logically we do a circular swap here, all elements in a chain // Using MaybeUninit to avoid unnecessary writes in the safe swap solution // // for elt in lane_iter { // std::mem::swap(dst, elt); // dst = elt; // } // let guard = AbortIfPanic(&"rotate1_front: temporarily moving out of owned value"); let mut slot = MaybeUninit::::uninit(); unsafe { slot.as_mut_ptr().copy_from_nonoverlapping(dst, 1); for elt in lane_iter { (dst as *mut A).copy_from_nonoverlapping(elt, 1); dst = elt; } (dst as *mut A).copy_from_nonoverlapping(slot.as_ptr(), 1); } guard.defuse(); } } ndarray-0.16.1/src/impl_2d.rs000064400000000000000000000100341046102023000140320ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Methods for two-dimensional arrays. use crate::imp_prelude::*; /// # Methods For 2-D Arrays impl ArrayBase where S: RawData { /// Return an array view of row `index`. /// /// **Panics** if `index` is out of bounds. /// /// ``` /// use ndarray::array; /// let array = array![[1., 2.], [3., 4.]]; /// assert_eq!(array.row(0), array![1., 2.]); /// ``` #[track_caller] pub fn row(&self, index: Ix) -> ArrayView1<'_, A> where S: Data { self.index_axis(Axis(0), index) } /// Return a mutable array view of row `index`. /// /// **Panics** if `index` is out of bounds. /// /// ``` /// use ndarray::array; /// let mut array = array![[1., 2.], [3., 4.]]; /// array.row_mut(0)[1] = 5.; /// assert_eq!(array, array![[1., 5.], [3., 4.]]); /// ``` #[track_caller] pub fn row_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> where S: DataMut { self.index_axis_mut(Axis(0), index) } /// Return the number of rows (length of `Axis(0)`) in the two-dimensional array. /// /// ``` /// use ndarray::{array, Axis}; /// /// let array = array![[1., 2.], /// [3., 4.], /// [5., 6.]]; /// assert_eq!(array.nrows(), 3); /// /// // equivalent ways of getting the dimensions /// // get nrows, ncols by using dim: /// let (m, n) = array.dim(); /// assert_eq!(m, array.nrows()); /// // get length of any particular axis with .len_of() /// assert_eq!(m, array.len_of(Axis(0))); /// ``` pub fn nrows(&self) -> usize { self.len_of(Axis(0)) } /// Return an array view of column `index`. /// /// **Panics** if `index` is out of bounds. /// /// ``` /// use ndarray::array; /// let array = array![[1., 2.], [3., 4.]]; /// assert_eq!(array.column(0), array![1., 3.]); /// ``` #[track_caller] pub fn column(&self, index: Ix) -> ArrayView1<'_, A> where S: Data { self.index_axis(Axis(1), index) } /// Return a mutable array view of column `index`. /// /// **Panics** if `index` is out of bounds. /// /// ``` /// use ndarray::array; /// let mut array = array![[1., 2.], [3., 4.]]; /// array.column_mut(0)[1] = 5.; /// assert_eq!(array, array![[1., 2.], [5., 4.]]); /// ``` #[track_caller] pub fn column_mut(&mut self, index: Ix) -> ArrayViewMut1<'_, A> where S: DataMut { self.index_axis_mut(Axis(1), index) } /// Return the number of columns (length of `Axis(1)`) in the two-dimensional array. /// /// ``` /// use ndarray::{array, Axis}; /// /// let array = array![[1., 2.], /// [3., 4.], /// [5., 6.]]; /// assert_eq!(array.ncols(), 2); /// /// // equivalent ways of getting the dimensions /// // get nrows, ncols by using dim: /// let (m, n) = array.dim(); /// assert_eq!(n, array.ncols()); /// // get length of any particular axis with .len_of() /// assert_eq!(n, array.len_of(Axis(1))); /// ``` pub fn ncols(&self) -> usize { self.len_of(Axis(1)) } /// Return true if the array is square, false otherwise. /// /// # Examples /// Square: /// ``` /// use ndarray::array; /// let array = array![[1., 2.], [3., 4.]]; /// assert!(array.is_square()); /// ``` /// Not square: /// ``` /// use ndarray::array; /// let array = array![[1., 2., 5.], [3., 4., 6.]]; /// assert!(!array.is_square()); /// ``` pub fn is_square(&self) -> bool { let (m, n) = self.dim(); m == n } } ndarray-0.16.1/src/impl_arc_array.rs000064400000000000000000000017031046102023000154730ustar 00000000000000// Copyright 2019 ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::imp_prelude::*; #[cfg(target_has_atomic = "ptr")] use alloc::sync::Arc; #[cfg(not(target_has_atomic = "ptr"))] use portable_atomic_util::Arc; /// Methods specific to `ArcArray`. /// /// ***See also all methods for [`ArrayBase`]*** impl ArcArray where D: Dimension { /// Returns `true` iff the inner `Arc` is not shared. /// If you want to ensure the `Arc` is not concurrently cloned, you need to provide a `&mut self` to this function. pub fn is_unique(&self) -> bool { // Only strong pointers are used in this crate. Arc::strong_count(&self.data.0) == 1 } } ndarray-0.16.1/src/impl_clone.rs000064400000000000000000000025421046102023000146320ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::imp_prelude::*; use crate::RawDataClone; impl Clone for ArrayBase { fn clone(&self) -> ArrayBase { // safe because `clone_with_ptr` promises to provide equivalent data and ptr unsafe { let (data, ptr) = self.data.clone_with_ptr(self.ptr); ArrayBase { data, ptr, dim: self.dim.clone(), strides: self.strides.clone(), } } } /// `Array` implements `.clone_from()` to reuse an array's existing /// allocation. Semantically equivalent to `*self = other.clone()`, but /// potentially more efficient. fn clone_from(&mut self, other: &Self) { unsafe { self.ptr = self.data.clone_from_with_ptr(&other.data, other.ptr); self.dim.clone_from(&other.dim); self.strides.clone_from(&other.strides); } } } impl Copy for ArrayBase {} ndarray-0.16.1/src/impl_constructors.rs000064400000000000000000000523041046102023000163030ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Constructor methods for ndarray //! //! #![allow(clippy::match_wild_err_arm)] use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; #[cfg(feature = "std")] use num_traits::Float; use num_traits::{One, Zero}; use std::mem; use std::mem::MaybeUninit; use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::dimension::{self, CanIndexCheckMode}; use crate::error::{self, ShapeError}; use crate::extension::nonnull::nonnull_from_vec_data; use crate::imp_prelude::*; use crate::indexes; use crate::indices; #[cfg(feature = "std")] use crate::iterators::to_vec; use crate::iterators::to_vec_mapped; use crate::iterators::TrustedIterator; use crate::StrideShape; #[cfg(feature = "std")] use crate::{geomspace, linspace, logspace}; #[allow(unused_imports)] use rawpointer::PointerExt; /// # Constructor Methods for Owned Arrays /// /// Note that the constructor methods apply to `Array` and `ArcArray`, /// the two array types that have owned storage. /// /// ## Constructor methods for one-dimensional arrays. impl ArrayBase where S: DataOwned { /// Create a one-dimensional array from a vector (no copying needed). /// /// **Panics** if the length is greater than `isize::MAX`. /// /// ```rust /// use ndarray::Array; /// /// let array = Array::from_vec(vec![1., 2., 3., 4.]); /// ``` pub fn from_vec(v: Vec) -> Self { if mem::size_of::() == 0 { assert!( v.len() <= isize::MAX as usize, "Length must fit in `isize`.", ); } unsafe { Self::from_shape_vec_unchecked(v.len() as Ix, v) } } /// Create a one-dimensional array from an iterator or iterable. /// /// **Panics** if the length is greater than `isize::MAX`. /// /// ```rust /// use ndarray::Array; /// /// let array = Array::from_iter(0..10); /// ``` #[allow(clippy::should_implement_trait)] pub fn from_iter>(iterable: I) -> Self { Self::from_vec(iterable.into_iter().collect()) } /// Create a one-dimensional array with `n` evenly spaced elements from /// `start` to `end` (inclusive). `A` must be a floating point type. /// /// Note that if `start > end`, the first element will still be `start`, /// and the following elements will be decreasing. This is different from /// the behavior of `std::ops::RangeInclusive`, which interprets `start > /// end` to mean that the range is empty. /// /// **Panics** if `n` is greater than `isize::MAX` or if converting `n - 1` /// to type `A` fails. /// /// ```rust /// use ndarray::{Array, arr1}; /// /// let array = Array::linspace(0., 1., 5); /// assert!(array == arr1(&[0.0, 0.25, 0.5, 0.75, 1.0])) /// ``` #[cfg(feature = "std")] pub fn linspace(start: A, end: A, n: usize) -> Self where A: Float { Self::from(to_vec(linspace::linspace(start, end, n))) } /// Create a one-dimensional array with elements from `start` to `end` /// (exclusive), incrementing by `step`. `A` must be a floating point type. /// /// **Panics** if the length is greater than `isize::MAX`. /// /// ```rust /// use ndarray::{Array, arr1}; /// /// let array = Array::range(0., 5., 1.); /// assert!(array == arr1(&[0., 1., 2., 3., 4.])) /// ``` #[cfg(feature = "std")] pub fn range(start: A, end: A, step: A) -> Self where A: Float { Self::from(to_vec(linspace::range(start, end, step))) } /// Create a one-dimensional array with `n` logarithmically spaced /// elements, with the starting value being `base.powf(start)` and the /// final one being `base.powf(end)`. `A` must be a floating point type. /// /// If `base` is negative, all values will be negative. /// /// **Panics** if `n` is greater than `isize::MAX` or if converting `n - 1` /// to type `A` fails. /// /// ```rust /// # #[cfg(feature = "approx")] { /// use approx::assert_abs_diff_eq; /// use ndarray::{Array, arr1}; /// /// let array = Array::logspace(10.0, 0.0, 3.0, 4); /// assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3])); /// /// let array = Array::logspace(-10.0, 3.0, 0.0, 4); /// assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0])); /// # } /// ``` #[cfg(feature = "std")] pub fn logspace(base: A, start: A, end: A, n: usize) -> Self where A: Float { Self::from(to_vec(logspace::logspace(base, start, end, n))) } /// Create a one-dimensional array with `n` geometrically spaced elements /// from `start` to `end` (inclusive). `A` must be a floating point type. /// /// Returns `None` if `start` and `end` have different signs or if either /// one is zero. Conceptually, this means that in order to obtain a `Some` /// result, `end / start` must be positive. /// /// **Panics** if `n` is greater than `isize::MAX` or if converting `n - 1` /// to type `A` fails. /// /// ```rust /// # fn example() -> Option<()> { /// # #[cfg(feature = "approx")] { /// use approx::assert_abs_diff_eq; /// use ndarray::{Array, arr1}; /// /// let array = Array::geomspace(1e0, 1e3, 4)?; /// assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]), epsilon = 1e-12); /// /// let array = Array::geomspace(-1e3, -1e0, 4)?; /// assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]), epsilon = 1e-12); /// # } /// # Some(()) /// # } /// # /// # example().unwrap(); /// ``` #[cfg(feature = "std")] pub fn geomspace(start: A, end: A, n: usize) -> Option where A: Float { Some(Self::from(to_vec(geomspace::geomspace(start, end, n)?))) } } /// ## Constructor methods for two-dimensional arrays. impl ArrayBase where S: DataOwned { /// Create an identity matrix of size `n` (square 2D array). /// /// **Panics** if `n * n` would overflow `isize`. pub fn eye(n: Ix) -> Self where S: DataMut, A: Clone + Zero + One, { let mut eye = Self::zeros((n, n)); for a_ii in eye.diag_mut() { *a_ii = A::one(); } eye } /// Create a 2D matrix from its diagonal /// /// **Panics** if `diag.len() * diag.len()` would overflow `isize`. /// /// ```rust /// use ndarray::{Array2, arr1, arr2}; /// /// let diag = arr1(&[1, 2]); /// let array = Array2::from_diag(&diag); /// assert_eq!(array, arr2(&[[1, 0], [0, 2]])); /// ``` pub fn from_diag(diag: &ArrayBase) -> Self where A: Clone + Zero, S: DataMut, S2: Data, { let n = diag.len(); let mut arr = Self::zeros((n, n)); arr.diag_mut().assign(diag); arr } /// Create a square 2D matrix of the specified size, with the specified /// element along the diagonal and zeros elsewhere. /// /// **Panics** if `n * n` would overflow `isize`. /// /// ```rust /// use ndarray::{array, Array2}; /// /// let array = Array2::from_diag_elem(2, 5.); /// assert_eq!(array, array![[5., 0.], [0., 5.]]); /// ``` pub fn from_diag_elem(n: usize, elem: A) -> Self where S: DataMut, A: Clone + Zero, { let mut eye = Self::zeros((n, n)); for a_ii in eye.diag_mut() { *a_ii = elem.clone(); } eye } } #[cfg(not(debug_assertions))] #[allow(clippy::match_wild_err_arm)] macro_rules! size_of_shape_checked_unwrap { ($dim:expr) => { match dimension::size_of_shape_checked($dim) { Ok(sz) => sz, Err(_) => { panic!("ndarray: Shape too large, product of non-zero axis lengths overflows isize") } } }; } #[cfg(debug_assertions)] macro_rules! size_of_shape_checked_unwrap { ($dim:expr) => { match dimension::size_of_shape_checked($dim) { Ok(sz) => sz, Err(_) => panic!( "ndarray: Shape too large, product of non-zero axis lengths \ overflows isize in shape {:?}", $dim ), } }; } /// ## Constructor methods for n-dimensional arrays. /// /// The `shape` argument can be an integer or a tuple of integers to specify /// a static size. For example `10` makes a length 10 one-dimensional array /// (dimension type `Ix1`) and `(5, 6)` a 5 × 6 array (dimension type `Ix2`). /// /// With the trait `ShapeBuilder` in scope, there is the method `.f()` to select /// column major (“f” order) memory layout instead of the default row major. /// For example `Array::zeros((5, 6).f())` makes a column major 5 × 6 array. /// /// Use [`type@IxDyn`] for the shape to create an array with dynamic /// number of axes. /// /// Finally, the few constructors that take a completely general /// `Into` argument *optionally* support custom strides, for /// example a shape given like `(10, 2, 2).strides((1, 10, 20))` is valid. impl ArrayBase where S: DataOwned, D: Dimension, { /// Create an array with copies of `elem`, shape `shape`. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. /// /// ``` /// use ndarray::{Array, arr3, ShapeBuilder}; /// /// let a = Array::from_elem((2, 2, 2), 1.); /// /// assert!( /// a == arr3(&[[[1., 1.], /// [1., 1.]], /// [[1., 1.], /// [1., 1.]]]) /// ); /// assert!(a.strides() == &[4, 2, 1]); /// /// let b = Array::from_elem((2, 2, 2).f(), 1.); /// assert!(b.strides() == &[1, 2, 4]); /// ``` pub fn from_elem(shape: Sh, elem: A) -> Self where A: Clone, Sh: ShapeBuilder, { let shape = shape.into_shape_with_order(); let size = size_of_shape_checked_unwrap!(&shape.dim); let v = vec![elem; size]; unsafe { Self::from_shape_vec_unchecked(shape, v) } } /// Create an array with zeros, shape `shape`. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. pub fn zeros(shape: Sh) -> Self where A: Clone + Zero, Sh: ShapeBuilder, { Self::from_elem(shape, A::zero()) } /// Create an array with ones, shape `shape`. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. pub fn ones(shape: Sh) -> Self where A: Clone + One, Sh: ShapeBuilder, { Self::from_elem(shape, A::one()) } /// Create an array with default values, shape `shape` /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. pub fn default(shape: Sh) -> Self where A: Default, Sh: ShapeBuilder, { Self::from_shape_simple_fn(shape, A::default) } /// Create an array with values created by the function `f`. /// /// `f` is called with no argument, and it should return the element to /// create. If the precise index of the element to create is needed, /// use [`from_shape_fn`](ArrayBase::from_shape_fn) instead. /// /// This constructor can be useful if the element order is not important, /// for example if they are identical or random. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. pub fn from_shape_simple_fn(shape: Sh, mut f: F) -> Self where Sh: ShapeBuilder, F: FnMut() -> A, { let shape = shape.into_shape_with_order(); let len = size_of_shape_checked_unwrap!(&shape.dim); let v = to_vec_mapped(0..len, move |_| f()); unsafe { Self::from_shape_vec_unchecked(shape, v) } } /// Create an array with values created by the function `f`. /// /// `f` is called with the index of the element to create; the elements are /// visited in arbitrary order. /// /// **Panics** if the product of non-zero axis lengths overflows `isize`. /// /// ``` /// use ndarray::{Array, arr2}; /// /// // Create a table of i × j (with i and j from 1 to 3) /// let ij_table = Array::from_shape_fn((3, 3), |(i, j)| (1 + i) * (1 + j)); /// /// assert_eq!( /// ij_table, /// arr2(&[[1, 2, 3], /// [2, 4, 6], /// [3, 6, 9]]) /// ); /// ``` pub fn from_shape_fn(shape: Sh, f: F) -> Self where Sh: ShapeBuilder, F: FnMut(D::Pattern) -> A, { let shape = shape.into_shape_with_order(); let _ = size_of_shape_checked_unwrap!(&shape.dim); if shape.is_c() { let v = to_vec_mapped(indices(shape.dim.clone()).into_iter(), f); unsafe { Self::from_shape_vec_unchecked(shape, v) } } else { let dim = shape.dim.clone(); let v = to_vec_mapped(indexes::indices_iter_f(dim), f); unsafe { Self::from_shape_vec_unchecked(shape, v) } } } /// Create an array with the given shape from a vector. (No cloning of /// elements needed.) /// /// ---- /// /// For a contiguous c- or f-order shape, the following applies: /// /// **Errors** if `shape` does not correspond to the number of elements in /// `v` or if the shape/strides would result in overflowing `isize`. /// /// ---- /// /// For custom strides, the following applies: /// /// **Errors** if strides and dimensions can point out of bounds of `v`, if /// strides allow multiple indices to point to the same element, or if the /// shape/strides would result in overflowing `isize`. /// /// ``` /// use ndarray::Array; /// use ndarray::ShapeBuilder; // Needed for .strides() method /// use ndarray::arr2; /// /// let a = Array::from_shape_vec((2, 2), vec![1., 2., 3., 4.]); /// assert!(a.is_ok()); /// /// let b = Array::from_shape_vec((2, 2).strides((1, 2)), /// vec![1., 2., 3., 4.]).unwrap(); /// assert!( /// b == arr2(&[[1., 3.], /// [2., 4.]]) /// ); /// ``` pub fn from_shape_vec(shape: Sh, v: Vec) -> Result where Sh: Into> { // eliminate the type parameter Sh as soon as possible Self::from_shape_vec_impl(shape.into(), v) } fn from_shape_vec_impl(shape: StrideShape, v: Vec) -> Result { let dim = shape.dim; let is_custom = shape.strides.is_custom(); dimension::can_index_slice_with_strides(&v, &dim, &shape.strides, dimension::CanIndexCheckMode::OwnedMutable)?; if !is_custom && dim.size() != v.len() { return Err(error::incompatible_shapes(&Ix1(v.len()), &dim)); } let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::from_vec_dim_stride_unchecked(dim, strides, v)) } } /// Creates an array from a vector and interpret it according to the /// provided shape and strides. (No cloning of elements needed.) /// /// # Safety /// /// The caller must ensure that the following conditions are met: /// /// 1. The ndim of `dim` and `strides` must be the same. /// /// 2. The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// 3. For axes with length > 1, the pointer cannot move outside the /// slice. /// /// 4. If the array will be empty (any axes are zero-length), the /// difference between the least address and greatest address accessible /// by moving along all axes must be ≤ `v.len()`. /// /// If the array will not be empty, the difference between the least /// address and greatest address accessible by moving along all axes /// must be < `v.len()`. /// /// 5. The strides must not allow any element to be referenced by two different /// indices. pub unsafe fn from_shape_vec_unchecked(shape: Sh, v: Vec) -> Self where Sh: Into> { let shape = shape.into(); let dim = shape.dim; let strides = shape.strides.strides_for_dim(&dim); Self::from_vec_dim_stride_unchecked(dim, strides, v) } unsafe fn from_vec_dim_stride_unchecked(dim: D, strides: D, mut v: Vec) -> Self { // debug check for issues that indicates wrong use of this constructor debug_assert!(dimension::can_index_slice(&v, &dim, &strides, CanIndexCheckMode::OwnedMutable).is_ok()); let ptr = nonnull_from_vec_data(&mut v).add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)); ArrayBase::from_data_ptr(DataOwned::new(v), ptr).with_strides_dim(strides, dim) } /// Creates an array from an iterator, mapped by `map` and interpret it according to the /// provided shape and strides. /// /// # Safety /// /// See from_shape_vec_unchecked pub(crate) unsafe fn from_shape_trusted_iter_unchecked(shape: Sh, iter: I, map: F) -> Self where Sh: Into>, I: TrustedIterator + ExactSizeIterator, F: FnMut(I::Item) -> A, { let shape = shape.into(); let dim = shape.dim; let strides = shape.strides.strides_for_dim(&dim); let v = to_vec_mapped(iter, map); Self::from_vec_dim_stride_unchecked(dim, strides, v) } /// Create an array with uninitialized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, /// an easier way to handle uninit values correctly. /// /// Only *when* the array is completely initialized with valid elements, can it be /// converted to an array of `A` elements using [`.assume_init()`]. /// /// **Panics** if the number of elements in `shape` would overflow isize. /// /// ### Safety /// /// The whole of the array must be initialized before it is converted /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`. /// /// ### Examples /// /// It is possible to assign individual values through `*elt = MaybeUninit::new(value)` /// and so on. /// /// [`.assume_init()`]: ArrayBase::assume_init /// /// ``` /// use ndarray::{s, Array2}; /// /// // Example Task: Let's create a column shifted copy of the input /// /// fn shift_by_two(a: &Array2) -> Array2 { /// // create an uninitialized array /// let mut b = Array2::uninit(a.dim()); /// /// // two first columns in b are two last in a /// // rest of columns in b are the initial columns in a /// /// a.slice(s![.., -2..]).assign_to(b.slice_mut(s![.., ..2])); /// a.slice(s![.., 2..]).assign_to(b.slice_mut(s![.., ..-2])); /// /// // Now we can promise that `b` is safe to use with all operations /// unsafe { /// b.assume_init() /// } /// } /// /// # let _ = shift_by_two; /// ``` pub fn uninit(shape: Sh) -> ArrayBase where Sh: ShapeBuilder { unsafe { let shape = shape.into_shape_with_order(); let size = size_of_shape_checked_unwrap!(&shape.dim); let mut v = Vec::with_capacity(size); v.set_len(size); ArrayBase::from_shape_vec_unchecked(shape, v) } } /// Create an array with uninitialized elements, shape `shape`. /// /// The uninitialized elements of type `A` are represented by the type `MaybeUninit`, /// an easier way to handle uninit values correctly. /// /// The `builder` closure gets unshared access to the array through a view and can use it to /// modify the array before it is returned. This allows initializing the array for any owned /// array type (avoiding clone requirements for copy-on-write, because the array is unshared /// when initially created). /// /// Only *when* the array is completely initialized with valid elements, can it be /// converted to an array of `A` elements using [`.assume_init()`]. /// /// **Panics** if the number of elements in `shape` would overflow isize. /// /// ### Safety /// /// The whole of the array must be initialized before it is converted /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`. /// /// [`.assume_init()`]: ArrayBase::assume_init pub fn build_uninit(shape: Sh, builder: F) -> ArrayBase where Sh: ShapeBuilder, F: FnOnce(ArrayViewMut, D>), { let mut array = Self::uninit(shape); // Safe because: the array is unshared here unsafe { builder(array.raw_view_mut_unchecked().deref_into_view_mut()); } array } } ndarray-0.16.1/src/impl_cow.rs000064400000000000000000000043611046102023000143230ustar 00000000000000// Copyright 2019 ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::imp_prelude::*; /// Methods specific to `CowArray`. /// /// ***See also all methods for [`ArrayBase`]*** impl<'a, A, D> CowArray<'a, A, D> where D: Dimension { /// Returns `true` iff the array is the view (borrowed) variant. pub fn is_view(&self) -> bool { self.data.is_view() } /// Returns `true` iff the array is the owned variant. pub fn is_owned(&self) -> bool { self.data.is_owned() } } impl<'a, A, D> From> for CowArray<'a, A, D> where D: Dimension { fn from(view: ArrayView<'a, A, D>) -> CowArray<'a, A, D> { // safe because equivalent data unsafe { ArrayBase::from_data_ptr(CowRepr::View(view.data), view.ptr).with_strides_dim(view.strides, view.dim) } } } impl<'a, A, D> From> for CowArray<'a, A, D> where D: Dimension { fn from(array: Array) -> CowArray<'a, A, D> { // safe because equivalent data unsafe { ArrayBase::from_data_ptr(CowRepr::Owned(array.data), array.ptr).with_strides_dim(array.strides, array.dim) } } } impl<'a, A, Slice: ?Sized> From<&'a Slice> for CowArray<'a, A, Ix1> where Slice: AsRef<[A]> { /// Create a one-dimensional clone-on-write view of the data in `slice`. /// /// **Panics** if the slice length is greater than [`isize::MAX`]. /// /// ``` /// use ndarray::{array, CowArray}; /// /// let array = CowArray::from(&[1., 2., 3., 4.]); /// assert!(array.is_view()); /// assert_eq!(array, array![1., 2., 3., 4.]); /// ``` fn from(slice: &'a Slice) -> Self { Self::from(ArrayView1::from(slice)) } } impl<'a, A, S, D> From<&'a ArrayBase> for CowArray<'a, A, D> where S: Data, D: Dimension, { /// Create a read-only clone-on-write view of the array. fn from(array: &'a ArrayBase) -> Self { Self::from(array.view()) } } ndarray-0.16.1/src/impl_dyn.rs000064400000000000000000000070161046102023000143250ustar 00000000000000// Copyright 2018 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Methods for dynamic-dimensional arrays. use crate::imp_prelude::*; /// # Methods for Dynamic-Dimensional Arrays impl ArrayBase where S: Data { /// Insert new array axis of length 1 at `axis`, modifying the shape and /// strides in-place. /// /// **Panics** if the axis is out of bounds. /// /// ``` /// use ndarray::{Axis, arr2, arr3}; /// /// let mut a = arr2(&[[1, 2, 3], [4, 5, 6]]).into_dyn(); /// assert_eq!(a.shape(), &[2, 3]); /// /// a.insert_axis_inplace(Axis(1)); /// assert_eq!(a, arr3(&[[[1, 2, 3]], [[4, 5, 6]]]).into_dyn()); /// assert_eq!(a.shape(), &[2, 1, 3]); /// ``` #[track_caller] pub fn insert_axis_inplace(&mut self, axis: Axis) { assert!(axis.index() <= self.ndim()); self.dim = self.dim.insert_axis(axis); self.strides = self.strides.insert_axis(axis); } /// Collapses the array to `index` along the axis and removes the axis, /// modifying the shape and strides in-place. /// /// **Panics** if `axis` or `index` is out of bounds. /// /// ``` /// use ndarray::{Axis, arr1, arr2}; /// /// let mut a = arr2(&[[1, 2, 3], [4, 5, 6]]).into_dyn(); /// assert_eq!(a.shape(), &[2, 3]); /// /// a.index_axis_inplace(Axis(1), 1); /// assert_eq!(a, arr1(&[2, 5]).into_dyn()); /// assert_eq!(a.shape(), &[2]); /// ``` #[track_caller] pub fn index_axis_inplace(&mut self, axis: Axis, index: usize) { self.collapse_axis(axis, index); self.dim = self.dim.remove_axis(axis); self.strides = self.strides.remove_axis(axis); } /// Remove axes of length 1 and return the modified array. /// /// If the array has more the one dimension, the result array will always /// have at least one dimension, even if it has a length of 1. /// /// ``` /// use ndarray::{arr1, arr2, arr3}; /// /// let a = arr3(&[[[1, 2, 3]], [[4, 5, 6]]]).into_dyn(); /// assert_eq!(a.shape(), &[2, 1, 3]); /// let b = a.squeeze(); /// assert_eq!(b, arr2(&[[1, 2, 3], [4, 5, 6]]).into_dyn()); /// assert_eq!(b.shape(), &[2, 3]); /// /// let c = arr2(&[[1]]).into_dyn(); /// assert_eq!(c.shape(), &[1, 1]); /// let d = c.squeeze(); /// assert_eq!(d, arr1(&[1]).into_dyn()); /// assert_eq!(d.shape(), &[1]); /// ``` #[track_caller] pub fn squeeze(self) -> Self { let mut out = self; for axis in (0..out.shape().len()).rev() { if out.shape()[axis] == 1 && out.shape().len() > 1 { out = out.remove_axis(Axis(axis)); } } out } } #[cfg(test)] mod tests { use crate::{arr1, arr2, arr3}; #[test] fn test_squeeze() { let a = arr3(&[[[1, 2, 3]], [[4, 5, 6]]]).into_dyn(); assert_eq!(a.shape(), &[2, 1, 3]); let b = a.squeeze(); assert_eq!(b, arr2(&[[1, 2, 3], [4, 5, 6]]).into_dyn()); assert_eq!(b.shape(), &[2, 3]); let c = arr2(&[[1]]).into_dyn(); assert_eq!(c.shape(), &[1, 1]); let d = c.squeeze(); assert_eq!(d, arr1(&[1]).into_dyn()); assert_eq!(d.shape(), &[1]); } } ndarray-0.16.1/src/impl_internal_constructors.rs000064400000000000000000000035531046102023000202010ustar 00000000000000// Copyright 2021 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::ptr::NonNull; use crate::imp_prelude::*; // internal "builder-like" methods impl ArrayBase where S: RawData { /// Create an (initially) empty one-dimensional array from the given data and array head /// pointer /// /// ## Safety /// /// The caller must ensure that the data storage and pointer is valid. /// /// See ArrayView::from_shape_ptr for general pointer validity documentation. #[inline] pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self { let array = ArrayBase { data, ptr, dim: Ix1(0), strides: Ix1(1), }; debug_assert!(array.pointer_is_inbounds()); array } } // internal "builder-like" methods impl ArrayBase where S: RawData, D: Dimension, { /// Set strides and dimension of the array to the new values /// /// The argument order with strides before dimensions is used because strides are often /// computed as derived from the dimension. /// /// ## Safety /// /// The caller needs to ensure that the new strides and dimensions are correct /// for the array data. #[inline] pub(crate) unsafe fn with_strides_dim(self, strides: E, dim: E) -> ArrayBase where E: Dimension { debug_assert_eq!(strides.ndim(), dim.ndim()); ArrayBase { data: self.data, ptr: self.ptr, dim, strides, } } } ndarray-0.16.1/src/impl_methods.rs000064400000000000000000003227541046102023000152070ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use alloc::slice; use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; #[allow(unused_imports)] use rawpointer::PointerExt; use std::mem::{size_of, ManuallyDrop}; use crate::imp_prelude::*; use crate::argument_traits::AssignElem; use crate::dimension; use crate::dimension::broadcast::co_broadcast; use crate::dimension::reshape_dim; use crate::dimension::IntoDimension; use crate::dimension::{ abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last, offset_from_low_addr_ptr_to_logical_ptr, size_of_shape_checked, stride_offset, Axes, }; use crate::error::{self, from_kind, ErrorKind, ShapeError}; use crate::itertools::zip; use crate::math_cell::MathCell; use crate::order::Order; use crate::shape_builder::ShapeArg; use crate::zip::{IntoNdProducer, Zip}; use crate::AxisDescription; use crate::{arraytraits, DimMax}; use crate::iter::{ AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, AxisWindows, ExactChunks, ExactChunksMut, IndexedIter, IndexedIterMut, Iter, IterMut, Lanes, LanesMut, Windows, }; use crate::slice::{MultiSliceArg, SliceArg}; use crate::stacking::concatenate; use crate::{NdIndex, Slice, SliceInfoElem}; /// # Methods For All Array Types impl ArrayBase where S: RawData, D: Dimension, { /// Return the total number of elements in the array. pub fn len(&self) -> usize { self.dim.size() } /// Return the length of `axis`. /// /// The axis should be in the range `Axis(` 0 .. *n* `)` where *n* is the /// number of dimensions (axes) of the array. /// /// ***Panics*** if the axis is out of bounds. #[track_caller] pub fn len_of(&self, axis: Axis) -> usize { self.dim[axis.index()] } /// Return whether the array has any elements pub fn is_empty(&self) -> bool { self.len() == 0 } /// Return the number of dimensions (axes) in the array pub fn ndim(&self) -> usize { self.dim.ndim() } /// Return the shape of the array in its “pattern” form, /// an integer in the one-dimensional case, tuple in the n-dimensional cases /// and so on. pub fn dim(&self) -> D::Pattern { self.dim.clone().into_pattern() } /// Return the shape of the array as it's stored in the array. /// /// This is primarily useful for passing to other `ArrayBase` /// functions, such as when creating another array of the same /// shape and dimensionality. /// /// ``` /// use ndarray::Array; /// /// let a = Array::from_elem((2, 3), 5.); /// /// // Create an array of zeros that's the same shape and dimensionality as `a`. /// let b = Array::::zeros(a.raw_dim()); /// ``` pub fn raw_dim(&self) -> D { self.dim.clone() } /// Return the shape of the array as a slice. /// /// Note that you probably don't want to use this to create an array of the /// same shape as another array because creating an array with e.g. /// [`Array::zeros()`](ArrayBase::zeros) using a shape of type `&[usize]` /// results in a dynamic-dimensional array. If you want to create an array /// that has the same shape and dimensionality as another array, use /// [`.raw_dim()`](ArrayBase::raw_dim) instead: /// /// ```rust /// use ndarray::{Array, Array2}; /// /// let a = Array2::::zeros((3, 4)); /// let shape = a.shape(); /// assert_eq!(shape, &[3, 4]); /// /// // Since `a.shape()` returned `&[usize]`, we get an `ArrayD` instance: /// let b = Array::zeros(shape); /// assert_eq!(a.clone().into_dyn(), b); /// /// // To get the same dimension type, use `.raw_dim()` instead: /// let c = Array::zeros(a.raw_dim()); /// assert_eq!(a, c); /// ``` pub fn shape(&self) -> &[usize] { self.dim.slice() } /// Return the strides of the array as a slice. pub fn strides(&self) -> &[isize] { let s = self.strides.slice(); // reinterpret unsigned integer as signed unsafe { slice::from_raw_parts(s.as_ptr() as *const _, s.len()) } } /// Return the stride of `axis`. /// /// The axis should be in the range `Axis(` 0 .. *n* `)` where *n* is the /// number of dimensions (axes) of the array. /// /// ***Panics*** if the axis is out of bounds. #[track_caller] pub fn stride_of(&self, axis: Axis) -> isize { // strides are reinterpreted as isize self.strides[axis.index()] as isize } /// Return a read-only view of the array pub fn view(&self) -> ArrayView<'_, A, D> where S: Data { debug_assert!(self.pointer_is_inbounds()); unsafe { ArrayView::new(self.ptr, self.dim.clone(), self.strides.clone()) } } /// Return a read-write view of the array pub fn view_mut(&mut self) -> ArrayViewMut<'_, A, D> where S: DataMut { self.ensure_unique(); unsafe { ArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } } /// Return a shared view of the array with elements as if they were embedded in cells. /// /// The cell view requires a mutable borrow of the array. Once borrowed the /// cell view itself can be copied and accessed without exclusivity. /// /// The view acts "as if" the elements are temporarily in cells, and elements /// can be changed through shared references using the regular cell methods. pub fn cell_view(&mut self) -> ArrayView<'_, MathCell, D> where S: DataMut { self.view_mut().into_cell_view() } /// Return an uniquely owned copy of the array. /// /// If the input array is contiguous, then the output array will have the same /// memory layout. Otherwise, the layout of the output array is unspecified. /// If you need a particular layout, you can allocate a new array with the /// desired memory layout and [`.assign()`](Self::assign) the data. /// Alternatively, you can collectan iterator, like this for a result in /// standard layout: /// /// ``` /// # use ndarray::prelude::*; /// # let arr = Array::from_shape_vec((2, 2).f(), vec![1, 2, 3, 4]).unwrap(); /// # let owned = { /// Array::from_shape_vec(arr.raw_dim(), arr.iter().cloned().collect()).unwrap() /// # }; /// # assert!(owned.is_standard_layout()); /// # assert_eq!(arr, owned); /// ``` /// /// or this for a result in column-major (Fortran) layout: /// /// ``` /// # use ndarray::prelude::*; /// # let arr = Array::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); /// # let owned = { /// Array::from_shape_vec(arr.raw_dim().f(), arr.t().iter().cloned().collect()).unwrap() /// # }; /// # assert!(owned.t().is_standard_layout()); /// # assert_eq!(arr, owned); /// ``` pub fn to_owned(&self) -> Array where A: Clone, S: Data, { if let Some(slc) = self.as_slice_memory_order() { unsafe { Array::from_shape_vec_unchecked(self.dim.clone().strides(self.strides.clone()), slc.to_vec()) } } else { self.map(A::clone) } } /// Return a shared ownership (copy on write) array, cloning the array /// elements if necessary. pub fn to_shared(&self) -> ArcArray where A: Clone, S: Data, { S::to_shared(self) } /// Turn the array into a uniquely owned array, cloning the array elements /// if necessary. pub fn into_owned(self) -> Array where A: Clone, S: Data, { S::into_owned(self) } /// Converts the array into `Array` if this is possible without /// cloning the array elements. Otherwise, returns `self` unchanged. /// /// ``` /// use ndarray::{array, rcarr2, ArcArray2, Array2}; /// /// // Reference-counted, clone-on-write `ArcArray`. /// let a: ArcArray2<_> = rcarr2(&[[1., 2.], [3., 4.]]); /// { /// // Another reference to the same data. /// let b: ArcArray2<_> = a.clone(); /// // Since there are two references to the same data, `.into_owned()` /// // would require cloning the data, so `.try_into_owned_nocopy()` /// // returns `Err`. /// assert!(b.try_into_owned_nocopy().is_err()); /// } /// // Here, since the second reference has been dropped, the `ArcArray` /// // can be converted into an `Array` without cloning the data. /// let unique: Array2<_> = a.try_into_owned_nocopy().unwrap(); /// assert_eq!(unique, array![[1., 2.], [3., 4.]]); /// ``` pub fn try_into_owned_nocopy(self) -> Result, Self> where S: Data { S::try_into_owned_nocopy(self) } /// Turn the array into a shared ownership (copy on write) array, /// cloning the array elements if necessary. /// /// If you want to generalize over `Array` and `ArcArray` inputs but avoid /// an `A: Clone` bound, use `Into::>::into` instead of this /// method. pub fn into_shared(self) -> ArcArray where A: Clone, S: DataOwned, { S::into_shared(self) } /// Returns a reference to the first element of the array, or `None` if it /// is empty. /// /// # Example /// /// ```rust /// use ndarray::Array3; /// /// let mut a = Array3::::zeros([3, 4, 2]); /// a[[0, 0, 0]] = 42.; /// assert_eq!(a.first(), Some(&42.)); /// /// let b = Array3::::zeros([3, 0, 5]); /// assert_eq!(b.first(), None); /// ``` pub fn first(&self) -> Option<&A> where S: Data { if self.is_empty() { None } else { Some(unsafe { &*self.as_ptr() }) } } /// Returns a mutable reference to the first element of the array, or /// `None` if it is empty. /// /// # Example /// /// ```rust /// use ndarray::Array3; /// /// let mut a = Array3::::zeros([3, 4, 2]); /// *a.first_mut().unwrap() = 42.; /// assert_eq!(a[[0, 0, 0]], 42.); /// /// let mut b = Array3::::zeros([3, 0, 5]); /// assert_eq!(b.first_mut(), None); /// ``` pub fn first_mut(&mut self) -> Option<&mut A> where S: DataMut { if self.is_empty() { None } else { Some(unsafe { &mut *self.as_mut_ptr() }) } } /// Returns a reference to the last element of the array, or `None` if it /// is empty. /// /// # Example /// /// ```rust /// use ndarray::Array3; /// /// let mut a = Array3::::zeros([3, 4, 2]); /// a[[2, 3, 1]] = 42.; /// assert_eq!(a.last(), Some(&42.)); /// /// let b = Array3::::zeros([3, 0, 5]); /// assert_eq!(b.last(), None); /// ``` pub fn last(&self) -> Option<&A> where S: Data { if self.is_empty() { None } else { let mut index = self.raw_dim(); for ax in 0..index.ndim() { index[ax] -= 1; } Some(unsafe { self.uget(index) }) } } /// Returns a mutable reference to the last element of the array, or `None` /// if it is empty. /// /// # Example /// /// ```rust /// use ndarray::Array3; /// /// let mut a = Array3::::zeros([3, 4, 2]); /// *a.last_mut().unwrap() = 42.; /// assert_eq!(a[[2, 3, 1]], 42.); /// /// let mut b = Array3::::zeros([3, 0, 5]); /// assert_eq!(b.last_mut(), None); /// ``` pub fn last_mut(&mut self) -> Option<&mut A> where S: DataMut { if self.is_empty() { None } else { let mut index = self.raw_dim(); for ax in 0..index.ndim() { index[ax] -= 1; } Some(unsafe { self.uget_mut(index) }) } } /// Return an iterator of references to the elements of the array. /// /// Elements are visited in the *logical order* of the array, which /// is where the rightmost index is varying the fastest. /// /// Iterator element type is `&A`. pub fn iter(&self) -> Iter<'_, A, D> where S: Data { debug_assert!(self.pointer_is_inbounds()); self.view().into_iter_() } /// Return an iterator of mutable references to the elements of the array. /// /// Elements are visited in the *logical order* of the array, which /// is where the rightmost index is varying the fastest. /// /// Iterator element type is `&mut A`. pub fn iter_mut(&mut self) -> IterMut<'_, A, D> where S: DataMut { self.view_mut().into_iter_() } /// Return an iterator of indexes and references to the elements of the array. /// /// Elements are visited in the *logical order* of the array, which /// is where the rightmost index is varying the fastest. /// /// Iterator element type is `(D::Pattern, &A)`. /// /// See also [`Zip::indexed`] pub fn indexed_iter(&self) -> IndexedIter<'_, A, D> where S: Data { IndexedIter::new(self.view().into_elements_base()) } /// Return an iterator of indexes and mutable references to the elements of the array. /// /// Elements are visited in the *logical order* of the array, which /// is where the rightmost index is varying the fastest. /// /// Iterator element type is `(D::Pattern, &mut A)`. pub fn indexed_iter_mut(&mut self) -> IndexedIterMut<'_, A, D> where S: DataMut { IndexedIterMut::new(self.view_mut().into_elements_base()) } /// Return a sliced view of the array. /// /// See [*Slicing*](#slicing) for full documentation. /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) #[track_caller] pub fn slice(&self, info: I) -> ArrayView<'_, A, I::OutDim> where I: SliceArg, S: Data, { self.view().slice_move(info) } /// Return a sliced read-write view of the array. /// /// See [*Slicing*](#slicing) for full documentation. /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) #[track_caller] pub fn slice_mut(&mut self, info: I) -> ArrayViewMut<'_, A, I::OutDim> where I: SliceArg, S: DataMut, { self.view_mut().slice_move(info) } /// Return multiple disjoint, sliced, mutable views of the array. /// /// See [*Slicing*](#slicing) for full documentation. See also /// [`MultiSliceArg`], [`s!`], [`SliceArg`], and /// [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if any of the following occur: /// /// * if any of the views would intersect (i.e. if any element would appear in multiple slices) /// * if an index is out of bounds or step size is zero /// * if `D` is `IxDyn` and `info` does not match the number of array axes /// /// # Example /// /// ``` /// use ndarray::{arr2, s}; /// /// let mut a = arr2(&[[1, 2, 3], [4, 5, 6]]); /// let (mut edges, mut middle) = a.multi_slice_mut((s![.., ..;2], s![.., 1])); /// edges.fill(1); /// middle.fill(0); /// assert_eq!(a, arr2(&[[1, 0, 1], [1, 0, 1]])); /// ``` #[track_caller] pub fn multi_slice_mut<'a, M>(&'a mut self, info: M) -> M::Output where M: MultiSliceArg<'a, A, D>, S: DataMut, { info.multi_slice_move(self.view_mut()) } /// Slice the array, possibly changing the number of dimensions. /// /// See [*Slicing*](#slicing) for full documentation. /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** if an index is out of bounds or step size is zero.
/// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.) #[track_caller] pub fn slice_move(mut self, info: I) -> ArrayBase where I: SliceArg { assert_eq!( info.in_ndim(), self.ndim(), "The input dimension of `info` must match the array to be sliced.", ); let out_ndim = info.out_ndim(); let mut new_dim = I::OutDim::zeros(out_ndim); let mut new_strides = I::OutDim::zeros(out_ndim); let mut old_axis = 0; let mut new_axis = 0; info.as_ref().iter().for_each(|&ax_info| match ax_info { SliceInfoElem::Slice { start, end, step } => { // Slice the axis in-place to update the `dim`, `strides`, and `ptr`. self.slice_axis_inplace(Axis(old_axis), Slice { start, end, step }); // Copy the sliced dim and stride to corresponding axis. new_dim[new_axis] = self.dim[old_axis]; new_strides[new_axis] = self.strides[old_axis]; old_axis += 1; new_axis += 1; } SliceInfoElem::Index(index) => { // Collapse the axis in-place to update the `ptr`. let i_usize = abs_index(self.len_of(Axis(old_axis)), index); self.collapse_axis(Axis(old_axis), i_usize); // Skip copying the axis since it should be removed. Note that // removing this axis is safe because `.collapse_axis()` panics // if the index is out-of-bounds, so it will panic if the axis // is zero length. old_axis += 1; } SliceInfoElem::NewAxis => { // Set the dim and stride of the new axis. new_dim[new_axis] = 1; new_strides[new_axis] = 0; new_axis += 1; } }); debug_assert_eq!(old_axis, self.ndim()); debug_assert_eq!(new_axis, out_ndim); // safe because new dimension, strides allow access to a subset of old data unsafe { self.with_strides_dim(new_strides, new_dim) } } /// Slice the array in place without changing the number of dimensions. /// /// In particular, if an axis is sliced with an index, the axis is /// collapsed, as in [`.collapse_axis()`], rather than removed, as in /// [`.slice_move()`] or [`.index_axis_move()`]. /// /// [`.collapse_axis()`]: Self::collapse_axis /// [`.slice_move()`]: Self::slice_move /// [`.index_axis_move()`]: Self::index_axis_move /// /// See [*Slicing*](#slicing) for full documentation. /// See also [`s!`], [`SliceArg`], and [`SliceInfo`](crate::SliceInfo). /// /// **Panics** in the following cases: /// /// - if an index is out of bounds /// - if a step size is zero /// - if [`SliceInfoElem::NewAxis`] is in `info`, e.g. if [`NewAxis`] was /// used in the [`s!`] macro /// - if `D` is `IxDyn` and `info` does not match the number of array axes #[track_caller] pub fn slice_collapse(&mut self, info: I) where I: SliceArg { assert_eq!( info.in_ndim(), self.ndim(), "The input dimension of `info` must match the array to be sliced.", ); let mut axis = 0; info.as_ref().iter().for_each(|&ax_info| match ax_info { SliceInfoElem::Slice { start, end, step } => { self.slice_axis_inplace(Axis(axis), Slice { start, end, step }); axis += 1; } SliceInfoElem::Index(index) => { let i_usize = abs_index(self.len_of(Axis(axis)), index); self.collapse_axis(Axis(axis), i_usize); axis += 1; } SliceInfoElem::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."), }); debug_assert_eq!(axis, self.ndim()); } /// Return a view of the array, sliced along the specified axis. /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. #[track_caller] #[must_use = "slice_axis returns an array view with the sliced result"] pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView<'_, A, D> where S: Data { let mut view = self.view(); view.slice_axis_inplace(axis, indices); view } /// Return a mutable view of the array, sliced along the specified axis. /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. #[track_caller] #[must_use = "slice_axis_mut returns an array view with the sliced result"] pub fn slice_axis_mut(&mut self, axis: Axis, indices: Slice) -> ArrayViewMut<'_, A, D> where S: DataMut { let mut view_mut = self.view_mut(); view_mut.slice_axis_inplace(axis, indices); view_mut } /// Slice the array in place along the specified axis. /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. #[track_caller] pub fn slice_axis_inplace(&mut self, axis: Axis, indices: Slice) { let offset = do_slice(&mut self.dim.slice_mut()[axis.index()], &mut self.strides.slice_mut()[axis.index()], indices); unsafe { self.ptr = self.ptr.offset(offset); } debug_assert!(self.pointer_is_inbounds()); } /// Slice the array in place along the specified axis, then return the sliced array. /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. #[must_use = "slice_axis_move returns an array with the sliced result"] pub fn slice_axis_move(mut self, axis: Axis, indices: Slice) -> Self { self.slice_axis_inplace(axis, indices); self } /// Return a view of a slice of the array, with a closure specifying the /// slice for each axis. /// /// This is especially useful for code which is generic over the /// dimensionality of the array. /// /// **Panics** if an index is out of bounds or step size is zero. #[track_caller] pub fn slice_each_axis(&self, f: F) -> ArrayView<'_, A, D> where F: FnMut(AxisDescription) -> Slice, S: Data, { let mut view = self.view(); view.slice_each_axis_inplace(f); view } /// Return a mutable view of a slice of the array, with a closure /// specifying the slice for each axis. /// /// This is especially useful for code which is generic over the /// dimensionality of the array. /// /// **Panics** if an index is out of bounds or step size is zero. #[track_caller] pub fn slice_each_axis_mut(&mut self, f: F) -> ArrayViewMut<'_, A, D> where F: FnMut(AxisDescription) -> Slice, S: DataMut, { let mut view = self.view_mut(); view.slice_each_axis_inplace(f); view } /// Slice the array in place, with a closure specifying the slice for each /// axis. /// /// This is especially useful for code which is generic over the /// dimensionality of the array. /// /// **Panics** if an index is out of bounds or step size is zero. #[track_caller] pub fn slice_each_axis_inplace(&mut self, mut f: F) where F: FnMut(AxisDescription) -> Slice { for ax in 0..self.ndim() { self.slice_axis_inplace( Axis(ax), f(AxisDescription { axis: Axis(ax), len: self.dim[ax], stride: self.strides[ax] as isize, }), ) } } /// Return a reference to the element at `index`, or return `None` /// if the index is out of bounds. /// /// Arrays also support indexing syntax: `array[index]`. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[1., 2.], /// [3., 4.]]); /// /// assert!( /// a.get((0, 1)) == Some(&2.) && /// a.get((0, 2)) == None && /// a[(0, 1)] == 2. && /// a[[0, 1]] == 2. /// ); /// ``` pub fn get(&self, index: I) -> Option<&A> where S: Data, I: NdIndex, { unsafe { self.get_ptr(index).map(|ptr| &*ptr) } } /// Return a raw pointer to the element at `index`, or return `None` /// if the index is out of bounds. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[1., 2.], [3., 4.]]); /// /// let v = a.raw_view(); /// let p = a.get_ptr((0, 1)).unwrap(); /// /// assert_eq!(unsafe { *p }, 2.); /// ``` pub fn get_ptr(&self, index: I) -> Option<*const A> where I: NdIndex { let ptr = self.ptr; index .index_checked(&self.dim, &self.strides) .map(move |offset| unsafe { ptr.as_ptr().offset(offset) as *const _ }) } /// Return a mutable reference to the element at `index`, or return `None` /// if the index is out of bounds. pub fn get_mut(&mut self, index: I) -> Option<&mut A> where S: DataMut, I: NdIndex, { unsafe { self.get_mut_ptr(index).map(|ptr| &mut *ptr) } } /// Return a raw pointer to the element at `index`, or return `None` /// if the index is out of bounds. /// /// ``` /// use ndarray::arr2; /// /// let mut a = arr2(&[[1., 2.], [3., 4.]]); /// /// let v = a.raw_view_mut(); /// let p = a.get_mut_ptr((0, 1)).unwrap(); /// /// unsafe { /// *p = 5.; /// } /// /// assert_eq!(a.get((0, 1)), Some(&5.)); /// ``` pub fn get_mut_ptr(&mut self, index: I) -> Option<*mut A> where S: RawDataMut, I: NdIndex, { // const and mut are separate to enforce &mutness as well as the // extra code in as_mut_ptr let ptr = self.as_mut_ptr(); index .index_checked(&self.dim, &self.strides) .map(move |offset| unsafe { ptr.offset(offset) }) } /// Perform *unchecked* array indexing. /// /// Return a reference to the element at `index`. /// /// **Note:** only unchecked for non-debug builds of ndarray. /// /// # Safety /// /// The caller must ensure that the index is in-bounds. #[inline] pub unsafe fn uget(&self, index: I) -> &A where S: Data, I: NdIndex, { arraytraits::debug_bounds_check(self, &index); let off = index.index_unchecked(&self.strides); &*self.ptr.as_ptr().offset(off) } /// Perform *unchecked* array indexing. /// /// Return a mutable reference to the element at `index`. /// /// **Note:** Only unchecked for non-debug builds of ndarray. /// /// # Safety /// /// The caller must ensure that: /// /// 1. the index is in-bounds and /// /// 2. the data is uniquely held by the array. (This property is guaranteed /// for `Array` and `ArrayViewMut`, but not for `ArcArray` or `CowArray`.) #[inline] pub unsafe fn uget_mut(&mut self, index: I) -> &mut A where S: DataMut, I: NdIndex, { debug_assert!(self.data.is_unique()); arraytraits::debug_bounds_check(self, &index); let off = index.index_unchecked(&self.strides); &mut *self.ptr.as_ptr().offset(off) } /// Swap elements at indices `index1` and `index2`. /// /// Indices may be equal. /// /// ***Panics*** if an index is out of bounds. #[track_caller] pub fn swap(&mut self, index1: I, index2: I) where S: DataMut, I: NdIndex, { let ptr = self.as_mut_ptr(); let offset1 = index1.index_checked(&self.dim, &self.strides); let offset2 = index2.index_checked(&self.dim, &self.strides); if let Some(offset1) = offset1 { if let Some(offset2) = offset2 { unsafe { std::ptr::swap(ptr.offset(offset1), ptr.offset(offset2)); } return; } } panic!("swap: index out of bounds for indices {:?} {:?}", index1, index2); } /// Swap elements *unchecked* at indices `index1` and `index2`. /// /// Indices may be equal. /// /// **Note:** only unchecked for non-debug builds of ndarray. /// /// # Safety /// /// The caller must ensure that: /// /// 1. both `index1` and `index2` are in-bounds and /// /// 2. the data is uniquely held by the array. (This property is guaranteed /// for `Array` and `ArrayViewMut`, but not for `ArcArray` or `CowArray`.) pub unsafe fn uswap(&mut self, index1: I, index2: I) where S: DataMut, I: NdIndex, { debug_assert!(self.data.is_unique()); arraytraits::debug_bounds_check(self, &index1); arraytraits::debug_bounds_check(self, &index2); let off1 = index1.index_unchecked(&self.strides); let off2 = index2.index_unchecked(&self.strides); std::ptr::swap(self.ptr.as_ptr().offset(off1), self.ptr.as_ptr().offset(off2)); } // `get` for zero-dimensional arrays // panics if dimension is not zero. otherwise an element is always present. fn get_0d(&self) -> &A where S: Data { assert!(self.ndim() == 0); unsafe { &*self.as_ptr() } } /// Returns a view restricted to `index` along the axis, with the axis /// removed. /// /// See [*Subviews*](#subviews) for full documentation. /// /// **Panics** if `axis` or `index` is out of bounds. /// /// ``` /// use ndarray::{arr2, ArrayView, Axis}; /// /// let a = arr2(&[[1., 2. ], // ... axis 0, row 0 /// [3., 4. ], // --- axis 0, row 1 /// [5., 6. ]]); // ... axis 0, row 2 /// // . \ /// // . axis 1, column 1 /// // axis 1, column 0 /// assert!( /// a.index_axis(Axis(0), 1) == ArrayView::from(&[3., 4.]) && /// a.index_axis(Axis(1), 1) == ArrayView::from(&[2., 4., 6.]) /// ); /// ``` #[track_caller] pub fn index_axis(&self, axis: Axis, index: usize) -> ArrayView<'_, A, D::Smaller> where S: Data, D: RemoveAxis, { self.view().index_axis_move(axis, index) } /// Returns a mutable view restricted to `index` along the axis, with the /// axis removed. /// /// **Panics** if `axis` or `index` is out of bounds. /// /// ``` /// use ndarray::{arr2, aview2, Axis}; /// /// let mut a = arr2(&[[1., 2. ], /// [3., 4. ]]); /// // . \ /// // . axis 1, column 1 /// // axis 1, column 0 /// /// { /// let mut column1 = a.index_axis_mut(Axis(1), 1); /// column1 += 10.; /// } /// /// assert!( /// a == aview2(&[[1., 12.], /// [3., 14.]]) /// ); /// ``` #[track_caller] pub fn index_axis_mut(&mut self, axis: Axis, index: usize) -> ArrayViewMut<'_, A, D::Smaller> where S: DataMut, D: RemoveAxis, { self.view_mut().index_axis_move(axis, index) } /// Collapses the array to `index` along the axis and removes the axis. /// /// See [`.index_axis()`](Self::index_axis) and [*Subviews*](#subviews) for full documentation. /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] pub fn index_axis_move(mut self, axis: Axis, index: usize) -> ArrayBase where D: RemoveAxis { self.collapse_axis(axis, index); let dim = self.dim.remove_axis(axis); let strides = self.strides.remove_axis(axis); // safe because new dimension, strides allow access to a subset of old data unsafe { self.with_strides_dim(strides, dim) } } /// Selects `index` along the axis, collapsing the axis into length one. /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] pub fn collapse_axis(&mut self, axis: Axis, index: usize) { let offset = dimension::do_collapse_axis(&mut self.dim, &self.strides, axis.index(), index); self.ptr = unsafe { self.ptr.offset(offset) }; debug_assert!(self.pointer_is_inbounds()); } /// Along `axis`, select arbitrary subviews corresponding to `indices` /// and and copy them into a new array. /// /// **Panics** if `axis` or an element of `indices` is out of bounds. /// /// ``` /// use ndarray::{arr2, Axis}; /// /// let x = arr2(&[[0., 1.], /// [2., 3.], /// [4., 5.], /// [6., 7.], /// [8., 9.]]); /// /// let r = x.select(Axis(0), &[0, 4, 3]); /// assert!( /// r == arr2(&[[0., 1.], /// [8., 9.], /// [6., 7.]]) ///); /// ``` #[track_caller] pub fn select(&self, axis: Axis, indices: &[Ix]) -> Array where A: Clone, S: Data, D: RemoveAxis, { if self.ndim() == 1 { // using .len_of(axis) means that we check if `axis` is in bounds too. let axis_len = self.len_of(axis); // bounds check the indices first if let Some(max_index) = indices.iter().cloned().max() { if max_index >= axis_len { panic!("ndarray: index {} is out of bounds in array of len {}", max_index, self.len_of(axis)); } } // else: indices empty is ok let view = self.view().into_dimensionality::().unwrap(); Array::from_iter(indices.iter().map(move |&index| { // Safety: bounds checked indexes unsafe { view.uget(index).clone() } })) .into_dimensionality::() .unwrap() } else { let mut subs = vec![self.view(); indices.len()]; for (&i, sub) in zip(indices, &mut subs[..]) { sub.collapse_axis(axis, i); } if subs.is_empty() { let mut dim = self.raw_dim(); dim.set_axis(axis, 0); unsafe { Array::from_shape_vec_unchecked(dim, vec![]) } } else { concatenate(axis, &subs).unwrap() } } } /// Return a producer and iterable that traverses over the *generalized* /// rows of the array. For a 2D array these are the regular rows. /// /// This is equivalent to `.lanes(Axis(n - 1))` where *n* is `self.ndim()`. /// /// For an array of dimensions *a* × *b* × *c* × ... × *l* × *m* /// it has *a* × *b* × *c* × ... × *l* rows each of length *m*. /// /// For example, in a 2 × 2 × 3 array, each row is 3 elements long /// and there are 2 × 2 = 4 rows in total. /// /// Iterator element is `ArrayView1
` (1D array view). /// /// ``` /// use ndarray::arr3; /// /// let a = arr3(&[[[ 0, 1, 2], // -- row 0, 0 /// [ 3, 4, 5]], // -- row 0, 1 /// [[ 6, 7, 8], // -- row 1, 0 /// [ 9, 10, 11]]]); // -- row 1, 1 /// /// // `rows` will yield the four generalized rows of the array. /// for row in a.rows() { /// /* loop body */ /// } /// ``` pub fn rows(&self) -> Lanes<'_, A, D::Smaller> where S: Data { let mut n = self.ndim(); if n == 0 { n += 1; } Lanes::new(self.view(), Axis(n - 1)) } /// Return a producer and iterable that traverses over the *generalized* /// rows of the array and yields mutable array views. /// /// Iterator element is `ArrayView1` (1D read-write array view). pub fn rows_mut(&mut self) -> LanesMut<'_, A, D::Smaller> where S: DataMut { let mut n = self.ndim(); if n == 0 { n += 1; } LanesMut::new(self.view_mut(), Axis(n - 1)) } /// Return a producer and iterable that traverses over the *generalized* /// columns of the array. For a 2D array these are the regular columns. /// /// This is equivalent to `.lanes(Axis(0))`. /// /// For an array of dimensions *a* × *b* × *c* × ... × *l* × *m* /// it has *b* × *c* × ... × *l* × *m* columns each of length *a*. /// /// For example, in a 2 × 2 × 3 array, each column is 2 elements long /// and there are 2 × 3 = 6 columns in total. /// /// Iterator element is `ArrayView1` (1D array view). /// /// ``` /// use ndarray::arr3; /// /// // The generalized columns of a 3D array: /// // are directed along the 0th axis: 0 and 6, 1 and 7 and so on... /// let a = arr3(&[[[ 0, 1, 2], [ 3, 4, 5]], /// [[ 6, 7, 8], [ 9, 10, 11]]]); /// /// // Here `columns` will yield the six generalized columns of the array. /// for column in a.columns() { /// /* loop body */ /// } /// ``` pub fn columns(&self) -> Lanes<'_, A, D::Smaller> where S: Data { Lanes::new(self.view(), Axis(0)) } /// Return a producer and iterable that traverses over the *generalized* /// columns of the array and yields mutable array views. /// /// Iterator element is `ArrayView1` (1D read-write array view). pub fn columns_mut(&mut self) -> LanesMut<'_, A, D::Smaller> where S: DataMut { LanesMut::new(self.view_mut(), Axis(0)) } /// Return a producer and iterable that traverses over all 1D lanes /// pointing in the direction of `axis`. /// /// When pointing in the direction of the first axis, they are *columns*, /// in the direction of the last axis *rows*; in general they are all /// *lanes* and are one dimensional. /// /// Iterator element is `ArrayView1` (1D array view). /// /// ``` /// use ndarray::{arr3, aview1, Axis}; /// /// let a = arr3(&[[[ 0, 1, 2], /// [ 3, 4, 5]], /// [[ 6, 7, 8], /// [ 9, 10, 11]]]); /// /// let inner0 = a.lanes(Axis(0)); /// let inner1 = a.lanes(Axis(1)); /// let inner2 = a.lanes(Axis(2)); /// /// // The first lane for axis 0 is [0, 6] /// assert_eq!(inner0.into_iter().next().unwrap(), aview1(&[0, 6])); /// // The first lane for axis 1 is [0, 3] /// assert_eq!(inner1.into_iter().next().unwrap(), aview1(&[0, 3])); /// // The first lane for axis 2 is [0, 1, 2] /// assert_eq!(inner2.into_iter().next().unwrap(), aview1(&[0, 1, 2])); /// ``` pub fn lanes(&self, axis: Axis) -> Lanes<'_, A, D::Smaller> where S: Data { Lanes::new(self.view(), axis) } /// Return a producer and iterable that traverses over all 1D lanes /// pointing in the direction of `axis`. /// /// Iterator element is `ArrayViewMut1` (1D read-write array view). pub fn lanes_mut(&mut self, axis: Axis) -> LanesMut<'_, A, D::Smaller> where S: DataMut { LanesMut::new(self.view_mut(), axis) } /// Return an iterator that traverses over the outermost dimension /// and yields each subview. /// /// This is equivalent to `.axis_iter(Axis(0))`. /// /// Iterator element is `ArrayView` (read-only array view). #[allow(deprecated)] pub fn outer_iter(&self) -> AxisIter<'_, A, D::Smaller> where S: Data, D: RemoveAxis, { self.view().into_outer_iter() } /// Return an iterator that traverses over the outermost dimension /// and yields each subview. /// /// This is equivalent to `.axis_iter_mut(Axis(0))`. /// /// Iterator element is `ArrayViewMut` (read-write array view). #[allow(deprecated)] pub fn outer_iter_mut(&mut self) -> AxisIterMut<'_, A, D::Smaller> where S: DataMut, D: RemoveAxis, { self.view_mut().into_outer_iter() } /// Return an iterator that traverses over `axis` /// and yields each subview along it. /// /// For example, in a 3 × 4 × 5 array, with `axis` equal to `Axis(2)`, /// the iterator element /// is a 3 × 4 subview (and there are 5 in total), as shown /// in the picture below. /// /// Iterator element is `ArrayView` (read-only array view). /// /// See [*Subviews*](#subviews) for full documentation. /// /// **Panics** if `axis` is out of bounds. /// /// #[track_caller] pub fn axis_iter(&self, axis: Axis) -> AxisIter<'_, A, D::Smaller> where S: Data, D: RemoveAxis, { AxisIter::new(self.view(), axis) } /// Return an iterator that traverses over `axis` /// and yields each mutable subview along it. /// /// Iterator element is `ArrayViewMut` /// (read-write array view). /// /// **Panics** if `axis` is out of bounds. #[track_caller] pub fn axis_iter_mut(&mut self, axis: Axis) -> AxisIterMut<'_, A, D::Smaller> where S: DataMut, D: RemoveAxis, { AxisIterMut::new(self.view_mut(), axis) } /// Return an iterator that traverses over `axis` by chunks of `size`, /// yielding non-overlapping views along that axis. /// /// Iterator element is `ArrayView` /// /// The last view may have less elements if `size` does not divide /// the axis' dimension. /// /// **Panics** if `axis` is out of bounds or if `size` is zero. /// /// ``` /// use ndarray::Array; /// use ndarray::{arr3, Axis}; /// /// let a = Array::from_iter(0..28).into_shape_with_order((2, 7, 2)).unwrap(); /// let mut iter = a.axis_chunks_iter(Axis(1), 2); /// /// // first iteration yields a 2 × 2 × 2 view /// assert_eq!(iter.next().unwrap(), /// arr3(&[[[ 0, 1], [ 2, 3]], /// [[14, 15], [16, 17]]])); /// /// // however the last element is a 2 × 1 × 2 view since 7 % 2 == 1 /// assert_eq!(iter.next_back().unwrap(), arr3(&[[[12, 13]], /// [[26, 27]]])); /// ``` #[track_caller] pub fn axis_chunks_iter(&self, axis: Axis, size: usize) -> AxisChunksIter<'_, A, D> where S: Data { AxisChunksIter::new(self.view(), axis, size) } /// Return an iterator that traverses over `axis` by chunks of `size`, /// yielding non-overlapping read-write views along that axis. /// /// Iterator element is `ArrayViewMut` /// /// **Panics** if `axis` is out of bounds or if `size` is zero. #[track_caller] pub fn axis_chunks_iter_mut(&mut self, axis: Axis, size: usize) -> AxisChunksIterMut<'_, A, D> where S: DataMut { AxisChunksIterMut::new(self.view_mut(), axis, size) } /// Return an exact chunks producer (and iterable). /// /// It produces the whole chunks of a given n-dimensional chunk size, /// skipping the remainder along each dimension that doesn't fit evenly. /// /// The produced element is a `ArrayView` with exactly the dimension /// `chunk_size`. /// /// **Panics** if any dimension of `chunk_size` is zero
/// (**Panics** if `D` is `IxDyn` and `chunk_size` does not match the /// number of array axes.) #[track_caller] pub fn exact_chunks(&self, chunk_size: E) -> ExactChunks<'_, A, D> where E: IntoDimension, S: Data, { ExactChunks::new(self.view(), chunk_size) } /// Return an exact chunks producer (and iterable). /// /// It produces the whole chunks of a given n-dimensional chunk size, /// skipping the remainder along each dimension that doesn't fit evenly. /// /// The produced element is a `ArrayViewMut` with exactly /// the dimension `chunk_size`. /// /// **Panics** if any dimension of `chunk_size` is zero
/// (**Panics** if `D` is `IxDyn` and `chunk_size` does not match the /// number of array axes.) /// /// ```rust /// use ndarray::Array; /// use ndarray::arr2; /// let mut a = Array::zeros((6, 7)); /// /// // Fill each 2 × 2 chunk with the index of where it appeared in iteration /// for (i, mut chunk) in a.exact_chunks_mut((2, 2)).into_iter().enumerate() { /// chunk.fill(i); /// } /// /// // The resulting array is: /// assert_eq!( /// a, /// arr2(&[[0, 0, 1, 1, 2, 2, 0], /// [0, 0, 1, 1, 2, 2, 0], /// [3, 3, 4, 4, 5, 5, 0], /// [3, 3, 4, 4, 5, 5, 0], /// [6, 6, 7, 7, 8, 8, 0], /// [6, 6, 7, 7, 8, 8, 0]])); /// ``` #[track_caller] pub fn exact_chunks_mut(&mut self, chunk_size: E) -> ExactChunksMut<'_, A, D> where E: IntoDimension, S: DataMut, { ExactChunksMut::new(self.view_mut(), chunk_size) } /// Return a window producer and iterable. /// /// The windows are all distinct overlapping views of size `window_size` /// that fit into the array's shape. /// /// This is essentially equivalent to [`.windows_with_stride()`] with unit stride. #[track_caller] pub fn windows(&self, window_size: E) -> Windows<'_, A, D> where E: IntoDimension, S: Data, { Windows::new(self.view(), window_size) } /// Return a window producer and iterable. /// /// The windows are all distinct views of size `window_size` /// that fit into the array's shape. /// /// The stride is ordered by the outermost axis.
/// Hence, a (x₀, x₁, ..., xₙ) stride will be applied to /// (A₀, A₁, ..., Aₙ) where Aₓ stands for `Axis(x)`. /// /// This produces all windows that fit within the array for the given stride, /// assuming the window size is not larger than the array size. /// /// The produced element is an `ArrayView` with exactly the dimension /// `window_size`. /// /// Note that passing a stride of only ones is similar to /// calling [`ArrayBase::windows()`]. /// /// **Panics** if any dimension of `window_size` or `stride` is zero.
/// (**Panics** if `D` is `IxDyn` and `window_size` or `stride` does not match the /// number of array axes.) /// /// This is the same illustration found in [`ArrayBase::windows()`], /// 2×2 windows in a 3×4 array, but now with a (1, 2) stride: /// /// ```text /// ──▶ Axis(1) /// /// │ ┏━━━━━┳━━━━━┱─────┬─────┐ ┌─────┬─────┲━━━━━┳━━━━━┓ /// ▼ ┃ a₀₀ ┃ a₀₁ ┃ │ │ │ │ ┃ a₀₂ ┃ a₀₃ ┃ /// Axis(0) ┣━━━━━╋━━━━━╉─────┼─────┤ ├─────┼─────╊━━━━━╋━━━━━┫ /// ┃ a₁₀ ┃ a₁₁ ┃ │ │ │ │ ┃ a₁₂ ┃ a₁₃ ┃ /// ┡━━━━━╇━━━━━╃─────┼─────┤ ├─────┼─────╄━━━━━╇━━━━━┩ /// │ │ │ │ │ │ │ │ │ │ /// └─────┴─────┴─────┴─────┘ └─────┴─────┴─────┴─────┘ /// /// ┌─────┬─────┬─────┬─────┐ ┌─────┬─────┬─────┬─────┐ /// │ │ │ │ │ │ │ │ │ │ /// ┢━━━━━╈━━━━━╅─────┼─────┤ ├─────┼─────╆━━━━━╈━━━━━┪ /// ┃ a₁₀ ┃ a₁₁ ┃ │ │ │ │ ┃ a₁₂ ┃ a₁₃ ┃ /// ┣━━━━━╋━━━━━╉─────┼─────┤ ├─────┼─────╊━━━━━╋━━━━━┫ /// ┃ a₂₀ ┃ a₂₁ ┃ │ │ │ │ ┃ a₂₂ ┃ a₂₃ ┃ /// ┗━━━━━┻━━━━━┹─────┴─────┘ └─────┴─────┺━━━━━┻━━━━━┛ /// ``` #[track_caller] pub fn windows_with_stride(&self, window_size: E, stride: E) -> Windows<'_, A, D> where E: IntoDimension, S: Data, { Windows::new_with_stride(self.view(), window_size, stride) } /// Returns a producer which traverses over all windows of a given length along an axis. /// /// The windows are all distinct, possibly-overlapping views. The shape of each window /// is the shape of `self`, with the length of `axis` replaced with `window_size`. /// /// **Panics** if `axis` is out-of-bounds or if `window_size` is zero. /// /// ``` /// use ndarray::{Array3, Axis, s}; /// /// let arr = Array3::from_shape_fn([4, 5, 2], |(i, j, k)| i * 100 + j * 10 + k); /// let correct = vec![ /// arr.slice(s![.., 0..3, ..]), /// arr.slice(s![.., 1..4, ..]), /// arr.slice(s![.., 2..5, ..]), /// ]; /// for (window, correct) in arr.axis_windows(Axis(1), 3).into_iter().zip(&correct) { /// assert_eq!(window, correct); /// assert_eq!(window.shape(), &[4, 3, 2]); /// } /// ``` pub fn axis_windows(&self, axis: Axis, window_size: usize) -> AxisWindows<'_, A, D> where S: Data { let axis_index = axis.index(); ndassert!( axis_index < self.ndim(), concat!( "Window axis {} does not match array dimension {} ", "(with array of shape {:?})" ), axis_index, self.ndim(), self.shape() ); AxisWindows::new(self.view(), axis, window_size) } // Return (length, stride) for diagonal fn diag_params(&self) -> (Ix, Ixs) { /* empty shape has len 1 */ let len = self.dim.slice().iter().cloned().min().unwrap_or(1); let stride = self.strides().iter().sum(); (len, stride) } /// Return a view of the diagonal elements of the array. /// /// The diagonal is simply the sequence indexed by *(0, 0, .., 0)*, /// *(1, 1, ..., 1)* etc as long as all axes have elements. pub fn diag(&self) -> ArrayView1<'_, A> where S: Data { self.view().into_diag() } /// Return a read-write view over the diagonal elements of the array. pub fn diag_mut(&mut self) -> ArrayViewMut1<'_, A> where S: DataMut { self.view_mut().into_diag() } /// Return the diagonal as a one-dimensional array. pub fn into_diag(self) -> ArrayBase { let (len, stride) = self.diag_params(); // safe because new len stride allows access to a subset of the current elements unsafe { self.with_strides_dim(Ix1(stride as Ix), Ix1(len)) } } /// Try to make the array unshared. /// /// This is equivalent to `.ensure_unique()` if `S: DataMut`. /// /// This method is mostly only useful with unsafe code. fn try_ensure_unique(&mut self) where S: RawDataMut { debug_assert!(self.pointer_is_inbounds()); S::try_ensure_unique(self); debug_assert!(self.pointer_is_inbounds()); } /// Make the array unshared. /// /// This method is mostly only useful with unsafe code. fn ensure_unique(&mut self) where S: DataMut { debug_assert!(self.pointer_is_inbounds()); S::ensure_unique(self); debug_assert!(self.pointer_is_inbounds()); } /// Return `true` if the array data is laid out in contiguous “C order” in /// memory (where the last index is the most rapidly varying). /// /// Return `false` otherwise, i.e. the array is possibly not /// contiguous in memory, it has custom strides, etc. pub fn is_standard_layout(&self) -> bool { dimension::is_layout_c(&self.dim, &self.strides) } /// Return true if the array is known to be contiguous. pub(crate) fn is_contiguous(&self) -> bool { D::is_contiguous(&self.dim, &self.strides) } /// Return a standard-layout array containing the data, cloning if /// necessary. /// /// If `self` is in standard layout, a COW view of the data is returned /// without cloning. Otherwise, the data is cloned, and the returned array /// owns the cloned data. /// /// ``` /// use ndarray::Array2; /// /// let standard = Array2::::zeros((3, 4)); /// assert!(standard.is_standard_layout()); /// let cow_view = standard.as_standard_layout(); /// assert!(cow_view.is_view()); /// assert!(cow_view.is_standard_layout()); /// /// let fortran = standard.reversed_axes(); /// assert!(!fortran.is_standard_layout()); /// let cow_owned = fortran.as_standard_layout(); /// assert!(cow_owned.is_owned()); /// assert!(cow_owned.is_standard_layout()); /// ``` pub fn as_standard_layout(&self) -> CowArray<'_, A, D> where S: Data, A: Clone, { if self.is_standard_layout() { CowArray::from(self.view()) } else { let v = crate::iterators::to_vec_mapped(self.iter(), A::clone); let dim = self.dim.clone(); debug_assert_eq!(v.len(), dim.size()); unsafe { // Safe because the shape and element type are from the existing array // and the strides are the default strides. CowArray::from(Array::from_shape_vec_unchecked(dim, v)) } } } /// Return a pointer to the first element in the array. /// /// Raw access to array elements needs to follow the strided indexing /// scheme: an element at multi-index *I* in an array with strides *S* is /// located at offset /// /// *Σ0 ≤ k < d Ik × Sk* /// /// where *d* is `self.ndim()`. #[inline(always)] pub fn as_ptr(&self) -> *const A { self.ptr.as_ptr() as *const A } /// Return a mutable pointer to the first element in the array. /// /// This method attempts to unshare the data. If `S: DataMut`, then the /// data is guaranteed to be uniquely held on return. /// /// # Warning /// /// When accessing elements through this pointer, make sure to use strides /// obtained *after* calling this method, since the process of unsharing /// the data may change the strides. #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut A where S: RawDataMut { self.try_ensure_unique(); // for ArcArray self.ptr.as_ptr() } /// Return a raw view of the array. #[inline] pub fn raw_view(&self) -> RawArrayView { unsafe { RawArrayView::new(self.ptr, self.dim.clone(), self.strides.clone()) } } /// Return a raw mutable view of the array. /// /// This method attempts to unshare the data. If `S: DataMut`, then the /// data is guaranteed to be uniquely held on return. #[inline] pub fn raw_view_mut(&mut self) -> RawArrayViewMut where S: RawDataMut { self.try_ensure_unique(); // for ArcArray unsafe { RawArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } } /// Return a raw mutable view of the array. /// /// Safety: The caller must ensure that the owned array is unshared when this is called #[inline] pub(crate) unsafe fn raw_view_mut_unchecked(&mut self) -> RawArrayViewMut where S: DataOwned { RawArrayViewMut::new(self.ptr, self.dim.clone(), self.strides.clone()) } /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// /// If this function returns `Some(_)`, then the element order in the slice /// corresponds to the logical order of the array’s elements. pub fn as_slice(&self) -> Option<&[A]> where S: Data { if self.is_standard_layout() { unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) } } else { None } } /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. pub fn as_slice_mut(&mut self) -> Option<&mut [A]> where S: DataMut { if self.is_standard_layout() { self.ensure_unique(); unsafe { Some(slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len())) } } else { None } } /// Return the array’s data as a slice if it is contiguous, /// return `None` otherwise. /// /// If this function returns `Some(_)`, then the elements in the slice /// have whatever order the elements have in memory. pub fn as_slice_memory_order(&self) -> Option<&[A]> where S: Data { if self.is_contiguous() { let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); unsafe { Some(slice::from_raw_parts(self.ptr.sub(offset).as_ptr(), self.len())) } } else { None } } /// Return the array’s data as a slice if it is contiguous, /// return `None` otherwise. /// /// In the contiguous case, in order to return a unique reference, this /// method unshares the data if necessary, but it preserves the existing /// strides. pub fn as_slice_memory_order_mut(&mut self) -> Option<&mut [A]> where S: DataMut { self.try_as_slice_memory_order_mut().ok() } /// Return the array’s data as a slice if it is contiguous, otherwise /// return `self` in the `Err` variant. pub(crate) fn try_as_slice_memory_order_mut(&mut self) -> Result<&mut [A], &mut Self> where S: DataMut { if self.is_contiguous() { self.ensure_unique(); let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); unsafe { Ok(slice::from_raw_parts_mut(self.ptr.sub(offset).as_ptr(), self.len())) } } else { Err(self) } } /// Transform the array into `new_shape`; any shape with the same number of elements is /// accepted. /// /// `order` specifies the *logical* order in which the array is to be read and reshaped. /// The array is returned as a `CowArray`; a view if possible, otherwise an owned array. /// /// For example, when starting from the one-dimensional sequence 1 2 3 4 5 6, it would be /// understood as a 2 x 3 array in row major ("C") order this way: /// /// ```text /// 1 2 3 /// 4 5 6 /// ``` /// /// and as 2 x 3 in column major ("F") order this way: /// /// ```text /// 1 3 5 /// 2 4 6 /// ``` /// /// This example should show that any time we "reflow" the elements in the array to a different /// number of rows and columns (or more axes if applicable), it is important to pick an index /// ordering, and that's the reason for the function parameter for `order`. /// /// The `new_shape` parameter should be a dimension and an optional order like these examples: /// /// ```text /// (3, 4) // Shape 3 x 4 with default order (RowMajor) /// ((3, 4), Order::RowMajor)) // use specific order /// ((3, 4), Order::ColumnMajor)) // use specific order /// ((3, 4), Order::C)) // use shorthand for order - shorthands C and F /// ``` /// /// **Errors** if the new shape doesn't have the same number of elements as the array's current /// shape. /// /// # Example /// /// ``` /// use ndarray::array; /// use ndarray::Order; /// /// assert!( /// array![1., 2., 3., 4., 5., 6.].to_shape(((2, 3), Order::RowMajor)).unwrap() /// == array![[1., 2., 3.], /// [4., 5., 6.]] /// ); /// /// assert!( /// array![1., 2., 3., 4., 5., 6.].to_shape(((2, 3), Order::ColumnMajor)).unwrap() /// == array![[1., 3., 5.], /// [2., 4., 6.]] /// ); /// ``` pub fn to_shape(&self, new_shape: E) -> Result, ShapeError> where E: ShapeArg, A: Clone, S: Data, { let (shape, order) = new_shape.into_shape_and_order(); self.to_shape_order(shape, order.unwrap_or(Order::RowMajor)) } fn to_shape_order(&self, shape: E, order: Order) -> Result, ShapeError> where E: Dimension, A: Clone, S: Data, { let len = self.dim.size(); if size_of_shape_checked(&shape) != Ok(len) { return Err(error::incompatible_shapes(&self.dim, &shape)); } // Create a view if the length is 0, safe because the array and new shape is empty. if len == 0 { unsafe { return Ok(CowArray::from(ArrayView::from_shape_ptr(shape, self.as_ptr()))); } } // Try to reshape the array as a view into the existing data match reshape_dim(&self.dim, &self.strides, &shape, order) { Ok(to_strides) => unsafe { return Ok(CowArray::from(ArrayView::new(self.ptr, shape, to_strides))); }, Err(err) if err.kind() == ErrorKind::IncompatibleShape => { return Err(error::incompatible_shapes(&self.dim, &shape)); } _otherwise => {} } // otherwise create a new array and copy the elements unsafe { let (shape, view) = match order { Order::RowMajor => (shape.set_f(false), self.view()), Order::ColumnMajor => (shape.set_f(true), self.t()), }; Ok(CowArray::from(Array::from_shape_trusted_iter_unchecked(shape, view.into_iter(), A::clone))) } } /// Transform the array into `shape`; any shape with the same number of /// elements is accepted, but the source array must be contiguous. /// /// If an index ordering is not specified, the default is `RowMajor`. /// The operation will only succeed if the array's memory layout is compatible with /// the index ordering, so that the array elements can be rearranged in place. /// /// If required use `.to_shape()` or `.into_shape_clone` instead for more flexible reshaping of /// arrays, which allows copying elements if required. /// /// **Errors** if the shapes don't have the same number of elements.
/// **Errors** if order RowMajor is given but input is not c-contiguous. /// **Errors** if order ColumnMajor is given but input is not f-contiguous. /// /// If shape is not given: use memory layout of incoming array. Row major arrays are /// reshaped using row major index ordering, column major arrays with column major index /// ordering. /// /// The `new_shape` parameter should be a dimension and an optional order like these examples: /// /// ```text /// (3, 4) // Shape 3 x 4 with default order (RowMajor) /// ((3, 4), Order::RowMajor)) // use specific order /// ((3, 4), Order::ColumnMajor)) // use specific order /// ((3, 4), Order::C)) // use shorthand for order - shorthands C and F /// ``` /// /// # Example /// /// ``` /// use ndarray::{aview1, aview2}; /// use ndarray::Order; /// /// assert!( /// aview1(&[1., 2., 3., 4.]).into_shape_with_order((2, 2)).unwrap() /// == aview2(&[[1., 2.], /// [3., 4.]]) /// ); /// /// assert!( /// aview1(&[1., 2., 3., 4.]).into_shape_with_order(((2, 2), Order::ColumnMajor)).unwrap() /// == aview2(&[[1., 3.], /// [2., 4.]]) /// ); /// ``` pub fn into_shape_with_order(self, shape: E) -> Result, ShapeError> where E: ShapeArg { let (shape, order) = shape.into_shape_and_order(); self.into_shape_with_order_impl(shape, order.unwrap_or(Order::RowMajor)) } fn into_shape_with_order_impl(self, shape: E, order: Order) -> Result, ShapeError> where E: Dimension { let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { return Err(error::incompatible_shapes(&self.dim, &shape)); } // Check if contiguous, then we can change shape unsafe { // safe because arrays are contiguous and len is unchanged match order { Order::RowMajor if self.is_standard_layout() => Ok(self.with_strides_dim(shape.default_strides(), shape)), Order::ColumnMajor if self.raw_view().reversed_axes().is_standard_layout() => Ok(self.with_strides_dim(shape.fortran_strides(), shape)), _otherwise => Err(error::from_kind(error::ErrorKind::IncompatibleLayout)), } } } /// Transform the array into `shape`; any shape with the same number of /// elements is accepted, but the source array or view must be in standard /// or column-major (Fortran) layout. /// /// **Note** that `.into_shape()` "moves" elements differently depending on if the input array /// is C-contig or F-contig, it follows the index order that corresponds to the memory order. /// Prefer to use `.to_shape()` or `.into_shape_with_order()`. /// /// Because of this, the method **is deprecated**. That reshapes depend on memory order is not /// intuitive. /// /// **Errors** if the shapes don't have the same number of elements.
/// **Errors** if the input array is not c- or f-contiguous. /// /// ``` /// use ndarray::{aview1, aview2}; /// /// assert!( /// aview1(&[1., 2., 3., 4.]).into_shape((2, 2)).unwrap() /// == aview2(&[[1., 2.], /// [3., 4.]]) /// ); /// ``` #[deprecated(note = "Use `.into_shape_with_order()` or `.to_shape()`", since = "0.16.0")] pub fn into_shape(self, shape: E) -> Result, ShapeError> where E: IntoDimension { let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { return Err(error::incompatible_shapes(&self.dim, &shape)); } // Check if contiguous, if not => copy all, else just adapt strides unsafe { // safe because arrays are contiguous and len is unchanged if self.is_standard_layout() { Ok(self.with_strides_dim(shape.default_strides(), shape)) } else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() { Ok(self.with_strides_dim(shape.fortran_strides(), shape)) } else { Err(error::from_kind(error::ErrorKind::IncompatibleLayout)) } } } /// Transform the array into `shape`; any shape with the same number of /// elements is accepted. Array elements are reordered in place if /// possible, otherwise they are copied to create a new array. /// /// If an index ordering is not specified, the default is `RowMajor`. /// /// # `.to_shape` vs `.into_shape_clone` /// /// - `to_shape` supports views and outputting views /// - `to_shape` borrows the original array, `into_shape_clone` consumes the original /// - `into_shape_clone` preserves array type (Array vs ArcArray), but does not support views. /// /// **Errors** if the shapes don't have the same number of elements.
pub fn into_shape_clone(self, shape: E) -> Result, ShapeError> where S: DataOwned, A: Clone, E: ShapeArg, { let (shape, order) = shape.into_shape_and_order(); let order = order.unwrap_or(Order::RowMajor); self.into_shape_clone_order(shape, order) } fn into_shape_clone_order(self, shape: E, order: Order) -> Result, ShapeError> where S: DataOwned, A: Clone, E: Dimension, { let len = self.dim.size(); if size_of_shape_checked(&shape) != Ok(len) { return Err(error::incompatible_shapes(&self.dim, &shape)); } // Safe because the array and new shape is empty. if len == 0 { unsafe { return Ok(self.with_strides_dim(shape.default_strides(), shape)); } } // Try to reshape the array's current data match reshape_dim(&self.dim, &self.strides, &shape, order) { Ok(to_strides) => unsafe { return Ok(self.with_strides_dim(to_strides, shape)); }, Err(err) if err.kind() == ErrorKind::IncompatibleShape => { return Err(error::incompatible_shapes(&self.dim, &shape)); } _otherwise => {} } // otherwise, clone and allocate a new array unsafe { let (shape, view) = match order { Order::RowMajor => (shape.set_f(false), self.view()), Order::ColumnMajor => (shape.set_f(true), self.t()), }; Ok(ArrayBase::from_shape_trusted_iter_unchecked(shape, view.into_iter(), A::clone)) } } /// *Note: Reshape is for `ArcArray` only. Use `.into_shape_with_order()` for /// other arrays and array views.* /// /// Transform the array into `shape`; any shape with the same number of /// elements is accepted. /// /// May clone all elements if needed to arrange elements in standard /// layout (and break sharing). /// /// **Panics** if shapes are incompatible. /// /// *This method is obsolete, because it is inflexible in how logical order /// of the array is handled. See [`.to_shape()`].* /// /// ``` /// use ndarray::{rcarr1, rcarr2}; /// /// assert!( /// rcarr1(&[1., 2., 3., 4.]).reshape((2, 2)) /// == rcarr2(&[[1., 2.], /// [3., 4.]]) /// ); /// ``` #[track_caller] #[deprecated(note = "Use `.into_shape_with_order()` or `.to_shape()`", since = "0.16.0")] pub fn reshape(&self, shape: E) -> ArrayBase where S: DataShared + DataOwned, A: Clone, E: IntoDimension, { let shape = shape.into_dimension(); if size_of_shape_checked(&shape) != Ok(self.dim.size()) { panic!( "ndarray: incompatible shapes in reshape, attempted from: {:?}, to: {:?}", self.dim.slice(), shape.slice() ) } // Check if contiguous, if not => copy all, else just adapt strides if self.is_standard_layout() { let cl = self.clone(); // safe because array is contiguous and shape has equal number of elements unsafe { cl.with_strides_dim(shape.default_strides(), shape) } } else { let v = self.iter().cloned().collect::>(); unsafe { ArrayBase::from_shape_vec_unchecked(shape, v) } } } /// Flatten the array to a one-dimensional array. /// /// The array is returned as a `CowArray`; a view if possible, otherwise an owned array. /// /// ``` /// use ndarray::{arr1, arr3}; /// /// let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); /// let flattened = array.flatten(); /// assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); /// ``` pub fn flatten(&self) -> CowArray<'_, A, Ix1> where A: Clone, S: Data, { self.flatten_with_order(Order::RowMajor) } /// Flatten the array to a one-dimensional array. /// /// `order` specifies the *logical* order in which the array is to be read and reshaped. /// The array is returned as a `CowArray`; a view if possible, otherwise an owned array. /// /// ``` /// use ndarray::{arr1, arr2}; /// use ndarray::Order; /// /// let array = arr2(&[[1, 2], [3, 4], [5, 6], [7, 8]]); /// let flattened = array.flatten_with_order(Order::RowMajor); /// assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); /// let flattened = array.flatten_with_order(Order::ColumnMajor); /// assert_eq!(flattened, arr1(&[1, 3, 5, 7, 2, 4, 6, 8])); /// ``` pub fn flatten_with_order(&self, order: Order) -> CowArray<'_, A, Ix1> where A: Clone, S: Data, { self.to_shape((self.len(), order)).unwrap() } /// Flatten the array to a one-dimensional array, consuming the array. /// /// If possible, no copy is made, and the new array use the same memory as the original array. /// Otherwise, a new array is allocated and the elements are copied. /// /// ``` /// use ndarray::{arr1, arr3}; /// /// let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); /// let flattened = array.into_flat(); /// assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); /// ``` pub fn into_flat(self) -> ArrayBase where A: Clone, S: DataOwned, { let len = self.len(); self.into_shape_clone(Ix1(len)).unwrap() } /// Convert any array or array view to a dynamic dimensional array or /// array view (respectively). /// /// ``` /// use ndarray::{arr2, ArrayD}; /// /// let array: ArrayD = arr2(&[[1, 2], /// [3, 4]]).into_dyn(); /// ``` pub fn into_dyn(self) -> ArrayBase { // safe because new dims equivalent unsafe { ArrayBase::from_data_ptr(self.data, self.ptr).with_strides_dim(self.strides.into_dyn(), self.dim.into_dyn()) } } /// Convert an array or array view to another with the same type, but different dimensionality /// type. Errors if the dimensions don't agree (the number of axes must match). /// /// Note that conversion to a dynamic dimensional array will never fail (and is equivalent to /// the `into_dyn` method). /// /// ``` /// use ndarray::{ArrayD, Ix2, IxDyn}; /// /// // Create a dynamic dimensionality array and convert it to an Array2 /// // (Ix2 dimension type). /// /// let array = ArrayD::::zeros(IxDyn(&[10, 10])); /// /// assert!(array.into_dimensionality::().is_ok()); /// ``` pub fn into_dimensionality(self) -> Result, ShapeError> where D2: Dimension { unsafe { if D::NDIM == D2::NDIM { // safe because D == D2 let dim = unlimited_transmute::(self.dim); let strides = unlimited_transmute::(self.strides); return Ok(ArrayBase::from_data_ptr(self.data, self.ptr).with_strides_dim(strides, dim)); } else if D::NDIM.is_none() || D2::NDIM.is_none() { // one is dynamic dim // safe because dim, strides are equivalent under a different type if let Some(dim) = D2::from_dimension(&self.dim) { if let Some(strides) = D2::from_dimension(&self.strides) { return Ok(self.with_strides_dim(strides, dim)); } } } } Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)) } /// Act like a larger size and/or shape array by *broadcasting* /// into a larger shape, if possible. /// /// Return `None` if shapes can not be broadcast together. /// /// ***Background*** /// /// * Two axes are compatible if they are equal, or one of them is 1. /// * In this instance, only the axes of the smaller side (self) can be 1. /// /// Compare axes beginning with the *last* axis of each shape. /// /// For example (1, 2, 4) can be broadcast into (7, 6, 2, 4) /// because its axes are either equal or 1 (or missing); /// while (2, 2) can *not* be broadcast into (2, 4). /// /// The implementation creates a view with strides set to zero for the /// axes that are to be repeated. /// /// The broadcasting documentation for Numpy has more information. /// /// ``` /// use ndarray::{aview1, aview2}; /// /// assert!( /// aview1(&[1., 0.]).broadcast((10, 2)).unwrap() /// == aview2(&[[1., 0.]; 10]) /// ); /// ``` pub fn broadcast(&self, dim: E) -> Option> where E: IntoDimension, S: Data, { /// Return new stride when trying to grow `from` into shape `to` /// /// Broadcasting works by returning a "fake stride" where elements /// to repeat are in axes with 0 stride, so that several indexes point /// to the same element. /// /// **Note:** Cannot be used for mutable iterators, since repeating /// elements would create aliasing pointers. fn upcast(to: &D, from: &E, stride: &E) -> Option { // Make sure the product of non-zero axis lengths does not exceed // `isize::MAX`. This is the only safety check we need to perform // because all the other constraints of `ArrayBase` are guaranteed // to be met since we're starting from a valid `ArrayBase`. let _ = size_of_shape_checked(to).ok()?; let mut new_stride = to.clone(); // begin at the back (the least significant dimension) // size of the axis has to either agree or `from` has to be 1 if to.ndim() < from.ndim() { return None; } { let mut new_stride_iter = new_stride.slice_mut().iter_mut().rev(); for ((er, es), dr) in from .slice() .iter() .rev() .zip(stride.slice().iter().rev()) .zip(new_stride_iter.by_ref()) { /* update strides */ if *dr == *er { /* keep stride */ *dr = *es; } else if *er == 1 { /* dead dimension, zero stride */ *dr = 0 } else { return None; } } /* set remaining strides to zero */ for dr in new_stride_iter { *dr = 0; } } Some(new_stride) } let dim = dim.into_dimension(); // Note: zero strides are safe precisely because we return an read-only view let broadcast_strides = match upcast(&dim, &self.dim, &self.strides) { Some(st) => st, None => return None, }; unsafe { Some(ArrayView::new(self.ptr, dim, broadcast_strides)) } } /// For two arrays or views, find their common shape if possible and /// broadcast them as array views into that shape. /// /// Return `ShapeError` if their shapes can not be broadcast together. #[allow(clippy::type_complexity)] pub(crate) fn broadcast_with<'a, 'b, B, S2, E>( &'a self, other: &'b ArrayBase, ) -> Result<(ArrayView<'a, A, DimMaxOf>, ArrayView<'b, B, DimMaxOf>), ShapeError> where S: Data, S2: Data, D: Dimension + DimMax, E: Dimension, { let shape = co_broadcast::>::Output>(&self.dim, &other.dim)?; let view1 = if shape.slice() == self.dim.slice() { self.view() .into_dimensionality::<>::Output>() .unwrap() } else if let Some(view1) = self.broadcast(shape.clone()) { view1 } else { return Err(from_kind(ErrorKind::IncompatibleShape)); }; let view2 = if shape.slice() == other.dim.slice() { other .view() .into_dimensionality::<>::Output>() .unwrap() } else if let Some(view2) = other.broadcast(shape) { view2 } else { return Err(from_kind(ErrorKind::IncompatibleShape)); }; Ok((view1, view2)) } /// Swap axes `ax` and `bx`. /// /// This does not move any data, it just adjusts the array’s dimensions /// and strides. /// /// **Panics** if the axes are out of bounds. /// /// ``` /// use ndarray::arr2; /// /// let mut a = arr2(&[[1., 2., 3.]]); /// a.swap_axes(0, 1); /// assert!( /// a == arr2(&[[1.], [2.], [3.]]) /// ); /// ``` #[track_caller] pub fn swap_axes(&mut self, ax: usize, bx: usize) { self.dim.slice_mut().swap(ax, bx); self.strides.slice_mut().swap(ax, bx); } /// Permute the axes. /// /// This does not move any data, it just adjusts the array’s dimensions /// and strides. /// /// *i* in the *j*-th place in the axes sequence means `self`'s *i*-th axis /// becomes `self.permuted_axes()`'s *j*-th axis /// /// **Panics** if any of the axes are out of bounds, if an axis is missing, /// or if an axis is repeated more than once. /// /// # Examples /// /// ``` /// use ndarray::{arr2, Array3}; /// /// let a = arr2(&[[0, 1], [2, 3]]); /// assert_eq!(a.view().permuted_axes([1, 0]), a.t()); /// /// let b = Array3::::zeros((1, 2, 3)); /// assert_eq!(b.permuted_axes([1, 0, 2]).shape(), &[2, 1, 3]); /// ``` #[track_caller] pub fn permuted_axes(self, axes: T) -> ArrayBase where T: IntoDimension { let axes = axes.into_dimension(); // Ensure that each axis is used exactly once. let mut usage_counts = D::zeros(self.ndim()); for axis in axes.slice() { usage_counts[*axis] += 1; } for count in usage_counts.slice() { assert_eq!(*count, 1, "each axis must be listed exactly once"); } // Determine the new shape and strides. let mut new_dim = usage_counts; // reuse to avoid an allocation let mut new_strides = D::zeros(self.ndim()); { let dim = self.dim.slice(); let strides = self.strides.slice(); for (new_axis, &axis) in axes.slice().iter().enumerate() { new_dim[new_axis] = dim[axis]; new_strides[new_axis] = strides[axis]; } } // safe because axis invariants are checked above; they are a permutation of the old unsafe { self.with_strides_dim(new_strides, new_dim) } } /// Transpose the array by reversing axes. /// /// Transposition reverses the order of the axes (dimensions and strides) /// while retaining the same data. pub fn reversed_axes(mut self) -> ArrayBase { self.dim.slice_mut().reverse(); self.strides.slice_mut().reverse(); self } /// Return a transposed view of the array. /// /// This is a shorthand for `self.view().reversed_axes()`. /// /// See also the more general methods `.reversed_axes()` and `.swap_axes()`. pub fn t(&self) -> ArrayView<'_, A, D> where S: Data { self.view().reversed_axes() } /// Return an iterator over the length and stride of each axis. pub fn axes(&self) -> Axes<'_, D> { axes_of(&self.dim, &self.strides) } /* /// Return the axis with the least stride (by absolute value) pub fn min_stride_axis(&self) -> Axis { self.dim.min_stride_axis(&self.strides) } */ /// Return the axis with the greatest stride (by absolute value), /// preferring axes with len > 1. pub fn max_stride_axis(&self) -> Axis { self.dim.max_stride_axis(&self.strides) } /// Reverse the stride of `axis`. /// /// ***Panics*** if the axis is out of bounds. #[track_caller] pub fn invert_axis(&mut self, axis: Axis) { unsafe { let s = self.strides.axis(axis) as Ixs; let m = self.dim.axis(axis); if m != 0 { self.ptr = self.ptr.offset(stride_offset(m - 1, s as Ix)); } self.strides.set_axis(axis, (-s) as Ix); } } /// If possible, merge in the axis `take` to `into`. /// /// Returns `true` iff the axes are now merged. /// /// This method merges the axes if movement along the two original axes /// (moving fastest along the `into` axis) can be equivalently represented /// as movement along one (merged) axis. Merging the axes preserves this /// order in the merged axis. If `take` and `into` are the same axis, then /// the axis is "merged" if its length is ≤ 1. /// /// If the return value is `true`, then the following hold: /// /// * The new length of the `into` axis is the product of the original /// lengths of the two axes. /// /// * The new length of the `take` axis is 0 if the product of the original /// lengths of the two axes is 0, and 1 otherwise. /// /// If the return value is `false`, then merging is not possible, and the /// original shape and strides have been preserved. /// /// Note that the ordering constraint means that if it's possible to merge /// `take` into `into`, it's usually not possible to merge `into` into /// `take`, and vice versa. /// /// ``` /// use ndarray::Array3; /// use ndarray::Axis; /// /// let mut a = Array3::::zeros((2, 3, 4)); /// assert!(a.merge_axes(Axis(1), Axis(2))); /// assert_eq!(a.shape(), &[2, 1, 12]); /// ``` /// /// ***Panics*** if an axis is out of bounds. #[track_caller] pub fn merge_axes(&mut self, take: Axis, into: Axis) -> bool { merge_axes(&mut self.dim, &mut self.strides, take, into) } /// Insert new array axis at `axis` and return the result. /// /// ``` /// use ndarray::{Array3, Axis, arr1, arr2}; /// /// // Convert a 1-D array into a row vector (2-D). /// let a = arr1(&[1, 2, 3]); /// let row = a.insert_axis(Axis(0)); /// assert_eq!(row, arr2(&[[1, 2, 3]])); /// /// // Convert a 1-D array into a column vector (2-D). /// let b = arr1(&[1, 2, 3]); /// let col = b.insert_axis(Axis(1)); /// assert_eq!(col, arr2(&[[1], [2], [3]])); /// /// // The new axis always has length 1. /// let b = Array3::::zeros((3, 4, 5)); /// assert_eq!(b.insert_axis(Axis(2)).shape(), &[3, 4, 1, 5]); /// ``` /// /// ***Panics*** if the axis is out of bounds. #[track_caller] pub fn insert_axis(self, axis: Axis) -> ArrayBase { assert!(axis.index() <= self.ndim()); // safe because a new axis of length one does not affect memory layout unsafe { let strides = self.strides.insert_axis(axis); let dim = self.dim.insert_axis(axis); self.with_strides_dim(strides, dim) } } /// Remove array axis `axis` and return the result. /// /// This is equivalent to `.index_axis_move(axis, 0)` and makes most sense to use if the /// axis to remove is of length 1. /// /// **Panics** if the axis is out of bounds or its length is zero. #[track_caller] pub fn remove_axis(self, axis: Axis) -> ArrayBase where D: RemoveAxis { self.index_axis_move(axis, 0) } pub(crate) fn pointer_is_inbounds(&self) -> bool { self.data._is_pointer_inbounds(self.as_ptr()) } /// Perform an elementwise assigment to `self` from `rhs`. /// /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// **Panics** if broadcasting isn’t possible. #[track_caller] pub fn assign(&mut self, rhs: &ArrayBase) where S: DataMut, A: Clone, S2: Data, { self.zip_mut_with(rhs, |x, y| x.clone_from(y)); } /// Perform an elementwise assigment of values cloned from `self` into array or producer `to`. /// /// The destination `to` can be another array or a producer of assignable elements. /// [`AssignElem`] determines how elements are assigned. /// /// **Panics** if shapes disagree. #[track_caller] pub fn assign_to

(&self, to: P) where S: Data, P: IntoNdProducer, P::Item: AssignElem, A: Clone, { Zip::from(self).map_assign_into(to, A::clone); } /// Perform an elementwise assigment to `self` from element `x`. pub fn fill(&mut self, x: A) where S: DataMut, A: Clone, { self.map_inplace(move |elt| elt.clone_from(&x)); } pub(crate) fn zip_mut_with_same_shape(&mut self, rhs: &ArrayBase, mut f: F) where S: DataMut, S2: Data, E: Dimension, F: FnMut(&mut A, &B), { debug_assert_eq!(self.shape(), rhs.shape()); if self.dim.strides_equivalent(&self.strides, &rhs.strides) { if let Some(self_s) = self.as_slice_memory_order_mut() { if let Some(rhs_s) = rhs.as_slice_memory_order() { for (s, r) in self_s.iter_mut().zip(rhs_s) { f(s, r); } return; } } } // Otherwise, fall back to the outer iter self.zip_mut_with_by_rows(rhs, f); } // zip two arrays where they have different layout or strides #[inline(always)] fn zip_mut_with_by_rows(&mut self, rhs: &ArrayBase, mut f: F) where S: DataMut, S2: Data, E: Dimension, F: FnMut(&mut A, &B), { debug_assert_eq!(self.shape(), rhs.shape()); debug_assert_ne!(self.ndim(), 0); // break the arrays up into their inner rows let n = self.ndim(); let dim = self.raw_dim(); Zip::from(LanesMut::new(self.view_mut(), Axis(n - 1))) .and(Lanes::new(rhs.broadcast_assume(dim), Axis(n - 1))) .for_each(move |s_row, r_row| Zip::from(s_row).and(r_row).for_each(&mut f)); } fn zip_mut_with_elem(&mut self, rhs_elem: &B, mut f: F) where S: DataMut, F: FnMut(&mut A, &B), { self.map_inplace(move |elt| f(elt, rhs_elem)); } /// Traverse two arrays in unspecified order, in lock step, /// calling the closure `f` on each element pair. /// /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// **Panics** if broadcasting isn’t possible. #[track_caller] #[inline] pub fn zip_mut_with(&mut self, rhs: &ArrayBase, f: F) where S: DataMut, S2: Data, E: Dimension, F: FnMut(&mut A, &B), { if rhs.dim.ndim() == 0 { // Skip broadcast from 0-dim array self.zip_mut_with_elem(rhs.get_0d(), f); } else if self.dim.ndim() == rhs.dim.ndim() && self.shape() == rhs.shape() { self.zip_mut_with_same_shape(rhs, f); } else { let rhs_broadcast = rhs.broadcast_unwrap(self.raw_dim()); self.zip_mut_with_by_rows(&rhs_broadcast, f); } } /// Traverse the array elements and apply a fold, /// returning the resulting value. /// /// Elements are visited in arbitrary order. pub fn fold<'a, F, B>(&'a self, init: B, f: F) -> B where F: FnMut(B, &'a A) -> B, A: 'a, S: Data, { if let Some(slc) = self.as_slice_memory_order() { slc.iter().fold(init, f) } else { let mut v = self.view(); move_min_stride_axis_to_last(&mut v.dim, &mut v.strides); v.into_elements_base().fold(init, f) } } /// Call `f` by reference on each element and create a new array /// with the new values. /// /// Elements are visited in arbitrary order. /// /// Return an array with the same shape as `self`. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[ 0., 1.], /// [-1., 2.]]); /// assert!( /// a.map(|x| *x >= 1.0) /// == arr2(&[[false, true], /// [false, true]]) /// ); /// ``` pub fn map<'a, B, F>(&'a self, f: F) -> Array where F: FnMut(&'a A) -> B, A: 'a, S: Data, { unsafe { if let Some(slc) = self.as_slice_memory_order() { ArrayBase::from_shape_trusted_iter_unchecked( self.dim.clone().strides(self.strides.clone()), slc.iter(), f, ) } else { ArrayBase::from_shape_trusted_iter_unchecked(self.dim.clone(), self.iter(), f) } } } /// Call `f` on a mutable reference of each element and create a new array /// with the new values. /// /// Elements are visited in arbitrary order. /// /// Return an array with the same shape as `self`. pub fn map_mut<'a, B, F>(&'a mut self, f: F) -> Array where F: FnMut(&'a mut A) -> B, A: 'a, S: DataMut, { let dim = self.dim.clone(); if self.is_contiguous() { let strides = self.strides.clone(); let slc = self.as_slice_memory_order_mut().unwrap(); unsafe { ArrayBase::from_shape_trusted_iter_unchecked(dim.strides(strides), slc.iter_mut(), f) } } else { unsafe { ArrayBase::from_shape_trusted_iter_unchecked(dim, self.iter_mut(), f) } } } /// Call `f` by **v**alue on each element and create a new array /// with the new values. /// /// Elements are visited in arbitrary order. /// /// Return an array with the same shape as `self`. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[ 0., 1.], /// [-1., 2.]]); /// assert!( /// a.mapv(f32::abs) == arr2(&[[0., 1.], /// [1., 2.]]) /// ); /// ``` pub fn mapv(&self, mut f: F) -> Array where F: FnMut(A) -> B, A: Clone, S: Data, { self.map(move |x| f(x.clone())) } /// Call `f` by **v**alue on each element, update the array with the new values /// and return it. /// /// Elements are visited in arbitrary order. pub fn mapv_into(mut self, f: F) -> Self where S: DataMut, F: FnMut(A) -> A, A: Clone, { self.mapv_inplace(f); self } /// Consume the array, call `f` by **v**alue on each element, and return an /// owned array with the new values. Works for **any** `F: FnMut(A)->B`. /// /// If `A` and `B` are the same type then the map is performed by delegating /// to [`mapv_into`] and then converting into an owned array. This avoids /// unnecessary memory allocations in [`mapv`]. /// /// If `A` and `B` are different types then a new array is allocated and the /// map is performed as in [`mapv`]. /// /// Elements are visited in arbitrary order. /// /// [`mapv_into`]: ArrayBase::mapv_into /// [`mapv`]: ArrayBase::mapv pub fn mapv_into_any(self, mut f: F) -> Array where S: DataMut, F: FnMut(A) -> B, A: Clone + 'static, B: 'static, { if core::any::TypeId::of::() == core::any::TypeId::of::() { // A and B are the same type. // Wrap f in a closure of type FnMut(A) -> A . let f = |a| { let b = f(a); // Safe because A and B are the same type. unsafe { unlimited_transmute::(b) } }; // Delegate to mapv_into() using the wrapped closure. // Convert output to a uniquely owned array of type Array. let output = self.mapv_into(f).into_owned(); // Change the return type from Array to Array. // Again, safe because A and B are the same type. unsafe { unlimited_transmute::, Array>(output) } } else { // A and B are not the same type. // Fallback to mapv(). self.mapv(f) } } /// Modify the array in place by calling `f` by mutable reference on each element. /// /// Elements are visited in arbitrary order. pub fn map_inplace<'a, F>(&'a mut self, f: F) where S: DataMut, A: 'a, F: FnMut(&'a mut A), { match self.try_as_slice_memory_order_mut() { Ok(slc) => slc.iter_mut().for_each(f), Err(arr) => { let mut v = arr.view_mut(); move_min_stride_axis_to_last(&mut v.dim, &mut v.strides); v.into_elements_base().for_each(f); } } } /// Modify the array in place by calling `f` by **v**alue on each element. /// The array is updated with the new values. /// /// Elements are visited in arbitrary order. /// /// ``` /// # #[cfg(feature = "approx")] { /// use approx::assert_abs_diff_eq; /// use ndarray::arr2; /// /// let mut a = arr2(&[[ 0., 1.], /// [-1., 2.]]); /// a.mapv_inplace(f32::exp); /// assert_abs_diff_eq!( /// a, /// arr2(&[[1.00000, 2.71828], /// [0.36788, 7.38906]]), /// epsilon = 1e-5, /// ); /// # } /// ``` pub fn mapv_inplace(&mut self, mut f: F) where S: DataMut, F: FnMut(A) -> A, A: Clone, { self.map_inplace(move |x| *x = f(x.clone())); } /// Call `f` for each element in the array. /// /// Elements are visited in arbitrary order. pub fn for_each<'a, F>(&'a self, mut f: F) where F: FnMut(&'a A), A: 'a, S: Data, { self.fold((), move |(), elt| f(elt)) } /// Fold along an axis. /// /// Combine the elements of each subview with the previous using the `fold` /// function and initial value `init`. /// /// Return the result as an `Array`. /// /// **Panics** if `axis` is out of bounds. #[track_caller] pub fn fold_axis(&self, axis: Axis, init: B, mut fold: F) -> Array where D: RemoveAxis, F: FnMut(&B, &A) -> B, B: Clone, S: Data, { let mut res = Array::from_elem(self.raw_dim().remove_axis(axis), init); for subview in self.axis_iter(axis) { res.zip_mut_with(&subview, |x, y| *x = fold(x, y)); } res } /// Reduce the values along an axis into just one value, producing a new /// array with one less dimension. /// /// Elements are visited in arbitrary order. /// /// Return the result as an `Array`. /// /// **Panics** if `axis` is out of bounds. #[track_caller] pub fn map_axis<'a, B, F>(&'a self, axis: Axis, mut mapping: F) -> Array where D: RemoveAxis, F: FnMut(ArrayView1<'a, A>) -> B, A: 'a, S: Data, { if self.len_of(axis) == 0 { let new_dim = self.dim.remove_axis(axis); Array::from_shape_simple_fn(new_dim, move || mapping(ArrayView::from(&[]))) } else { Zip::from(self.lanes(axis)).map_collect(mapping) } } /// Reduce the values along an axis into just one value, producing a new /// array with one less dimension. /// 1-dimensional lanes are passed as mutable references to the reducer, /// allowing for side-effects. /// /// Elements are visited in arbitrary order. /// /// Return the result as an `Array`. /// /// **Panics** if `axis` is out of bounds. #[track_caller] pub fn map_axis_mut<'a, B, F>(&'a mut self, axis: Axis, mut mapping: F) -> Array where D: RemoveAxis, F: FnMut(ArrayViewMut1<'a, A>) -> B, A: 'a, S: DataMut, { if self.len_of(axis) == 0 { let new_dim = self.dim.remove_axis(axis); Array::from_shape_simple_fn(new_dim, move || mapping(ArrayViewMut::from(&mut []))) } else { Zip::from(self.lanes_mut(axis)).map_collect(mapping) } } /// Remove the `index`th elements along `axis` and shift down elements from higher indexes. /// /// Note that this "removes" the elements by swapping them around to the end of the axis and /// shortening the length of the axis; the elements are not deinitialized or dropped by this, /// just moved out of view (this only matters for elements with ownership semantics). It's /// similar to slicing an owned array in place. /// /// Decreases the length of `axis` by one. /// /// ***Panics*** if `axis` is out of bounds
/// ***Panics*** if not `index < self.len_of(axis)`. pub fn remove_index(&mut self, axis: Axis, index: usize) where S: DataOwned + DataMut { assert!(index < self.len_of(axis), "index {} must be less than length of Axis({})", index, axis.index()); let (_, mut tail) = self.view_mut().split_at(axis, index); // shift elements to the front Zip::from(tail.lanes_mut(axis)).for_each(|mut lane| lane.rotate1_front()); // then slice the axis in place to cut out the removed final element self.slice_axis_inplace(axis, Slice::new(0, Some(-1), 1)); } /// Iterates over pairs of consecutive elements along the axis. /// /// The first argument to the closure is an element, and the second /// argument is the next element along the axis. Iteration is guaranteed to /// proceed in order along the specified axis, but in all other respects /// the iteration order is unspecified. /// /// # Example /// /// For example, this can be used to compute the cumulative sum along an /// axis: /// /// ``` /// use ndarray::{array, Axis}; /// /// let mut arr = array![ /// [[1, 2], [3, 4], [5, 6]], /// [[7, 8], [9, 10], [11, 12]], /// ]; /// arr.accumulate_axis_inplace(Axis(1), |&prev, curr| *curr += prev); /// assert_eq!( /// arr, /// array![ /// [[1, 2], [4, 6], [9, 12]], /// [[7, 8], [16, 18], [27, 30]], /// ], /// ); /// ``` pub fn accumulate_axis_inplace(&mut self, axis: Axis, mut f: F) where F: FnMut(&A, &mut A), S: DataMut, { if self.len_of(axis) <= 1 { return; } let mut curr = self.raw_view_mut(); // mut borrow of the array here let mut prev = curr.raw_view(); // derive further raw views from the same borrow prev.slice_axis_inplace(axis, Slice::from(..-1)); curr.slice_axis_inplace(axis, Slice::from(1..)); // This implementation relies on `Zip` iterating along `axis` in order. Zip::from(prev).and(curr).for_each(|prev, curr| unsafe { // These pointer dereferences and borrows are safe because: // // 1. They're pointers to elements in the array. // // 2. `S: DataMut` guarantees that elements are safe to borrow // mutably and that they don't alias. // // 3. The lifetimes of the borrows last only for the duration // of the call to `f`, so aliasing across calls to `f` // cannot occur. f(&*prev, &mut *curr) }); } } /// Transmute from A to B. /// /// Like transmute, but does not have the compile-time size check which blocks /// using regular transmute in some cases. /// /// **Panics** if the size of A and B are different. #[track_caller] #[inline] unsafe fn unlimited_transmute(data: A) -> B { // safe when sizes are equal and caller guarantees that representations are equal assert_eq!(size_of::
(), size_of::()); let old_data = ManuallyDrop::new(data); (&*old_data as *const A as *const B).read() } type DimMaxOf = >::Output; #[cfg(test)] mod tests { use super::*; use crate::arr3; #[test] fn test_flatten() { let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); let flattened = array.flatten(); assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); } #[test] fn test_flatten_with_order() { let array = arr2(&[[1, 2], [3, 4], [5, 6], [7, 8]]); let flattened = array.flatten_with_order(Order::RowMajor); assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); let flattened = array.flatten_with_order(Order::ColumnMajor); assert_eq!(flattened, arr1(&[1, 3, 5, 7, 2, 4, 6, 8])); } #[test] fn test_into_flat() { let array = arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); let flattened = array.into_flat(); assert_eq!(flattened, arr1(&[1, 2, 3, 4, 5, 6, 7, 8])); } } ndarray-0.16.1/src/impl_ops.rs000064400000000000000000000423451046102023000143400ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::dimension::DimMax; use crate::Zip; use num_complex::Complex; /// Elements that can be used as direct operands in arithmetic with arrays. /// /// For example, `f64` is a `ScalarOperand` which means that for an array `a`, /// arithmetic like `a + 1.0`, and, `a * 2.`, and `a += 3.` are allowed. /// /// In the description below, let `A` be an array or array view, /// let `B` be an array with owned data, /// and let `C` be an array with mutable data. /// /// `ScalarOperand` determines for which scalars `K` operations `&A @ K`, and `B @ K`, /// and `C @= K` are defined, as ***right hand side operands***, for applicable /// arithmetic operators (denoted `@`). /// /// ***Left hand side*** scalar operands are not related to this trait /// (they need one `impl` per concrete scalar type); but they are still /// implemented for the same types, allowing operations /// `K @ &A`, and `K @ B` for primitive numeric types `K`. /// /// This trait ***does not*** limit which elements can be stored in an array in general. /// Non-`ScalarOperand` types can still participate in arithmetic as array elements in /// in array-array operations. pub trait ScalarOperand: 'static + Clone {} impl ScalarOperand for bool {} impl ScalarOperand for i8 {} impl ScalarOperand for u8 {} impl ScalarOperand for i16 {} impl ScalarOperand for u16 {} impl ScalarOperand for i32 {} impl ScalarOperand for u32 {} impl ScalarOperand for i64 {} impl ScalarOperand for u64 {} impl ScalarOperand for i128 {} impl ScalarOperand for u128 {} impl ScalarOperand for isize {} impl ScalarOperand for usize {} impl ScalarOperand for f32 {} impl ScalarOperand for f64 {} impl ScalarOperand for Complex {} impl ScalarOperand for Complex {} macro_rules! impl_binary_op( ($trt:ident, $operator:tt, $mth:ident, $iop:tt, $doc:expr) => ( /// Perform elementwise #[doc=$doc] /// between `self` and `rhs`, /// and return the result. /// /// `self` must be an `Array` or `ArcArray`. /// /// If their shapes disagree, `self` is broadcast to their broadcast shape. /// /// **Panics** if broadcasting isn’t possible. impl $trt> for ArrayBase where A: Clone + $trt, B: Clone, S: DataOwned + DataMut, S2: Data, D: Dimension + DimMax, E: Dimension, { type Output = ArrayBase>::Output>; #[track_caller] fn $mth(self, rhs: ArrayBase) -> Self::Output { self.$mth(&rhs) } } /// Perform elementwise #[doc=$doc] /// between `self` and reference `rhs`, /// and return the result. /// /// `rhs` must be an `Array` or `ArcArray`. /// /// If their shapes disagree, `self` is broadcast to their broadcast shape, /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase where A: Clone + $trt, B: Clone, S: DataOwned + DataMut, S2: Data, D: Dimension + DimMax, E: Dimension, { type Output = ArrayBase>::Output>; #[track_caller] fn $mth(self, rhs: &ArrayBase) -> Self::Output { if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { let mut out = self.into_dimensionality::<>::Output>().unwrap(); out.zip_mut_with_same_shape(rhs, clone_iopf(A::$mth)); out } else { let (lhs_view, rhs_view) = self.broadcast_with(&rhs).unwrap(); if lhs_view.shape() == self.shape() { let mut out = self.into_dimensionality::<>::Output>().unwrap(); out.zip_mut_with_same_shape(&rhs_view, clone_iopf(A::$mth)); out } else { Zip::from(&lhs_view).and(&rhs_view).map_collect_owned(clone_opf(A::$mth)) } } } } /// Perform elementwise #[doc=$doc] /// between reference `self` and `rhs`, /// and return the result. /// /// `rhs` must be an `Array` or `ArcArray`. /// /// If their shapes disagree, `self` is broadcast to their broadcast shape, /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt> for &'a ArrayBase where A: Clone + $trt, B: Clone, S: Data, S2: DataOwned + DataMut, D: Dimension, E: Dimension + DimMax, { type Output = ArrayBase>::Output>; #[track_caller] fn $mth(self, rhs: ArrayBase) -> Self::Output where { if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { let mut out = rhs.into_dimensionality::<>::Output>().unwrap(); out.zip_mut_with_same_shape(self, clone_iopf_rev(A::$mth)); out } else { let (rhs_view, lhs_view) = rhs.broadcast_with(self).unwrap(); if rhs_view.shape() == rhs.shape() { let mut out = rhs.into_dimensionality::<>::Output>().unwrap(); out.zip_mut_with_same_shape(&lhs_view, clone_iopf_rev(A::$mth)); out } else { Zip::from(&lhs_view).and(&rhs_view).map_collect_owned(clone_opf(A::$mth)) } } } } /// Perform elementwise #[doc=$doc] /// between references `self` and `rhs`, /// and return the result as a new `Array`. /// /// If their shapes disagree, `self` and `rhs` is broadcast to their broadcast shape, /// cloning the data if needed. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, B, S, S2, D, E> $trt<&'a ArrayBase> for &'a ArrayBase where A: Clone + $trt, B: Clone, S: Data, S2: Data, D: Dimension + DimMax, E: Dimension, { type Output = Array>::Output>; #[track_caller] fn $mth(self, rhs: &'a ArrayBase) -> Self::Output { let (lhs, rhs) = if self.ndim() == rhs.ndim() && self.shape() == rhs.shape() { let lhs = self.view().into_dimensionality::<>::Output>().unwrap(); let rhs = rhs.view().into_dimensionality::<>::Output>().unwrap(); (lhs, rhs) } else { self.broadcast_with(rhs).unwrap() }; Zip::from(lhs).and(rhs).map_collect(clone_opf(A::$mth)) } } /// Perform elementwise #[doc=$doc] /// between `self` and the scalar `x`, /// and return the result (based on `self`). /// /// `self` must be an `Array` or `ArcArray`. impl $trt for ArrayBase where A: Clone + $trt, S: DataOwned + DataMut, D: Dimension, B: ScalarOperand, { type Output = ArrayBase; fn $mth(mut self, x: B) -> ArrayBase { self.map_inplace(move |elt| { *elt = elt.clone() $operator x.clone(); }); self } } /// Perform elementwise #[doc=$doc] /// between the reference `self` and the scalar `x`, /// and return the result as a new `Array`. impl<'a, A, S, D, B> $trt for &'a ArrayBase where A: Clone + $trt, S: Data, D: Dimension, B: ScalarOperand, { type Output = Array; fn $mth(self, x: B) -> Self::Output { self.map(move |elt| elt.clone() $operator x.clone()) } } ); ); // Pick the expression $a for commutative and $b for ordered binop macro_rules! if_commutative { (Commute { $a:expr } or { $b:expr }) => { $a }; (Ordered { $a:expr } or { $b:expr }) => { $b }; } macro_rules! impl_scalar_lhs_op { // $commutative flag. Reuse the self + scalar impl if we can. // We can do this safely since these are the primitive numeric types ($scalar:ty, $commutative:ident, $operator:tt, $trt:ident, $mth:ident, $doc:expr) => ( // these have no doc -- they are not visible in rustdoc // Perform elementwise // between the scalar `self` and array `rhs`, // and return the result (based on `self`). impl $trt> for $scalar where S: DataOwned + DataMut, D: Dimension, { type Output = ArrayBase; fn $mth(self, rhs: ArrayBase) -> ArrayBase { if_commutative!($commutative { rhs.$mth(self) } or {{ let mut rhs = rhs; rhs.map_inplace(move |elt| { *elt = self $operator *elt; }); rhs }}) } } // Perform elementwise // between the scalar `self` and array `rhs`, // and return the result as a new `Array`. impl<'a, S, D> $trt<&'a ArrayBase> for $scalar where S: Data, D: Dimension, { type Output = Array<$scalar, D>; fn $mth(self, rhs: &ArrayBase) -> Self::Output { if_commutative!($commutative { rhs.$mth(self) } or { rhs.map(move |elt| self.clone() $operator elt.clone()) }) } } ); } mod arithmetic_ops { use super::*; use crate::imp_prelude::*; use std::ops::*; fn clone_opf(f: impl Fn(A, B) -> C) -> impl FnMut(&A, &B) -> C { move |x, y| f(x.clone(), y.clone()) } fn clone_iopf(f: impl Fn(A, B) -> A) -> impl FnMut(&mut A, &B) { move |x, y| *x = f(x.clone(), y.clone()) } fn clone_iopf_rev(f: impl Fn(A, B) -> B) -> impl FnMut(&mut B, &A) { move |x, y| *x = f(y.clone(), x.clone()) } impl_binary_op!(Add, +, add, +=, "addition"); impl_binary_op!(Sub, -, sub, -=, "subtraction"); impl_binary_op!(Mul, *, mul, *=, "multiplication"); impl_binary_op!(Div, /, div, /=, "division"); impl_binary_op!(Rem, %, rem, %=, "remainder"); impl_binary_op!(BitAnd, &, bitand, &=, "bit and"); impl_binary_op!(BitOr, |, bitor, |=, "bit or"); impl_binary_op!(BitXor, ^, bitxor, ^=, "bit xor"); impl_binary_op!(Shl, <<, shl, <<=, "left shift"); impl_binary_op!(Shr, >>, shr, >>=, "right shift"); macro_rules! all_scalar_ops { ($int_scalar:ty) => ( impl_scalar_lhs_op!($int_scalar, Commute, +, Add, add, "addition"); impl_scalar_lhs_op!($int_scalar, Ordered, -, Sub, sub, "subtraction"); impl_scalar_lhs_op!($int_scalar, Commute, *, Mul, mul, "multiplication"); impl_scalar_lhs_op!($int_scalar, Ordered, /, Div, div, "division"); impl_scalar_lhs_op!($int_scalar, Ordered, %, Rem, rem, "remainder"); impl_scalar_lhs_op!($int_scalar, Commute, &, BitAnd, bitand, "bit and"); impl_scalar_lhs_op!($int_scalar, Commute, |, BitOr, bitor, "bit or"); impl_scalar_lhs_op!($int_scalar, Commute, ^, BitXor, bitxor, "bit xor"); impl_scalar_lhs_op!($int_scalar, Ordered, <<, Shl, shl, "left shift"); impl_scalar_lhs_op!($int_scalar, Ordered, >>, Shr, shr, "right shift"); ); } all_scalar_ops!(i8); all_scalar_ops!(u8); all_scalar_ops!(i16); all_scalar_ops!(u16); all_scalar_ops!(i32); all_scalar_ops!(u32); all_scalar_ops!(i64); all_scalar_ops!(u64); all_scalar_ops!(isize); all_scalar_ops!(usize); all_scalar_ops!(i128); all_scalar_ops!(u128); impl_scalar_lhs_op!(bool, Commute, &, BitAnd, bitand, "bit and"); impl_scalar_lhs_op!(bool, Commute, |, BitOr, bitor, "bit or"); impl_scalar_lhs_op!(bool, Commute, ^, BitXor, bitxor, "bit xor"); impl_scalar_lhs_op!(f32, Commute, +, Add, add, "addition"); impl_scalar_lhs_op!(f32, Ordered, -, Sub, sub, "subtraction"); impl_scalar_lhs_op!(f32, Commute, *, Mul, mul, "multiplication"); impl_scalar_lhs_op!(f32, Ordered, /, Div, div, "division"); impl_scalar_lhs_op!(f32, Ordered, %, Rem, rem, "remainder"); impl_scalar_lhs_op!(f64, Commute, +, Add, add, "addition"); impl_scalar_lhs_op!(f64, Ordered, -, Sub, sub, "subtraction"); impl_scalar_lhs_op!(f64, Commute, *, Mul, mul, "multiplication"); impl_scalar_lhs_op!(f64, Ordered, /, Div, div, "division"); impl_scalar_lhs_op!(f64, Ordered, %, Rem, rem, "remainder"); impl_scalar_lhs_op!(Complex, Commute, +, Add, add, "addition"); impl_scalar_lhs_op!(Complex, Ordered, -, Sub, sub, "subtraction"); impl_scalar_lhs_op!(Complex, Commute, *, Mul, mul, "multiplication"); impl_scalar_lhs_op!(Complex, Ordered, /, Div, div, "division"); impl_scalar_lhs_op!(Complex, Commute, +, Add, add, "addition"); impl_scalar_lhs_op!(Complex, Ordered, -, Sub, sub, "subtraction"); impl_scalar_lhs_op!(Complex, Commute, *, Mul, mul, "multiplication"); impl_scalar_lhs_op!(Complex, Ordered, /, Div, div, "division"); impl Neg for ArrayBase where A: Clone + Neg, S: DataOwned + DataMut, D: Dimension, { type Output = Self; /// Perform an elementwise negation of `self` and return the result. fn neg(mut self) -> Self { self.map_inplace(|elt| { *elt = -elt.clone(); }); self } } impl<'a, A, S, D> Neg for &'a ArrayBase where &'a A: 'a + Neg, S: Data, D: Dimension, { type Output = Array; /// Perform an elementwise negation of reference `self` and return the /// result as a new `Array`. fn neg(self) -> Array { self.map(Neg::neg) } } impl Not for ArrayBase where A: Clone + Not, S: DataOwned + DataMut, D: Dimension, { type Output = Self; /// Perform an elementwise unary not of `self` and return the result. fn not(mut self) -> Self { self.map_inplace(|elt| { *elt = !elt.clone(); }); self } } impl<'a, A, S, D> Not for &'a ArrayBase where &'a A: 'a + Not, S: Data, D: Dimension, { type Output = Array; /// Perform an elementwise unary not of reference `self` and return the /// result as a new `Array`. fn not(self) -> Array { self.map(Not::not) } } } mod assign_ops { use super::*; use crate::imp_prelude::*; macro_rules! impl_assign_op { ($trt:ident, $method:ident, $doc:expr) => { use std::ops::$trt; #[doc=$doc] /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// **Panics** if broadcasting isn’t possible. impl<'a, A, S, S2, D, E> $trt<&'a ArrayBase> for ArrayBase where A: Clone + $trt, S: DataMut, S2: Data, D: Dimension, E: Dimension, { #[track_caller] fn $method(&mut self, rhs: &ArrayBase) { self.zip_mut_with(rhs, |x, y| { x.$method(y.clone()); }); } } #[doc=$doc] impl $trt for ArrayBase where A: ScalarOperand + $trt, S: DataMut, D: Dimension, { fn $method(&mut self, rhs: A) { self.map_inplace(move |elt| { elt.$method(rhs.clone()); }); } } }; } impl_assign_op!( AddAssign, add_assign, "Perform `self += rhs` as elementwise addition (in place).\n" ); impl_assign_op!( SubAssign, sub_assign, "Perform `self -= rhs` as elementwise subtraction (in place).\n" ); impl_assign_op!( MulAssign, mul_assign, "Perform `self *= rhs` as elementwise multiplication (in place).\n" ); impl_assign_op!( DivAssign, div_assign, "Perform `self /= rhs` as elementwise division (in place).\n" ); impl_assign_op!( RemAssign, rem_assign, "Perform `self %= rhs` as elementwise remainder (in place).\n" ); impl_assign_op!( BitAndAssign, bitand_assign, "Perform `self &= rhs` as elementwise bit and (in place).\n" ); impl_assign_op!( BitOrAssign, bitor_assign, "Perform `self |= rhs` as elementwise bit or (in place).\n" ); impl_assign_op!( BitXorAssign, bitxor_assign, "Perform `self ^= rhs` as elementwise bit xor (in place).\n" ); impl_assign_op!( ShlAssign, shl_assign, "Perform `self <<= rhs` as elementwise left shift (in place).\n" ); impl_assign_op!( ShrAssign, shr_assign, "Perform `self >>= rhs` as elementwise right shift (in place).\n" ); } ndarray-0.16.1/src/impl_owned_array.rs000064400000000000000000001151501046102023000160440ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::ptr::NonNull; use std::mem; use std::mem::MaybeUninit; #[allow(unused_imports)] // Needed for Rust 1.64 use rawpointer::PointerExt; use crate::imp_prelude::*; use crate::dimension; use crate::error::{ErrorKind, ShapeError}; use crate::iterators::Baseiter; use crate::low_level_util::AbortIfPanic; use crate::OwnedRepr; use crate::Zip; /// Methods specific to `Array0`. /// /// ***See also all methods for [`ArrayBase`]*** impl Array { /// Returns the single element in the array without cloning it. /// /// ``` /// use ndarray::{arr0, Array0}; /// /// // `Foo` doesn't implement `Clone`. /// #[derive(Debug, Eq, PartialEq)] /// struct Foo; /// /// let array: Array0 = arr0(Foo); /// let scalar: Foo = array.into_scalar(); /// assert_eq!(scalar, Foo); /// ``` pub fn into_scalar(self) -> A { let size = mem::size_of::(); if size == 0 { // Any index in the `Vec` is fine since all elements are identical. self.data.into_vec().remove(0) } else { // Find the index in the `Vec` corresponding to `self.ptr`. // (This is necessary because the element in the array might not be // the first element in the `Vec`, such as if the array was created // by `array![1, 2, 3, 4].slice_move(s![2])`.) let first = self.ptr.as_ptr() as usize; let base = self.data.as_ptr() as usize; let index = (first - base) / size; debug_assert_eq!((first - base) % size, 0); // Remove the element at the index and return it. self.data.into_vec().remove(index) } } } /// Methods specific to `Array`. /// /// ***See also all methods for [`ArrayBase`]*** impl Array where D: Dimension { /// Returns the offset (in units of `A`) from the start of the allocation /// to the first element, or `None` if the array is empty. fn offset_from_alloc_to_logical_ptr(&self) -> Option { if self.is_empty() { return None; } if std::mem::size_of::() == 0 { Some(dimension::offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides)) } else { let offset = unsafe { self.as_ptr().offset_from(self.data.as_ptr()) }; debug_assert!(offset >= 0); Some(offset as usize) } } /// Return a vector of the elements in the array, in the way they are /// stored internally, and the index in the vector corresponding to the /// logically first element of the array (or 0 if the array is empty). /// /// If the array is in standard memory layout, the logical element order /// of the array (`.iter()` order) and of the returned vector will be the same. /// /// ``` /// use ndarray::{array, Array2, Axis}; /// /// let mut arr: Array2 = array![[1., 2.], [3., 4.], [5., 6.]]; /// arr.slice_axis_inplace(Axis(0), (1..).into()); /// assert_eq!(arr[[0, 0]], 3.); /// let copy = arr.clone(); /// /// let shape = arr.shape().to_owned(); /// let strides = arr.strides().to_owned(); /// let (v, offset) = arr.into_raw_vec_and_offset(); /// /// assert_eq!(v, &[1., 2., 3., 4., 5., 6.]); /// assert_eq!(offset, Some(2)); /// assert_eq!(v[offset.unwrap()], 3.); /// for row in 0..shape[0] { /// for col in 0..shape[1] { /// let index = ( /// offset.unwrap() as isize /// + row as isize * strides[0] /// + col as isize * strides[1] /// ) as usize; /// assert_eq!(v[index], copy[[row, col]]); /// } /// } /// ``` /// /// In the case of zero-sized elements, the offset to the logically first /// element is somewhat meaningless. For convenience, an offset will be /// returned such that all indices computed using the offset, shape, and /// strides will be in-bounds for the `Vec`. Note that this offset won't /// necessarily be the same as the offset for an array of nonzero-sized /// elements sliced in the same way. /// /// ``` /// use ndarray::{array, Array2, Axis}; /// /// let mut arr: Array2<()> = array![[(), ()], [(), ()], [(), ()]]; /// arr.slice_axis_inplace(Axis(0), (1..).into()); /// /// let shape = arr.shape().to_owned(); /// let strides = arr.strides().to_owned(); /// let (v, offset) = arr.into_raw_vec_and_offset(); /// /// assert_eq!(v, &[(), (), (), (), (), ()]); /// for row in 0..shape[0] { /// for col in 0..shape[1] { /// let index = ( /// offset.unwrap() as isize /// + row as isize * strides[0] /// + col as isize * strides[1] /// ) as usize; /// assert_eq!(v[index], ()); /// } /// } /// ``` pub fn into_raw_vec_and_offset(self) -> (Vec, Option) { let offset = self.offset_from_alloc_to_logical_ptr(); (self.data.into_vec(), offset) } /// Return a vector of the elements in the array, in the way they are /// stored internally. /// /// Depending on slicing and strides, the logically first element of the /// array can be located at an offset. Because of this, prefer to use /// `.into_raw_vec_and_offset()` instead. #[deprecated(note = "Use .into_raw_vec_and_offset() instead", since = "0.16.0")] pub fn into_raw_vec(self) -> Vec { self.into_raw_vec_and_offset().0 } } /// Methods specific to `Array2`. /// /// ***See also all methods for [`ArrayBase`]*** impl Array { /// Append a row to an array /// /// The elements from `row` are cloned and added as a new row in the array. /// /// ***Errors*** with a shape error if the length of the row does not match the length of the /// rows in the array. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to /// along the "growing axis". However, if the memory layout needs adjusting, the array must /// reallocate and move memory. /// /// The operation leaves the existing data in place and is most efficent if one of these is /// true: /// /// - The axis being appended to is the longest stride axis, i.e the array is in row major /// ("C") layout. /// - The array has 0 or 1 rows (It is converted to row major) /// /// Ensure appending is efficient by, for example, appending to an empty array and then always /// pushing/appending along the same axis. For pushing rows, ndarray's default layout (C order) /// is efficient. /// /// When repeatedly appending to a single axis, the amortized average complexity of each /// append is O(m), where *m* is the length of the row. /// /// ```rust /// use ndarray::{Array, ArrayView, array}; /// /// // create an empty array and append /// let mut a = Array::zeros((0, 4)); /// a.push_row(ArrayView::from(&[ 1., 2., 3., 4.])).unwrap(); /// a.push_row(ArrayView::from(&[-1., -2., -3., -4.])).unwrap(); /// /// assert_eq!( /// a, /// array![[ 1., 2., 3., 4.], /// [-1., -2., -3., -4.]]); /// ``` pub fn push_row(&mut self, row: ArrayView) -> Result<(), ShapeError> where A: Clone { self.append(Axis(0), row.insert_axis(Axis(0))) } /// Append a column to an array /// /// The elements from `column` are cloned and added as a new column in the array. /// /// ***Errors*** with a shape error if the length of the column does not match the length of /// the columns in the array. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to /// along the "growing axis". However, if the memory layout needs adjusting, the array must /// reallocate and move memory. /// /// The operation leaves the existing data in place and is most efficent if one of these is /// true: /// /// - The axis being appended to is the longest stride axis, i.e the array is in column major /// ("F") layout. /// - The array has 0 or 1 columns (It is converted to column major) /// /// Ensure appending is efficient by, for example, appending to an empty array and then always /// pushing/appending along the same axis. For pushing columns, column major layout (F order) /// is efficient. /// /// When repeatedly appending to a single axis, the amortized average complexity of each append /// is O(m), where *m* is the length of the column. /// /// ```rust /// use ndarray::{Array, ArrayView, array}; /// /// // create an empty array and append /// let mut a = Array::zeros((2, 0)); /// a.push_column(ArrayView::from(&[1., 2.])).unwrap(); /// a.push_column(ArrayView::from(&[-1., -2.])).unwrap(); /// /// assert_eq!( /// a, /// array![[1., -1.], /// [2., -2.]]); /// ``` pub fn push_column(&mut self, column: ArrayView) -> Result<(), ShapeError> where A: Clone { self.append(Axis(1), column.insert_axis(Axis(1))) } /// Reserve capacity to grow array by at least `additional` rows. /// /// Existing elements of `array` are untouched and the backing storage is grown by /// calling the underlying `reserve` method of the `OwnedRepr`. /// /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by /// `additional` exceeds `isize::MAX`. /// /// ```rust /// use ndarray::Array2; /// let mut a = Array2::::zeros((2,4)); /// a.reserve_rows(1000).unwrap(); /// assert!(a.into_raw_vec().capacity() >= 4*1002); /// ``` pub fn reserve_rows(&mut self, additional: usize) -> Result<(), ShapeError> { self.reserve(Axis(0), additional) } /// Reserve capacity to grow array by at least `additional` columns. /// /// Existing elements of `array` are untouched and the backing storage is grown by /// calling the underlying `reserve` method of the `OwnedRepr`. /// /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by /// `additional` exceeds `isize::MAX`. /// /// ```rust /// use ndarray::Array2; /// let mut a = Array2::::zeros((2,4)); /// a.reserve_columns(1000).unwrap(); /// assert!(a.into_raw_vec().capacity() >= 2*1002); /// ``` pub fn reserve_columns(&mut self, additional: usize) -> Result<(), ShapeError> { self.reserve(Axis(1), additional) } } impl Array where D: Dimension { /// Move all elements from self into `new_array`, which must be of the same shape but /// can have a different memory layout. The destination is overwritten completely. /// /// The destination should be a mut reference to an array or an `ArrayViewMut` with /// `A` elements. /// /// ***Panics*** if the shapes don't agree. /// /// ## Example /// /// ``` /// use ndarray::Array; /// /// // Usage example of move_into in safe code /// let mut a = Array::default((10, 10)); /// let b = Array::from_shape_fn((10, 10), |(i, j)| (i + j).to_string()); /// b.move_into(&mut a); /// ``` pub fn move_into<'a, AM>(self, new_array: AM) where AM: Into>, A: 'a, { // Remove generic parameter P and call the implementation let new_array = new_array.into(); if mem::needs_drop::() { self.move_into_needs_drop(new_array); } else { // If `A` doesn't need drop, we can overwrite the destination. // Safe because: move_into_uninit only writes initialized values unsafe { self.move_into_uninit(new_array.into_maybe_uninit()) } } } fn move_into_needs_drop(mut self, new_array: ArrayViewMut) { // Simple case where `A` has a destructor: just swap values between self and new_array. // Afterwards, `self` drops full of initialized values and dropping works as usual. // This avoids moving out of owned values in `self` while at the same time managing // the dropping if the values being overwritten in `new_array`. Zip::from(&mut self) .and(new_array) .for_each(|src, dst| mem::swap(src, dst)); } /// Move all elements from self into `new_array`, which must be of the same shape but /// can have a different memory layout. The destination is overwritten completely. /// /// The destination should be a mut reference to an array or an `ArrayViewMut` with /// `MaybeUninit` elements (which are overwritten without dropping any existing value). /// /// Minor implementation note: Owned arrays like `self` may be sliced in place and own elements /// that are not part of their active view; these are dropped at the end of this function, /// after all elements in the "active view" are moved into `new_array`. If there is a panic in /// drop of any such element, other elements may be leaked. /// /// ***Panics*** if the shapes don't agree. /// /// ## Example /// /// ``` /// use ndarray::Array; /// /// let a = Array::from_iter(0..100).into_shape_with_order((10, 10)).unwrap(); /// let mut b = Array::uninit((10, 10)); /// a.move_into_uninit(&mut b); /// unsafe { /// // we can now promise we have fully initialized `b`. /// let b = b.assume_init(); /// } /// ``` pub fn move_into_uninit<'a, AM>(self, new_array: AM) where AM: Into, D>>, A: 'a, { // Remove generic parameter AM and call the implementation self.move_into_impl(new_array.into()) } fn move_into_impl(mut self, new_array: ArrayViewMut, D>) { unsafe { // Safety: copy_to_nonoverlapping cannot panic let guard = AbortIfPanic(&"move_into: moving out of owned value"); // Move all reachable elements; we move elements out of `self`. // and thus must not panic for the whole section until we call `self.data.set_len(0)`. Zip::from(self.raw_view_mut()) .and(new_array) .for_each(|src, dst| { src.copy_to_nonoverlapping(dst.as_mut_ptr(), 1); }); guard.defuse(); // Drop all unreachable elements self.drop_unreachable_elements(); } } /// This drops all "unreachable" elements in the data storage of self. /// /// That means those elements that are not visible in the slicing of the array. /// *Reachable elements are assumed to already have been moved from.* /// /// # Safety /// /// This is a panic critical section since `self` is already moved-from. fn drop_unreachable_elements(mut self) -> OwnedRepr { let self_len = self.len(); // "deconstruct" self; the owned repr releases ownership of all elements and we // and carry on with raw view methods let data_len = self.data.len(); let has_unreachable_elements = self_len != data_len; if !has_unreachable_elements || mem::size_of::() == 0 || !mem::needs_drop::() { unsafe { self.data.set_len(0); } self.data } else { self.drop_unreachable_elements_slow() } } #[inline(never)] #[cold] fn drop_unreachable_elements_slow(mut self) -> OwnedRepr { // "deconstruct" self; the owned repr releases ownership of all elements and we // carry on with raw view methods let data_len = self.data.len(); let data_ptr = self.data.as_nonnull_mut(); unsafe { // Safety: self.data releases ownership of the elements. Any panics below this point // will result in leaking elements instead of double drops. let self_ = self.raw_view_mut(); self.data.set_len(0); drop_unreachable_raw(self_, data_ptr, data_len); } self.data } /// Create an empty array with an all-zeros shape /// /// ***Panics*** if D is zero-dimensional, because it can't be empty pub(crate) fn empty() -> Array { assert_ne!(D::NDIM, Some(0)); let ndim = D::NDIM.unwrap_or(1); Array::from_shape_simple_fn(D::zeros(ndim), || unreachable!()) } /// Create new_array with the right layout for appending to `growing_axis` #[cold] fn change_to_contig_append_layout(&mut self, growing_axis: Axis) { let ndim = self.ndim(); let mut dim = self.raw_dim(); // The array will be created with 0 (C) or ndim-1 (F) as the biggest stride // axis. Rearrange the shape so that `growing_axis` is the biggest stride axis // afterwards. let mut new_array; if growing_axis == Axis(ndim - 1) { new_array = Self::uninit(dim.f()); } else { dim.slice_mut()[..=growing_axis.index()].rotate_right(1); new_array = Self::uninit(dim); new_array.dim.slice_mut()[..=growing_axis.index()].rotate_left(1); new_array.strides.slice_mut()[..=growing_axis.index()].rotate_left(1); } // self -> old_self. // dummy array -> self. // old_self elements are moved -> new_array. let old_self = std::mem::replace(self, Self::empty()); old_self.move_into_uninit(new_array.view_mut()); // new_array -> self. unsafe { *self = new_array.assume_init(); } } /// Append an array to the array along an axis. /// /// The elements of `array` are cloned and extend the axis `axis` in the present array; /// `self` will grow in size by 1 along `axis`. /// /// Append to the array, where the array being pushed to the array has one dimension less than /// the `self` array. This method is equivalent to [append](ArrayBase::append) in this way: /// `self.append(axis, array.insert_axis(axis))`. /// /// ***Errors*** with a shape error if the shape of self does not match the array-to-append; /// all axes *except* the axis along which it being appended matter for this check: /// the shape of `self` with `axis` removed must be the same as the shape of `array`. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to /// along the "growing axis". However, if the memory layout needs adjusting, the array must /// reallocate and move memory. /// /// The operation leaves the existing data in place and is most efficent if `axis` is a /// "growing axis" for the array, i.e. one of these is true: /// /// - The axis is the longest stride axis, for example the 0th axis in a C-layout or the /// *n-1*th axis in an F-layout array. /// - The axis has length 0 or 1 (It is converted to the new growing axis) /// /// Ensure appending is efficient by for example starting from an empty array and/or always /// appending to an array along the same axis. /// /// The amortized average complexity of the append, when appending along its growing axis, is /// O(*m*) where *m* is the number of individual elements to append. /// /// The memory layout of the argument `array` does not matter to the same extent. /// /// ```rust /// use ndarray::{Array, ArrayView, array, Axis}; /// /// // create an empty array and push rows to it /// let mut a = Array::zeros((0, 4)); /// let ones = ArrayView::from(&[1.; 4]); /// let zeros = ArrayView::from(&[0.; 4]); /// a.push(Axis(0), ones).unwrap(); /// a.push(Axis(0), zeros).unwrap(); /// a.push(Axis(0), ones).unwrap(); /// /// assert_eq!( /// a, /// array![[1., 1., 1., 1.], /// [0., 0., 0., 0.], /// [1., 1., 1., 1.]]); /// ``` pub fn push(&mut self, axis: Axis, array: ArrayView) -> Result<(), ShapeError> where A: Clone, D: RemoveAxis, { // same-dimensionality conversion self.append(axis, array.insert_axis(axis).into_dimensionality::().unwrap()) } /// Append an array to the array along an axis. /// /// The elements of `array` are cloned and extend the axis `axis` in the present array; /// `self` will grow in size by `array.len_of(axis)` along `axis`. /// /// ***Errors*** with a shape error if the shape of self does not match the array-to-append; /// all axes *except* the axis along which it being appended matter for this check: /// the shape of `self` with `axis` removed must be the same as the shape of `array` with /// `axis` removed. /// /// The memory layout of the `self` array matters for ensuring that the append is efficient. /// Appending automatically changes memory layout of the array so that it is appended to /// along the "growing axis". However, if the memory layout needs adjusting, the array must /// reallocate and move memory. /// /// The operation leaves the existing data in place and is most efficent if `axis` is a /// "growing axis" for the array, i.e. one of these is true: /// /// - The axis is the longest stride axis, for example the 0th axis in a C-layout or the /// *n-1*th axis in an F-layout array. /// - The axis has length 0 or 1 (It is converted to the new growing axis) /// /// Ensure appending is efficient by for example starting from an empty array and/or always /// appending to an array along the same axis. /// /// The amortized average complexity of the append, when appending along its growing axis, is /// O(*m*) where *m* is the number of individual elements to append. /// /// The memory layout of the argument `array` does not matter to the same extent. /// /// ```rust /// use ndarray::{Array, ArrayView, array, Axis}; /// /// // create an empty array and append two rows at a time /// let mut a = Array::zeros((0, 4)); /// let ones = ArrayView::from(&[1.; 8]).into_shape_with_order((2, 4)).unwrap(); /// let zeros = ArrayView::from(&[0.; 8]).into_shape_with_order((2, 4)).unwrap(); /// a.append(Axis(0), ones).unwrap(); /// a.append(Axis(0), zeros).unwrap(); /// a.append(Axis(0), ones).unwrap(); /// /// assert_eq!( /// a, /// array![[1., 1., 1., 1.], /// [1., 1., 1., 1.], /// [0., 0., 0., 0.], /// [0., 0., 0., 0.], /// [1., 1., 1., 1.], /// [1., 1., 1., 1.]]); /// ``` pub fn append(&mut self, axis: Axis, mut array: ArrayView) -> Result<(), ShapeError> where A: Clone, D: RemoveAxis, { if self.ndim() == 0 { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } let current_axis_len = self.len_of(axis); let self_dim = self.raw_dim(); let array_dim = array.raw_dim(); let remaining_shape = self_dim.remove_axis(axis); let array_rem_shape = array_dim.remove_axis(axis); if remaining_shape != array_rem_shape { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } let len_to_append = array.len(); let mut res_dim = self_dim; res_dim[axis.index()] += array_dim[axis.index()]; let new_len = dimension::size_of_shape_checked(&res_dim)?; if len_to_append == 0 { // There are no elements to append and shapes are compatible: // either the dimension increment is zero, or there is an existing // zero in another axis in self. debug_assert_eq!(self.len(), new_len); self.dim = res_dim; return Ok(()); } let self_is_empty = self.is_empty(); let mut incompatible_layout = false; // array must be empty or have `axis` as the outermost (longest stride) axis if !self_is_empty && current_axis_len > 1 { // `axis` must be max stride axis or equal to its stride let axis_stride = self.stride_of(axis); if axis_stride < 0 { incompatible_layout = true; } else { for ax in self.axes() { if ax.axis == axis { continue; } if ax.len > 1 && ax.stride.abs() > axis_stride { incompatible_layout = true; break; } } } } // array must be be "full" (contiguous and have no exterior holes) if self.len() != self.data.len() { incompatible_layout = true; } if incompatible_layout { self.change_to_contig_append_layout(axis); // safety-check parameters after remodeling debug_assert_eq!(self_is_empty, self.is_empty()); debug_assert_eq!(current_axis_len, self.len_of(axis)); } let strides = if self_is_empty { // recompute strides - if the array was previously empty, it could have zeros in // strides. // The new order is based on c/f-contig but must have `axis` as outermost axis. if axis == Axis(self.ndim() - 1) { // prefer f-contig when appending to the last axis // Axis n - 1 is outermost axis res_dim.fortran_strides() } else { // standard axis order except for the growing axis; // anticipates that it's likely that `array` has standard order apart from the // growing axis. res_dim.slice_mut()[..=axis.index()].rotate_right(1); let mut strides = res_dim.default_strides(); res_dim.slice_mut()[..=axis.index()].rotate_left(1); strides.slice_mut()[..=axis.index()].rotate_left(1); strides } } else if current_axis_len == 1 { // This is the outermost/longest stride axis; so we find the max across the other axes let new_stride = self.axes().fold(1, |acc, ax| { if ax.axis == axis || ax.len <= 1 { acc } else { let this_ax = ax.len as isize * ax.stride.abs(); if this_ax > acc { this_ax } else { acc } } }); let mut strides = self.strides.clone(); strides[axis.index()] = new_stride as usize; strides } else { self.strides.clone() }; // grow backing storage and update head ptr self.reserve(axis, array_dim[axis.index()])?; unsafe { // clone elements from view to the array now // // To be robust for panics and drop the right elements, we want // to fill the tail in memory order, so that we can drop the right elements on panic. // // We have: Zip::from(tail_view).and(array) // Transform tail_view into standard order by inverting and moving its axes. // Keep the Zip traversal unchanged by applying the same axis transformations to // `array`. This ensures the Zip traverses the underlying memory in order. // // XXX It would be possible to skip this transformation if the element // doesn't have drop. However, in the interest of code coverage, all elements // use this code initially. // Invert axes in tail_view by inverting strides let mut tail_strides = strides.clone(); if tail_strides.ndim() > 1 { for i in 0..tail_strides.ndim() { let s = tail_strides[i] as isize; if s < 0 { tail_strides.set_axis(Axis(i), -s as usize); array.invert_axis(Axis(i)); } } } // With > 0 strides, the current end of data is the correct base pointer for tail_view let tail_ptr = self.data.as_end_nonnull(); let mut tail_view = RawArrayViewMut::new(tail_ptr, array_dim, tail_strides); if tail_view.ndim() > 1 { sort_axes_in_default_order_tandem(&mut tail_view, &mut array); debug_assert!(tail_view.is_standard_layout(), "not std layout dim: {:?}, strides: {:?}", tail_view.shape(), tail_view.strides()); } // Keep track of currently filled length of `self.data` and update it // on scope exit (panic or loop finish). This "indirect" way to // write the length is used to help the compiler, the len store to self.data may // otherwise be mistaken to alias with other stores in the loop. struct SetLenOnDrop<'a, A: 'a> { len: usize, data: &'a mut OwnedRepr, } impl Drop for SetLenOnDrop<'_, A> { fn drop(&mut self) { unsafe { self.data.set_len(self.len); } } } let mut data_length_guard = SetLenOnDrop { len: self.data.len(), data: &mut self.data, }; // Safety: tail_view is constructed to have the same shape as array Zip::from(tail_view) .and_unchecked(array) .debug_assert_c_order() .for_each(|to, from| { to.write(from.clone()); data_length_guard.len += 1; }); drop(data_length_guard); // update array dimension self.strides = strides; self.dim = res_dim; } // multiple assertions after pointer & dimension update debug_assert_eq!(self.data.len(), self.len()); debug_assert_eq!(self.len(), new_len); debug_assert!(self.pointer_is_inbounds()); Ok(()) } /// Reserve capacity to grow array along `axis` by at least `additional` elements. /// /// The axis should be in the range `Axis(` 0 .. *n* `)` where *n* is the /// number of dimensions (axes) of the array. /// /// Existing elements of `array` are untouched and the backing storage is grown by /// calling the underlying `reserve` method of the `OwnedRepr`. /// /// This is useful when pushing or appending repeatedly to an array to avoid multiple /// allocations. /// /// ***Panics*** if the axis is out of bounds. /// /// ***Errors*** with a shape error if the resultant capacity is larger than the addressable /// bounds; that is, the product of non-zero axis lengths once `axis` has been extended by /// `additional` exceeds `isize::MAX`. /// /// ```rust /// use ndarray::{Array3, Axis}; /// let mut a = Array3::::zeros((0,2,4)); /// a.reserve(Axis(0), 1000).unwrap(); /// assert!(a.into_raw_vec().capacity() >= 2*4*1000); /// ``` /// pub fn reserve(&mut self, axis: Axis, additional: usize) -> Result<(), ShapeError> where D: RemoveAxis { debug_assert!(axis.index() < self.ndim()); let self_dim = self.raw_dim(); let remaining_shape = self_dim.remove_axis(axis); // Make sure added capacity doesn't overflow usize::MAX let len_to_append = remaining_shape .size() .checked_mul(additional) .ok_or(ShapeError::from_kind(ErrorKind::Overflow))?; // Make sure new capacity is still in bounds let mut res_dim = self_dim; res_dim[axis.index()] += additional; let new_len = dimension::size_of_shape_checked(&res_dim)?; // Check whether len_to_append would cause an overflow debug_assert_eq!(self.len().checked_add(len_to_append).unwrap(), new_len); unsafe { // grow backing storage and update head ptr let data_to_array_offset = if std::mem::size_of::() != 0 { self.as_ptr().offset_from(self.data.as_ptr()) } else { 0 }; debug_assert!(data_to_array_offset >= 0); self.ptr = self .data .reserve(len_to_append) .offset(data_to_array_offset); } debug_assert!(self.pointer_is_inbounds()); Ok(()) } } /// This drops all "unreachable" elements in `self_` given the data pointer and data length. /// /// # Safety /// /// This is an internal function for use by move_into and IntoIter only, safety invariants may need /// to be upheld across the calls from those implementations. pub(crate) unsafe fn drop_unreachable_raw( mut self_: RawArrayViewMut, data_ptr: NonNull, data_len: usize, ) where D: Dimension { let self_len = self_.len(); for i in 0..self_.ndim() { if self_.stride_of(Axis(i)) < 0 { self_.invert_axis(Axis(i)); } } sort_axes_in_default_order(&mut self_); // with uninverted axes this is now the element with lowest address let array_memory_head_ptr = self_.ptr; let data_end_ptr = data_ptr.add(data_len); debug_assert!(data_ptr <= array_memory_head_ptr); debug_assert!(array_memory_head_ptr <= data_end_ptr); // The idea is simply this: the iterator will yield the elements of self_ in // increasing address order. // // The pointers produced by the iterator are those that we *do not* touch. // The pointers *not mentioned* by the iterator are those we have to drop. // // We have to drop elements in the range from `data_ptr` until (not including) // `data_end_ptr`, except those that are produced by `iter`. // As an optimization, the innermost axis is removed if it has stride 1, because // we then have a long stretch of contiguous elements we can skip as one. let inner_lane_len; if self_.ndim() > 1 && self_.strides.last_elem() == 1 { self_.dim.slice_mut().rotate_right(1); self_.strides.slice_mut().rotate_right(1); inner_lane_len = self_.dim[0]; self_.dim[0] = 1; self_.strides[0] = 1; } else { inner_lane_len = 1; } // iter is a raw pointer iterator traversing the array in memory order now with the // sorted axes. let mut iter = Baseiter::new(self_.ptr, self_.dim, self_.strides); let mut dropped_elements = 0; let mut last_ptr = data_ptr; while let Some(elem_ptr) = iter.next() { // The interval from last_ptr up until (not including) elem_ptr // should now be dropped. This interval may be empty, then we just skip this loop. while last_ptr != elem_ptr { debug_assert!(last_ptr < data_end_ptr); std::ptr::drop_in_place(last_ptr.as_mut()); last_ptr = last_ptr.add(1); dropped_elements += 1; } // Next interval will continue one past the current lane last_ptr = elem_ptr.add(inner_lane_len); } while last_ptr < data_end_ptr { std::ptr::drop_in_place(last_ptr.as_mut()); last_ptr = last_ptr.add(1); dropped_elements += 1; } assert_eq!(data_len, dropped_elements + self_len, "Internal error: inconsistency in move_into"); } /// Sort axes to standard order, i.e Axis(0) has biggest stride and Axis(n - 1) least stride /// /// The axes should have stride >= 0 before calling this method. fn sort_axes_in_default_order(a: &mut ArrayBase) where S: RawData, D: Dimension, { if a.ndim() <= 1 { return; } sort_axes1_impl(&mut a.dim, &mut a.strides); } fn sort_axes1_impl(adim: &mut D, astrides: &mut D) where D: Dimension { debug_assert!(adim.ndim() > 1); debug_assert_eq!(adim.ndim(), astrides.ndim()); // bubble sort axes let mut changed = true; while changed { changed = false; for i in 0..adim.ndim() - 1 { let axis_i = i; let next_axis = i + 1; // make sure higher stride axes sort before. debug_assert!(astrides.slice()[axis_i] as isize >= 0); if (astrides.slice()[axis_i] as isize) < astrides.slice()[next_axis] as isize { changed = true; adim.slice_mut().swap(axis_i, next_axis); astrides.slice_mut().swap(axis_i, next_axis); } } } } /// Sort axes to standard order, i.e Axis(0) has biggest stride and Axis(n - 1) least stride /// /// Axes in a and b are sorted by the strides of `a`, and `a`'s axes should have stride >= 0 before /// calling this method. fn sort_axes_in_default_order_tandem(a: &mut ArrayBase, b: &mut ArrayBase) where S: RawData, S2: RawData, D: Dimension, { if a.ndim() <= 1 { return; } sort_axes2_impl(&mut a.dim, &mut a.strides, &mut b.dim, &mut b.strides); } fn sort_axes2_impl(adim: &mut D, astrides: &mut D, bdim: &mut D, bstrides: &mut D) where D: Dimension { debug_assert!(adim.ndim() > 1); debug_assert_eq!(adim.ndim(), bdim.ndim()); // bubble sort axes let mut changed = true; while changed { changed = false; for i in 0..adim.ndim() - 1 { let axis_i = i; let next_axis = i + 1; // make sure higher stride axes sort before. debug_assert!(astrides.slice()[axis_i] as isize >= 0); if (astrides.slice()[axis_i] as isize) < astrides.slice()[next_axis] as isize { changed = true; adim.slice_mut().swap(axis_i, next_axis); astrides.slice_mut().swap(axis_i, next_axis); bdim.slice_mut().swap(axis_i, next_axis); bstrides.slice_mut().swap(axis_i, next_axis); } } } } ndarray-0.16.1/src/impl_raw_views.rs000064400000000000000000000365161046102023000155500ustar 00000000000000use num_complex::Complex; use std::mem; use std::ptr::NonNull; use crate::dimension::{self, stride_offset}; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; use crate::is_aligned; use crate::shape_builder::{StrideShape, Strides}; impl RawArrayView where D: Dimension { /// Create a new `RawArrayView`. /// /// Unsafe because caller is responsible for ensuring that the array will /// meet all of the invariants of the `ArrayBase` type. #[inline] pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { RawArrayView::from_data_ptr(RawViewRepr::new(), ptr).with_strides_dim(strides, dim) } #[inline] unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { Self::new(nonnull_debug_checked_from_ptr(ptr as *mut A), dim, strides) } /// Create an `RawArrayView` from shape information and a raw pointer /// to the elements. /// /// # Safety /// /// The caller is responsible for ensuring all of the following: /// /// * `ptr` must be non-null, and it must be safe to [`.offset()`] `ptr` by /// zero. /// /// * It must be safe to [`.offset()`] the pointer repeatedly along all /// axes and calculate the `count`s for the `.offset()` calls without /// overflow, even if the array is empty or the elements are zero-sized. /// /// In other words, /// /// * All possible pointers generated by moving along all axes must be in /// bounds or one byte past the end of a single allocation with element /// type `A`. The only exceptions are if the array is empty or the element /// type is zero-sized. In these cases, `ptr` may be dangling, but it must /// still be safe to [`.offset()`] the pointer along the axes. /// /// * The offset in units of bytes between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents the computed offset, in bytes, from overflowing /// `isize` regardless of the starting point due to past offsets. /// /// * The offset in units of `A` between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents overflow when calculating the `count` parameter to /// [`.offset()`] regardless of the starting point due to past offsets. /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// * Strides must be non-negative. /// /// This function can use debug assertions to check some of these requirements, /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self where Sh: Into> { let shape = shape.into(); let dim = shape.dim; if cfg!(debug_assertions) { assert!(!ptr.is_null(), "The pointer must be non-null."); if let Strides::Custom(strides) = &shape.strides { dimension::strides_non_negative(strides).unwrap(); dimension::max_abs_offset_check_overflow::(&dim, strides).unwrap(); } else { dimension::size_of_shape_checked(&dim).unwrap(); } } let strides = shape.strides.strides_for_dim(&dim); RawArrayView::new_(ptr, dim, strides) } /// Converts to a read-only view of the array. /// /// # Safety /// /// From a safety standpoint, this is equivalent to dereferencing a raw /// pointer for every element in the array. You must ensure that all of the /// data is valid, ensure that the pointer is aligned, and choose the /// correct lifetime. #[inline] pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { debug_assert!( is_aligned(self.ptr.as_ptr()), "The pointer must be aligned." ); ArrayView::new(self.ptr, self.dim, self.strides) } /// Split the array view along `axis` and return one array pointer strictly /// before the split and one array pointer after the split. /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { assert!(index <= self.len_of(axis)); let left_ptr = self.ptr.as_ptr(); let right_ptr = if index == self.len_of(axis) { self.ptr.as_ptr() } else { let offset = stride_offset(index, self.strides.axis(axis)); // The `.offset()` is safe due to the guarantees of `RawData`. unsafe { self.ptr.as_ptr().offset(offset) } }; let mut dim_left = self.dim.clone(); dim_left.set_axis(axis, index); let left = unsafe { Self::new_(left_ptr, dim_left, self.strides.clone()) }; let mut dim_right = self.dim; let right_len = dim_right.axis(axis) - index; dim_right.set_axis(axis, right_len); let right = unsafe { Self::new_(right_ptr, dim_right, self.strides) }; (left, right) } /// Cast the raw pointer of the raw array view to a different type /// /// **Panics** if element size is not compatible. /// /// Lack of panic does not imply it is a valid cast. The cast works the same /// way as regular raw pointer casts. /// /// While this method is safe, for the same reason as regular raw pointer /// casts are safe, access through the produced raw view is only possible /// in an unsafe block or function. #[track_caller] pub fn cast(self) -> RawArrayView { assert_eq!( mem::size_of::(), mem::size_of::(), "size mismatch in raw view cast" ); let ptr = self.ptr.cast::(); unsafe { RawArrayView::new(ptr, self.dim, self.strides) } } } impl RawArrayView, D> where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. pub fn split_complex(self) -> Complex> { // Check that the size and alignment of `Complex` are as expected. // These assertions should always pass, for arbitrary `T`. assert_eq!( mem::size_of::>(), mem::size_of::().checked_mul(2).unwrap() ); assert_eq!(mem::align_of::>(), mem::align_of::()); let dim = self.dim.clone(); // Double the strides. In the zero-sized element case and for axes of // length <= 1, we leave the strides as-is to avoid possible overflow. let mut strides = self.strides.clone(); if mem::size_of::() != 0 { for ax in 0..strides.ndim() { if dim[ax] > 1 { strides[ax] = (strides[ax] as isize * 2) as usize; } } } let ptr_re: *mut T = self.ptr.as_ptr().cast(); let ptr_im: *mut T = if self.is_empty() { // In the empty case, we can just reuse the existing pointer since // it won't be dereferenced anyway. It is not safe to offset by // one, since the allocation may be empty. ptr_re } else { // In the nonempty case, we can safely offset into the first // (complex) element. unsafe { ptr_re.add(1) } }; // `Complex` is `repr(C)` with only fields `re: T` and `im: T`. So, the // real components of the elements start at the same pointer, and the // imaginary components start at the pointer offset by one, with // exactly double the strides. The new, doubled strides still meet the // overflow constraints: // // - For the zero-sized element case, the strides are unchanged in // units of bytes and in units of the element type. // // - For the nonzero-sized element case: // // - In units of bytes, the strides are unchanged. The only exception // is axes of length <= 1, but those strides are irrelevant anyway. // // - Since `Complex` for nonzero `T` is always at least 2 bytes, // and the original strides did not overflow in units of bytes, we // know that the new, doubled strides will not overflow in units of // `T`. unsafe { Complex { re: RawArrayView::new_(ptr_re, dim.clone(), strides.clone()), im: RawArrayView::new_(ptr_im, dim, strides), } } } } impl RawArrayViewMut where D: Dimension { /// Create a new `RawArrayViewMut`. /// /// Unsafe because caller is responsible for ensuring that the array will /// meet all of the invariants of the `ArrayBase` type. #[inline] pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { RawArrayViewMut::from_data_ptr(RawViewRepr::new(), ptr).with_strides_dim(strides, dim) } #[inline] unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { Self::new(nonnull_debug_checked_from_ptr(ptr), dim, strides) } /// Create an `RawArrayViewMut` from shape information and a raw /// pointer to the elements. /// /// # Safety /// /// The caller is responsible for ensuring all of the following: /// /// * `ptr` must be non-null, and it must be safe to [`.offset()`] `ptr` by /// zero. /// /// * It must be safe to [`.offset()`] the pointer repeatedly along all /// axes and calculate the `count`s for the `.offset()` calls without /// overflow, even if the array is empty or the elements are zero-sized. /// /// In other words, /// /// * All possible pointers generated by moving along all axes must be in /// bounds or one byte past the end of a single allocation with element /// type `A`. The only exceptions are if the array is empty or the element /// type is zero-sized. In these cases, `ptr` may be dangling, but it must /// still be safe to [`.offset()`] the pointer along the axes. /// /// * The offset in units of bytes between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents the computed offset, in bytes, from overflowing /// `isize` regardless of the starting point due to past offsets. /// /// * The offset in units of `A` between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents overflow when calculating the `count` parameter to /// [`.offset()`] regardless of the starting point due to past offsets. /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// * Strides must be non-negative. /// /// This function can use debug assertions to check some of these requirements, /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self where Sh: Into> { let shape = shape.into(); let dim = shape.dim; if cfg!(debug_assertions) { assert!(!ptr.is_null(), "The pointer must be non-null."); if let Strides::Custom(strides) = &shape.strides { dimension::strides_non_negative(strides).unwrap(); dimension::max_abs_offset_check_overflow::(&dim, strides).unwrap(); assert!(!dimension::dim_stride_overlap(&dim, strides), "The strides must not allow any element to be referenced by two different indices"); } else { dimension::size_of_shape_checked(&dim).unwrap(); } } let strides = shape.strides.strides_for_dim(&dim); RawArrayViewMut::new_(ptr, dim, strides) } /// Converts to a non-mutable `RawArrayView`. #[inline] pub(crate) fn into_raw_view(self) -> RawArrayView { unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) } } /// Converts to a read-only view of the array. /// /// # Safety /// /// From a safety standpoint, this is equivalent to dereferencing a raw /// pointer for every element in the array. You must ensure that all of the /// data is valid, ensure that the pointer is aligned, and choose the /// correct lifetime. #[inline] pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { debug_assert!( is_aligned(self.ptr.as_ptr()), "The pointer must be aligned." ); ArrayView::new(self.ptr, self.dim, self.strides) } /// Converts to a mutable view of the array. /// /// # Safety /// /// From a safety standpoint, this is equivalent to dereferencing a raw /// pointer for every element in the array. You must ensure that all of the /// data is valid, ensure that the pointer is aligned, and choose the /// correct lifetime. #[inline] pub unsafe fn deref_into_view_mut<'a>(self) -> ArrayViewMut<'a, A, D> { debug_assert!( is_aligned(self.ptr.as_ptr()), "The pointer must be aligned." ); ArrayViewMut::new(self.ptr, self.dim, self.strides) } /// Split the array view along `axis` and return one array pointer strictly /// before the split and one array pointer after the split. /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { let (left, right) = self.into_raw_view().split_at(axis, index); unsafe { (Self::new(left.ptr, left.dim, left.strides), Self::new(right.ptr, right.dim, right.strides)) } } /// Cast the raw pointer of the raw array view to a different type /// /// **Panics** if element size is not compatible. /// /// Lack of panic does not imply it is a valid cast. The cast works the same /// way as regular raw pointer casts. /// /// While this method is safe, for the same reason as regular raw pointer /// casts are safe, access through the produced raw view is only possible /// in an unsafe block or function. #[track_caller] pub fn cast(self) -> RawArrayViewMut { assert_eq!( mem::size_of::(), mem::size_of::(), "size mismatch in raw view cast" ); let ptr = self.ptr.cast::(); unsafe { RawArrayViewMut::new(ptr, self.dim, self.strides) } } } impl RawArrayViewMut, D> where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. pub fn split_complex(self) -> Complex> { let Complex { re, im } = self.into_raw_view().split_complex(); unsafe { Complex { re: RawArrayViewMut::new(re.ptr, re.dim, re.strides), im: RawArrayViewMut::new(im.ptr, im.dim, im.strides), } } } } ndarray-0.16.1/src/impl_special_element_types.rs000064400000000000000000000032251046102023000201060ustar 00000000000000// Copyright 2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::mem::MaybeUninit; use crate::imp_prelude::*; use crate::RawDataSubst; /// Methods specific to arrays with `MaybeUninit` elements. /// /// ***See also all methods for [`ArrayBase`]*** impl ArrayBase where S: RawDataSubst>, D: Dimension, { /// **Promise** that the array's elements are all fully initialized, and convert /// the array from element type `MaybeUninit` to `A`. /// /// For example, it can convert an `Array, D>` to `Array`. /// /// ## Safety /// /// Safe to use if all the array's elements have been initialized. /// /// Note that for owned and shared ownership arrays, the promise must include all of the /// array's storage; it is for example possible to slice these in place, but that must /// only be done after all elements have been initialized. pub unsafe fn assume_init(self) -> ArrayBase<>::Output, D> { let ArrayBase { data, ptr, dim, strides, } = self; // "transmute" from storage of MaybeUninit to storage of A let data = S::data_subst(data); let ptr = ptr.cast::(); ArrayBase::from_data_ptr(data, ptr).with_strides_dim(strides, dim) } } ndarray-0.16.1/src/impl_views/constructors.rs000064400000000000000000000253711046102023000174440ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::ptr::NonNull; use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::dimension::{self, CanIndexCheckMode}; use crate::error::ShapeError; use crate::extension::nonnull::nonnull_debug_checked_from_ptr; use crate::imp_prelude::*; use crate::{is_aligned, StrideShape}; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> where D: Dimension { /// Create a read-only array view borrowing its data from a slice. /// /// Checks whether `shape` are compatible with the slice's /// length, returning an `Err` if not compatible. /// /// ``` /// use ndarray::ArrayView; /// use ndarray::arr3; /// use ndarray::ShapeBuilder; /// /// // advanced example where we are even specifying exact strides to use (which is optional). /// let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; /// let a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), /// &s).unwrap(); /// /// assert!( /// a == arr3(&[[[0, 2], /// [4, 6], /// [8, 10]], /// [[1, 3], /// [5, 7], /// [9, 11]]]) /// ); /// assert!(a.strides() == &[1, 4, 2]); /// ``` pub fn from_shape(shape: Sh, xs: &'a [A]) -> Result where Sh: Into> { // eliminate the type parameter Sh as soon as possible Self::from_shape_impl(shape.into(), xs) } fn from_shape_impl(shape: StrideShape, xs: &'a [A]) -> Result { let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides, CanIndexCheckMode::ReadOnly)?; let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::new_( xs.as_ptr() .add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides, )) } } /// Create an `ArrayView` from shape information and a raw pointer to /// the elements. /// /// # Safety /// /// The caller is responsible for ensuring all of the following: /// /// * The elements seen by moving `ptr` according to the shape and strides /// must live at least as long as `'a` and must not be not mutably /// aliased for the duration of `'a`. /// /// * `ptr` must be non-null and aligned, and it must be safe to /// [`.offset()`] `ptr` by zero. /// /// * It must be safe to [`.offset()`] the pointer repeatedly along all /// axes and calculate the `count`s for the `.offset()` calls without /// overflow, even if the array is empty or the elements are zero-sized. /// /// In other words, /// /// * All possible pointers generated by moving along all axes must be in /// bounds or one byte past the end of a single allocation with element /// type `A`. The only exceptions are if the array is empty or the element /// type is zero-sized. In these cases, `ptr` may be dangling, but it must /// still be safe to [`.offset()`] the pointer along the axes. /// /// * The offset in units of bytes between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents the computed offset, in bytes, from overflowing /// `isize` regardless of the starting point due to past offsets. /// /// * The offset in units of `A` between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents overflow when calculating the `count` parameter to /// [`.offset()`] regardless of the starting point due to past offsets. /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// * Strides must be non-negative. /// /// This function can use debug assertions to check some of these requirements, /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self where Sh: Into> { RawArrayView::from_shape_ptr(shape, ptr).deref_into_view() } } /// Methods for read-write array views. impl<'a, A, D> ArrayViewMut<'a, A, D> where D: Dimension { /// Create a read-write array view borrowing its data from a slice. /// /// Checks whether `dim` and `strides` are compatible with the slice's /// length, returning an `Err` if not compatible. /// /// ``` /// use ndarray::ArrayViewMut; /// use ndarray::arr3; /// use ndarray::ShapeBuilder; /// /// let mut s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; /// let mut a = ArrayViewMut::from_shape((2, 3, 2).strides((1, 4, 2)), /// &mut s).unwrap(); /// /// a[[0, 0, 0]] = 1; /// assert!( /// a == arr3(&[[[1, 2], /// [4, 6], /// [8, 10]], /// [[1, 3], /// [5, 7], /// [9, 11]]]) /// ); /// assert!(a.strides() == &[1, 4, 2]); /// ``` pub fn from_shape(shape: Sh, xs: &'a mut [A]) -> Result where Sh: Into> { // eliminate the type parameter Sh as soon as possible Self::from_shape_impl(shape.into(), xs) } fn from_shape_impl(shape: StrideShape, xs: &'a mut [A]) -> Result { let dim = shape.dim; dimension::can_index_slice_with_strides(xs, &dim, &shape.strides, CanIndexCheckMode::OwnedMutable)?; let strides = shape.strides.strides_for_dim(&dim); unsafe { Ok(Self::new_( xs.as_mut_ptr() .add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides, )) } } /// Create an `ArrayViewMut` from shape information and a /// raw pointer to the elements. /// /// # Safety /// /// The caller is responsible for ensuring all of the following: /// /// * The elements seen by moving `ptr` according to the shape and strides /// must live at least as long as `'a` and must not be aliased for the /// duration of `'a`. /// /// * `ptr` must be non-null and aligned, and it must be safe to /// [`.offset()`] `ptr` by zero. /// /// * It must be safe to [`.offset()`] the pointer repeatedly along all /// axes and calculate the `count`s for the `.offset()` calls without /// overflow, even if the array is empty or the elements are zero-sized. /// /// In other words, /// /// * All possible pointers generated by moving along all axes must be in /// bounds or one byte past the end of a single allocation with element /// type `A`. The only exceptions are if the array is empty or the element /// type is zero-sized. In these cases, `ptr` may be dangling, but it must /// still be safe to [`.offset()`] the pointer along the axes. /// /// * The offset in units of bytes between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents the computed offset, in bytes, from overflowing /// `isize` regardless of the starting point due to past offsets. /// /// * The offset in units of `A` between the least address and greatest /// address by moving along all axes must not exceed `isize::MAX`. This /// constraint prevents overflow when calculating the `count` parameter to /// [`.offset()`] regardless of the starting point due to past offsets. /// /// * The product of non-zero axis lengths must not exceed `isize::MAX`. /// /// * Strides must be non-negative. /// /// This function can use debug assertions to check some of these requirements, /// but it's not a complete check. /// /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset #[inline] pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self where Sh: Into> { RawArrayViewMut::from_shape_ptr(shape, ptr).deref_into_view_mut() } /// Convert the view into an `ArrayViewMut<'b, A, D>` where `'b` is a lifetime /// outlived by `'a'`. pub fn reborrow<'b>(self) -> ArrayViewMut<'b, A, D> where 'a: 'b { unsafe { ArrayViewMut::new(self.ptr, self.dim, self.strides) } } } /// Private array view methods impl<'a, A, D> ArrayView<'a, A, D> where D: Dimension { /// Create a new `ArrayView` /// /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline(always)] pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { if cfg!(debug_assertions) { assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); } ArrayView::from_data_ptr(ViewRepr::new(), ptr).with_strides_dim(strides, dim) } /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline] pub(crate) unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { Self::new(nonnull_debug_checked_from_ptr(ptr as *mut A), dim, strides) } } impl<'a, A, D> ArrayViewMut<'a, A, D> where D: Dimension { /// Create a new `ArrayView` /// /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline(always)] pub(crate) unsafe fn new(ptr: NonNull, dim: D, strides: D) -> Self { if cfg!(debug_assertions) { assert!(is_aligned(ptr.as_ptr()), "The pointer must be aligned."); dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); } ArrayViewMut::from_data_ptr(ViewRepr::new(), ptr).with_strides_dim(strides, dim) } /// Create a new `ArrayView` /// /// Unsafe because: `ptr` must be valid for the given dimension and strides. #[inline(always)] pub(crate) unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { Self::new(nonnull_debug_checked_from_ptr(ptr), dim, strides) } } ndarray-0.16.1/src/impl_views/conversions.rs000064400000000000000000000233621046102023000172420ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use alloc::slice; #[allow(unused_imports)] use rawpointer::PointerExt; use std::mem::MaybeUninit; use crate::imp_prelude::*; use crate::{Baseiter, ElementsBase, ElementsBaseMut, Iter, IterMut}; use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::iter::{self, AxisIter, AxisIterMut}; use crate::math_cell::MathCell; use crate::IndexLonger; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> where D: Dimension { /// Convert the view into an `ArrayView<'b, A, D>` where `'b` is a lifetime /// outlived by `'a'`. pub fn reborrow<'b>(self) -> ArrayView<'b, A, D> where 'a: 'b { unsafe { ArrayView::new(self.ptr, self.dim, self.strides) } } /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// /// Note that while the method is similar to [`ArrayBase::as_slice()`], this method transfers /// the view's lifetime to the slice, so it is a bit more powerful. pub fn to_slice(&self) -> Option<&'a [A]> { if self.is_standard_layout() { unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.len())) } } else { None } } /// Return the array’s data as a slice, if it is contiguous. /// Return `None` otherwise. /// /// Note that while the method is similar to /// [`ArrayBase::as_slice_memory_order()`], this method transfers the view's /// lifetime to the slice, so it is a bit more powerful. pub fn to_slice_memory_order(&self) -> Option<&'a [A]> { if self.is_contiguous() { let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); unsafe { Some(slice::from_raw_parts(self.ptr.sub(offset).as_ptr(), self.len())) } } else { None } } /// Converts to a raw array view. #[inline] pub(crate) fn into_raw_view(self) -> RawArrayView { unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) } } } /// Methods specific to `ArrayView0`. /// /// ***See also all methods for [`ArrayView`] and [`ArrayBase`]*** impl<'a, A> ArrayView<'a, A, Ix0> { /// Consume the view and return a reference to the single element in the array. /// /// The lifetime of the returned reference matches the lifetime of the data /// the array view was pointing to. /// /// ``` /// use ndarray::{arr0, Array0}; /// /// // `Foo` doesn't implement `Clone`. /// #[derive(Debug, Eq, PartialEq)] /// struct Foo; /// /// let array: Array0 = arr0(Foo); /// let view = array.view(); /// let scalar: &Foo = view.into_scalar(); /// assert_eq!(scalar, &Foo); /// ``` pub fn into_scalar(self) -> &'a A { self.index(Ix0()) } } /// Methods specific to `ArrayViewMut0`. /// /// ***See also all methods for [`ArrayViewMut`] and [`ArrayBase`]*** impl<'a, A> ArrayViewMut<'a, A, Ix0> { /// Consume the mutable view and return a mutable reference to the single element in the array. /// /// The lifetime of the returned reference matches the lifetime of the data /// the array view was pointing to. /// /// ``` /// use ndarray::{arr0, Array0}; /// /// let mut array: Array0 = arr0(5.); /// let view = array.view_mut(); /// let scalar = view.into_scalar(); /// *scalar = 7.; /// assert_eq!(scalar, &7.); /// assert_eq!(array[()], 7.); /// ``` pub fn into_scalar(self) -> &'a mut A { self.index(Ix0()) } } /// Methods for read-write array views. impl<'a, A, D> ArrayViewMut<'a, A, D> where D: Dimension { /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// /// Note that while this is similar to [`ArrayBase::as_slice_mut()`], this method transfers the /// view's lifetime to the slice. pub fn into_slice(self) -> Option<&'a mut [A]> { self.try_into_slice().ok() } /// Return the array’s data as a slice, if it is contiguous. /// Return `None` otherwise. /// /// Note that while this is similar to /// [`ArrayBase::as_slice_memory_order_mut()`], this method transfers the /// view's lifetime to the slice. pub fn into_slice_memory_order(self) -> Option<&'a mut [A]> { self.try_into_slice_memory_order().ok() } /// Return a shared view of the array with elements as if they were embedded in cells. /// /// The cell view itself can be copied and accessed without exclusivity. /// /// The view acts "as if" the elements are temporarily in cells, and elements /// can be changed through shared references using the regular cell methods. pub fn into_cell_view(self) -> ArrayView<'a, MathCell, D> { // safety: valid because // A and MathCell have the same representation // &'a mut T is interchangeable with &'a Cell -- see method Cell::from_mut in std unsafe { self.into_raw_view_mut() .cast::>() .deref_into_view() } } /// Return the array view as a view of `MaybeUninit` elements /// /// This conversion leaves the elements as they were (presumably initialized), but /// they are represented with the `MaybeUninit` type. Effectively this means that /// the elements can be overwritten without dropping the old element in its place. /// (In some situations this is not what you want, while for `Copy` elements it makes /// no difference at all.) /// /// # Safety /// /// This method allows writing uninitialized data into the view, which could leave any /// original array that we borrow from in an inconsistent state. This is not allowed /// when using the resulting array view. pub(crate) unsafe fn into_maybe_uninit(self) -> ArrayViewMut<'a, MaybeUninit, D> { // Safe because: A and MaybeUninit have the same representation; // and we can go from initialized to (maybe) not unconditionally in terms of // representation. However, the user must be careful to not write uninit elements // through the view. self.into_raw_view_mut() .cast::>() .deref_into_view_mut() } } /// Private raw array view methods impl RawArrayView where D: Dimension { #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } } impl RawArrayViewMut where D: Dimension { #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } } /// Private array view methods impl<'a, A, D> ArrayView<'a, A, D> where D: Dimension { #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } #[inline] pub(crate) fn into_elements_base(self) -> ElementsBase<'a, A, D> { ElementsBase::new(self) } pub(crate) fn into_iter_(self) -> Iter<'a, A, D> { Iter::new(self) } /// Return an outer iterator for this view. #[doc(hidden)] // not official #[deprecated(note = "This method will be replaced.")] pub fn into_outer_iter(self) -> iter::AxisIter<'a, A, D::Smaller> where D: RemoveAxis { AxisIter::new(self, Axis(0)) } } impl<'a, A, D> ArrayViewMut<'a, A, D> where D: Dimension { // Convert into a read-only view pub(crate) fn into_view(self) -> ArrayView<'a, A, D> { unsafe { ArrayView::new(self.ptr, self.dim, self.strides) } } /// Converts to a mutable raw array view. pub(crate) fn into_raw_view_mut(self) -> RawArrayViewMut { unsafe { RawArrayViewMut::new(self.ptr, self.dim, self.strides) } } #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { unsafe { Baseiter::new(self.ptr, self.dim, self.strides) } } #[inline] pub(crate) fn into_elements_base(self) -> ElementsBaseMut<'a, A, D> { ElementsBaseMut::new(self) } /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Otherwise return self in the Err branch of the result. pub(crate) fn try_into_slice(self) -> Result<&'a mut [A], Self> { if self.is_standard_layout() { unsafe { Ok(slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len())) } } else { Err(self) } } /// Return the array’s data as a slice, if it is contiguous. /// Otherwise return self in the Err branch of the result. fn try_into_slice_memory_order(self) -> Result<&'a mut [A], Self> { if self.is_contiguous() { let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides); unsafe { Ok(slice::from_raw_parts_mut(self.ptr.sub(offset).as_ptr(), self.len())) } } else { Err(self) } } pub(crate) fn into_iter_(self) -> IterMut<'a, A, D> { IterMut::new(self) } /// Return an outer iterator for this view. #[doc(hidden)] // not official #[deprecated(note = "This method will be replaced.")] pub fn into_outer_iter(self) -> iter::AxisIterMut<'a, A, D::Smaller> where D: RemoveAxis { AxisIterMut::new(self, Axis(0)) } } ndarray-0.16.1/src/impl_views/indexing.rs000064400000000000000000000156461046102023000165050ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::arraytraits::array_out_of_bounds; use crate::imp_prelude::*; use crate::NdIndex; /// Extra indexing methods for array views /// /// These methods are very similar to regular indexing or calling of the /// `get`/`get_mut` methods that we can use on any array or array view. The /// difference here is in the length of lifetime in the resulting reference. /// /// **Note** that the `ArrayView` (read-only) and `ArrayViewMut` (read-write) differ /// in how they are allowed implement this trait -- `ArrayView`'s implementation /// is usual. If you put in a `ArrayView<'a, T, D>` here, you get references /// `&'a T` out. /// /// For `ArrayViewMut` to obey the borrowing rules we have to consume the /// view if we call any of these methods. (The equivalent of reborrow is /// `.view_mut()` for read-write array views, but if you can use that, /// then the regular indexing / `get_mut` should suffice, too.) /// /// ``` /// use ndarray::IndexLonger; /// use ndarray::ArrayView; /// /// let data = [0.; 256]; /// let long_life_ref = { /// // make a 16 × 16 array view /// let view = ArrayView::from(&data[..]).into_shape_with_order((16, 16)).unwrap(); /// /// // index the view and with `IndexLonger`. /// // Note here that we get a reference with a life that is derived from /// // `data`, the base data, instead of being derived from the view /// IndexLonger::index(&view, [0, 1]) /// }; /// /// // view goes out of scope /// /// assert_eq!(long_life_ref, &0.); /// /// ``` pub trait IndexLonger { /// The type of the reference to the element that is produced, including /// its lifetime. type Output; /// Get a reference of a element through the view. /// /// This method is like `Index::index` but with a longer lifetime (matching /// the array view); which we can only do for the array view and not in the /// `Index` trait. /// /// See also [the `get` method][1] which works for all arrays and array /// views. /// /// [1]: ArrayBase::get /// /// **Panics** if index is out of bounds. #[track_caller] fn index(self, index: I) -> Self::Output; /// Get a reference of a element through the view. /// /// This method is like `ArrayBase::get` but with a longer lifetime (matching /// the array view); which we can only do for the array view and not in the /// `Index` trait. /// /// See also [the `get` method][1] (and [`get_mut`][2]) which works for all arrays and array /// views. /// /// [1]: ArrayBase::get /// [2]: ArrayBase::get_mut /// /// **Panics** if index is out of bounds. #[track_caller] fn get(self, index: I) -> Option; /// Get a reference of a element through the view without boundary check /// /// This method is like `elem` with a longer lifetime (matching the array /// view); which we can't do for general arrays. /// /// See also [the `uget` method][1] which works for all arrays and array /// views. /// /// [1]: ArrayBase::uget /// /// **Note:** only unchecked for non-debug builds of ndarray. /// /// # Safety /// /// The caller must ensure that the index is in-bounds. unsafe fn uget(self, index: I) -> Self::Output; } impl<'a, 'b, I, A, D> IndexLonger for &'b ArrayView<'a, A, D> where I: NdIndex, D: Dimension, { type Output = &'a A; /// Get a reference of a element through the view. /// /// This method is like `Index::index` but with a longer lifetime (matching /// the array view); which we can only do for the array view and not in the /// `Index` trait. /// /// See also [the `get` method][1] which works for all arrays and array /// views. /// /// [1]: ArrayBase::get /// /// **Panics** if index is out of bounds. #[track_caller] fn index(self, index: I) -> &'a A { debug_bounds_check!(self, index); unsafe { &*self.get_ptr(index).unwrap_or_else(|| array_out_of_bounds()) } } fn get(self, index: I) -> Option<&'a A> { unsafe { self.get_ptr(index).map(|ptr| &*ptr) } } /// Get a reference of a element through the view without boundary check /// /// This method is like `elem` with a longer lifetime (matching the array /// view); which we can't do for general arrays. /// /// See also [the `uget` method][1] which works for all arrays and array /// views. /// /// [1]: ArrayBase::uget /// /// **Note:** only unchecked for non-debug builds of ndarray. unsafe fn uget(self, index: I) -> &'a A { debug_bounds_check!(self, index); &*self.as_ptr().offset(index.index_unchecked(&self.strides)) } } impl<'a, I, A, D> IndexLonger for ArrayViewMut<'a, A, D> where I: NdIndex, D: Dimension, { type Output = &'a mut A; /// Convert a mutable array view to a mutable reference of a element. /// /// This method is like `IndexMut::index_mut` but with a longer lifetime /// (matching the array view); which we can only do for the array view and /// not in the `Index` trait. /// /// See also [the `get_mut` method][1] which works for all arrays and array /// views. /// /// [1]: ArrayBase::get_mut /// /// **Panics** if index is out of bounds. #[track_caller] fn index(mut self, index: I) -> &'a mut A { debug_bounds_check!(self, index); unsafe { match self.get_mut_ptr(index) { Some(ptr) => &mut *ptr, None => array_out_of_bounds(), } } } /// Convert a mutable array view to a mutable reference of a element, with /// checked access. /// /// See also [the `get_mut` method][1] which works for all arrays and array /// views. /// /// [1]: ArrayBase::get_mut /// fn get(mut self, index: I) -> Option<&'a mut A> { debug_bounds_check!(self, index); unsafe { match self.get_mut_ptr(index) { Some(ptr) => Some(&mut *ptr), None => None, } } } /// Convert a mutable array view to a mutable reference of a element without /// boundary check. /// /// See also [the `uget_mut` method][1] which works for all arrays and array /// views. /// /// [1]: ArrayBase::uget_mut /// /// **Note:** only unchecked for non-debug builds of ndarray. unsafe fn uget(mut self, index: I) -> &'a mut A { debug_bounds_check!(self, index); &mut *self .as_mut_ptr() .offset(index.index_unchecked(&self.strides)) } } ndarray-0.16.1/src/impl_views/mod.rs000064400000000000000000000001261046102023000154420ustar 00000000000000mod constructors; mod conversions; mod indexing; mod splitting; pub use indexing::*; ndarray-0.16.1/src/impl_views/splitting.rs000064400000000000000000000202241046102023000167010ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::imp_prelude::*; use crate::slice::MultiSliceArg; use num_complex::Complex; /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> where D: Dimension { /// Split the array view along `axis` and return one view strictly before the /// split and one view after the split. /// /// **Panics** if `axis` or `index` is out of bounds. /// /// **Examples:** /// ```rust /// # use ndarray::prelude::*; /// let a = aview2(&[[0, 1, 2, 3], /// [4, 5, 6, 7], /// [8, 9, 0, 1]]); /// /// ``` /// The array view `a` has two axes and shape 3 × 4: /// ```text /// ──▶ Axis(1) /// ┌─────┬─────┬─────┬─────┐ 0 /// │ │ a₀₀ │ a₀₁ │ a₀₂ │ a₀₃ │ /// ▼ ├─────┼─────┼─────┼─────┤ 1 /// Axis(0)│ a₁₀ │ a₁₁ │ a₁₂ │ a₁₃ │ /// ├─────┼─────┼─────┼─────┤ 2 /// │ a₂₀ │ a₂₁ │ a₂₂ │ a₂₃ │ /// └─────┴─────┴─────┴─────┘ 3 ↑ /// 0 1 2 3 4 ← possible split_at indices. /// ``` /// /// Row indices increase along `Axis(0)`, and column indices increase along /// `Axis(1)`. Note that we split “before” an element index, and that /// both 0 and the endpoint are valid split indices. /// /// **Example 1**: Split `a` along the first axis, in this case the rows, at /// index 2.
/// This produces views v1 and v2 of shapes 2 × 4 and 1 × 4: /// /// ```rust /// # use ndarray::prelude::*; /// # let a = aview2(&[[0; 4]; 3]); /// let (v1, v2) = a.split_at(Axis(0), 2); /// ``` /// ```text /// ┌─────┬─────┬─────┬─────┐ 0 ↓ indices /// │ a₀₀ │ a₀₁ │ a₀₂ │ a₀₃ │ along Axis(0) /// ├─────┼─────┼─────┼─────┤ v1 1 /// │ a₁₀ │ a₁₁ │ a₁₂ │ a₁₃ │ /// └─────┴─────┴─────┴─────┘ /// ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ 2 /// ┌─────┬─────┬─────┬─────┐ /// │ a₂₀ │ a₂₁ │ a₂₂ │ a₂₃ │ v2 /// └─────┴─────┴─────┴─────┘ 3 /// ``` /// /// **Example 2**: Split `a` along the second axis, in this case the /// columns, at index 2.
/// This produces views u1 and u2 of shapes 3 × 2 and 3 × 2: /// /// ```rust /// # use ndarray::prelude::*; /// # let a = aview2(&[[0; 4]; 3]); /// let (u1, u2) = a.split_at(Axis(1), 2); /// /// ``` /// ```text /// u1 u2 /// ┌─────┬─────┐┊┌─────┬─────┐ /// │ a₀₀ │ a₀₁ │┊│ a₀₂ │ a₀₃ │ /// ├─────┼─────┤┊├─────┼─────┤ /// │ a₁₀ │ a₁₁ │┊│ a₁₂ │ a₁₃ │ /// ├─────┼─────┤┊├─────┼─────┤ /// │ a₂₀ │ a₂₁ │┊│ a₂₂ │ a₂₃ │ /// └─────┴─────┘┊└─────┴─────┘ /// 0 1 2 3 4 indices → /// along Axis(1) /// ``` #[track_caller] #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { unsafe { let (left, right) = self.into_raw_view().split_at(axis, index); (left.deref_into_view(), right.deref_into_view()) } } } impl<'a, T, D> ArrayView<'a, Complex, D> where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. /// /// ``` /// use ndarray::prelude::*; /// use num_complex::{Complex, Complex64}; /// /// let arr = array![ /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], /// ]; /// let Complex { re, im } = arr.view().split_complex(); /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); /// ``` pub fn split_complex(self) -> Complex> { unsafe { let Complex { re, im } = self.into_raw_view().split_complex(); Complex { re: re.deref_into_view(), im: im.deref_into_view(), } } } } /// Methods for read-write array views. impl<'a, A, D> ArrayViewMut<'a, A, D> where D: Dimension { /// Split the array view along `axis` and return one mutable view strictly /// before the split and one mutable view after the split. /// /// **Panics** if `axis` or `index` is out of bounds. #[track_caller] #[inline] pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { unsafe { let (left, right) = self.into_raw_view_mut().split_at(axis, index); (left.deref_into_view_mut(), right.deref_into_view_mut()) } } /// Split the view into multiple disjoint slices. /// /// This is similar to [`.multi_slice_mut()`], but `.multi_slice_move()` /// consumes `self` and produces views with lifetimes matching that of /// `self`. /// /// See [*Slicing*](#slicing) for full documentation. See also /// [`MultiSliceArg`], [`s!`], [`SliceArg`](crate::SliceArg), and /// [`SliceInfo`](crate::SliceInfo). /// /// [`.multi_slice_mut()`]: ArrayBase::multi_slice_mut /// /// **Panics** if any of the following occur: /// /// * if any of the views would intersect (i.e. if any element would appear in multiple slices) /// * if an index is out of bounds or step size is zero /// * if `D` is `IxDyn` and `info` does not match the number of array axes #[track_caller] pub fn multi_slice_move(self, info: M) -> M::Output where M: MultiSliceArg<'a, A, D> { info.multi_slice_move(self) } } impl<'a, T, D> ArrayViewMut<'a, Complex, D> where D: Dimension { /// Splits the view into views of the real and imaginary components of the /// elements. /// /// ``` /// use ndarray::prelude::*; /// use num_complex::{Complex, Complex64}; /// /// let mut arr = array![ /// [Complex64::new(1., 2.), Complex64::new(3., 4.)], /// [Complex64::new(5., 6.), Complex64::new(7., 8.)], /// [Complex64::new(9., 10.), Complex64::new(11., 12.)], /// ]; /// /// let Complex { mut re, mut im } = arr.view_mut().split_complex(); /// assert_eq!(re, array![[1., 3.], [5., 7.], [9., 11.]]); /// assert_eq!(im, array![[2., 4.], [6., 8.], [10., 12.]]); /// /// re[[0, 1]] = 13.; /// im[[2, 0]] = 14.; /// /// assert_eq!(arr[[0, 1]], Complex64::new(13., 4.)); /// assert_eq!(arr[[2, 0]], Complex64::new(9., 14.)); /// ``` pub fn split_complex(self) -> Complex> { unsafe { let Complex { re, im } = self.into_raw_view_mut().split_complex(); Complex { re: re.deref_into_view_mut(), im: im.deref_into_view_mut(), } } } } ndarray-0.16.1/src/indexes.rs000064400000000000000000000213461046102023000141530ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::Dimension; use crate::dimension::IntoDimension; use crate::split_at::SplitAt; use crate::zip::Offset; use crate::Axis; use crate::Layout; use crate::NdProducer; use crate::{ArrayBase, Data}; /// An iterator over the indexes of an array shape. /// /// Iterator element type is `D`. #[derive(Clone)] pub struct IndicesIter { dim: D, index: Option, } /// Create an iterable of the array shape `shape`. /// /// *Note:* prefer higher order methods, arithmetic operations and /// non-indexed iteration before using indices. pub fn indices(shape: E) -> Indices where E: IntoDimension { let dim = shape.into_dimension(); Indices { start: E::Dim::zeros(dim.ndim()), dim, } } /// Return an iterable of the indices of the passed-in array. /// /// *Note:* prefer higher order methods, arithmetic operations and /// non-indexed iteration before using indices. pub fn indices_of(array: &ArrayBase) -> Indices where S: Data, D: Dimension, { indices(array.dim()) } impl Iterator for IndicesIter where D: Dimension { type Item = D::Pattern; #[inline] fn next(&mut self) -> Option { let index = match self.index { None => return None, Some(ref ix) => ix.clone(), }; self.index = self.dim.next_for(index.clone()); Some(index.into_pattern()) } fn size_hint(&self) -> (usize, Option) { let l = match self.index { None => 0, Some(ref ix) => { let gone = self .dim .default_strides() .slice() .iter() .zip(ix.slice().iter()) .fold(0, |s, (&a, &b)| s + a * b); self.dim.size() - gone } }; (l, Some(l)) } fn fold(self, init: B, mut f: F) -> B where F: FnMut(B, D::Pattern) -> B { let IndicesIter { mut index, dim } = self; let ndim = dim.ndim(); if ndim == 0 { return match index { Some(ix) => f(init, ix.into_pattern()), None => init, }; } let inner_axis = ndim - 1; let inner_len = dim[inner_axis]; let mut acc = init; while let Some(mut ix) = index { // unroll innermost axis for i in ix[inner_axis]..inner_len { ix[inner_axis] = i; acc = f(acc, ix.clone().into_pattern()); } index = dim.next_for(ix); } acc } } impl ExactSizeIterator for IndicesIter where D: Dimension {} impl IntoIterator for Indices where D: Dimension { type Item = D::Pattern; type IntoIter = IndicesIter; fn into_iter(self) -> Self::IntoIter { let sz = self.dim.size(); let index = if sz != 0 { Some(self.start) } else { None }; IndicesIter { index, dim: self.dim } } } /// Indices producer and iterable. /// /// `Indices` is an `NdProducer` that produces the indices of an array shape. #[derive(Copy, Clone, Debug)] pub struct Indices where D: Dimension { start: D, dim: D, } #[derive(Copy, Clone, Debug)] pub struct IndexPtr { index: D, } impl Offset for IndexPtr where D: Dimension + Copy { // stride: The axis to increment type Stride = usize; unsafe fn stride_offset(mut self, stride: Self::Stride, index: usize) -> Self { self.index[stride] += index; self } private_impl! {} } // How the NdProducer for Indices works. // // NdProducer allows for raw pointers (Ptr), strides (Stride) and the produced // item (Item). // // Instead of Ptr, there is `IndexPtr` which is an index value, like [0, 0, 0] // for the three dimensional case. // // The stride is simply which axis is currently being incremented. The stride for axis 1, is 1. // // .stride_offset(stride, index) simply computes the new index along that axis, for example: // [0, 0, 0].stride_offset(1, 10) => [0, 10, 0] axis 1 is incremented by 10. // // .as_ref() converts the Ptr value to an Item. For example [0, 10, 0] => (0, 10, 0) impl NdProducer for Indices { type Item = D::Pattern; type Dim = D; type Ptr = IndexPtr; type Stride = usize; private_impl! {} fn raw_dim(&self) -> Self::Dim { self.dim } fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } fn as_ptr(&self) -> Self::Ptr { IndexPtr { index: self.start } } fn layout(&self) -> Layout { if self.dim.ndim() <= 1 { Layout::one_dimensional() } else { Layout::none() } } unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ptr.index.into_pattern() } unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { let mut index = *i; index += &self.start; IndexPtr { index } } fn stride_of(&self, axis: Axis) -> Self::Stride { axis.index() } #[inline(always)] fn contiguous_stride(&self) -> Self::Stride { 0 } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let start_a = self.start; let mut start_b = start_a; let (a, b) = self.dim.split_at(axis, index); start_b[axis.index()] += index; (Indices { start: start_a, dim: a }, Indices { start: start_b, dim: b }) } } /// An iterator over the indexes of an array shape. /// /// Iterator element type is `D`. #[derive(Clone)] pub struct IndicesIterF { dim: D, index: D, has_remaining: bool, } pub fn indices_iter_f(shape: E) -> IndicesIterF where E: IntoDimension { let dim = shape.into_dimension(); let zero = E::Dim::zeros(dim.ndim()); IndicesIterF { has_remaining: dim.size_checked() != Some(0), index: zero, dim, } } impl Iterator for IndicesIterF where D: Dimension { type Item = D::Pattern; #[inline] fn next(&mut self) -> Option { if !self.has_remaining { None } else { let elt = self.index.clone().into_pattern(); self.has_remaining = self.dim.next_for_f(&mut self.index); Some(elt) } } fn size_hint(&self) -> (usize, Option) { if !self.has_remaining { return (0, Some(0)); } let gone = self .dim .fortran_strides() .slice() .iter() .zip(self.index.slice().iter()) .fold(0, |s, (&a, &b)| s + a * b); let l = self.dim.size() - gone; (l, Some(l)) } } impl ExactSizeIterator for IndicesIterF where D: Dimension {} #[cfg(test)] mod tests { use super::indices; use super::indices_iter_f; #[test] fn test_indices_iter_c_size_hint() { let dim = (3, 4); let mut it = indices(dim).into_iter(); let mut len = dim.0 * dim.1; assert_eq!(it.len(), len); while let Some(_) = it.next() { len -= 1; assert_eq!(it.len(), len); } assert_eq!(len, 0); } #[test] fn test_indices_iter_c_fold() { macro_rules! run_test { ($dim:expr) => { for num_consume in 0..3 { let mut it = indices($dim).into_iter(); for _ in 0..num_consume { it.next(); } let clone = it.clone(); let len = it.len(); let acc = clone.fold(0, |acc, ix| { assert_eq!(ix, it.next().unwrap()); acc + 1 }); assert_eq!(acc, len); assert!(it.next().is_none()); } }; } run_test!(()); run_test!((2,)); run_test!((2, 3)); run_test!((2, 0, 3)); run_test!((2, 3, 4)); run_test!((2, 3, 4, 2)); } #[test] fn test_indices_iter_f_size_hint() { let dim = (3, 4); let mut it = indices_iter_f(dim); let mut len = dim.0 * dim.1; assert_eq!(it.len(), len); while let Some(_) = it.next() { len -= 1; assert_eq!(it.len(), len); } assert_eq!(len, 0); } } ndarray-0.16.1/src/iterators/chunks.rs000064400000000000000000000134501046102023000160200ustar 00000000000000use std::marker::PhantomData; use crate::imp_prelude::*; use crate::Baseiter; use crate::IntoDimension; use crate::{Layout, NdProducer}; impl_ndproducer! { ['a, A, D: Dimension] [Clone => 'a, A, D: Clone ] ExactChunks { base, life, chunk, inner_strides, } ExactChunks<'a, A, D> { type Item = ArrayView<'a, A, D>; type Dim = D; unsafe fn item(&self, ptr) { ArrayView::new_(ptr, self.chunk.clone(), self.inner_strides.clone()) } } } /// Exact chunks producer and iterable. /// /// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. //#[derive(Debug)] pub struct ExactChunks<'a, A, D> { base: RawArrayView, life: PhantomData<&'a A>, chunk: D, inner_strides: D, } impl<'a, A, D: Dimension> ExactChunks<'a, A, D> { /// Creates a new exact chunks producer. /// /// **Panics** if any chunk dimension is zero pub(crate) fn new(a: ArrayView<'a, A, D>, chunk: E) -> Self where E: IntoDimension { let mut a = a.into_raw_view(); let chunk = chunk.into_dimension(); ndassert!( a.ndim() == chunk.ndim(), concat!( "Chunk dimension {} does not match array dimension {} ", "(with array of shape {:?})" ), chunk.ndim(), a.ndim(), a.shape() ); for i in 0..a.ndim() { a.dim[i] /= chunk[i]; } let inner_strides = a.strides.clone(); a.strides *= &chunk; ExactChunks { base: a, life: PhantomData, chunk, inner_strides, } } } impl<'a, A, D> IntoIterator for ExactChunks<'a, A, D> where D: Dimension, A: 'a, { type Item = ::Item; type IntoIter = ExactChunksIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { ExactChunksIter { iter: self.base.into_base_iter(), life: self.life, chunk: self.chunk, inner_strides: self.inner_strides, } } } /// Exact chunks iterator. /// /// See [`.exact_chunks()`](ArrayBase::exact_chunks) for more /// information. pub struct ExactChunksIter<'a, A, D> { iter: Baseiter, life: PhantomData<&'a A>, chunk: D, inner_strides: D, } impl_ndproducer! { ['a, A, D: Dimension] [Clone => ] ExactChunksMut { base, life, chunk, inner_strides, } ExactChunksMut<'a, A, D> { type Item = ArrayViewMut<'a, A, D>; type Dim = D; unsafe fn item(&self, ptr) { ArrayViewMut::new_(ptr, self.chunk.clone(), self.inner_strides.clone()) } } } /// Exact chunks producer and iterable. /// /// See [`.exact_chunks_mut()`](ArrayBase::exact_chunks_mut) /// for more information. //#[derive(Debug)] pub struct ExactChunksMut<'a, A, D> { base: RawArrayViewMut, life: PhantomData<&'a mut A>, chunk: D, inner_strides: D, } impl<'a, A, D: Dimension> ExactChunksMut<'a, A, D> { /// Creates a new exact chunks producer. /// /// **Panics** if any chunk dimension is zero pub(crate) fn new(a: ArrayViewMut<'a, A, D>, chunk: E) -> Self where E: IntoDimension { let mut a = a.into_raw_view_mut(); let chunk = chunk.into_dimension(); ndassert!( a.ndim() == chunk.ndim(), concat!( "Chunk dimension {} does not match array dimension {} ", "(with array of shape {:?})" ), chunk.ndim(), a.ndim(), a.shape() ); for i in 0..a.ndim() { a.dim[i] /= chunk[i]; } let inner_strides = a.strides.clone(); a.strides *= &chunk; ExactChunksMut { base: a, life: PhantomData, chunk, inner_strides, } } } impl<'a, A, D> IntoIterator for ExactChunksMut<'a, A, D> where D: Dimension, A: 'a, { type Item = ::Item; type IntoIter = ExactChunksIterMut<'a, A, D>; fn into_iter(self) -> Self::IntoIter { ExactChunksIterMut { iter: self.base.into_base_iter(), life: self.life, chunk: self.chunk, inner_strides: self.inner_strides, } } } impl_iterator! { ['a, A, D: Dimension] [Clone => 'a, A, D: Clone] ExactChunksIter { iter, life, chunk, inner_strides, } ExactChunksIter<'a, A, D> { type Item = ArrayView<'a, A, D>; fn item(&mut self, ptr) { unsafe { ArrayView::new( ptr, self.chunk.clone(), self.inner_strides.clone()) } } } } impl_iterator! { ['a, A, D: Dimension] [Clone => ] ExactChunksIterMut { iter, chunk, inner_strides, } ExactChunksIterMut<'a, A, D> { type Item = ArrayViewMut<'a, A, D>; fn item(&mut self, ptr) { unsafe { ArrayViewMut::new( ptr, self.chunk.clone(), self.inner_strides.clone()) } } } } /// Exact chunks iterator. /// /// See [`.exact_chunks_mut()`](ArrayBase::exact_chunks_mut) /// for more information. pub struct ExactChunksIterMut<'a, A, D> { iter: Baseiter, life: PhantomData<&'a mut A>, chunk: D, inner_strides: D, } send_sync_read_only!(ExactChunks); send_sync_read_only!(ExactChunksIter); send_sync_read_write!(ExactChunksMut); send_sync_read_write!(ExactChunksIterMut); ndarray-0.16.1/src/iterators/into_iter.rs000064400000000000000000000066571046102023000165340ustar 00000000000000// Copyright 2020-2021 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::mem; use std::ptr::NonNull; use crate::imp_prelude::*; use crate::OwnedRepr; use super::Baseiter; use crate::impl_owned_array::drop_unreachable_raw; /// By-value iterator for an array pub struct IntoIter where D: Dimension { array_data: OwnedRepr
, inner: Baseiter, data_len: usize, /// first memory address of an array element array_head_ptr: NonNull, // if true, the array owns elements that are not reachable by indexing // through all the indices of the dimension. has_unreachable_elements: bool, } impl IntoIter where D: Dimension { /// Create a new by-value iterator that consumes `array` pub(crate) fn new(array: Array) -> Self { unsafe { let array_head_ptr = array.ptr; let mut array_data = array.data; let data_len = array_data.release_all_elements(); debug_assert!(data_len >= array.dim.size()); let has_unreachable_elements = array.dim.size() != data_len; let inner = Baseiter::new(array_head_ptr, array.dim, array.strides); IntoIter { array_data, inner, data_len, array_head_ptr, has_unreachable_elements, } } } } impl Iterator for IntoIter { type Item = A; #[inline] fn next(&mut self) -> Option { self.inner.next().map(|p| unsafe { p.as_ptr().read() }) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { self.inner.len() } } impl Drop for IntoIter where D: Dimension { fn drop(&mut self) { if !self.has_unreachable_elements || mem::size_of::() == 0 || !mem::needs_drop::() { return; } // iterate til the end while let Some(_) = self.next() {} unsafe { let data_ptr = self.array_data.as_nonnull_mut(); let view = RawArrayViewMut::new(self.array_head_ptr, self.inner.dim.clone(), self.inner.strides.clone()); debug_assert!(self.inner.dim.size() < self.data_len, "data_len {} and dim size {}", self.data_len, self.inner.dim.size()); drop_unreachable_raw(view, data_ptr, self.data_len); } } } impl IntoIterator for Array where D: Dimension { type Item = A; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter::new(self) } } impl IntoIterator for ArcArray where D: Dimension, A: Clone, { type Item = A; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter::new(self.into_owned()) } } impl IntoIterator for CowArray<'_, A, D> where D: Dimension, A: Clone, { type Item = A; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter::new(self.into_owned()) } } ndarray-0.16.1/src/iterators/iter.rs000064400000000000000000000011641046102023000154670ustar 00000000000000//! Producers, iterables and iterators. //! //! This module collects all concrete producer, iterable and iterator //! implementation structs. //! //! //! See also [`NdProducer`](crate::NdProducer). pub use crate::dimension::Axes; pub use crate::indexes::{Indices, IndicesIter}; pub use crate::iterators::{ AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, AxisWindows, ExactChunks, ExactChunksIter, ExactChunksIterMut, ExactChunksMut, IndexedIter, IndexedIterMut, IntoIter, Iter, IterMut, Lanes, LanesIter, LanesIterMut, LanesMut, Windows, }; ndarray-0.16.1/src/iterators/lanes.rs000064400000000000000000000064311046102023000156300ustar 00000000000000use std::marker::PhantomData; use super::LanesIter; use super::LanesIterMut; use crate::imp_prelude::*; use crate::{Layout, NdProducer}; impl_ndproducer! { ['a, A, D: Dimension] [Clone => 'a, A, D: Clone ] Lanes { base, inner_len, inner_stride, } Lanes<'a, A, D> { type Item = ArrayView<'a, A, Ix1>; type Dim = D; unsafe fn item(&self, ptr) { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) } } } /// See [`.lanes()`](ArrayBase::lanes) /// for more information. pub struct Lanes<'a, A, D> { base: ArrayView<'a, A, D>, inner_len: Ix, inner_stride: Ixs, } impl<'a, A, D: Dimension> Lanes<'a, A, D> { pub(crate) fn new(v: ArrayView<'a, A, Di>, axis: Axis) -> Self where Di: Dimension { let ndim = v.ndim(); let len; let stride; let iter_v = if ndim == 0 { len = 1; stride = 1; v.try_remove_axis(Axis(0)) } else { let i = axis.index(); len = v.dim[i]; stride = v.strides[i] as isize; v.try_remove_axis(axis) }; Lanes { inner_len: len, inner_stride: stride, base: iter_v, } } } impl_ndproducer! { ['a, A, D: Dimension] [Clone =>] LanesMut { base, inner_len, inner_stride, } LanesMut<'a, A, D> { type Item = ArrayViewMut<'a, A, Ix1>; type Dim = D; unsafe fn item(&self, ptr) { ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) } } } impl<'a, A, D> IntoIterator for Lanes<'a, A, D> where D: Dimension { type Item = ::Item; type IntoIter = LanesIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { LanesIter { iter: self.base.into_base_iter(), inner_len: self.inner_len, inner_stride: self.inner_stride, life: PhantomData, } } } /// See [`.lanes_mut()`](ArrayBase::lanes_mut) /// for more information. pub struct LanesMut<'a, A, D> { base: ArrayViewMut<'a, A, D>, inner_len: Ix, inner_stride: Ixs, } impl<'a, A, D: Dimension> LanesMut<'a, A, D> { pub(crate) fn new(v: ArrayViewMut<'a, A, Di>, axis: Axis) -> Self where Di: Dimension { let ndim = v.ndim(); let len; let stride; let iter_v = if ndim == 0 { len = 1; stride = 1; v.try_remove_axis(Axis(0)) } else { let i = axis.index(); len = v.dim[i]; stride = v.strides[i] as isize; v.try_remove_axis(axis) }; LanesMut { inner_len: len, inner_stride: stride, base: iter_v, } } } impl<'a, A, D> IntoIterator for LanesMut<'a, A, D> where D: Dimension { type Item = ::Item; type IntoIter = LanesIterMut<'a, A, D>; fn into_iter(self) -> Self::IntoIter { LanesIterMut { iter: self.base.into_base_iter(), inner_len: self.inner_len, inner_stride: self.inner_stride, life: PhantomData, } } } ndarray-0.16.1/src/iterators/macros.rs000064400000000000000000000074341046102023000160160ustar 00000000000000// Send and Sync // All the iterators are thread safe the same way the slice's iterator are // read-only iterators use Sync => Send rules, same as `std::slice::Iter`. macro_rules! send_sync_read_only { ($name:ident) => { unsafe impl<'a, A, D> Send for $name<'a, A, D> where A: Sync, D: Send, { } unsafe impl<'a, A, D> Sync for $name<'a, A, D> where A: Sync, D: Sync, { } }; } // read-write iterators use Send => Send rules, same as `std::slice::IterMut`. macro_rules! send_sync_read_write { ($name:ident) => { unsafe impl<'a, A, D> Send for $name<'a, A, D> where A: Send, D: Send, { } unsafe impl<'a, A, D> Sync for $name<'a, A, D> where A: Sync, D: Sync, { } }; } macro_rules! impl_ndproducer { ( [$($typarm:tt)*] [Clone => $($cloneparm:tt)*] $typename:ident { $base:ident, $( $fieldname:ident, )* } $fulltype:ty { $( type $atyn:ident = $atyv:ty; )* unsafe fn item(&$self_:ident, $ptr:pat) { $refexpr:expr } }) => { impl<$($typarm)*> NdProducer for $fulltype { $( type $atyn = $atyv; )* type Ptr = *mut A; type Stride = isize; fn raw_dim(&self) -> D { self.$base.raw_dim() } fn layout(&self) -> Layout { self.$base.layout() } fn as_ptr(&self) -> *mut A { self.$base.as_ptr() as *mut _ } fn contiguous_stride(&self) -> isize { self.$base.contiguous_stride() } unsafe fn as_ref(&$self_, $ptr: *mut A) -> Self::Item { $refexpr } unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.$base.uget_ptr(i) as *mut _ } fn stride_of(&self, axis: Axis) -> isize { self.$base.stride_of(axis) } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let (a, b) = self.$base.split_at(axis, index); ($typename { $base: a, $( $fieldname: self.$fieldname.clone(), )* }, $typename { $base: b, $( $fieldname: self.$fieldname, )* }) } private_impl!{} } expand_if!(@nonempty [$($cloneparm)*] impl<$($cloneparm)*> Clone for $fulltype { fn clone(&self) -> Self { $typename { $base: self.base.clone(), $( $fieldname: self.$fieldname.clone(), )* } } } ); }; } macro_rules! impl_iterator { ( [$($typarm:tt)*] [Clone => $($cloneparm:tt)*] $typename:ident { $base:ident, $( $fieldname:ident, )* } $fulltype:ty { type Item = $ity:ty; fn item(&mut $self_:ident, $elt:pat) { $refexpr:expr } }) => { expand_if!(@nonempty [$($cloneparm)*] impl<$($cloneparm)*> Clone for $fulltype { fn clone(&self) -> Self { $typename { $base: self.$base.clone(), $( $fieldname: self.$fieldname.clone(), )* } } } ); impl<$($typarm)*> Iterator for $fulltype { type Item = $ity; fn next(&mut $self_) -> Option { $self_.$base.next().map(|$elt| { $refexpr }) } fn size_hint(&self) -> (usize, Option) { self.$base.size_hint() } } }; } ndarray-0.16.1/src/iterators/mod.rs000064400000000000000000001214751046102023000153130ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[macro_use] mod macros; mod chunks; mod into_iter; pub mod iter; mod lanes; mod windows; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::iter::FromIterator; use std::marker::PhantomData; use std::ptr; use std::ptr::NonNull; #[allow(unused_imports)] // Needed for Rust 1.64 use rawpointer::PointerExt; use crate::Ix1; use super::{ArrayBase, ArrayView, ArrayViewMut, Axis, Data, NdProducer, RemoveAxis}; use super::{Dimension, Ix, Ixs}; pub use self::chunks::{ExactChunks, ExactChunksIter, ExactChunksIterMut, ExactChunksMut}; pub use self::into_iter::IntoIter; pub use self::lanes::{Lanes, LanesMut}; pub use self::windows::{AxisWindows, Windows}; use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut}; /// Base for iterators over all axes. /// /// Iterator element type is `NonNull`. #[derive(Debug)] pub struct Baseiter { ptr: NonNull, dim: D, strides: D, index: Option, } impl Baseiter { /// Creating a Baseiter is unsafe because shape and stride parameters need /// to be correct to avoid performing an unsafe pointer offset while /// iterating. #[inline] pub unsafe fn new(ptr: NonNull, len: D, stride: D) -> Baseiter { Baseiter { ptr, index: len.first_index(), dim: len, strides: stride, } } } impl Iterator for Baseiter { type Item = NonNull; #[inline] fn next(&mut self) -> Option { let index = match self.index { None => return None, Some(ref ix) => ix.clone(), }; let offset = D::stride_offset(&index, &self.strides); self.index = self.dim.next_for(index); unsafe { Some(self.ptr.offset(offset)) } } fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } fn fold(mut self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { let ndim = self.dim.ndim(); debug_assert_ne!(ndim, 0); let mut accum = init; while let Some(mut index) = self.index { let stride = self.strides.last_elem() as isize; let elem_index = index.last_elem(); let len = self.dim.last_elem(); let offset = D::stride_offset(&index, &self.strides); unsafe { let row_ptr = self.ptr.offset(offset); let mut i = 0; let i_end = len - elem_index; while i < i_end { accum = g(accum, row_ptr.offset(i as isize * stride)); i += 1; } } index.set_last_elem(len - 1); self.index = self.dim.next_for(index); } accum } } impl ExactSizeIterator for Baseiter { fn len(&self) -> usize { match self.index { None => 0, Some(ref ix) => { let gone = self .dim .default_strides() .slice() .iter() .zip(ix.slice().iter()) .fold(0, |s, (&a, &b)| s + a * b); self.dim.size() - gone } } } } impl DoubleEndedIterator for Baseiter { #[inline] fn next_back(&mut self) -> Option { let index = match self.index { None => return None, Some(ix) => ix, }; self.dim[0] -= 1; let offset = Ix1::stride_offset(&self.dim, &self.strides); if index == self.dim { self.index = None; } unsafe { Some(self.ptr.offset(offset)) } } fn nth_back(&mut self, n: usize) -> Option { let index = self.index?; let len = self.dim[0] - index[0]; if n < len { self.dim[0] -= n + 1; let offset = Ix1::stride_offset(&self.dim, &self.strides); if index == self.dim { self.index = None; } unsafe { Some(self.ptr.offset(offset)) } } else { self.index = None; None } } fn rfold(mut self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { let mut accum = init; if let Some(index) = self.index { let elem_index = index[0]; unsafe { // self.dim[0] is the current length while self.dim[0] > elem_index { self.dim[0] -= 1; accum = g( accum, self.ptr .offset(Ix1::stride_offset(&self.dim, &self.strides)), ); } } } accum } } clone_bounds!( [A, D: Clone] Baseiter[A, D] { @copy { ptr, } dim, strides, index, } ); clone_bounds!( ['a, A, D: Clone] ElementsBase['a, A, D] { @copy { life, } inner, } ); impl<'a, A, D: Dimension> ElementsBase<'a, A, D> { pub fn new(v: ArrayView<'a, A, D>) -> Self { ElementsBase { inner: v.into_base_iter(), life: PhantomData, } } } impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D> { type Item = &'a A; #[inline] fn next(&mut self) -> Option<&'a A> { self.inner.next().map(|p| unsafe { p.as_ref() }) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } fn fold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner.fold(init, move |acc, ptr| g(acc, ptr.as_ref())) } } } impl<'a, A> DoubleEndedIterator for ElementsBase<'a, A, Ix1> { #[inline] fn next_back(&mut self) -> Option<&'a A> { self.inner.next_back().map(|p| unsafe { p.as_ref() }) } fn rfold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, ptr.as_ref())) } } } impl<'a, A, D> ExactSizeIterator for ElementsBase<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.inner.len() } } macro_rules! either { ($value:expr, $inner:pat => $result:expr) => { match $value { ElementsRepr::Slice($inner) => $result, ElementsRepr::Counted($inner) => $result, } }; } macro_rules! either_mut { ($value:expr, $inner:ident => $result:expr) => { match $value { ElementsRepr::Slice(ref mut $inner) => $result, ElementsRepr::Counted(ref mut $inner) => $result, } }; } clone_bounds!( ['a, A, D: Clone] Iter['a, A, D] { @copy { } inner, } ); impl<'a, A, D> Iter<'a, A, D> where D: Dimension { pub(crate) fn new(self_: ArrayView<'a, A, D>) -> Self { Iter { inner: if let Some(slc) = self_.to_slice() { ElementsRepr::Slice(slc.iter()) } else { ElementsRepr::Counted(self_.into_elements_base()) }, } } } impl<'a, A, D> IterMut<'a, A, D> where D: Dimension { pub(crate) fn new(self_: ArrayViewMut<'a, A, D>) -> Self { IterMut { inner: match self_.try_into_slice() { Ok(x) => ElementsRepr::Slice(x.iter_mut()), Err(self_) => ElementsRepr::Counted(self_.into_elements_base()), }, } } } #[derive(Clone, Debug)] pub enum ElementsRepr { Slice(S), Counted(C), } /// An iterator over the elements of an array. /// /// Iterator element type is `&'a A`. /// /// See [`.iter()`](ArrayBase::iter) for more information. #[derive(Debug)] pub struct Iter<'a, A, D> { inner: ElementsRepr, ElementsBase<'a, A, D>>, } /// Counted read only iterator #[derive(Debug)] pub struct ElementsBase<'a, A, D> { inner: Baseiter, life: PhantomData<&'a A>, } /// An iterator over the elements of an array (mutable). /// /// Iterator element type is `&'a mut A`. /// /// See [`.iter_mut()`](ArrayBase::iter_mut) for more information. #[derive(Debug)] pub struct IterMut<'a, A, D> { inner: ElementsRepr, ElementsBaseMut<'a, A, D>>, } /// An iterator over the elements of an array. /// /// Iterator element type is `&'a mut A`. #[derive(Debug)] pub struct ElementsBaseMut<'a, A, D> { inner: Baseiter, life: PhantomData<&'a mut A>, } impl<'a, A, D: Dimension> ElementsBaseMut<'a, A, D> { pub fn new(v: ArrayViewMut<'a, A, D>) -> Self { ElementsBaseMut { inner: v.into_base_iter(), life: PhantomData, } } } /// An iterator over the indexes and elements of an array. /// /// See [`.indexed_iter()`](ArrayBase::indexed_iter) for more information. #[derive(Clone)] pub struct IndexedIter<'a, A, D>(ElementsBase<'a, A, D>); /// An iterator over the indexes and elements of an array (mutable). /// /// See [`.indexed_iter_mut()`](ArrayBase::indexed_iter_mut) for more information. pub struct IndexedIterMut<'a, A, D>(ElementsBaseMut<'a, A, D>); impl<'a, A, D> IndexedIter<'a, A, D> where D: Dimension { pub(crate) fn new(x: ElementsBase<'a, A, D>) -> Self { IndexedIter(x) } } impl<'a, A, D> IndexedIterMut<'a, A, D> where D: Dimension { pub(crate) fn new(x: ElementsBaseMut<'a, A, D>) -> Self { IndexedIterMut(x) } } impl<'a, A, D: Dimension> Iterator for Iter<'a, A, D> { type Item = &'a A; #[inline] fn next(&mut self) -> Option<&'a A> { either_mut!(self.inner, iter => iter.next()) } fn size_hint(&self) -> (usize, Option) { either!(self.inner, ref iter => iter.size_hint()) } fn fold(self, init: Acc, g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.fold(init, g)) } fn nth(&mut self, n: usize) -> Option { either_mut!(self.inner, iter => iter.nth(n)) } fn collect(self) -> B where B: FromIterator { either!(self.inner, iter => iter.collect()) } fn all(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.all(f)) } fn any(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.any(f)) } fn find

(&mut self, predicate: P) -> Option where P: FnMut(&Self::Item) -> bool { either_mut!(self.inner, iter => iter.find(predicate)) } fn find_map(&mut self, f: F) -> Option where F: FnMut(Self::Item) -> Option { either_mut!(self.inner, iter => iter.find_map(f)) } fn count(self) -> usize { either!(self.inner, iter => iter.count()) } fn last(self) -> Option { either!(self.inner, iter => iter.last()) } fn position

(&mut self, predicate: P) -> Option where P: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.position(predicate)) } } impl<'a, A> DoubleEndedIterator for Iter<'a, A, Ix1> { #[inline] fn next_back(&mut self) -> Option<&'a A> { either_mut!(self.inner, iter => iter.next_back()) } fn nth_back(&mut self, n: usize) -> Option<&'a A> { either_mut!(self.inner, iter => iter.nth_back(n)) } fn rfold(self, init: Acc, g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.rfold(init, g)) } } impl<'a, A, D> ExactSizeIterator for Iter<'a, A, D> where D: Dimension { fn len(&self) -> usize { either!(self.inner, ref iter => iter.len()) } } impl<'a, A, D: Dimension> Iterator for IndexedIter<'a, A, D> { type Item = (D::Pattern, &'a A); #[inline] fn next(&mut self) -> Option { let index = match self.0.inner.index { None => return None, Some(ref ix) => ix.clone(), }; match self.0.next() { None => None, Some(elem) => Some((index.into_pattern(), elem)), } } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } impl<'a, A, D> ExactSizeIterator for IndexedIter<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.0.inner.len() } } impl<'a, A, D: Dimension> Iterator for IterMut<'a, A, D> { type Item = &'a mut A; #[inline] fn next(&mut self) -> Option<&'a mut A> { either_mut!(self.inner, iter => iter.next()) } fn size_hint(&self) -> (usize, Option) { either!(self.inner, ref iter => iter.size_hint()) } fn fold(self, init: Acc, g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.fold(init, g)) } fn nth(&mut self, n: usize) -> Option { either_mut!(self.inner, iter => iter.nth(n)) } fn collect(self) -> B where B: FromIterator { either!(self.inner, iter => iter.collect()) } fn all(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.all(f)) } fn any(&mut self, f: F) -> bool where F: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.any(f)) } fn find

(&mut self, predicate: P) -> Option where P: FnMut(&Self::Item) -> bool { either_mut!(self.inner, iter => iter.find(predicate)) } fn find_map(&mut self, f: F) -> Option where F: FnMut(Self::Item) -> Option { either_mut!(self.inner, iter => iter.find_map(f)) } fn count(self) -> usize { either!(self.inner, iter => iter.count()) } fn last(self) -> Option { either!(self.inner, iter => iter.last()) } fn position

(&mut self, predicate: P) -> Option where P: FnMut(Self::Item) -> bool { either_mut!(self.inner, iter => iter.position(predicate)) } } impl<'a, A> DoubleEndedIterator for IterMut<'a, A, Ix1> { #[inline] fn next_back(&mut self) -> Option<&'a mut A> { either_mut!(self.inner, iter => iter.next_back()) } fn nth_back(&mut self, n: usize) -> Option<&'a mut A> { either_mut!(self.inner, iter => iter.nth_back(n)) } fn rfold(self, init: Acc, g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { either!(self.inner, iter => iter.rfold(init, g)) } } impl<'a, A, D> ExactSizeIterator for IterMut<'a, A, D> where D: Dimension { fn len(&self) -> usize { either!(self.inner, ref iter => iter.len()) } } impl<'a, A, D: Dimension> Iterator for ElementsBaseMut<'a, A, D> { type Item = &'a mut A; #[inline] fn next(&mut self) -> Option<&'a mut A> { self.inner.next().map(|mut p| unsafe { p.as_mut() }) } fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } fn fold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner .fold(init, move |acc, mut ptr| g(acc, ptr.as_mut())) } } } impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A, Ix1> { #[inline] fn next_back(&mut self) -> Option<&'a mut A> { self.inner.next_back().map(|mut p| unsafe { p.as_mut() }) } fn rfold(self, init: Acc, mut g: G) -> Acc where G: FnMut(Acc, Self::Item) -> Acc { unsafe { self.inner .rfold(init, move |acc, mut ptr| g(acc, ptr.as_mut())) } } } impl<'a, A, D> ExactSizeIterator for ElementsBaseMut<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.inner.len() } } impl<'a, A, D: Dimension> Iterator for IndexedIterMut<'a, A, D> { type Item = (D::Pattern, &'a mut A); #[inline] fn next(&mut self) -> Option { let index = match self.0.inner.index { None => return None, Some(ref ix) => ix.clone(), }; match self.0.next() { None => None, Some(elem) => Some((index.into_pattern(), elem)), } } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } impl<'a, A, D> ExactSizeIterator for IndexedIterMut<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.0.inner.len() } } /// An iterator that traverses over all axes but one, and yields a view for /// each lane along that axis. /// /// See [`.lanes()`](ArrayBase::lanes) for more information. pub struct LanesIter<'a, A, D> { inner_len: Ix, inner_stride: Ixs, iter: Baseiter, life: PhantomData<&'a A>, } clone_bounds!( ['a, A, D: Clone] LanesIter['a, A, D] { @copy { inner_len, inner_stride, life, } iter, } ); impl<'a, A, D> Iterator for LanesIter<'a, A, D> where D: Dimension { type Item = ArrayView<'a, A, Ix1>; fn next(&mut self) -> Option { self.iter .next() .map(|ptr| unsafe { ArrayView::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, A, D> ExactSizeIterator for LanesIter<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.iter.len() } } impl<'a, A> DoubleEndedIterator for LanesIter<'a, A, Ix1> { fn next_back(&mut self) -> Option { self.iter .next_back() .map(|ptr| unsafe { ArrayView::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } } // NOTE: LanesIterMut is a mutable iterator and must not expose aliasing // pointers. Due to this we use an empty slice for the raw data (it's unused // anyway). /// An iterator that traverses over all dimensions but the innermost, /// and yields each inner row (mutable). /// /// See [`.lanes_mut()`](ArrayBase::lanes_mut) /// for more information. pub struct LanesIterMut<'a, A, D> { inner_len: Ix, inner_stride: Ixs, iter: Baseiter, life: PhantomData<&'a mut A>, } impl<'a, A, D> Iterator for LanesIterMut<'a, A, D> where D: Dimension { type Item = ArrayViewMut<'a, A, Ix1>; fn next(&mut self) -> Option { self.iter .next() .map(|ptr| unsafe { ArrayViewMut::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, A, D> ExactSizeIterator for LanesIterMut<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.iter.len() } } impl<'a, A> DoubleEndedIterator for LanesIterMut<'a, A, Ix1> { fn next_back(&mut self) -> Option { self.iter .next_back() .map(|ptr| unsafe { ArrayViewMut::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) }) } } #[derive(Debug)] pub struct AxisIterCore { /// Index along the axis of the value of `.next()`, relative to the start /// of the axis. index: Ix, /// (Exclusive) upper bound on `index`. Initially, this is equal to the /// length of the axis. end: Ix, /// Stride along the axis (offset between consecutive pointers). stride: Ixs, /// Shape of the iterator's items. inner_dim: D, /// Strides of the iterator's items. inner_strides: D, /// Pointer corresponding to `index == 0`. ptr: *mut A, } clone_bounds!( [A, D: Clone] AxisIterCore[A, D] { @copy { index, end, stride, ptr, } inner_dim, inner_strides, } ); impl AxisIterCore { /// Constructs a new iterator over the specified axis. fn new(v: ArrayBase, axis: Axis) -> Self where Di: RemoveAxis, S: Data, { AxisIterCore { index: 0, end: v.len_of(axis), stride: v.stride_of(axis), inner_dim: v.dim.remove_axis(axis), inner_strides: v.strides.remove_axis(axis), ptr: v.ptr.as_ptr(), } } #[inline] unsafe fn offset(&self, index: usize) -> *mut A { debug_assert!( index < self.end, "index={}, end={}, stride={}", index, self.end, self.stride ); self.ptr.offset(index as isize * self.stride) } /// Splits the iterator at `index`, yielding two disjoint iterators. /// /// `index` is relative to the current state of the iterator (which is not /// necessarily the start of the axis). /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. #[track_caller] fn split_at(self, index: usize) -> (Self, Self) { assert!(index <= self.len()); let mid = self.index + index; let left = AxisIterCore { index: self.index, end: mid, stride: self.stride, inner_dim: self.inner_dim.clone(), inner_strides: self.inner_strides.clone(), ptr: self.ptr, }; let right = AxisIterCore { index: mid, end: self.end, stride: self.stride, inner_dim: self.inner_dim, inner_strides: self.inner_strides, ptr: self.ptr, }; (left, right) } /// Does the same thing as `.next()` but also returns the index of the item /// relative to the start of the axis. fn next_with_index(&mut self) -> Option<(usize, *mut A)> { let index = self.index; self.next().map(|ptr| (index, ptr)) } /// Does the same thing as `.next_back()` but also returns the index of the /// item relative to the start of the axis. fn next_back_with_index(&mut self) -> Option<(usize, *mut A)> { self.next_back().map(|ptr| (self.end, ptr)) } } impl Iterator for AxisIterCore where D: Dimension { type Item = *mut A; fn next(&mut self) -> Option { if self.index >= self.end { None } else { let ptr = unsafe { self.offset(self.index) }; self.index += 1; Some(ptr) } } fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } } impl DoubleEndedIterator for AxisIterCore where D: Dimension { fn next_back(&mut self) -> Option { if self.index >= self.end { None } else { let ptr = unsafe { self.offset(self.end - 1) }; self.end -= 1; Some(ptr) } } } impl ExactSizeIterator for AxisIterCore where D: Dimension { fn len(&self) -> usize { self.end - self.index } } /// An iterator that traverses over an axis and /// and yields each subview. /// /// The outermost dimension is `Axis(0)`, created with `.outer_iter()`, /// but you can traverse arbitrary dimension with `.axis_iter()`. /// /// For example, in a 3 × 5 × 5 array, with `axis` equal to `Axis(2)`, /// the iterator element is a 3 × 5 subview (and there are 5 in total). /// /// Iterator element type is `ArrayView<'a, A, D>`. /// /// See [`.outer_iter()`](ArrayBase::outer_iter) /// or [`.axis_iter()`](ArrayBase::axis_iter) /// for more information. #[derive(Debug)] pub struct AxisIter<'a, A, D> { iter: AxisIterCore, life: PhantomData<&'a A>, } clone_bounds!( ['a, A, D: Clone] AxisIter['a, A, D] { @copy { life, } iter, } ); impl<'a, A, D: Dimension> AxisIter<'a, A, D> { /// Creates a new iterator over the specified axis. pub(crate) fn new(v: ArrayView<'a, A, Di>, axis: Axis) -> Self where Di: RemoveAxis { AxisIter { iter: AxisIterCore::new(v, axis), life: PhantomData, } } /// Splits the iterator at `index`, yielding two disjoint iterators. /// /// `index` is relative to the current state of the iterator (which is not /// necessarily the start of the axis). /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. #[track_caller] pub fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.iter.split_at(index); ( AxisIter { iter: left, life: self.life, }, AxisIter { iter: right, life: self.life, }, ) } } impl<'a, A, D> Iterator for AxisIter<'a, A, D> where D: Dimension { type Item = ArrayView<'a, A, D>; fn next(&mut self) -> Option { self.iter.next().map(|ptr| unsafe { self.as_ref(ptr) }) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, A, D> DoubleEndedIterator for AxisIter<'a, A, D> where D: Dimension { fn next_back(&mut self) -> Option { self.iter.next_back().map(|ptr| unsafe { self.as_ref(ptr) }) } } impl<'a, A, D> ExactSizeIterator for AxisIter<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.iter.len() } } /// An iterator that traverses over an axis and /// and yields each subview (mutable) /// /// The outermost dimension is `Axis(0)`, created with `.outer_iter()`, /// but you can traverse arbitrary dimension with `.axis_iter()`. /// /// For example, in a 3 × 5 × 5 array, with `axis` equal to `Axis(2)`, /// the iterator element is a 3 × 5 subview (and there are 5 in total). /// /// Iterator element type is `ArrayViewMut<'a, A, D>`. /// /// See [`.outer_iter_mut()`](ArrayBase::outer_iter_mut) /// or [`.axis_iter_mut()`](ArrayBase::axis_iter_mut) /// for more information. pub struct AxisIterMut<'a, A, D> { iter: AxisIterCore, life: PhantomData<&'a mut A>, } impl<'a, A, D: Dimension> AxisIterMut<'a, A, D> { /// Creates a new iterator over the specified axis. pub(crate) fn new(v: ArrayViewMut<'a, A, Di>, axis: Axis) -> Self where Di: RemoveAxis { AxisIterMut { iter: AxisIterCore::new(v, axis), life: PhantomData, } } /// Splits the iterator at `index`, yielding two disjoint iterators. /// /// `index` is relative to the current state of the iterator (which is not /// necessarily the start of the axis). /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. #[track_caller] pub fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.iter.split_at(index); ( AxisIterMut { iter: left, life: self.life, }, AxisIterMut { iter: right, life: self.life, }, ) } } impl<'a, A, D> Iterator for AxisIterMut<'a, A, D> where D: Dimension { type Item = ArrayViewMut<'a, A, D>; fn next(&mut self) -> Option { self.iter.next().map(|ptr| unsafe { self.as_ref(ptr) }) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, A, D> DoubleEndedIterator for AxisIterMut<'a, A, D> where D: Dimension { fn next_back(&mut self) -> Option { self.iter.next_back().map(|ptr| unsafe { self.as_ref(ptr) }) } } impl<'a, A, D> ExactSizeIterator for AxisIterMut<'a, A, D> where D: Dimension { fn len(&self) -> usize { self.iter.len() } } impl<'a, A, D: Dimension> NdProducer for AxisIter<'a, A, D> { type Item = ::Item; type Dim = Ix1; type Ptr = *mut A; type Stride = isize; fn layout(&self) -> crate::Layout { crate::Layout::one_dimensional() } fn raw_dim(&self) -> Self::Dim { Ix1(self.len()) } fn as_ptr(&self) -> Self::Ptr { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the // iterator remains (i.e. if `self.len() > 0`). unsafe { self.iter.offset(self.iter.index) } } else { // In this case, `self.iter.index` may be past the end, so we must // not call `.offset()`. It's okay to return a dangling pointer // because it will never be used in the length 0 case. std::ptr::NonNull::dangling().as_ptr() } } fn contiguous_stride(&self) -> isize { self.iter.stride } unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayView::new_(ptr, self.iter.inner_dim.clone(), self.iter.inner_strides.clone()) } unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.offset(self.iter.index + i[0]) } fn stride_of(&self, _axis: Axis) -> isize { self.contiguous_stride() } fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { self.split_at(index) } private_impl! {} } impl<'a, A, D: Dimension> NdProducer for AxisIterMut<'a, A, D> { type Item = ::Item; type Dim = Ix1; type Ptr = *mut A; type Stride = isize; fn layout(&self) -> crate::Layout { crate::Layout::one_dimensional() } fn raw_dim(&self) -> Self::Dim { Ix1(self.len()) } fn as_ptr(&self) -> Self::Ptr { if self.len() > 0 { // `self.iter.index` is guaranteed to be in-bounds if any of the // iterator remains (i.e. if `self.len() > 0`). unsafe { self.iter.offset(self.iter.index) } } else { // In this case, `self.iter.index` may be past the end, so we must // not call `.offset()`. It's okay to return a dangling pointer // because it will never be used in the length 0 case. std::ptr::NonNull::dangling().as_ptr() } } fn contiguous_stride(&self) -> isize { self.iter.stride } unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { ArrayViewMut::new_(ptr, self.iter.inner_dim.clone(), self.iter.inner_strides.clone()) } unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.iter.offset(self.iter.index + i[0]) } fn stride_of(&self, _axis: Axis) -> isize { self.contiguous_stride() } fn split_at(self, _axis: Axis, index: usize) -> (Self, Self) { self.split_at(index) } private_impl! {} } /// An iterator that traverses over the specified axis /// and yields views of the specified size on this axis. /// /// For example, in a 2 × 8 × 3 array, if the axis of iteration /// is 1 and the chunk size is 2, the yielded elements /// are 2 × 2 × 3 views (and there are 4 in total). /// /// Iterator element type is `ArrayView<'a, A, D>`. /// /// See [`.axis_chunks_iter()`](ArrayBase::axis_chunks_iter) for more information. pub struct AxisChunksIter<'a, A, D> { iter: AxisIterCore, /// Index of the partial chunk (the chunk smaller than the specified chunk /// size due to the axis length not being evenly divisible). If the axis /// length is evenly divisible by the chunk size, this index is larger than /// the maximum valid index. partial_chunk_index: usize, /// Dimension of the partial chunk. partial_chunk_dim: D, life: PhantomData<&'a A>, } clone_bounds!( ['a, A, D: Clone] AxisChunksIter['a, A, D] { @copy { life, partial_chunk_index, } iter, partial_chunk_dim, } ); /// Computes the information necessary to construct an iterator over chunks /// along an axis, given a `view` of the array, the `axis` to iterate over, and /// the chunk `size`. /// /// Returns an axis iterator with the correct stride to move between chunks, /// the number of chunks, and the shape of the last chunk. /// /// **Panics** if `size == 0`. #[track_caller] fn chunk_iter_parts(v: ArrayView<'_, A, D>, axis: Axis, size: usize) -> (AxisIterCore, usize, D) { assert_ne!(size, 0, "Chunk size must be nonzero."); let axis_len = v.len_of(axis); let n_whole_chunks = axis_len / size; let chunk_remainder = axis_len % size; let iter_len = if chunk_remainder == 0 { n_whole_chunks } else { n_whole_chunks + 1 }; let stride = if n_whole_chunks == 0 { // This case avoids potential overflow when `size > axis_len`. 0 } else { v.stride_of(axis) * size as isize }; let axis = axis.index(); let mut inner_dim = v.dim.clone(); inner_dim[axis] = size; let mut partial_chunk_dim = v.dim; partial_chunk_dim[axis] = chunk_remainder; let partial_chunk_index = n_whole_chunks; let iter = AxisIterCore { index: 0, end: iter_len, stride, inner_dim, inner_strides: v.strides, ptr: v.ptr.as_ptr(), }; (iter, partial_chunk_index, partial_chunk_dim) } impl<'a, A, D: Dimension> AxisChunksIter<'a, A, D> { pub(crate) fn new(v: ArrayView<'a, A, D>, axis: Axis, size: usize) -> Self { let (iter, partial_chunk_index, partial_chunk_dim) = chunk_iter_parts(v, axis, size); AxisChunksIter { iter, partial_chunk_index, partial_chunk_dim, life: PhantomData, } } } macro_rules! chunk_iter_impl { ($iter:ident, $array:ident) => { impl<'a, A, D> $iter<'a, A, D> where D: Dimension, { fn get_subview(&self, index: usize, ptr: *mut A) -> $array<'a, A, D> { if index != self.partial_chunk_index { unsafe { $array::new_( ptr, self.iter.inner_dim.clone(), self.iter.inner_strides.clone(), ) } } else { unsafe { $array::new_( ptr, self.partial_chunk_dim.clone(), self.iter.inner_strides.clone(), ) } } } /// Splits the iterator at index, yielding two disjoint iterators. /// /// `index` is relative to the current state of the iterator (which is not /// necessarily the start of the axis). /// /// **Panics** if `index` is strictly greater than the iterator's remaining /// length. #[track_caller] pub fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.iter.split_at(index); ( Self { iter: left, partial_chunk_index: self.partial_chunk_index, partial_chunk_dim: self.partial_chunk_dim.clone(), life: self.life, }, Self { iter: right, partial_chunk_index: self.partial_chunk_index, partial_chunk_dim: self.partial_chunk_dim, life: self.life, }, ) } } impl<'a, A, D> Iterator for $iter<'a, A, D> where D: Dimension, { type Item = $array<'a, A, D>; fn next(&mut self) -> Option { self.iter .next_with_index() .map(|(index, ptr)| self.get_subview(index, ptr)) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, A, D> DoubleEndedIterator for $iter<'a, A, D> where D: Dimension, { fn next_back(&mut self) -> Option { self.iter .next_back_with_index() .map(|(index, ptr)| self.get_subview(index, ptr)) } } impl<'a, A, D> ExactSizeIterator for $iter<'a, A, D> where D: Dimension {} }; } /// An iterator that traverses over the specified axis /// and yields mutable views of the specified size on this axis. /// /// For example, in a 2 × 8 × 3 array, if the axis of iteration /// is 1 and the chunk size is 2, the yielded elements /// are 2 × 2 × 3 views (and there are 4 in total). /// /// Iterator element type is `ArrayViewMut<'a, A, D>`. /// /// See [`.axis_chunks_iter_mut()`](ArrayBase::axis_chunks_iter_mut) /// for more information. pub struct AxisChunksIterMut<'a, A, D> { iter: AxisIterCore, partial_chunk_index: usize, partial_chunk_dim: D, life: PhantomData<&'a mut A>, } impl<'a, A, D: Dimension> AxisChunksIterMut<'a, A, D> { pub(crate) fn new(v: ArrayViewMut<'a, A, D>, axis: Axis, size: usize) -> Self { let (iter, partial_chunk_index, partial_chunk_dim) = chunk_iter_parts(v.into_view(), axis, size); AxisChunksIterMut { iter, partial_chunk_index, partial_chunk_dim, life: PhantomData, } } } chunk_iter_impl!(AxisChunksIter, ArrayView); chunk_iter_impl!(AxisChunksIterMut, ArrayViewMut); send_sync_read_only!(Iter); send_sync_read_only!(IndexedIter); send_sync_read_only!(LanesIter); send_sync_read_only!(AxisIter); send_sync_read_only!(AxisChunksIter); send_sync_read_only!(ElementsBase); send_sync_read_write!(IterMut); send_sync_read_write!(IndexedIterMut); send_sync_read_write!(LanesIterMut); send_sync_read_write!(AxisIterMut); send_sync_read_write!(AxisChunksIterMut); send_sync_read_write!(ElementsBaseMut); /// (Trait used internally) An iterator that we trust /// to deliver exactly as many items as it said it would. /// /// The iterator must produce exactly the number of elements it reported or /// diverge before reaching the end. #[allow(clippy::missing_safety_doc)] // not nameable downstream pub unsafe trait TrustedIterator {} use crate::indexes::IndicesIterF; use crate::iter::IndicesIter; #[cfg(feature = "std")] use crate::{geomspace::Geomspace, linspace::Linspace, logspace::Logspace}; #[cfg(feature = "std")] unsafe impl TrustedIterator for Linspace {} #[cfg(feature = "std")] unsafe impl TrustedIterator for Geomspace {} #[cfg(feature = "std")] unsafe impl TrustedIterator for Logspace {} unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> {} unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> {} unsafe impl TrustedIterator for std::iter::Cloned where I: TrustedIterator {} unsafe impl TrustedIterator for std::iter::Map where I: TrustedIterator {} unsafe impl<'a, A> TrustedIterator for slice::Iter<'a, A> {} unsafe impl<'a, A> TrustedIterator for slice::IterMut<'a, A> {} unsafe impl TrustedIterator for ::std::ops::Range {} // FIXME: These indices iter are dubious -- size needs to be checked up front. unsafe impl TrustedIterator for IndicesIter where D: Dimension {} unsafe impl TrustedIterator for IndicesIterF where D: Dimension {} unsafe impl TrustedIterator for IntoIter where D: Dimension {} /// Like Iterator::collect, but only for trusted length iterators pub fn to_vec(iter: I) -> Vec where I: TrustedIterator + ExactSizeIterator { to_vec_mapped(iter, |x| x) } /// Like Iterator::collect, but only for trusted length iterators pub fn to_vec_mapped(iter: I, mut f: F) -> Vec where I: TrustedIterator + ExactSizeIterator, F: FnMut(I::Item) -> B, { // Use an `unsafe` block to do this efficiently. // We know that iter will produce exactly .size() elements, // and the loop can vectorize if it's clean (without branch to grow the vector). let (size, _) = iter.size_hint(); let mut result = Vec::with_capacity(size); let mut out_ptr = result.as_mut_ptr(); let mut len = 0; iter.fold((), |(), elt| unsafe { ptr::write(out_ptr, f(elt)); len += 1; result.set_len(len); out_ptr = out_ptr.offset(1); }); debug_assert_eq!(size, result.len()); result } ndarray-0.16.1/src/iterators/windows.rs000064400000000000000000000152251046102023000162210ustar 00000000000000use std::marker::PhantomData; use super::Baseiter; use crate::imp_prelude::*; use crate::IntoDimension; use crate::Layout; use crate::NdProducer; use crate::Slice; /// Window producer and iterable /// /// See [`.windows()`](ArrayBase::windows) for more /// information. pub struct Windows<'a, A, D> { base: RawArrayView, life: PhantomData<&'a A>, window: D, strides: D, } impl<'a, A, D: Dimension> Windows<'a, A, D> { pub(crate) fn new(a: ArrayView<'a, A, D>, window_size: E) -> Self where E: IntoDimension { let window = window_size.into_dimension(); let ndim = window.ndim(); let mut unit_stride = D::zeros(ndim); unit_stride.slice_mut().fill(1); Windows::new_with_stride(a, window, unit_stride) } pub(crate) fn new_with_stride(a: ArrayView<'a, A, D>, window_size: E, axis_strides: E) -> Self where E: IntoDimension { let window = window_size.into_dimension(); let strides = axis_strides.into_dimension(); let window_strides = a.strides.clone(); let base = build_base(a, window.clone(), strides); Windows { base: base.into_raw_view(), life: PhantomData, window, strides: window_strides, } } } impl_ndproducer! { ['a, A, D: Dimension] [Clone => 'a, A, D: Clone ] Windows { base, life, window, strides, } Windows<'a, A, D> { type Item = ArrayView<'a, A, D>; type Dim = D; unsafe fn item(&self, ptr) { ArrayView::new_(ptr, self.window.clone(), self.strides.clone()) } } } impl<'a, A, D> IntoIterator for Windows<'a, A, D> where D: Dimension, A: 'a, { type Item = ::Item; type IntoIter = WindowsIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { WindowsIter { iter: self.base.into_base_iter(), life: self.life, window: self.window, strides: self.strides, } } } /// Window iterator. /// /// See [`.windows()`](ArrayBase::windows) for more /// information. pub struct WindowsIter<'a, A, D> { iter: Baseiter, life: PhantomData<&'a A>, window: D, strides: D, } impl_iterator! { ['a, A, D: Dimension] [Clone => 'a, A, D: Clone] WindowsIter { iter, life, window, strides, } WindowsIter<'a, A, D> { type Item = ArrayView<'a, A, D>; fn item(&mut self, ptr) { unsafe { ArrayView::new( ptr, self.window.clone(), self.strides.clone()) } } } } send_sync_read_only!(Windows); send_sync_read_only!(WindowsIter); /// Window producer and iterable /// /// See [`.axis_windows()`](ArrayBase::axis_windows) for more /// information. pub struct AxisWindows<'a, A, D> { base: ArrayView<'a, A, D>, axis_idx: usize, window: D, strides: D, } impl<'a, A, D: Dimension> AxisWindows<'a, A, D> { pub(crate) fn new(a: ArrayView<'a, A, D>, axis: Axis, window_size: usize) -> Self { let window_strides = a.strides.clone(); let axis_idx = axis.index(); let mut window = a.raw_dim(); window[axis_idx] = window_size; let ndim = window.ndim(); let mut unit_stride = D::zeros(ndim); unit_stride.slice_mut().fill(1); let base = build_base(a, window.clone(), unit_stride); AxisWindows { base, axis_idx, window, strides: window_strides, } } } impl<'a, A, D: Dimension> NdProducer for AxisWindows<'a, A, D> { type Item = ArrayView<'a, A, D>; type Dim = Ix1; type Ptr = *mut A; type Stride = isize; fn raw_dim(&self) -> Ix1 { Ix1(self.base.raw_dim()[self.axis_idx]) } fn layout(&self) -> Layout { self.base.layout() } fn as_ptr(&self) -> *mut A { self.base.as_ptr() as *mut _ } fn contiguous_stride(&self) -> isize { self.base.contiguous_stride() } unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { ArrayView::new_(ptr, self.window.clone(), self.strides.clone()) } unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { let mut d = D::zeros(self.base.ndim()); d[self.axis_idx] = i[0]; self.base.uget_ptr(&d) } fn stride_of(&self, axis: Axis) -> isize { assert_eq!(axis, Axis(0)); self.base.stride_of(Axis(self.axis_idx)) } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { assert_eq!(axis, Axis(0)); let (a, b) = self.base.split_at(Axis(self.axis_idx), index); ( AxisWindows { base: a, axis_idx: self.axis_idx, window: self.window.clone(), strides: self.strides.clone(), }, AxisWindows { base: b, axis_idx: self.axis_idx, window: self.window, strides: self.strides, }, ) } private_impl!{} } impl<'a, A, D> IntoIterator for AxisWindows<'a, A, D> where D: Dimension, A: 'a, { type Item = ::Item; type IntoIter = WindowsIter<'a, A, D>; fn into_iter(self) -> Self::IntoIter { WindowsIter { iter: self.base.into_base_iter(), life: PhantomData, window: self.window, strides: self.strides, } } } /// build the base array of the `Windows` and `AxisWindows` structs fn build_base(a: ArrayView, window: D, strides: D) -> ArrayView where D: Dimension { ndassert!( a.ndim() == window.ndim(), concat!( "Window dimension {} does not match array dimension {} ", "(with array of shape {:?})" ), window.ndim(), a.ndim(), a.shape() ); ndassert!( a.ndim() == strides.ndim(), concat!( "Stride dimension {} does not match array dimension {} ", "(with array of shape {:?})" ), strides.ndim(), a.ndim(), a.shape() ); let mut base = a; base.slice_each_axis_inplace(|ax_desc| { let len = ax_desc.len; let wsz = window[ax_desc.axis.index()]; let stride = strides[ax_desc.axis.index()]; if len < wsz { Slice::new(0, Some(0), 1) } else { Slice::new(0, Some((len - wsz + 1) as isize), stride as isize) } }); base } ndarray-0.16.1/src/itertools.rs000064400000000000000000000071671046102023000145450ustar 00000000000000// Copyright 2014-2019 bluss and ndarray developers // and Michał Krasnoborski (krdln) // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A few iterator-related utilities and tools use std::iter; /// Iterate `iterable` with a running index. /// /// `IntoIterator` enabled version of `.enumerate()`. /// /// ``` /// use itertools::enumerate; /// /// for (i, elt) in enumerate(&[1, 2, 3]) { /// /* loop body */ /// } /// ``` pub(crate) fn enumerate(iterable: I) -> iter::Enumerate where I: IntoIterator { iterable.into_iter().enumerate() } /// Iterate `i` and `j` in lock step. /// /// `IntoIterator` enabled version of `i.zip(j)`. /// /// ``` /// use itertools::zip; /// /// let data = [1, 2, 3, 4, 5]; /// for (a, b) in zip(&data, &data[1..]) { /// /* loop body */ /// } /// ``` pub(crate) fn zip(i: I, j: J) -> iter::Zip where I: IntoIterator, J: IntoIterator, { i.into_iter().zip(j) } /// Create an iterator running multiple iterators in lockstep. /// /// The `izip!` iterator yields elements until any subiterator /// returns `None`. /// /// This is a version of the standard ``.zip()`` that's supporting more than /// two iterators. The iterator element type is a tuple with one element /// from each of the input iterators. Just like ``.zip()``, the iteration stops /// when the shortest of the inputs reaches its end. /// /// **Note:** The result of this macro is in the general case an iterator /// composed of repeated `.zip()` and a `.map()`; it has an anonymous type. /// The special cases of one and two arguments produce the equivalent of /// `$a.into_iter()` and `$a.into_iter().zip($b)` respectively. /// /// Prefer this macro `izip!()` over `multizip` for the performance benefits /// of using the standard library `.zip()`. /// /// ``` /// #[macro_use] extern crate itertools; /// # fn main() { /// /// // iterate over three sequences side-by-side /// let mut results = [0, 0, 0, 0]; /// let inputs = [3, 7, 9, 6]; /// /// for (r, index, input) in izip!(&mut results, 0..10, &inputs) { /// *r = index * 10 + input; /// } /// /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); /// # } /// ``` /// /// **Note:** To enable the macros in this crate, use the `#[macro_use]` /// attribute when importing the crate: /// /// ```no_run /// # #[allow(unused_imports)] /// #[macro_use] extern crate itertools; /// # fn main() { } /// ``` macro_rules! izip { // @closure creates a tuple-flattening closure for .map() call. usage: // @closure partial_pattern => partial_tuple , rest , of , iterators // eg. izip!( @closure ((a, b), c) => (a, b, c) , dd , ee ) ( @closure $p:pat => $tup:expr ) => { |$p| $tup }; // The "b" identifier is a different identifier on each recursion level thanks to hygiene. ( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => { izip!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*) }; // unary ($first:expr $(,)*) => { IntoIterator::into_iter($first) }; // binary ($first:expr, $second:expr $(,)*) => { izip!($first) .zip($second) }; // n-ary where n > 2 ( $first:expr $( , $rest:expr )* $(,)* ) => { izip!($first) $( .zip($rest) )* .map( izip!(@closure a => (a) $( , $rest )*) ) }; } ndarray-0.16.1/src/layout/layoutfmt.rs000064400000000000000000000016561046102023000160570ustar 00000000000000// Copyright 2017 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::Layout; const LAYOUT_NAMES: &[&str] = &["C", "F", "c", "f"]; use std::fmt; impl fmt::Debug for Layout { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.0 == 0 { write!(f, "Custom")? } else { (0..32).filter(|&i| self.is(1 << i)).try_fold((), |_, i| { if let Some(name) = LAYOUT_NAMES.get(i) { write!(f, "{}", name) } else { write!(f, "{:#x}", i) } })?; }; write!(f, " ({:#x})", self.0) } } ndarray-0.16.1/src/layout/mod.rs000064400000000000000000000163601046102023000146100ustar 00000000000000mod layoutfmt; // Layout it a bitset used for internal layout description of // arrays, producers and sets of producers. // The type is public but users don't interact with it. #[doc(hidden)] /// Memory layout description #[derive(Copy, Clone)] pub struct Layout(u32); impl Layout { pub(crate) const CORDER: u32 = 0b01; pub(crate) const FORDER: u32 = 0b10; pub(crate) const CPREFER: u32 = 0b0100; pub(crate) const FPREFER: u32 = 0b1000; #[inline(always)] pub(crate) fn is(self, flag: u32) -> bool { self.0 & flag != 0 } /// Return layout common to both inputs #[inline(always)] pub(crate) fn intersect(self, other: Layout) -> Layout { Layout(self.0 & other.0) } /// Return a layout that simultaneously "is" what both of the inputs are #[inline(always)] pub(crate) fn also(self, other: Layout) -> Layout { Layout(self.0 | other.0) } #[inline(always)] pub(crate) fn one_dimensional() -> Layout { Layout::c().also(Layout::f()) } #[inline(always)] pub(crate) fn c() -> Layout { Layout(Layout::CORDER | Layout::CPREFER) } #[inline(always)] pub(crate) fn f() -> Layout { Layout(Layout::FORDER | Layout::FPREFER) } #[inline(always)] pub(crate) fn cpref() -> Layout { Layout(Layout::CPREFER) } #[inline(always)] pub(crate) fn fpref() -> Layout { Layout(Layout::FPREFER) } #[inline(always)] pub(crate) fn none() -> Layout { Layout(0) } /// A simple "score" method which scores positive for preferring C-order, negative for F-order /// Subject to change when we can describe other layouts #[inline] pub(crate) fn tendency(self) -> i32 { (self.is(Layout::CORDER) as i32 - self.is(Layout::FORDER) as i32) + (self.is(Layout::CPREFER) as i32 - self.is(Layout::FPREFER) as i32) } } #[cfg(test)] mod tests { use super::*; use crate::imp_prelude::*; use crate::NdProducer; type M = Array2; type M1 = Array1; type M0 = Array0; macro_rules! assert_layouts { ($mat:expr, $($layout:ident),*) => {{ let layout = $mat.view().layout(); $( assert!(layout.is(Layout::$layout), "Assertion failed: array {:?} is not layout {}", $mat, stringify!($layout)); )* }}; } macro_rules! assert_not_layouts { ($mat:expr, $($layout:ident),*) => {{ let layout = $mat.view().layout(); $( assert!(!layout.is(Layout::$layout), "Assertion failed: array {:?} show not have layout {}", $mat, stringify!($layout)); )* }}; } #[test] fn contig_layouts() { let a = M::zeros((5, 5)); let b = M::zeros((5, 5).f()); let ac = a.view().layout(); let af = b.view().layout(); assert!(ac.is(Layout::CORDER) && ac.is(Layout::CPREFER)); assert!(!ac.is(Layout::FORDER) && !ac.is(Layout::FPREFER)); assert!(!af.is(Layout::CORDER) && !af.is(Layout::CPREFER)); assert!(af.is(Layout::FORDER) && af.is(Layout::FPREFER)); } #[test] fn contig_cf_layouts() { let a = M::zeros((5, 1)); let b = M::zeros((1, 5).f()); assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); let a = M1::zeros(5); let b = M1::zeros(5.f()); assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); assert_layouts!(b, CORDER, CPREFER, FORDER, FPREFER); let a = M0::zeros(()); assert_layouts!(a, CORDER, CPREFER, FORDER, FPREFER); let a = M::zeros((5, 5)); let b = M::zeros((5, 5).f()); let arow = a.slice(s![..1, ..]); let bcol = b.slice(s![.., ..1]); assert_layouts!(arow, CORDER, CPREFER, FORDER, FPREFER); assert_layouts!(bcol, CORDER, CPREFER, FORDER, FPREFER); let acol = a.slice(s![.., ..1]); let brow = b.slice(s![..1, ..]); assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); } #[test] fn stride_layouts() { let a = M::zeros((5, 5)); { let v1 = a.slice(s![1.., ..]).layout(); let v2 = a.slice(s![.., 1..]).layout(); assert!(v1.is(Layout::CORDER) && v1.is(Layout::CPREFER)); assert!(!v1.is(Layout::FORDER) && !v1.is(Layout::FPREFER)); assert!(!v2.is(Layout::CORDER) && v2.is(Layout::CPREFER)); assert!(!v2.is(Layout::FORDER) && !v2.is(Layout::FPREFER)); } let b = M::zeros((5, 5).f()); { let v1 = b.slice(s![1.., ..]).layout(); let v2 = b.slice(s![.., 1..]).layout(); assert!(!v1.is(Layout::CORDER) && !v1.is(Layout::CPREFER)); assert!(!v1.is(Layout::FORDER) && v1.is(Layout::FPREFER)); assert!(!v2.is(Layout::CORDER) && !v2.is(Layout::CPREFER)); assert!(v2.is(Layout::FORDER) && v2.is(Layout::FPREFER)); } } #[test] fn no_layouts() { let a = M::zeros((5, 5)); let b = M::zeros((5, 5).f()); // 2D row/column matrixes let arow = a.slice(s![0..1, ..]); let acol = a.slice(s![.., 0..1]); let brow = b.slice(s![0..1, ..]); let bcol = b.slice(s![.., 0..1]); assert_layouts!(arow, CORDER, FORDER); assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); assert_layouts!(bcol, CORDER, FORDER); assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); // 2D row/column matrixes - now made with insert axis for &axis in &[Axis(0), Axis(1)] { let arow = a.slice(s![0, ..]).insert_axis(axis); let acol = a.slice(s![.., 0]).insert_axis(axis); let brow = b.slice(s![0, ..]).insert_axis(axis); let bcol = b.slice(s![.., 0]).insert_axis(axis); assert_layouts!(arow, CORDER, FORDER); assert_not_layouts!(acol, CORDER, CPREFER, FORDER, FPREFER); assert_layouts!(bcol, CORDER, FORDER); assert_not_layouts!(brow, CORDER, CPREFER, FORDER, FPREFER); } } #[test] fn skip_layouts() { let a = M::zeros((5, 5)); { let v1 = a.slice(s![..;2, ..]).layout(); let v2 = a.slice(s![.., ..;2]).layout(); assert!(!v1.is(Layout::CORDER) && v1.is(Layout::CPREFER)); assert!(!v1.is(Layout::FORDER) && !v1.is(Layout::FPREFER)); assert!(!v2.is(Layout::CORDER) && !v2.is(Layout::CPREFER)); assert!(!v2.is(Layout::FORDER) && !v2.is(Layout::FPREFER)); } let b = M::zeros((5, 5).f()); { let v1 = b.slice(s![..;2, ..]).layout(); let v2 = b.slice(s![.., ..;2]).layout(); assert!(!v1.is(Layout::CORDER) && !v1.is(Layout::CPREFER)); assert!(!v1.is(Layout::FORDER) && !v1.is(Layout::FPREFER)); assert!(!v2.is(Layout::CORDER) && !v2.is(Layout::CPREFER)); assert!(!v2.is(Layout::FORDER) && v2.is(Layout::FPREFER)); } } } ndarray-0.16.1/src/lib.rs000064400000000000000000001544571046102023000132740ustar 00000000000000// Copyright 2014-2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![crate_name = "ndarray"] #![doc(html_root_url = "https://docs.rs/ndarray/0.15/")] #![doc(html_logo_url = "https://rust-ndarray.github.io/images/rust-ndarray_logo.svg")] #![allow( unstable_name_collisions, // our `PointerExt` collides with upcoming inherent methods on `NonNull` clippy::deref_addrof, clippy::manual_map, // is not an error clippy::while_let_on_iterator, // is not an error clippy::from_iter_instead_of_collect, // using from_iter is good style clippy::incompatible_msrv, // false positive PointerExt::offset )] #![doc(test(attr(deny(warnings))))] #![doc(test(attr(allow(unused_variables))))] #![doc(test(attr(allow(deprecated))))] #![cfg_attr(not(feature = "std"), no_std)] //! The `ndarray` crate provides an *n*-dimensional container for general elements //! and for numerics. //! //! In *n*-dimensional we include, for example, 1-dimensional rows or columns, //! 2-dimensional matrices, and higher dimensional arrays. If the array has *n* //! dimensions, then an element in the array is accessed by using that many indices. //! Each dimension is also called an *axis*. //! //! - **[`ArrayBase`]**: //! The *n*-dimensional array type itself.
//! It is used to implement both the owned arrays and the views; see its docs //! for an overview of all array features.
//! - The main specific array type is **[`Array`]**, which owns //! its elements. //! //! ## Highlights //! //! - Generic *n*-dimensional array //! - [Slicing](ArrayBase#slicing), also with arbitrary step size, and negative //! indices to mean elements from the end of the axis. //! - Views and subviews of arrays; iterators that yield subviews. //! - Higher order operations and arithmetic are performant //! - Array views can be used to slice and mutate any `[T]` data using //! `ArrayView::from` and `ArrayViewMut::from`. //! - [`Zip`] for lock step function application across two or more arrays or other //! item producers ([`NdProducer`] trait). //! //! ## Crate Status //! //! - Still iterating on and evolving the crate //! + The crate is continuously developing, and breaking changes are expected //! during evolution from version to version. We adopt the newest stable //! rust features if we need them. //! + Note that functions/methods/traits/etc. hidden from the docs are not //! considered part of the public API, so changes to them are not //! considered breaking changes. //! - Performance: //! + Prefer higher order methods and arithmetic operations on arrays first, //! then iteration, and as a last priority using indexed algorithms. //! + The higher order functions like [`.map()`](ArrayBase::map), //! [`.map_inplace()`](ArrayBase::map_inplace), [`.zip_mut_with()`](ArrayBase::zip_mut_with), //! [`Zip`] and [`azip!()`](azip) are the most efficient ways //! to perform single traversal and lock step traversal respectively. //! + Performance of an operation depends on the memory layout of the array //! or array view. Especially if it's a binary operation, which //! needs matching memory layout to be efficient (with some exceptions). //! + Efficient floating point matrix multiplication even for very large //! matrices; can optionally use BLAS to improve it further. //! //! - **MSRV: Requires Rust 1.64 or later** //! //! ## Crate Feature Flags //! //! The following crate feature flags are available. They are configured in your //! `Cargo.toml`. See [`doc::crate_feature_flags`] for more information. //! //! - `std`: Rust standard library-using functionality (enabled by default) //! - `serde`: serialization support for serde 1.x //! - `rayon`: Parallel iterators, parallelized methods, the [`parallel`] module and [`par_azip!`]. //! - `approx` Implementations of traits from the [`approx`] crate. //! - `blas`: transparent BLAS support for matrix multiplication, needs configuration. //! - `matrixmultiply-threading`: Use threading from `matrixmultiply`. //! //! ## Documentation //! //! * The docs for [`ArrayBase`] provide an overview of //! the *n*-dimensional array type. Other good pages to look at are the //! documentation for the [`s![]`](s!) and //! [`azip!()`](azip!) macros. //! //! * If you have experience with NumPy, you may also be interested in //! [`ndarray_for_numpy_users`](doc::ndarray_for_numpy_users). //! //! ## The ndarray ecosystem //! //! `ndarray` provides a lot of functionality, but it's not a one-stop solution. //! //! `ndarray` includes matrix multiplication and other binary/unary operations out of the box. //! More advanced linear algebra routines (e.g. SVD decomposition or eigenvalue computation) //! can be found in [`ndarray-linalg`](https://crates.io/crates/ndarray-linalg). //! //! The same holds for statistics: `ndarray` provides some basic functionalities (e.g. `mean`) //! but more advanced routines can be found in [`ndarray-stats`](https://crates.io/crates/ndarray-stats). //! //! If you are looking to generate random arrays instead, check out [`ndarray-rand`](https://crates.io/crates/ndarray-rand). //! //! For conversion between `ndarray`, [`nalgebra`](https://crates.io/crates/nalgebra) and //! [`image`](https://crates.io/crates/image) check out [`nshare`](https://crates.io/crates/nshare). extern crate alloc; #[cfg(not(feature = "std"))] extern crate core as std; #[cfg(feature = "std")] extern crate std; #[cfg(feature = "blas")] extern crate cblas_sys; #[cfg(feature = "docs")] pub mod doc; #[cfg(target_has_atomic = "ptr")] use alloc::sync::Arc; #[cfg(not(target_has_atomic = "ptr"))] use portable_atomic_util::Arc; use std::marker::PhantomData; pub use crate::dimension::dim::*; pub use crate::dimension::{Axis, AxisDescription, Dimension, IntoDimension, RemoveAxis}; pub use crate::dimension::{DimAdd, DimMax}; pub use crate::dimension::IxDynImpl; pub use crate::dimension::NdIndex; pub use crate::error::{ErrorKind, ShapeError}; pub use crate::indexes::{indices, indices_of}; pub use crate::order::Order; pub use crate::slice::{MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceInfoElem, SliceNextDim}; use crate::iterators::Baseiter; use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::LinalgScalar; #[cfg(feature = "std")] pub use crate::linalg_traits::NdFloat; pub use crate::stacking::{concatenate, stack}; pub use crate::impl_views::IndexLonger; pub use crate::math_cell::MathCell; pub use crate::shape_builder::{Shape, ShapeArg, ShapeBuilder, StrideShape}; #[macro_use] mod macro_utils; #[macro_use] mod private; mod aliases; #[macro_use] mod itertools; mod argument_traits; #[cfg(feature = "serde")] mod array_serde; mod arrayformat; mod arraytraits; pub use crate::argument_traits::AssignElem; mod data_repr; mod data_traits; pub use crate::aliases::*; pub use crate::data_traits::{Data, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut, RawDataSubst}; mod free_functions; pub use crate::free_functions::*; pub use crate::iterators::iter; mod error; mod extension; mod geomspace; mod indexes; mod iterators; mod layout; mod linalg_traits; mod linspace; #[cfg(feature = "std")] pub use crate::linspace::{linspace, range, Linspace}; mod logspace; #[cfg(feature = "std")] pub use crate::logspace::{logspace, Logspace}; mod math_cell; mod numeric_util; mod order; mod partial; mod shape_builder; #[macro_use] mod slice; mod split_at; mod stacking; mod low_level_util; #[macro_use] mod zip; mod dimension; pub use crate::zip::{FoldWhile, IntoNdProducer, NdProducer, Zip}; pub use crate::layout::Layout; /// Implementation's prelude. Common types used everywhere. mod imp_prelude { pub use crate::dimension::DimensionExt; pub use crate::prelude::*; pub use crate::ArcArray; pub use crate::{ CowRepr, Data, DataMut, DataOwned, DataShared, Ix, Ixs, RawData, RawDataMut, RawViewRepr, RemoveAxis, ViewRepr, }; } pub mod prelude; /// Array index type pub type Ix = usize; /// Array index type (signed) pub type Ixs = isize; /// An *n*-dimensional array. /// /// The array is a general container of elements. /// The array supports arithmetic operations by applying them elementwise, if the /// elements are numeric, but it supports non-numeric elements too. /// /// The arrays rarely grow or shrink, since those operations can be costly. On /// the other hand there is a rich set of methods and operations for taking views, /// slices, and making traversals over one or more arrays. /// /// In *n*-dimensional we include for example 1-dimensional rows or columns, /// 2-dimensional matrices, and higher dimensional arrays. If the array has *n* /// dimensions, then an element is accessed by using that many indices. /// /// The `ArrayBase` is parameterized by `S` for the data container and /// `D` for the dimensionality. /// /// Type aliases [`Array`], [`ArcArray`], [`CowArray`], [`ArrayView`], and /// [`ArrayViewMut`] refer to `ArrayBase` with different types for the data /// container: arrays with different kinds of ownership or different kinds of array views. /// /// ## Contents /// /// + [Array](#array) /// + [ArcArray](#arcarray) /// + [CowArray](#cowarray) /// + [Array Views](#array-views) /// + [Indexing and Dimension](#indexing-and-dimension) /// + [Loops, Producers and Iterators](#loops-producers-and-iterators) /// + [Slicing](#slicing) /// + [Subviews](#subviews) /// + [Arithmetic Operations](#arithmetic-operations) /// + [Broadcasting](#broadcasting) /// + [Conversions](#conversions) /// + [Constructor Methods for Owned Arrays](#constructor-methods-for-owned-arrays) /// + [Methods For All Array Types](#methods-for-all-array-types) /// + [Methods For 1-D Arrays](#methods-for-1-d-arrays) /// + [Methods For 2-D Arrays](#methods-for-2-d-arrays) /// + [Methods for Dynamic-Dimensional Arrays](#methods-for-dynamic-dimensional-arrays) /// + [Numerical Methods for Arrays](#numerical-methods-for-arrays) /// /// ## `Array` /// /// [`Array`] is an owned array that owns the underlying array /// elements directly (just like a `Vec`) and it is the default way to create and /// store n-dimensional data. `Array` has two type parameters: `A` for /// the element type, and `D` for the dimensionality. A particular /// dimensionality's type alias like `Array3
` just has the type parameter /// `A` for element type. /// /// An example: /// /// ``` /// // Create a three-dimensional f64 array, initialized with zeros /// use ndarray::Array3; /// let mut temperature = Array3::::zeros((3, 4, 5)); /// // Increase the temperature in this location /// temperature[[2, 2, 2]] += 0.5; /// ``` /// /// ## `ArcArray` /// /// [`ArcArray`] is an owned array with reference counted /// data (shared ownership). /// Sharing requires that it uses copy-on-write for mutable operations. /// Calling a method for mutating elements on `ArcArray`, for example /// [`view_mut()`](Self::view_mut) or [`get_mut()`](Self::get_mut), /// will break sharing and require a clone of the data (if it is not uniquely held). /// /// ## `CowArray` /// /// [`CowArray`] is analogous to [`std::borrow::Cow`]. /// It can represent either an immutable view or a uniquely owned array. If a /// `CowArray` instance is the immutable view variant, then calling a method /// for mutating elements in the array will cause it to be converted into the /// owned variant (by cloning all the elements) before the modification is /// performed. /// /// ## Array Views /// /// [`ArrayView`] and [`ArrayViewMut`] are read-only and read-write array views /// respectively. They use dimensionality, indexing, and almost all other /// methods the same way as the other array types. /// /// Methods for `ArrayBase` apply to array views too, when the trait bounds /// allow. /// /// Please see the documentation for the respective array view for an overview /// of methods specific to array views: [`ArrayView`], [`ArrayViewMut`]. /// /// A view is created from an array using [`.view()`](ArrayBase::view), /// [`.view_mut()`](ArrayBase::view_mut), using /// slicing ([`.slice()`](ArrayBase::slice), [`.slice_mut()`](ArrayBase::slice_mut)) or from one of /// the many iterators that yield array views. /// /// You can also create an array view from a regular slice of data not /// allocated with `Array` — see array view methods or their `From` impls. /// /// Note that all `ArrayBase` variants can change their view (slicing) of the /// data freely, even when their data can’t be mutated. /// /// ## Indexing and Dimension /// /// The dimensionality of the array determines the number of *axes*, for example /// a 2D array has two axes. These are listed in “big endian” order, so that /// the greatest dimension is listed first, the lowest dimension with the most /// rapidly varying index is the last. /// /// In a 2D array the index of each element is `[row, column]` as seen in this /// 4 × 3 example: /// /// ```ignore /// [[ [0, 0], [0, 1], [0, 2] ], // row 0 /// [ [1, 0], [1, 1], [1, 2] ], // row 1 /// [ [2, 0], [2, 1], [2, 2] ], // row 2 /// [ [3, 0], [3, 1], [3, 2] ]] // row 3 /// // \ \ \ /// // column 0 \ column 2 /// // column 1 /// ``` /// /// The number of axes for an array is fixed by its `D` type parameter: `Ix1` /// for a 1D array, `Ix2` for a 2D array etc. The dimension type `IxDyn` allows /// a dynamic number of axes. /// /// A fixed size array (`[usize; N]`) of the corresponding dimensionality is /// used to index the `Array`, making the syntax `array[[` i, j, ...`]]` /// /// ``` /// use ndarray::Array2; /// let mut array = Array2::zeros((4, 3)); /// array[[1, 1]] = 7; /// ``` /// /// Important traits and types for dimension and indexing: /// /// - A [`struct@Dim`] value represents a dimensionality or index. /// - Trait [`Dimension`] is implemented by all /// dimensionalities. It defines many operations for dimensions and indices. /// - Trait [`IntoDimension`] is used to convert into a /// `Dim` value. /// - Trait [`ShapeBuilder`] is an extension of /// `IntoDimension` and is used when constructing an array. A shape describes /// not just the extent of each axis but also their strides. /// - Trait [`NdIndex`] is an extension of `Dimension` and is /// for values that can be used with indexing syntax. /// /// /// The default memory order of an array is *row major* order (a.k.a “c” order), /// where each row is contiguous in memory. /// A *column major* (a.k.a. “f” or fortran) memory order array has /// columns (or, in general, the outermost axis) with contiguous elements. /// /// The logical order of any array’s elements is the row major order /// (the rightmost index is varying the fastest). /// The iterators `.iter(), .iter_mut()` always adhere to this order, for example. /// /// ## Loops, Producers and Iterators /// /// Using [`Zip`] is the most general way to apply a procedure /// across one or several arrays or *producers*. /// /// [`NdProducer`] is like an iterable but for /// multidimensional data. All producers have dimensions and axes, like an /// array view, and they can be split and used with parallelization using `Zip`. /// /// For example, `ArrayView` is a producer, it has the same dimensions /// as the array view and for each iteration it produces a reference to /// the array element (`&A` in this case). /// /// Another example, if we have a 10 × 10 array and use `.exact_chunks((2, 2))` /// we get a producer of chunks which has the dimensions 5 × 5 (because /// there are *10 / 2 = 5* chunks in either direction). The 5 × 5 chunks producer /// can be paired with any other producers of the same dimension with `Zip`, for /// example 5 × 5 arrays. /// /// ### `.iter()` and `.iter_mut()` /// /// These are the element iterators of arrays and they produce an element /// sequence in the logical order of the array, that means that the elements /// will be visited in the sequence that corresponds to increasing the /// last index first: *0, ..., 0, 0*; *0, ..., 0, 1*; *0, ...0, 2* and so on. /// /// ### `.outer_iter()` and `.axis_iter()` /// /// These iterators produce array views of one smaller dimension. /// /// For example, for a 2D array, `.outer_iter()` will produce the 1D rows. /// For a 3D array, `.outer_iter()` produces 2D subviews. /// /// `.axis_iter()` is like `outer_iter()` but allows you to pick which /// axis to traverse. /// /// The `outer_iter` and `axis_iter` are one dimensional producers. /// /// ## `.rows()`, `.columns()` and `.lanes()` /// /// [`.rows()`][gr] is a producer (and iterable) of all rows in an array. /// /// ``` /// use ndarray::Array; /// /// // 1. Loop over the rows of a 2D array /// let mut a = Array::zeros((10, 10)); /// for mut row in a.rows_mut() { /// row.fill(1.); /// } /// /// // 2. Use Zip to pair each row in 2D `a` with elements in 1D `b` /// use ndarray::Zip; /// let mut b = Array::zeros(a.nrows()); /// /// Zip::from(a.rows()) /// .and(&mut b) /// .for_each(|a_row, b_elt| { /// *b_elt = a_row[a.ncols() - 1] - a_row[0]; /// }); /// ``` /// /// The *lanes* of an array are 1D segments along an axis and when pointed /// along the last axis they are *rows*, when pointed along the first axis /// they are *columns*. /// /// A *m* × *n* array has *m* rows each of length *n* and conversely /// *n* columns each of length *m*. /// /// To generalize this, we say that an array of dimension *a* × *m* × *n* /// has *a m* rows. It's composed of *a* times the previous array, so it /// has *a* times as many rows. /// /// All methods: [`.rows()`][gr], [`.rows_mut()`][grm], /// [`.columns()`][gc], [`.columns_mut()`][gcm], /// [`.lanes(axis)`][l], [`.lanes_mut(axis)`][lm]. /// /// [gr]: Self::rows /// [grm]: Self::rows_mut /// [gc]: Self::columns /// [gcm]: Self::columns_mut /// [l]: Self::lanes /// [lm]: Self::lanes_mut /// /// Yes, for 2D arrays `.rows()` and `.outer_iter()` have about the same /// effect: /// /// + `rows()` is a producer with *n* - 1 dimensions of 1 dimensional items /// + `outer_iter()` is a producer with 1 dimension of *n* - 1 dimensional items /// /// ## Slicing /// /// You can use slicing to create a view of a subset of the data in /// the array. Slicing methods include [`.slice()`], [`.slice_mut()`], /// [`.slice_move()`], and [`.slice_collapse()`]. /// /// The slicing argument can be passed using the macro [`s![]`](s!), /// which will be used in all examples. (The explicit form is an instance of /// [`SliceInfo`] or another type which implements [`SliceArg`]; see their docs /// for more information.) /// /// If a range is used, the axis is preserved. If an index is used, that index /// is selected and the axis is removed; this selects a subview. See /// [*Subviews*](#subviews) for more information about subviews. If a /// [`NewAxis`] instance is used, a new axis is inserted. Note that /// [`.slice_collapse()`] panics on `NewAxis` elements and behaves like /// [`.collapse_axis()`] by preserving the number of dimensions. /// /// [`.slice()`]: Self::slice /// [`.slice_mut()`]: Self::slice_mut /// [`.slice_move()`]: Self::slice_move /// [`.slice_collapse()`]: Self::slice_collapse /// /// When slicing arrays with generic dimensionality, creating an instance of /// [`SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`] /// is awkward. In these cases, it's usually more convenient to use /// [`.slice_each_axis()`]/[`.slice_each_axis_mut()`]/[`.slice_each_axis_inplace()`] /// or to create a view and then slice individual axes of the view using /// methods such as [`.slice_axis_inplace()`] and [`.collapse_axis()`]. /// /// [`.slice_each_axis()`]: Self::slice_each_axis /// [`.slice_each_axis_mut()`]: Self::slice_each_axis_mut /// [`.slice_each_axis_inplace()`]: Self::slice_each_axis_inplace /// [`.slice_axis_inplace()`]: Self::slice_axis_inplace /// [`.collapse_axis()`]: Self::collapse_axis /// /// It's possible to take multiple simultaneous *mutable* slices with /// [`.multi_slice_mut()`] or (for [`ArrayViewMut`] only) /// [`.multi_slice_move()`]. /// /// [`.multi_slice_mut()`]: Self::multi_slice_mut /// [`.multi_slice_move()`]: ArrayViewMut#method.multi_slice_move /// /// ``` /// use ndarray::{arr2, arr3, s, ArrayBase, DataMut, Dimension, NewAxis, Slice}; /// /// // 2 submatrices of 2 rows with 3 elements per row, means a shape of `[2, 2, 3]`. /// /// let a = arr3(&[[[ 1, 2, 3], // -- 2 rows \_ /// [ 4, 5, 6]], // -- / /// [[ 7, 8, 9], // \_ 2 submatrices /// [10, 11, 12]]]); // / /// // 3 columns ..../.../.../ /// /// assert_eq!(a.shape(), &[2, 2, 3]); /// /// // Let’s create a slice with /// // /// // - Both of the submatrices of the greatest dimension: `..` /// // - Only the first row in each submatrix: `0..1` /// // - Every element in each row: `..` /// /// let b = a.slice(s![.., 0..1, ..]); /// let c = arr3(&[[[ 1, 2, 3]], /// [[ 7, 8, 9]]]); /// assert_eq!(b, c); /// assert_eq!(b.shape(), &[2, 1, 3]); /// /// // Let’s create a slice with /// // /// // - Both submatrices of the greatest dimension: `..` /// // - The last row in each submatrix: `-1..` /// // - Row elements in reverse order: `..;-1` /// let d = a.slice(s![.., -1.., ..;-1]); /// let e = arr3(&[[[ 6, 5, 4]], /// [[12, 11, 10]]]); /// assert_eq!(d, e); /// assert_eq!(d.shape(), &[2, 1, 3]); /// /// // Let’s create a slice while selecting a subview and inserting a new axis with /// // /// // - Both submatrices of the greatest dimension: `..` /// // - The last row in each submatrix, removing that axis: `-1` /// // - Row elements in reverse order: `..;-1` /// // - A new axis at the end. /// let f = a.slice(s![.., -1, ..;-1, NewAxis]); /// let g = arr3(&[[ [6], [5], [4]], /// [[12], [11], [10]]]); /// assert_eq!(f, g); /// assert_eq!(f.shape(), &[2, 3, 1]); /// /// // Let's take two disjoint, mutable slices of a matrix with /// // /// // - One containing all the even-index columns in the matrix /// // - One containing all the odd-index columns in the matrix /// let mut h = arr2(&[[0, 1, 2, 3], /// [4, 5, 6, 7]]); /// let (s0, s1) = h.multi_slice_mut((s![.., ..;2], s![.., 1..;2])); /// let i = arr2(&[[0, 2], /// [4, 6]]); /// let j = arr2(&[[1, 3], /// [5, 7]]); /// assert_eq!(s0, i); /// assert_eq!(s1, j); /// /// // Generic function which assigns the specified value to the elements which /// // have indices in the lower half along all axes. /// fn fill_lower(arr: &mut ArrayBase, x: S::Elem) /// where /// S: DataMut, /// S::Elem: Clone, /// D: Dimension, /// { /// arr.slice_each_axis_mut(|ax| Slice::from(0..ax.len / 2)).fill(x); /// } /// fill_lower(&mut h, 9); /// let k = arr2(&[[9, 9, 2, 3], /// [4, 5, 6, 7]]); /// assert_eq!(h, k); /// ``` /// /// ## Subviews /// /// Subview methods allow you to restrict the array view while removing one /// axis from the array. Methods for selecting individual subviews include /// [`.index_axis()`], [`.index_axis_mut()`], [`.index_axis_move()`], and /// [`.index_axis_inplace()`]. You can also select a subview by using a single /// index instead of a range when slicing. Some other methods, such as /// [`.fold_axis()`], [`.axis_iter()`], [`.axis_iter_mut()`], /// [`.outer_iter()`], and [`.outer_iter_mut()`] operate on all the subviews /// along an axis. /// /// A related method is [`.collapse_axis()`], which modifies the view in the /// same way as [`.index_axis()`] except for removing the collapsed axis, since /// it operates *in place*. The length of the axis becomes 1. /// /// Methods for selecting an individual subview take two arguments: `axis` and /// `index`. /// /// [`.axis_iter()`]: Self::axis_iter /// [`.axis_iter_mut()`]: Self::axis_iter_mut /// [`.fold_axis()`]: Self::fold_axis /// [`.index_axis()`]: Self::index_axis /// [`.index_axis_inplace()`]: Self::index_axis_inplace /// [`.index_axis_mut()`]: Self::index_axis_mut /// [`.index_axis_move()`]: Self::index_axis_move /// [`.collapse_axis()`]: Self::collapse_axis /// [`.outer_iter()`]: Self::outer_iter /// [`.outer_iter_mut()`]: Self::outer_iter_mut /// /// ``` /// /// use ndarray::{arr3, aview1, aview2, s, Axis}; /// /// /// // 2 submatrices of 2 rows with 3 elements per row, means a shape of `[2, 2, 3]`. /// /// let a = arr3(&[[[ 1, 2, 3], // \ axis 0, submatrix 0 /// [ 4, 5, 6]], // / /// [[ 7, 8, 9], // \ axis 0, submatrix 1 /// [10, 11, 12]]]); // / /// // \ /// // axis 2, column 0 /// /// assert_eq!(a.shape(), &[2, 2, 3]); /// /// // Let’s take a subview along the greatest dimension (axis 0), /// // taking submatrix 0, then submatrix 1 /// /// let sub_0 = a.index_axis(Axis(0), 0); /// let sub_1 = a.index_axis(Axis(0), 1); /// /// assert_eq!(sub_0, aview2(&[[ 1, 2, 3], /// [ 4, 5, 6]])); /// assert_eq!(sub_1, aview2(&[[ 7, 8, 9], /// [10, 11, 12]])); /// assert_eq!(sub_0.shape(), &[2, 3]); /// /// // This is the subview picking only axis 2, column 0 /// let sub_col = a.index_axis(Axis(2), 0); /// /// assert_eq!(sub_col, aview2(&[[ 1, 4], /// [ 7, 10]])); /// /// // You can take multiple subviews at once (and slice at the same time) /// let double_sub = a.slice(s![1, .., 0]); /// assert_eq!(double_sub, aview1(&[7, 10])); /// ``` /// /// ## Arithmetic Operations /// /// Arrays support all arithmetic operations the same way: they apply elementwise. /// /// Since the trait implementations are hard to overview, here is a summary. /// /// ### Binary Operators with Two Arrays /// /// Let `A` be an array or view of any kind. Let `B` be an array /// with owned storage (either `Array` or `ArcArray`). /// Let `C` be an array with mutable data (either `Array`, `ArcArray` /// or `ArrayViewMut`). /// The following combinations of operands /// are supported for an arbitrary binary operator denoted by `@` (it can be /// `+`, `-`, `*`, `/` and so on). /// /// - `&A @ &A` which produces a new `Array` /// - `B @ A` which consumes `B`, updates it with the result, and returns it /// - `B @ &A` which consumes `B`, updates it with the result, and returns it /// - `C @= &A` which performs an arithmetic operation in place /// /// Note that the element type needs to implement the operator trait and the /// `Clone` trait. /// /// ``` /// use ndarray::{array, ArrayView1}; /// /// let owned1 = array![1, 2]; /// let owned2 = array![3, 4]; /// let view1 = ArrayView1::from(&[5, 6]); /// let view2 = ArrayView1::from(&[7, 8]); /// let mut mutable = array![9, 10]; /// /// let sum1 = &view1 + &view2; // Allocates a new array. Note the explicit `&`. /// // let sum2 = view1 + &view2; // This doesn't work because `view1` is not an owned array. /// let sum3 = owned1 + view1; // Consumes `owned1`, updates it, and returns it. /// let sum4 = owned2 + &view2; // Consumes `owned2`, updates it, and returns it. /// mutable += &view2; // Updates `mutable` in-place. /// ``` /// /// ### Binary Operators with Array and Scalar /// /// The trait [`ScalarOperand`] marks types that can be used in arithmetic /// with arrays directly. For a scalar `K` the following combinations of operands /// are supported (scalar can be on either the left or right side, but /// `ScalarOperand` docs has the detailed conditions). /// /// - `&A @ K` or `K @ &A` which produces a new `Array` /// - `B @ K` or `K @ B` which consumes `B`, updates it with the result and returns it /// - `C @= K` which performs an arithmetic operation in place /// /// ### Unary Operators /// /// Let `A` be an array or view of any kind. Let `B` be an array with owned /// storage (either `Array` or `ArcArray`). The following operands are supported /// for an arbitrary unary operator denoted by `@` (it can be `-` or `!`). /// /// - `@&A` which produces a new `Array` /// - `@B` which consumes `B`, updates it with the result, and returns it /// /// ## Broadcasting /// /// Arrays support limited *broadcasting*, where arithmetic operations with /// array operands of different sizes can be carried out by repeating the /// elements of the smaller dimension array. See /// [`.broadcast()`](Self::broadcast) for a more detailed /// description. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[1., 1.], /// [1., 2.], /// [0., 3.], /// [0., 4.]]); /// /// let b = arr2(&[[0., 1.]]); /// /// let c = arr2(&[[1., 2.], /// [1., 3.], /// [0., 4.], /// [0., 5.]]); /// // We can add because the shapes are compatible even if not equal. /// // The `b` array is shape 1 × 2 but acts like a 4 × 2 array. /// assert!( /// c == a + b /// ); /// ``` /// /// ## Conversions /// /// ### Conversions Between Array Types /// /// This table is a summary of the conversions between arrays of different /// ownership, dimensionality, and element type. All of the conversions in this /// table preserve the shape of the array. /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
OutputInput
/// /// `Array` /// /// /// /// `ArcArray` /// /// /// /// `CowArray<'a, A, D>` /// /// /// /// `ArrayView<'a, A, D>` /// /// /// /// `ArrayViewMut<'a, A, D>` /// ///
/// /// `Array` /// /// /// /// no-op /// /// /// /// [`a.into_owned()`][.into_owned()] /// /// /// /// [`a.into_owned()`][.into_owned()] /// /// /// /// [`a.to_owned()`][.to_owned()] /// /// /// /// [`a.to_owned()`][.to_owned()] /// ///
/// /// `ArcArray` /// /// /// /// [`a.into_shared()`][.into_shared()] /// /// /// /// no-op /// /// /// /// [`a.into_owned().into_shared()`][.into_shared()] /// /// /// /// [`a.to_owned().into_shared()`][.into_shared()] /// /// /// /// [`a.to_owned().into_shared()`][.into_shared()] /// ///
/// /// `CowArray<'a, A, D>` /// /// /// /// [`CowArray::from(a)`](CowArray#impl-From%2C%20D>>) /// /// /// /// [`CowArray::from(a.into_owned())`](CowArray#impl-From%2C%20D>>) /// /// /// /// no-op /// /// /// /// [`CowArray::from(a)`](CowArray#impl-From%2C%20D>>) /// /// /// /// [`CowArray::from(a.view())`](CowArray#impl-From%2C%20D>>) /// ///
/// /// `ArrayView<'b, A, D>` /// /// /// /// [`a.view()`][.view()] /// /// /// /// [`a.view()`][.view()] /// /// /// /// [`a.view()`][.view()] /// /// /// /// [`a.view()`][.view()] or [`a.reborrow()`][ArrayView::reborrow()] /// /// /// /// [`a.view()`][.view()] /// ///
/// /// `ArrayViewMut<'b, A, D>` /// /// /// /// [`a.view_mut()`][.view_mut()] /// /// /// /// [`a.view_mut()`][.view_mut()] /// /// /// /// [`a.view_mut()`][.view_mut()] /// /// /// /// illegal /// /// /// /// [`a.view_mut()`][.view_mut()] or [`a.reborrow()`][ArrayViewMut::reborrow()] /// ///
/// /// equivalent with dim `D2` (e.g. converting from dynamic dim to const dim) /// /// /// /// [`a.into_dimensionality::()`][.into_dimensionality()] /// ///
/// /// equivalent with dim `IxDyn` /// /// /// /// [`a.into_dyn()`][.into_dyn()] /// ///
/// /// `Array` (new element type) /// /// /// /// [`a.map(|x| x.do_your_conversion())`][.map()] /// ///
/// /// ### Conversions Between Arrays and `Vec`s/Slices/Scalars /// /// This is a table of the safe conversions between arrays and /// `Vec`s/slices/scalars. Note that some of the return values are actually /// `Result`/`Option` wrappers around the indicated output types. /// /// Input | Output | Methods /// ------|--------|-------- /// `Vec
` | `ArrayBase` | [`::from_vec()`](Self::from_vec) /// `Vec` | `ArrayBase` | [`::from_shape_vec()`](Self::from_shape_vec) /// `&[A]` | `ArrayView1` | [`::from()`](ArrayView#method.from) /// `&[A]` | `ArrayView` | [`::from_shape()`](ArrayView#method.from_shape) /// `&mut [A]` | `ArrayViewMut1` | [`::from()`](ArrayViewMut#method.from) /// `&mut [A]` | `ArrayViewMut` | [`::from_shape()`](ArrayViewMut#method.from_shape) /// `&ArrayBase` | `Vec` | [`.to_vec()`](Self::to_vec) /// `Array` | `Vec` | [`.into_raw_vec()`](Array#method.into_raw_vec)[1](#into_raw_vec) /// `&ArrayBase` | `&[A]` | [`.as_slice()`](Self::as_slice)[2](#req_contig_std), [`.as_slice_memory_order()`](Self::as_slice_memory_order)[3](#req_contig) /// `&mut ArrayBase` | `&mut [A]` | [`.as_slice_mut()`](Self::as_slice_mut)[2](#req_contig_std), [`.as_slice_memory_order_mut()`](Self::as_slice_memory_order_mut)[3](#req_contig) /// `ArrayView` | `&[A]` | [`.to_slice()`](ArrayView#method.to_slice)[2](#req_contig_std) /// `ArrayViewMut` | `&mut [A]` | [`.into_slice()`](ArrayViewMut#method.into_slice)[2](#req_contig_std) /// `Array0` | `A` | [`.into_scalar()`](Array#method.into_scalar) /// /// 1Returns the data in memory order. /// /// 2Works only if the array is /// contiguous and in standard order. /// /// 3Works only if the array is contiguous. /// /// The table above does not include all the constructors; it only shows /// conversions to/from `Vec`s/slices. See /// [below](#constructor-methods-for-owned-arrays) for more constructors. /// /// [ArrayView::reborrow()]: ArrayView#method.reborrow /// [ArrayViewMut::reborrow()]: ArrayViewMut#method.reborrow /// [.into_dimensionality()]: Self::into_dimensionality /// [.into_dyn()]: Self::into_dyn /// [.into_owned()]: Self::into_owned /// [.into_shared()]: Self::into_shared /// [.to_owned()]: Self::to_owned /// [.map()]: Self::map /// [.view()]: Self::view /// [.view_mut()]: Self::view_mut /// /// ### Conversions from Nested `Vec`s/`Array`s /// /// It's generally a good idea to avoid nested `Vec`/`Array` types, such as /// `Vec>` or `Vec>` because: /// /// * they require extra heap allocations compared to a single `Array`, /// /// * they can scatter data all over memory (because of multiple allocations), /// /// * they cause unnecessary indirection (traversing multiple pointers to reach /// the data), /// /// * they don't enforce consistent shape within the nested /// `Vec`s/`ArrayBase`s, and /// /// * they are generally more difficult to work with. /// /// The most common case where users might consider using nested /// `Vec`s/`Array`s is when creating an array by appending rows/subviews in a /// loop, where the rows/subviews are computed within the loop. However, there /// are better ways than using nested `Vec`s/`Array`s. /// /// If you know ahead-of-time the shape of the final array, the cleanest /// solution is to allocate the final array before the loop, and then assign /// the data to it within the loop, like this: /// /// ```rust /// use ndarray::{array, Array2, Axis}; /// /// let mut arr = Array2::zeros((2, 3)); /// for (i, mut row) in arr.axis_iter_mut(Axis(0)).enumerate() { /// // Perform calculations and assign to `row`; this is a trivial example: /// row.fill(i); /// } /// assert_eq!(arr, array![[0, 0, 0], [1, 1, 1]]); /// ``` /// /// If you don't know ahead-of-time the shape of the final array, then the /// cleanest solution is generally to append the data to a flat `Vec`, and then /// convert it to an `Array` at the end with /// [`::from_shape_vec()`](Self::from_shape_vec). You just have to be careful /// that the layout of the data (the order of the elements in the flat `Vec`) /// is correct. /// /// ```rust /// use ndarray::{array, Array2}; /// /// let ncols = 3; /// let mut data = Vec::new(); /// let mut nrows = 0; /// for i in 0..2 { /// // Compute `row` and append it to `data`; this is a trivial example: /// let row = vec![i; ncols]; /// data.extend_from_slice(&row); /// nrows += 1; /// } /// let arr = Array2::from_shape_vec((nrows, ncols), data)?; /// assert_eq!(arr, array![[0, 0, 0], [1, 1, 1]]); /// # Ok::<(), ndarray::ShapeError>(()) /// ``` /// /// If neither of these options works for you, and you really need to convert /// nested `Vec`/`Array` instances to an `Array`, the cleanest solution is /// generally to use [`Iterator::flatten()`] /// to get a flat `Vec`, and then convert the `Vec` to an `Array` with /// [`::from_shape_vec()`](Self::from_shape_vec), like this: /// /// ```rust /// use ndarray::{array, Array2, Array3}; /// /// let nested: Vec> = vec![ /// array![[1, 2, 3], [4, 5, 6]], /// array![[7, 8, 9], [10, 11, 12]], /// ]; /// let inner_shape = nested[0].dim(); /// let shape = (nested.len(), inner_shape.0, inner_shape.1); /// let flat: Vec = nested.iter().flatten().cloned().collect(); /// let arr = Array3::from_shape_vec(shape, flat)?; /// assert_eq!(arr, array![ /// [[1, 2, 3], [4, 5, 6]], /// [[7, 8, 9], [10, 11, 12]], /// ]); /// # Ok::<(), ndarray::ShapeError>(()) /// ``` /// /// Note that this implementation assumes that the nested `Vec`s are all the /// same shape and that the `Vec` is non-empty. Depending on your application, /// it may be a good idea to add checks for these assumptions and possibly /// choose a different way to handle the empty case. /// // # For implementors // // All methods must uphold the following constraints: // // 1. `data` must correctly represent the data buffer / ownership information, // `ptr` must point into the data represented by `data`, and the `dim` and // `strides` must be consistent with `data`. For example, // // * If `data` is `OwnedRepr`, all elements represented by `ptr`, `dim`, // and `strides` must be owned by the `Vec` and not aliased by multiple // indices. // // * If `data` is `ViewRepr<&'a mut A>`, all elements represented by `ptr`, // `dim`, and `strides` must be exclusively borrowed and not aliased by // multiple indices. // // 2. If the type of `data` implements `Data`, then `ptr` must be aligned. // // 3. `ptr` must be non-null, and it must be safe to [`.offset()`] `ptr` by // zero. // // 4. It must be safe to [`.offset()`] the pointer repeatedly along all axes // and calculate the `count`s for the `.offset()` calls without overflow, // even if the array is empty or the elements are zero-sized. // // More specifically, the set of all possible (signed) offset counts // relative to `ptr` can be determined by the following (the casts and // arithmetic must not overflow): // // ```rust // /// Returns all the possible offset `count`s relative to `ptr`. // fn all_offset_counts(shape: &[usize], strides: &[isize]) -> BTreeSet { // assert_eq!(shape.len(), strides.len()); // let mut all_offsets = BTreeSet::::new(); // all_offsets.insert(0); // for axis in 0..shape.len() { // let old_offsets = all_offsets.clone(); // for index in 0..shape[axis] { // assert!(index <= isize::MAX as usize); // let off = (index as isize).checked_mul(strides[axis]).unwrap(); // for &old_offset in &old_offsets { // all_offsets.insert(old_offset.checked_add(off).unwrap()); // } // } // } // all_offsets // } // ``` // // Note that it must be safe to offset the pointer *repeatedly* along all // axes, so in addition for it being safe to offset `ptr` by each of these // counts, the difference between the least and greatest address reachable // by these offsets in units of `A` and in units of bytes must not be // greater than `isize::MAX`. // // In other words, // // * All possible pointers generated by moving along all axes must be in // bounds or one byte past the end of a single allocation with element // type `A`. The only exceptions are if the array is empty or the element // type is zero-sized. In these cases, `ptr` may be dangling, but it must // still be safe to [`.offset()`] the pointer along the axes. // // * The offset in units of bytes between the least address and greatest // address by moving along all axes must not exceed `isize::MAX`. This // constraint prevents the computed offset, in bytes, from overflowing // `isize` regardless of the starting point due to past offsets. // // * The offset in units of `A` between the least address and greatest // address by moving along all axes must not exceed `isize::MAX`. This // constraint prevents overflow when calculating the `count` parameter to // [`.offset()`] regardless of the starting point due to past offsets. // // For example, if the shape is [2, 0, 3] and the strides are [3, 6, -1], // the offsets of interest relative to `ptr` are -2, -1, 0, 1, 2, 3. So, // `ptr.offset(-2)`, `ptr.offset(-1)`, …, `ptr.offset(3)` must be pointers // within a single allocation with element type `A`; `(3 - (-2)) * // size_of::()` must not exceed `isize::MAX`, and `3 - (-2)` must not // exceed `isize::MAX`. Note that this is a requirement even though the // array is empty (axis 1 has length 0). // // A dangling pointer can be used when creating an empty array, but this // usually means all the strides have to be zero. A dangling pointer that // can safely be offset by zero bytes can be constructed with // `::std::ptr::NonNull::::dangling().as_ptr()`. (It isn't entirely clear // from the documentation that a pointer created this way is safe to // `.offset()` at all, even by zero bytes, but the implementation of // `Vec` does this, so we can too. See rust-lang/rust#54857 for details.) // // 5. The product of non-zero axis lengths must not exceed `isize::MAX`. (This // also implies that the length of any individual axis must not exceed // `isize::MAX`, and an array can contain at most `isize::MAX` elements.) // This constraint makes various calculations easier because they don't have // to worry about overflow and axis lengths can be freely cast to `isize`. // // Constraints 2–5 are carefully designed such that if they're upheld for the // array, they're also upheld for any subset of axes of the array as well as // slices/subviews/reshapes of the array. This is important for iterators that // produce subviews (and other similar cases) to be safe without extra (easy to // forget) checks for zero-length axes. Constraint 1 is similarly upheld for // any subset of axes and slices/subviews/reshapes, except when removing a // zero-length axis (since if the other axes are non-zero-length, that would // allow accessing elements that should not be possible to access). // // Method/function implementations can rely on these constraints being upheld. // The constraints can be temporarily violated within a method/function // implementation since `ArrayBase` doesn't implement `Drop` and `&mut // ArrayBase` is `!UnwindSafe`, but the implementation must not call // methods/functions on the array while it violates the constraints. // // Users of the `ndarray` crate cannot rely on these constraints because they // may change in the future. // // [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset-1 pub struct ArrayBase where S: RawData { /// Data buffer / ownership information. (If owned, contains the data /// buffer; if borrowed, contains the lifetime and mutability.) data: S, /// A non-null pointer into the buffer held by `data`; may point anywhere /// in its range. If `S: Data`, this pointer must be aligned. ptr: std::ptr::NonNull, /// The lengths of the axes. dim: D, /// The element count stride per axis. To be parsed as `isize`. strides: D, } /// An array where the data has shared ownership and is copy on write. /// /// The `ArcArray` is parameterized by `A` for the element type and `D` for /// the dimensionality. /// /// It can act as both an owner as the data as well as a shared reference (view /// like). /// Calling a method for mutating elements on `ArcArray`, for example /// [`view_mut()`](ArrayBase::view_mut) or /// [`get_mut()`](ArrayBase::get_mut), will break sharing and /// require a clone of the data (if it is not uniquely held). /// /// `ArcArray` uses atomic reference counting like `Arc`, so it is `Send` and /// `Sync` (when allowed by the element type of the array too). /// /// **[`ArrayBase`]** is used to implement both the owned /// arrays and the views; see its docs for an overview of all array features. /// /// See also: /// /// + [Constructor Methods for Owned Arrays](ArrayBase#constructor-methods-for-owned-arrays) /// + [Methods For All Array Types](ArrayBase#methods-for-all-array-types) pub type ArcArray = ArrayBase, D>; /// An array that owns its data uniquely. /// /// `Array` is the main n-dimensional array type, and it owns all its array /// elements. /// /// The `Array` is parameterized by `A` for the element type and `D` for /// the dimensionality. /// /// **[`ArrayBase`]** is used to implement both the owned /// arrays and the views; see its docs for an overview of all array features. /// /// See also: /// /// + [Constructor Methods for Owned Arrays](ArrayBase#constructor-methods-for-owned-arrays) /// + [Methods For All Array Types](ArrayBase#methods-for-all-array-types) /// + Dimensionality-specific type alises /// [`Array1`], /// [`Array2`], /// [`Array3`], ..., /// [`ArrayD`], /// and so on. pub type Array = ArrayBase, D>; /// An array with copy-on-write behavior. /// /// An `CowArray` represents either a uniquely owned array or a view of an /// array. The `'a` corresponds to the lifetime of the view variant. /// /// This type is analogous to [`std::borrow::Cow`]. /// If a `CowArray` instance is the immutable view variant, then calling a /// method for mutating elements in the array will cause it to be converted /// into the owned variant (by cloning all the elements) before the /// modification is performed. /// /// Array views have all the methods of an array (see [`ArrayBase`]). /// /// See also [`ArcArray`], which also provides /// copy-on-write behavior but has a reference-counted pointer to the data /// instead of either a view or a uniquely owned copy. pub type CowArray<'a, A, D> = ArrayBase, D>; /// A read-only array view. /// /// An array view represents an array or a part of it, created from /// an iterator, subview or slice of an array. /// /// The `ArrayView<'a, A, D>` is parameterized by `'a` for the scope of the /// borrow, `A` for the element type and `D` for the dimensionality. /// /// Array views have all the methods of an array (see [`ArrayBase`]). /// /// See also [`ArrayViewMut`]. pub type ArrayView<'a, A, D> = ArrayBase, D>; /// A read-write array view. /// /// An array view represents an array or a part of it, created from /// an iterator, subview or slice of an array. /// /// The `ArrayViewMut<'a, A, D>` is parameterized by `'a` for the scope of the /// borrow, `A` for the element type and `D` for the dimensionality. /// /// Array views have all the methods of an array (see [`ArrayBase`]). /// /// See also [`ArrayView`]. pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; /// A read-only array view without a lifetime. /// /// This is similar to [`ArrayView`] but does not carry any lifetime or /// ownership information, and its data cannot be read without an unsafe /// conversion into an [`ArrayView`]. The relationship between `RawArrayView` /// and [`ArrayView`] is somewhat analogous to the relationship between `*const /// T` and `&T`, but `RawArrayView` has additional requirements that `*const T` /// does not, such as non-nullness. /// /// The `RawArrayView` is parameterized by `A` for the element type and /// `D` for the dimensionality. /// /// Raw array views have all the methods of an array (see /// [`ArrayBase`]). /// /// See also [`RawArrayViewMut`]. /// /// # Warning /// /// You can't use this type with an arbitrary raw pointer; see /// [`from_shape_ptr`](#method.from_shape_ptr) for details. pub type RawArrayView = ArrayBase, D>; /// A mutable array view without a lifetime. /// /// This is similar to [`ArrayViewMut`] but does not carry any lifetime or /// ownership information, and its data cannot be read/written without an /// unsafe conversion into an [`ArrayViewMut`]. The relationship between /// `RawArrayViewMut` and [`ArrayViewMut`] is somewhat analogous to the /// relationship between `*mut T` and `&mut T`, but `RawArrayViewMut` has /// additional requirements that `*mut T` does not, such as non-nullness. /// /// The `RawArrayViewMut` is parameterized by `A` for the element type /// and `D` for the dimensionality. /// /// Raw array views have all the methods of an array (see /// [`ArrayBase`]). /// /// See also [`RawArrayView`]. /// /// # Warning /// /// You can't use this type with an arbitrary raw pointer; see /// [`from_shape_ptr`](#method.from_shape_ptr) for details. pub type RawArrayViewMut = ArrayBase, D>; pub use data_repr::OwnedRepr; /// ArcArray's representation. /// /// *Don’t use this type directly—use the type alias /// [`ArcArray`] for the array type!* #[derive(Debug)] pub struct OwnedArcRepr(Arc>); impl Clone for OwnedArcRepr { fn clone(&self) -> Self { OwnedArcRepr(self.0.clone()) } } /// Array pointer’s representation. /// /// *Don’t use this type directly—use the type aliases /// [`RawArrayView`] / [`RawArrayViewMut`] for the array type!* #[derive(Copy, Clone)] // This is just a marker type, to carry the mutability and element type. pub struct RawViewRepr { ptr: PhantomData, } impl RawViewRepr { #[inline(always)] const fn new() -> Self { RawViewRepr { ptr: PhantomData } } } /// Array view’s representation. /// /// *Don’t use this type directly—use the type aliases /// [`ArrayView`] / [`ArrayViewMut`] for the array type!* #[derive(Copy, Clone)] // This is just a marker type, to carry the lifetime parameter. pub struct ViewRepr { life: PhantomData, } impl ViewRepr { #[inline(always)] const fn new() -> Self { ViewRepr { life: PhantomData } } } /// CowArray's representation. /// /// *Don't use this type directly—use the type alias /// [`CowArray`] for the array type!* pub enum CowRepr<'a, A> { /// Borrowed data. View(ViewRepr<&'a A>), /// Owned data. Owned(OwnedRepr), } impl<'a, A> CowRepr<'a, A> { /// Returns `true` iff the data is the `View` variant. pub fn is_view(&self) -> bool { match self { CowRepr::View(_) => true, CowRepr::Owned(_) => false, } } /// Returns `true` iff the data is the `Owned` variant. pub fn is_owned(&self) -> bool { match self { CowRepr::View(_) => false, CowRepr::Owned(_) => true, } } } // NOTE: The order of modules decides in which order methods on the type ArrayBase // (mainly mentioning that as the most relevant type) show up in the documentation. // Consider the doc effect of ordering modules here. mod impl_clone; mod impl_internal_constructors; mod impl_constructors; mod impl_methods; mod impl_owned_array; mod impl_special_element_types; /// Private Methods impl ArrayBase where S: Data, D: Dimension, { #[inline] fn broadcast_unwrap(&self, dim: E) -> ArrayView<'_, A, E> where E: Dimension { #[cold] #[inline(never)] fn broadcast_panic(from: &D, to: &E) -> ! where D: Dimension, E: Dimension, { panic!( "ndarray: could not broadcast array from shape: {:?} to: {:?}", from.slice(), to.slice() ) } match self.broadcast(dim.clone()) { Some(it) => it, None => broadcast_panic(&self.dim, &dim), } } // Broadcast to dimension `E`, without checking that the dimensions match // (Checked in debug assertions). #[inline] fn broadcast_assume(&self, dim: E) -> ArrayView<'_, A, E> where E: Dimension { let dim = dim.into_dimension(); debug_assert_eq!(self.shape(), dim.slice()); let ptr = self.ptr; let mut strides = dim.clone(); strides.slice_mut().copy_from_slice(self.strides.slice()); unsafe { ArrayView::new(ptr, dim, strides) } } /// Remove array axis `axis` and return the result. fn try_remove_axis(self, axis: Axis) -> ArrayBase { let d = self.dim.try_remove_axis(axis); let s = self.strides.try_remove_axis(axis); // safe because new dimension, strides allow access to a subset of old data unsafe { self.with_strides_dim(s, d) } } } // parallel methods #[cfg(feature = "rayon")] pub mod parallel; mod impl_1d; mod impl_2d; mod impl_dyn; mod numeric; pub mod linalg; mod impl_ops; pub use crate::impl_ops::ScalarOperand; #[cfg(feature = "approx")] mod array_approx; // Array view methods mod impl_views; // Array raw view methods mod impl_raw_views; // Copy-on-write array methods mod impl_cow; // Arc array methods mod impl_arc_array; /// Returns `true` if the pointer is aligned. pub(crate) fn is_aligned(ptr: *const T) -> bool { (ptr as usize) % ::std::mem::align_of::() == 0 } // Triangular constructors mod tri; ndarray-0.16.1/src/linalg/impl_linalg.rs000064400000000000000000001010151046102023000162410ustar 00000000000000// Copyright 2014-2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::imp_prelude::*; #[cfg(feature = "blas")] use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr; use crate::numeric_util; use crate::{LinalgScalar, Zip}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::any::TypeId; use std::mem::MaybeUninit; use num_complex::Complex; use num_complex::{Complex32 as c32, Complex64 as c64}; #[cfg(feature = "blas")] use libc::c_int; #[cfg(feature = "blas")] use cblas_sys as blas_sys; #[cfg(feature = "blas")] use cblas_sys::{CblasNoTrans, CblasTrans, CBLAS_LAYOUT, CBLAS_TRANSPOSE}; /// len of vector before we use blas #[cfg(feature = "blas")] const DOT_BLAS_CUTOFF: usize = 32; /// side of matrix before we use blas #[cfg(feature = "blas")] const GEMM_BLAS_CUTOFF: usize = 7; #[cfg(feature = "blas")] #[allow(non_camel_case_types)] type blas_index = c_int; // blas index type impl ArrayBase where S: Data { /// Perform dot product or matrix multiplication of arrays `self` and `rhs`. /// /// `Rhs` may be either a one-dimensional or a two-dimensional array. /// /// If `Rhs` is one-dimensional, then the operation is a vector dot /// product, which is the sum of the elementwise products (no conjugation /// of complex operands, and thus not their inner product). In this case, /// `self` and `rhs` must be the same length. /// /// If `Rhs` is two-dimensional, then the operation is matrix /// multiplication, where `self` is treated as a row vector. In this case, /// if `self` is shape *M*, then `rhs` is shape *M* × *N* and the result is /// shape *N*. /// /// **Panics** if the array shapes are incompatible.
/// *Note:* If enabled, uses blas `dot` for elements of `f32, f64` when memory /// layout allows. #[track_caller] pub fn dot(&self, rhs: &Rhs) -> >::Output where Self: Dot { Dot::dot(self, rhs) } fn dot_generic(&self, rhs: &ArrayBase) -> A where S2: Data, A: LinalgScalar, { debug_assert_eq!(self.len(), rhs.len()); assert!(self.len() == rhs.len()); if let Some(self_s) = self.as_slice() { if let Some(rhs_s) = rhs.as_slice() { return numeric_util::unrolled_dot(self_s, rhs_s); } } let mut sum = A::zero(); for i in 0..self.len() { unsafe { sum = sum + *self.uget(i) * *rhs.uget(i); } } sum } #[cfg(not(feature = "blas"))] fn dot_impl(&self, rhs: &ArrayBase) -> A where S2: Data, A: LinalgScalar, { self.dot_generic(rhs) } #[cfg(feature = "blas")] fn dot_impl(&self, rhs: &ArrayBase) -> A where S2: Data, A: LinalgScalar, { // Use only if the vector is large enough to be worth it if self.len() >= DOT_BLAS_CUTOFF { debug_assert_eq!(self.len(), rhs.len()); assert!(self.len() == rhs.len()); macro_rules! dot { ($ty:ty, $func:ident) => {{ if blas_compat_1d::<$ty, _>(self) && blas_compat_1d::<$ty, _>(rhs) { unsafe { let (lhs_ptr, n, incx) = blas_1d_params(self.ptr.as_ptr(), self.len(), self.strides()[0]); let (rhs_ptr, _, incy) = blas_1d_params(rhs.ptr.as_ptr(), rhs.len(), rhs.strides()[0]); let ret = blas_sys::$func( n, lhs_ptr as *const $ty, incx, rhs_ptr as *const $ty, incy, ); return cast_as::<$ty, A>(&ret); } } }}; } dot! {f32, cblas_sdot}; dot! {f64, cblas_ddot}; } self.dot_generic(rhs) } } /// Return a pointer to the starting element in BLAS's view. /// /// BLAS wants a pointer to the element with lowest address, /// which agrees with our pointer for non-negative strides, but /// is at the opposite end for negative strides. #[cfg(feature = "blas")] unsafe fn blas_1d_params
(ptr: *const A, len: usize, stride: isize) -> (*const A, blas_index, blas_index) { // [x x x x] // ^--ptr // stride = -1 // ^--blas_ptr = ptr + (len - 1) * stride if stride >= 0 || len == 0 { (ptr, len as blas_index, stride as blas_index) } else { let ptr = ptr.offset((len - 1) as isize * stride); (ptr, len as blas_index, stride as blas_index) } } /// Matrix Multiplication /// /// For two-dimensional arrays, the dot method computes the matrix /// multiplication. pub trait Dot { /// The result of the operation. /// /// For two-dimensional arrays: a rectangular array. type Output; fn dot(&self, rhs: &Rhs) -> Self::Output; } impl Dot> for ArrayBase where S: Data, S2: Data, A: LinalgScalar, { type Output = A; /// Compute the dot product of one-dimensional arrays. /// /// The dot product is a sum of the elementwise products (no conjugation /// of complex operands, and thus not their inner product). /// /// **Panics** if the arrays are not of the same length.
/// *Note:* If enabled, uses blas `dot` for elements of `f32, f64` when memory /// layout allows. #[track_caller] fn dot(&self, rhs: &ArrayBase) -> A { self.dot_impl(rhs) } } impl Dot> for ArrayBase where S: Data, S2: Data, A: LinalgScalar, { type Output = Array; /// Perform the matrix multiplication of the row vector `self` and /// rectangular matrix `rhs`. /// /// The array shapes must agree in the way that /// if `self` is *M*, then `rhs` is *M* × *N*. /// /// Return a result array with shape *N*. /// /// **Panics** if shapes are incompatible. #[track_caller] fn dot(&self, rhs: &ArrayBase) -> Array { rhs.t().dot(self) } } impl ArrayBase where S: Data { /// Perform matrix multiplication of rectangular arrays `self` and `rhs`. /// /// `Rhs` may be either a one-dimensional or a two-dimensional array. /// /// If Rhs is two-dimensional, they array shapes must agree in the way that /// if `self` is *M* × *N*, then `rhs` is *N* × *K*. /// /// Return a result array with shape *M* × *K*. /// /// **Panics** if shapes are incompatible or the number of elements in the /// result would overflow `isize`. /// /// *Note:* If enabled, uses blas `gemv/gemm` for elements of `f32, f64` /// when memory layout allows. The default matrixmultiply backend /// is otherwise used for `f32, f64` for all memory layouts. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[1., 2.], /// [0., 1.]]); /// let b = arr2(&[[1., 2.], /// [2., 3.]]); /// /// assert!( /// a.dot(&b) == arr2(&[[5., 8.], /// [2., 3.]]) /// ); /// ``` #[track_caller] pub fn dot(&self, rhs: &Rhs) -> >::Output where Self: Dot { Dot::dot(self, rhs) } } impl Dot> for ArrayBase where S: Data, S2: Data, A: LinalgScalar, { type Output = Array2
; fn dot(&self, b: &ArrayBase) -> Array2 { let a = self.view(); let b = b.view(); let ((m, k), (k2, n)) = (a.dim(), b.dim()); if k != k2 || m.checked_mul(n).is_none() { dot_shape_error(m, k, k2, n); } let lhs_s0 = a.strides()[0]; let rhs_s0 = b.strides()[0]; let column_major = lhs_s0 == 1 && rhs_s0 == 1; // A is Copy so this is safe let mut v = Vec::with_capacity(m * n); let mut c; unsafe { v.set_len(m * n); c = Array::from_shape_vec_unchecked((m, n).set_f(column_major), v); } mat_mul_impl(A::one(), &a, &b, A::zero(), &mut c.view_mut()); c } } /// Assumes that `m` and `n` are ≤ `isize::MAX`. #[cold] #[inline(never)] fn dot_shape_error(m: usize, k: usize, k2: usize, n: usize) -> ! { match m.checked_mul(n) { Some(len) if len <= isize::MAX as usize => {} _ => panic!("ndarray: shape {} × {} overflows isize", m, n), } panic!( "ndarray: inputs {} × {} and {} × {} are not compatible for matrix multiplication", m, k, k2, n ); } #[cold] #[inline(never)] fn general_dot_shape_error(m: usize, k: usize, k2: usize, n: usize, c1: usize, c2: usize) -> ! { panic!("ndarray: inputs {} × {}, {} × {}, and output {} × {} are not compatible for matrix multiplication", m, k, k2, n, c1, c2); } /// Perform the matrix multiplication of the rectangular array `self` and /// column vector `rhs`. /// /// The array shapes must agree in the way that /// if `self` is *M* × *N*, then `rhs` is *N*. /// /// Return a result array with shape *M*. /// /// **Panics** if shapes are incompatible. impl Dot> for ArrayBase where S: Data, S2: Data, A: LinalgScalar, { type Output = Array; #[track_caller] fn dot(&self, rhs: &ArrayBase) -> Array { let ((m, a), n) = (self.dim(), rhs.dim()); if a != n { dot_shape_error(m, a, n, 1); } // Avoid initializing the memory in vec -- set it during iteration unsafe { let mut c = Array1::uninit(m); general_mat_vec_mul_impl(A::one(), self, rhs, A::zero(), c.raw_view_mut().cast::()); c.assume_init() } } } impl ArrayBase where S: Data, D: Dimension, { /// Perform the operation `self += alpha * rhs` efficiently, where /// `alpha` is a scalar and `rhs` is another array. This operation is /// also known as `axpy` in BLAS. /// /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// **Panics** if broadcasting isn’t possible. #[track_caller] pub fn scaled_add(&mut self, alpha: A, rhs: &ArrayBase) where S: DataMut, S2: Data, A: LinalgScalar, E: Dimension, { self.zip_mut_with(rhs, move |y, &x| *y = *y + (alpha * x)); } } // mat_mul_impl uses ArrayView arguments to send all array kinds into // the same instantiated implementation. #[cfg(not(feature = "blas"))] use self::mat_mul_general as mat_mul_impl; #[cfg(feature = "blas")] fn mat_mul_impl(alpha: A, a: &ArrayView2<'_, A>, b: &ArrayView2<'_, A>, beta: A, c: &mut ArrayViewMut2<'_, A>) where A: LinalgScalar { let ((m, k), (k2, n)) = (a.dim(), b.dim()); debug_assert_eq!(k, k2); if (m > GEMM_BLAS_CUTOFF || n > GEMM_BLAS_CUTOFF || k > GEMM_BLAS_CUTOFF) && (same_type::() || same_type::() || same_type::() || same_type::()) { // Compute A B -> C // We require for BLAS compatibility that: // A, B, C are contiguous (stride=1) in their fastest dimension, // but they can be either row major/"c" or col major/"f". // // The "normal case" is CblasRowMajor for cblas. // Select CblasRowMajor / CblasColMajor to fit C's memory order. // // Apply transpose to A, B as needed if they differ from the row major case. // If C is CblasColMajor then transpose both A, B (again!) if let (Some(a_layout), Some(b_layout), Some(c_layout)) = (get_blas_compatible_layout(a), get_blas_compatible_layout(b), get_blas_compatible_layout(c)) { let cblas_layout = c_layout.to_cblas_layout(); let a_trans = a_layout.to_cblas_transpose_for(cblas_layout); let lda = blas_stride(&a, a_layout); let b_trans = b_layout.to_cblas_transpose_for(cblas_layout); let ldb = blas_stride(&b, b_layout); let ldc = blas_stride(&c, c_layout); macro_rules! gemm_scalar_cast { (f32, $var:ident) => { cast_as(&$var) }; (f64, $var:ident) => { cast_as(&$var) }; (c32, $var:ident) => { &$var as *const A as *const _ }; (c64, $var:ident) => { &$var as *const A as *const _ }; } macro_rules! gemm { ($ty:tt, $gemm:ident) => { if same_type::() { // gemm is C ← αA^Op B^Op + βC // Where Op is notrans/trans/conjtrans unsafe { blas_sys::$gemm( cblas_layout, a_trans, b_trans, m as blas_index, // m, rows of Op(a) n as blas_index, // n, cols of Op(b) k as blas_index, // k, cols of Op(a) gemm_scalar_cast!($ty, alpha), // alpha a.ptr.as_ptr() as *const _, // a lda, // lda b.ptr.as_ptr() as *const _, // b ldb, // ldb gemm_scalar_cast!($ty, beta), // beta c.ptr.as_ptr() as *mut _, // c ldc, // ldc ); } return; } }; } gemm!(f32, cblas_sgemm); gemm!(f64, cblas_dgemm); gemm!(c32, cblas_cgemm); gemm!(c64, cblas_zgemm); unreachable!() // we checked above that A is one of f32, f64, c32, c64 } } mat_mul_general(alpha, a, b, beta, c) } /// C ← α A B + β C fn mat_mul_general( alpha: A, lhs: &ArrayView2<'_, A>, rhs: &ArrayView2<'_, A>, beta: A, c: &mut ArrayViewMut2<'_, A>, ) where A: LinalgScalar { let ((m, k), (_, n)) = (lhs.dim(), rhs.dim()); // common parameters for gemm let ap = lhs.as_ptr(); let bp = rhs.as_ptr(); let cp = c.as_mut_ptr(); let (rsc, csc) = (c.strides()[0], c.strides()[1]); if same_type::() { unsafe { matrixmultiply::sgemm( m, k, n, cast_as(&alpha), ap as *const _, lhs.strides()[0], lhs.strides()[1], bp as *const _, rhs.strides()[0], rhs.strides()[1], cast_as(&beta), cp as *mut _, rsc, csc, ); } } else if same_type::() { unsafe { matrixmultiply::dgemm( m, k, n, cast_as(&alpha), ap as *const _, lhs.strides()[0], lhs.strides()[1], bp as *const _, rhs.strides()[0], rhs.strides()[1], cast_as(&beta), cp as *mut _, rsc, csc, ); } } else if same_type::() { unsafe { matrixmultiply::cgemm( matrixmultiply::CGemmOption::Standard, matrixmultiply::CGemmOption::Standard, m, k, n, complex_array(cast_as(&alpha)), ap as *const _, lhs.strides()[0], lhs.strides()[1], bp as *const _, rhs.strides()[0], rhs.strides()[1], complex_array(cast_as(&beta)), cp as *mut _, rsc, csc, ); } } else if same_type::() { unsafe { matrixmultiply::zgemm( matrixmultiply::CGemmOption::Standard, matrixmultiply::CGemmOption::Standard, m, k, n, complex_array(cast_as(&alpha)), ap as *const _, lhs.strides()[0], lhs.strides()[1], bp as *const _, rhs.strides()[0], rhs.strides()[1], complex_array(cast_as(&beta)), cp as *mut _, rsc, csc, ); } } else { // It's a no-op if `c` has zero length. if c.is_empty() { return; } // initialize memory if beta is zero if beta.is_zero() { c.fill(beta); } let mut i = 0; let mut j = 0; loop { unsafe { let elt = c.uget_mut((i, j)); *elt = *elt * beta + alpha * (0..k).fold(A::zero(), move |s, x| s + *lhs.uget((i, x)) * *rhs.uget((x, j))); } j += 1; if j == n { j = 0; i += 1; if i == m { break; } } } } } /// General matrix-matrix multiplication. /// /// Compute C ← α A B + β C /// /// The array shapes must agree in the way that /// if `a` is *M* × *N*, then `b` is *N* × *K* and `c` is *M* × *K*. /// /// ***Panics*** if array shapes are not compatible
/// *Note:* If enabled, uses blas `gemm` for elements of `f32, f64` when memory /// layout allows. The default matrixmultiply backend is otherwise used for /// `f32, f64` for all memory layouts. #[track_caller] pub fn general_mat_mul( alpha: A, a: &ArrayBase, b: &ArrayBase, beta: A, c: &mut ArrayBase, ) where S1: Data, S2: Data, S3: DataMut, A: LinalgScalar, { let ((m, k), (k2, n)) = (a.dim(), b.dim()); let (m2, n2) = c.dim(); if k != k2 || m != m2 || n != n2 { general_dot_shape_error(m, k, k2, n, m2, n2); } else { mat_mul_impl(alpha, &a.view(), &b.view(), beta, &mut c.view_mut()); } } /// General matrix-vector multiplication. /// /// Compute y ← α A x + β y /// /// where A is a *M* × *N* matrix and x is an *N*-element column vector and /// y an *M*-element column vector (one dimensional arrays). /// /// ***Panics*** if array shapes are not compatible
/// *Note:* If enabled, uses blas `gemv` for elements of `f32, f64` when memory /// layout allows. #[track_caller] #[allow(clippy::collapsible_if)] pub fn general_mat_vec_mul( alpha: A, a: &ArrayBase, x: &ArrayBase, beta: A, y: &mut ArrayBase, ) where S1: Data, S2: Data, S3: DataMut, A: LinalgScalar, { unsafe { general_mat_vec_mul_impl(alpha, a, x, beta, y.raw_view_mut()) } } /// General matrix-vector multiplication /// /// Use a raw view for the destination vector, so that it can be uninitialized. /// /// ## Safety /// /// The caller must ensure that the raw view is valid for writing. /// the destination may be uninitialized iff beta is zero. #[allow(clippy::collapsible_else_if)] unsafe fn general_mat_vec_mul_impl( alpha: A, a: &ArrayBase, x: &ArrayBase, beta: A, y: RawArrayViewMut, ) where S1: Data, S2: Data, A: LinalgScalar, { let ((m, k), k2) = (a.dim(), x.dim()); let m2 = y.dim(); if k != k2 || m != m2 { general_dot_shape_error(m, k, k2, 1, m2, 1); } else { #[cfg(feature = "blas")] macro_rules! gemv { ($ty:ty, $gemv:ident) => { if same_type::() { if let Some(layout) = get_blas_compatible_layout(&a) { if blas_compat_1d::<$ty, _>(&x) && blas_compat_1d::<$ty, _>(&y) { // Determine stride between rows or columns. Note that the stride is // adjusted to at least `k` or `m` to handle the case of a matrix with a // trivial (length 1) dimension, since the stride for the trivial dimension // may be arbitrary. let a_trans = CblasNoTrans; let a_stride = blas_stride(&a, layout); let cblas_layout = layout.to_cblas_layout(); // Low addr in memory pointers required for x, y let x_offset = offset_from_low_addr_ptr_to_logical_ptr(&x.dim, &x.strides); let x_ptr = x.ptr.as_ptr().sub(x_offset); let y_offset = offset_from_low_addr_ptr_to_logical_ptr(&y.dim, &y.strides); let y_ptr = y.ptr.as_ptr().sub(y_offset); let x_stride = x.strides()[0] as blas_index; let y_stride = y.strides()[0] as blas_index; blas_sys::$gemv( cblas_layout, a_trans, m as blas_index, // m, rows of Op(a) k as blas_index, // n, cols of Op(a) cast_as(&alpha), // alpha a.ptr.as_ptr() as *const _, // a a_stride, // lda x_ptr as *const _, // x x_stride, cast_as(&beta), // beta y_ptr as *mut _, // y y_stride, ); return; } } } }; } #[cfg(feature = "blas")] gemv!(f32, cblas_sgemv); #[cfg(feature = "blas")] gemv!(f64, cblas_dgemv); /* general */ if beta.is_zero() { // when beta is zero, c may be uninitialized Zip::from(a.outer_iter()).and(y).for_each(|row, elt| { elt.write(row.dot(x) * alpha); }); } else { Zip::from(a.outer_iter()).and(y).for_each(|row, elt| { *elt = *elt * beta + row.dot(x) * alpha; }); } } } /// Kronecker product of 2D matrices. /// /// The kronecker product of a LxN matrix A and a MxR matrix B is a (L*M)x(N*R) /// matrix K formed by the block multiplication A_ij * B. pub fn kron(a: &ArrayBase, b: &ArrayBase) -> Array where S1: Data, S2: Data, A: LinalgScalar, { let dimar = a.shape()[0]; let dimac = a.shape()[1]; let dimbr = b.shape()[0]; let dimbc = b.shape()[1]; let mut out: Array2> = Array2::uninit(( dimar .checked_mul(dimbr) .expect("Dimensions of kronecker product output array overflows usize."), dimac .checked_mul(dimbc) .expect("Dimensions of kronecker product output array overflows usize."), )); Zip::from(out.exact_chunks_mut((dimbr, dimbc))) .and(a) .for_each(|out, &a| { Zip::from(out).and(b).for_each(|out, &b| { *out = MaybeUninit::new(a * b); }) }); unsafe { out.assume_init() } } #[inline(always)] /// Return `true` if `A` and `B` are the same type fn same_type() -> bool { TypeId::of::
() == TypeId::of::() } // Read pointer to type `A` as type `B`. // // **Panics** if `A` and `B` are not the same type fn cast_as(a: &A) -> B { assert!(same_type::(), "expect type {} and {} to match", std::any::type_name::(), std::any::type_name::()); unsafe { ::std::ptr::read(a as *const _ as *const B) } } /// Return the complex in the form of an array [re, im] #[inline] fn complex_array(z: Complex) -> [A; 2] { [z.re, z.im] } #[cfg(feature = "blas")] fn blas_compat_1d(a: &ArrayBase) -> bool where S: RawData, A: 'static, S::Elem: 'static, { if !same_type::() { return false; } if a.len() > blas_index::MAX as usize { return false; } let stride = a.strides()[0]; if stride == 0 || stride > blas_index::MAX as isize || stride < blas_index::MIN as isize { return false; } true } #[cfg(feature = "blas")] #[derive(Copy, Clone)] #[cfg_attr(test, derive(PartialEq, Eq, Debug))] enum BlasOrder { C, F, } #[cfg(feature = "blas")] impl BlasOrder { fn transpose(self) -> Self { match self { Self::C => Self::F, Self::F => Self::C, } } #[inline] /// Axis of leading stride (opposite of contiguous axis) fn get_blas_lead_axis(self) -> usize { match self { Self::C => 0, Self::F => 1, } } fn to_cblas_layout(self) -> CBLAS_LAYOUT { match self { Self::C => CBLAS_LAYOUT::CblasRowMajor, Self::F => CBLAS_LAYOUT::CblasColMajor, } } /// When using cblas_sgemm (etc) with C matrix using `for_layout`, /// how should this `self` matrix be transposed fn to_cblas_transpose_for(self, for_layout: CBLAS_LAYOUT) -> CBLAS_TRANSPOSE { let effective_order = match for_layout { CBLAS_LAYOUT::CblasRowMajor => self, CBLAS_LAYOUT::CblasColMajor => self.transpose(), }; match effective_order { Self::C => CblasNoTrans, Self::F => CblasTrans, } } } #[cfg(feature = "blas")] fn is_blas_2d(dim: &Ix2, stride: &Ix2, order: BlasOrder) -> bool { let (m, n) = dim.into_pattern(); let s0 = stride[0] as isize; let s1 = stride[1] as isize; let (inner_stride, outer_stride, inner_dim, outer_dim) = match order { BlasOrder::C => (s1, s0, m, n), BlasOrder::F => (s0, s1, n, m), }; if !(inner_stride == 1 || outer_dim == 1) { return false; } if s0 < 1 || s1 < 1 { return false; } if (s0 > blas_index::MAX as isize || s0 < blas_index::MIN as isize) || (s1 > blas_index::MAX as isize || s1 < blas_index::MIN as isize) { return false; } // leading stride must >= the dimension (no broadcasting/aliasing) if inner_dim > 1 && (outer_stride as usize) < outer_dim { return false; } if m > blas_index::MAX as usize || n > blas_index::MAX as usize { return false; } true } /// Get BLAS compatible layout if any (C or F, preferring the former) #[cfg(feature = "blas")] fn get_blas_compatible_layout(a: &ArrayBase) -> Option where S: Data { if is_blas_2d(&a.dim, &a.strides, BlasOrder::C) { Some(BlasOrder::C) } else if is_blas_2d(&a.dim, &a.strides, BlasOrder::F) { Some(BlasOrder::F) } else { None } } /// `a` should be blas compatible. /// axis: 0 or 1. /// /// Return leading stride (lda, ldb, ldc) of array #[cfg(feature = "blas")] fn blas_stride(a: &ArrayBase, order: BlasOrder) -> blas_index where S: Data { let axis = order.get_blas_lead_axis(); let other_axis = 1 - axis; let len_this = a.shape()[axis]; let len_other = a.shape()[other_axis]; let stride = a.strides()[axis]; // if current axis has length == 1, then stride does not matter for ndarray // but for BLAS we need a stride that makes sense, i.e. it's >= the other axis // cast: a should already be blas compatible (if len_this <= 1 { Ord::max(stride, len_other as isize) } else { stride }) as blas_index } #[cfg(test)] #[cfg(feature = "blas")] fn blas_row_major_2d(a: &ArrayBase) -> bool where S: Data, A: 'static, S::Elem: 'static, { if !same_type::() { return false; } is_blas_2d(&a.dim, &a.strides, BlasOrder::C) } #[cfg(test)] #[cfg(feature = "blas")] fn blas_column_major_2d(a: &ArrayBase) -> bool where S: Data, A: 'static, S::Elem: 'static, { if !same_type::() { return false; } is_blas_2d(&a.dim, &a.strides, BlasOrder::F) } #[cfg(test)] #[cfg(feature = "blas")] mod blas_tests { use super::*; #[test] fn blas_row_major_2d_normal_matrix() { let m: Array2 = Array2::zeros((3, 5)); assert!(blas_row_major_2d::(&m)); assert!(!blas_column_major_2d::(&m)); } #[test] fn blas_row_major_2d_row_matrix() { let m: Array2 = Array2::zeros((1, 5)); assert!(blas_row_major_2d::(&m)); assert!(blas_column_major_2d::(&m)); } #[test] fn blas_row_major_2d_column_matrix() { let m: Array2 = Array2::zeros((5, 1)); assert!(blas_row_major_2d::(&m)); assert!(blas_column_major_2d::(&m)); } #[test] fn blas_row_major_2d_transposed_row_matrix() { let m: Array2 = Array2::zeros((1, 5)); let m_t = m.t(); assert!(blas_row_major_2d::(&m_t)); assert!(blas_column_major_2d::(&m_t)); } #[test] fn blas_row_major_2d_transposed_column_matrix() { let m: Array2 = Array2::zeros((5, 1)); let m_t = m.t(); assert!(blas_row_major_2d::(&m_t)); assert!(blas_column_major_2d::(&m_t)); } #[test] fn blas_column_major_2d_normal_matrix() { let m: Array2 = Array2::zeros((3, 5).f()); assert!(!blas_row_major_2d::(&m)); assert!(blas_column_major_2d::(&m)); } #[test] fn blas_row_major_2d_skip_rows_ok() { let m: Array2 = Array2::zeros((5, 5)); let mv = m.slice(s![..;2, ..]); assert!(blas_row_major_2d::(&mv)); assert!(!blas_column_major_2d::(&mv)); } #[test] fn blas_row_major_2d_skip_columns_fail() { let m: Array2 = Array2::zeros((5, 5)); let mv = m.slice(s![.., ..;2]); assert!(!blas_row_major_2d::(&mv)); assert!(!blas_column_major_2d::(&mv)); } #[test] fn blas_col_major_2d_skip_columns_ok() { let m: Array2 = Array2::zeros((5, 5).f()); let mv = m.slice(s![.., ..;2]); assert!(blas_column_major_2d::(&mv)); assert!(!blas_row_major_2d::(&mv)); } #[test] fn blas_col_major_2d_skip_rows_fail() { let m: Array2 = Array2::zeros((5, 5).f()); let mv = m.slice(s![..;2, ..]); assert!(!blas_column_major_2d::(&mv)); assert!(!blas_row_major_2d::(&mv)); } #[test] fn blas_too_short_stride() { // leading stride must be longer than the other dimension // Example, in a 5 x 5 matrix, the leading stride must be >= 5 for BLAS. const N: usize = 5; const MAXSTRIDE: usize = N + 2; let mut data = [0; MAXSTRIDE * N]; let mut iter = 0..data.len(); data.fill_with(|| iter.next().unwrap()); for stride in 1..=MAXSTRIDE { let m = ArrayView::from_shape((N, N).strides((stride, 1)), &data).unwrap(); eprintln!("{:?}", m); if stride < N { assert_eq!(get_blas_compatible_layout(&m), None); } else { assert_eq!(get_blas_compatible_layout(&m), Some(BlasOrder::C)); } } } } ndarray-0.16.1/src/linalg/mod.rs000064400000000000000000000010441046102023000145320ustar 00000000000000// Copyright 2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Linear algebra. pub use self::impl_linalg::general_mat_mul; pub use self::impl_linalg::general_mat_vec_mul; pub use self::impl_linalg::kron; pub use self::impl_linalg::Dot; mod impl_linalg; ndarray-0.16.1/src/linalg_traits.rs000064400000000000000000000035371046102023000153520ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[cfg(feature = "std")] use num_traits::Float; use num_traits::{One, Zero}; #[cfg(feature = "std")] use std::fmt; use std::ops::{Add, Div, Mul, Sub}; #[cfg(feature = "std")] use std::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; #[cfg(feature = "std")] use crate::ScalarOperand; /// Elements that support linear algebra operations. /// /// `'static` for type-based specialization, `Copy` so that they don't need move /// semantics or destructors, and the rest are numerical traits. pub trait LinalgScalar: 'static + Copy + Zero + One + Add + Sub + Mul + Div { } impl LinalgScalar for T where T: 'static + Copy + Zero + One + Add + Sub + Mul + Div {} /// Floating-point element types `f32` and `f64`. /// /// Trait `NdFloat` is only implemented for `f32` and `f64` but encompasses as /// much float-relevant ndarray functionality as possible, including the traits /// needed for linear algebra and for *right hand side* scalar /// operations (`ScalarOperand`). /// /// This trait can only be implemented by `f32` and `f64`. #[cfg(feature = "std")] pub trait NdFloat: Float + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign + fmt::Display + fmt::Debug + fmt::LowerExp + fmt::UpperExp + ScalarOperand + LinalgScalar + Send + Sync { } #[cfg(feature = "std")] impl NdFloat for f32 {} #[cfg(feature = "std")] impl NdFloat for f64 {} ndarray-0.16.1/src/linspace.rs000064400000000000000000000057571046102023000143220ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![cfg(feature = "std")] use num_traits::Float; /// An iterator of a sequence of evenly spaced floats. /// /// Iterator element type is `F`. pub struct Linspace { start: F, step: F, index: usize, len: usize, } impl Iterator for Linspace where F: Float { type Item = F; #[inline] fn next(&mut self) -> Option { if self.index >= self.len { None } else { // Calculate the value just like numpy.linspace does let i = self.index; self.index += 1; Some(self.start + self.step * F::from(i).unwrap()) } } #[inline] fn size_hint(&self) -> (usize, Option) { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Linspace where F: Float { #[inline] fn next_back(&mut self) -> Option { if self.index >= self.len { None } else { // Calculate the value just like numpy.linspace does self.len -= 1; let i = self.len; Some(self.start + self.step * F::from(i).unwrap()) } } } impl ExactSizeIterator for Linspace where Linspace: Iterator {} /// Return an iterator of evenly spaced floats. /// /// The `Linspace` has `n` elements from `a` to `b` (inclusive). /// /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. /// [`f32`] or [`f64`]. /// /// **Panics** if converting `n - 1` to type `F` fails. #[inline] pub fn linspace(a: F, b: F, n: usize) -> Linspace where F: Float { let step = if n > 1 { let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); (b - a) / num_steps } else { F::zero() }; Linspace { start: a, step, index: 0, len: n, } } /// Return an iterator of floats from `a` to `b` (exclusive), /// incrementing by `step`. /// /// Numerical reasons can result in `b` being included in the result. /// /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. /// [`f32`] or [`f64`]. /// /// **Panics** if converting `((b - a) / step).ceil()` to type `F` fails. #[inline] pub fn range(a: F, b: F, step: F) -> Linspace where F: Float { let len = b - a; let steps = F::ceil(len / step); Linspace { start: a, step, len: steps.to_usize().expect( "Converting the length to `usize` must not fail. The most likely \ cause of this failure is if the sign of `end - start` is \ different from the sign of `step`.", ), index: 0, } } ndarray-0.16.1/src/logspace.rs000064400000000000000000000104331046102023000143040ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![cfg(feature = "std")] use num_traits::Float; /// An iterator of a sequence of logarithmically spaced number. /// /// Iterator element type is `F`. pub struct Logspace { sign: F, base: F, start: F, step: F, index: usize, len: usize, } impl Iterator for Logspace where F: Float { type Item = F; #[inline] fn next(&mut self) -> Option { if self.index >= self.len { None } else { // Calculate the value just like numpy.linspace does let i = self.index; self.index += 1; let exponent = self.start + self.step * F::from(i).unwrap(); Some(self.sign * self.base.powf(exponent)) } } #[inline] fn size_hint(&self) -> (usize, Option) { let n = self.len - self.index; (n, Some(n)) } } impl DoubleEndedIterator for Logspace where F: Float { #[inline] fn next_back(&mut self) -> Option { if self.index >= self.len { None } else { // Calculate the value just like numpy.linspace does self.len -= 1; let i = self.len; let exponent = self.start + self.step * F::from(i).unwrap(); Some(self.sign * self.base.powf(exponent)) } } } impl ExactSizeIterator for Logspace where Logspace: Iterator {} /// An iterator of a sequence of logarithmically spaced numbers. /// /// The [`Logspace`] has `n` elements, where the first element is `base.powf(a)` /// and the last element is `base.powf(b)`. If `base` is negative, this /// iterator will return all negative values. /// /// The iterator element type is `F`, where `F` must implement [`Float`], e.g. /// [`f32`] or [`f64`]. /// /// **Panics** if converting `n - 1` to type `F` fails. #[inline] pub fn logspace(base: F, a: F, b: F, n: usize) -> Logspace where F: Float { let step = if n > 1 { let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail."); (b - a) / num_steps } else { F::zero() }; Logspace { sign: base.signum(), base: base.abs(), start: a, step, index: 0, len: n, } } #[cfg(test)] mod tests { use super::logspace; #[test] #[cfg(feature = "approx")] fn valid() { use crate::{arr1, Array1}; use approx::assert_abs_diff_eq; let array: Array1<_> = logspace(10.0, 0.0, 3.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3])); let array: Array1<_> = logspace(10.0, 3.0, 0.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[1e3, 1e2, 1e1, 1e0])); let array: Array1<_> = logspace(-10.0, 3.0, 0.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0])); let array: Array1<_> = logspace(-10.0, 0.0, 3.0, 4).collect(); assert_abs_diff_eq!(array, arr1(&[-1e0, -1e1, -1e2, -1e3])); } #[test] fn iter_forward() { let mut iter = logspace(10.0f64, 0.0, 3.0, 4); assert!(iter.size_hint() == (4, Some(4))); assert!((iter.next().unwrap() - 1e0).abs() < 1e-5); assert!((iter.next().unwrap() - 1e1).abs() < 1e-5); assert!((iter.next().unwrap() - 1e2).abs() < 1e-5); assert!((iter.next().unwrap() - 1e3).abs() < 1e-5); assert!(iter.next().is_none()); assert!(iter.size_hint() == (0, Some(0))); } #[test] fn iter_backward() { let mut iter = logspace(10.0f64, 0.0, 3.0, 4); assert!(iter.size_hint() == (4, Some(4))); assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5); assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5); assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5); assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5); assert!(iter.next_back().is_none()); assert!(iter.size_hint() == (0, Some(0))); } } ndarray-0.16.1/src/low_level_util.rs000064400000000000000000000025261046102023000155400ustar 00000000000000// Copyright 2021 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /// Guard value that will abort if it is dropped. /// To defuse, this value must be forgotten before the end of the scope. /// /// The string value is added to the message printed if aborting. #[must_use] pub(crate) struct AbortIfPanic(pub(crate) &'static &'static str); impl AbortIfPanic { /// Defuse the AbortIfPanic guard. This *must* be done when finished. #[inline] pub(crate) fn defuse(self) { std::mem::forget(self); } } impl Drop for AbortIfPanic { // The compiler should be able to remove this, if it can see through that there // is no panic in the code section. fn drop(&mut self) { #[cfg(feature = "std")] { eprintln!("ndarray: panic in no-panic section, aborting: {}", self.0); std::process::abort() } #[cfg(not(feature = "std"))] { // no-std uses panic-in-panic (should abort) panic!("ndarray: panic in no-panic section, bailing out: {}", self.0); } } } ndarray-0.16.1/src/macro_utils.rs000064400000000000000000000037721046102023000150400ustar 00000000000000/// Derive Copy and Clone using the parameters (and bounds) as specified in [] macro_rules! copy_and_clone { ([$($parm:tt)*] $type_:ty) => { impl<$($parm)*> Copy for $type_ { } impl<$($parm)*> Clone for $type_ { #[inline(always)] fn clone(&self) -> Self { *self } } }; ($type_:ty) => { copy_and_clone!{ [] $type_ } }; } macro_rules! clone_bounds { ([$($parmbounds:tt)*] $typename:ident [$($parm:tt)*] { @copy { $($copyfield:ident,)* } $($field:ident,)* }) => { impl<$($parmbounds)*> Clone for $typename<$($parm)*> { fn clone(&self) -> Self { $typename { $( $copyfield: self.$copyfield, )* $( $field: self.$field.clone(), )* } } } }; } /// This assertion is always enabled but only verbose (formatting when /// debug assertions are enabled). #[cfg(debug_assertions)] macro_rules! ndassert { ($e:expr, $($t:tt)*) => { assert!($e, $($t)*) }; } #[cfg(not(debug_assertions))] macro_rules! ndassert { ($e:expr, $($_ignore:tt)*) => { assert!($e) }; } macro_rules! expand_if { (@bool [true] $($body:tt)*) => { $($body)* }; (@bool [false] $($body:tt)*) => { }; (@nonempty [$($if_present:tt)+] $($body:tt)*) => { $($body)* }; (@nonempty [] $($body:tt)*) => { }; } // Macro to insert more informative out of bounds message in debug builds #[cfg(debug_assertions)] macro_rules! debug_bounds_check { ($self_:ident, $index:expr) => { if $index.index_checked(&$self_.dim, &$self_.strides).is_none() { panic!( "ndarray: index {:?} is out of bounds for array of shape {:?}", $index, $self_.shape() ); } }; } #[cfg(not(debug_assertions))] macro_rules! debug_bounds_check { ($self_:ident, $index:expr) => {}; } ndarray-0.16.1/src/math_cell.rs000064400000000000000000000047251046102023000144460ustar 00000000000000use std::cell::Cell; use std::cmp::Ordering; use std::fmt; use std::ops::{Deref, DerefMut}; /// A transparent wrapper of [`Cell`](std::cell::Cell) which is identical in every way, except /// it will implement arithmetic operators as well. /// /// The purpose of `MathCell` is to be used from [.cell_view()](crate::ArrayBase::cell_view). /// The `MathCell` derefs to `Cell`, so all the cell's methods are available. #[repr(transparent)] #[derive(Default)] pub struct MathCell(Cell); impl MathCell { /// Create a new cell with the given value #[inline(always)] pub const fn new(value: T) -> Self { MathCell(Cell::new(value)) } /// Return the inner value pub fn into_inner(self) -> T { Cell::into_inner(self.0) } /// Swap value with another cell pub fn swap(&self, other: &Self) { Cell::swap(&self.0, &other.0) } } impl Deref for MathCell { type Target = Cell; #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for MathCell { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Clone for MathCell where T: Copy { fn clone(&self) -> Self { MathCell::new(self.get()) } } impl PartialEq for MathCell where T: Copy + PartialEq { fn eq(&self, rhs: &Self) -> bool { self.get() == rhs.get() } } impl Eq for MathCell where T: Copy + Eq {} impl PartialOrd for MathCell where T: Copy + PartialOrd { fn partial_cmp(&self, rhs: &Self) -> Option { self.get().partial_cmp(&rhs.get()) } fn lt(&self, rhs: &Self) -> bool { self.get().lt(&rhs.get()) } fn le(&self, rhs: &Self) -> bool { self.get().le(&rhs.get()) } fn gt(&self, rhs: &Self) -> bool { self.get().gt(&rhs.get()) } fn ge(&self, rhs: &Self) -> bool { self.get().ge(&rhs.get()) } } impl Ord for MathCell where T: Copy + Ord { fn cmp(&self, rhs: &Self) -> Ordering { self.get().cmp(&rhs.get()) } } impl fmt::Debug for MathCell where T: Copy + fmt::Debug { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.get().fmt(f) } } #[cfg(test)] mod tests { use super::MathCell; #[test] fn test_basic() { let c = &MathCell::new(0); c.set(1); assert_eq!(c.get(), 1); } } ndarray-0.16.1/src/numeric/impl_float_maths.rs000064400000000000000000000123221046102023000174720ustar 00000000000000// Element-wise methods for ndarray #[cfg(feature = "std")] use num_traits::Float; use crate::imp_prelude::*; #[cfg(feature = "std")] macro_rules! boolean_ops { ($(#[$meta1:meta])* fn $func:ident $(#[$meta2:meta])* fn $all:ident $(#[$meta3:meta])* fn $any:ident) => { $(#[$meta1])* #[must_use = "method returns a new array and does not mutate the original value"] pub fn $func(&self) -> Array { self.mapv(A::$func) } $(#[$meta2])* #[must_use = "method returns a new boolean value and does not mutate the original value"] pub fn $all(&self) -> bool { $crate::Zip::from(self).all(|&elt| !elt.$func()) } $(#[$meta3])* #[must_use = "method returns a new boolean value and does not mutate the original value"] pub fn $any(&self) -> bool { !self.$all() } }; } #[cfg(feature = "std")] macro_rules! unary_ops { ($($(#[$meta:meta])* fn $id:ident)+) => { $($(#[$meta])* #[must_use = "method returns a new array and does not mutate the original value"] pub fn $id(&self) -> Array { self.mapv(A::$id) })+ }; } #[cfg(feature = "std")] macro_rules! binary_ops { ($($(#[$meta:meta])* fn $id:ident($ty:ty))+) => { $($(#[$meta])* #[must_use = "method returns a new array and does not mutate the original value"] pub fn $id(&self, rhs: $ty) -> Array { self.mapv(|v| A::$id(v, rhs)) })+ }; } /// # Element-wise methods for float arrays /// /// Element-wise math functions for any array type that contains float number. #[cfg(feature = "std")] impl ArrayBase where A: 'static + Float, S: Data, D: Dimension, { boolean_ops! { /// If the number is `NaN` (not a number), then `true` is returned for each element. fn is_nan /// Return `true` if all elements are `NaN` (not a number). fn is_all_nan /// Return `true` if any element is `NaN` (not a number). fn is_any_nan } boolean_ops! { /// If the number is infinity, then `true` is returned for each element. fn is_infinite /// Return `true` if all elements are infinity. fn is_all_infinite /// Return `true` if any element is infinity. fn is_any_infinite } unary_ops! { /// The largest integer less than or equal to each element. fn floor /// The smallest integer less than or equal to each element. fn ceil /// The nearest integer of each element. fn round /// The integer part of each element. fn trunc /// The fractional part of each element. fn fract /// Absolute of each element. fn abs /// Sign number of each element. /// /// + `1.0` for all positive numbers. /// + `-1.0` for all negative numbers. /// + `NaN` for all `NaN` (not a number). fn signum /// The reciprocal (inverse) of each element, `1/x`. fn recip /// Square root of each element. fn sqrt /// `e^x` of each element (exponential function). fn exp /// `2^x` of each element. fn exp2 /// Natural logarithm of each element. fn ln /// Base 2 logarithm of each element. fn log2 /// Base 10 logarithm of each element. fn log10 /// Cubic root of each element. fn cbrt /// Sine of each element (in radians). fn sin /// Cosine of each element (in radians). fn cos /// Tangent of each element (in radians). fn tan /// Converts radians to degrees for each element. fn to_degrees /// Converts degrees to radians for each element. fn to_radians } binary_ops! { /// Integer power of each element. /// /// This function is generally faster than using float power. fn powi(i32) /// Float power of each element. fn powf(A) /// Logarithm of each element with respect to an arbitrary base. fn log(A) /// The positive difference between given number and each element. fn abs_sub(A) } /// Square (two powers) of each element. #[must_use = "method returns a new array and does not mutate the original value"] pub fn pow2(&self) -> Array { self.mapv(|v: A| v * v) } } impl ArrayBase where A: 'static + PartialOrd + Clone, S: Data, D: Dimension, { /// Limit the values for each element, similar to NumPy's `clip` function. /// /// ``` /// use ndarray::array; /// /// let a = array![0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]; /// assert_eq!(a.clamp(1., 8.), array![1., 1., 2., 3., 4., 5., 6., 7., 8., 8.]); /// assert_eq!(a.clamp(3., 6.), array![3., 3., 3., 3., 4., 5., 6., 6., 6., 6.]); /// ``` /// /// # Panics /// /// Panics if `!(min <= max)`. pub fn clamp(&self, min: A, max: A) -> Array { assert!(min <= max, "min must be less than or equal to max"); self.mapv(|a| num_traits::clamp(a, min.clone(), max.clone())) } } ndarray-0.16.1/src/numeric/impl_numeric.rs000064400000000000000000000332001046102023000166310ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[cfg(feature = "std")] use num_traits::Float; use num_traits::One; use num_traits::{FromPrimitive, Zero}; use std::ops::{Add, Div, Mul}; use crate::imp_prelude::*; use crate::numeric_util; /// # Numerical Methods for Arrays impl ArrayBase where S: Data, D: Dimension, { /// Return the sum of all elements in the array. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[1., 2.], /// [3., 4.]]); /// assert_eq!(a.sum(), 10.); /// ``` pub fn sum(&self) -> A where A: Clone + Add + num_traits::Zero { if let Some(slc) = self.as_slice_memory_order() { return numeric_util::unrolled_fold(slc, A::zero, A::add); } let mut sum = A::zero(); for row in self.rows() { if let Some(slc) = row.as_slice() { sum = sum + numeric_util::unrolled_fold(slc, A::zero, A::add); } else { sum = sum + row.iter().fold(A::zero(), |acc, elt| acc + elt.clone()); } } sum } /// Returns the [arithmetic mean] x̅ of all elements in the array: /// /// ```text /// 1 n /// x̅ = ― ∑ xᵢ /// n i=1 /// ``` /// /// If the array is empty, `None` is returned. /// /// **Panics** if `A::from_usize()` fails to convert the number of elements in the array. /// /// [arithmetic mean]: https://en.wikipedia.org/wiki/Arithmetic_mean pub fn mean(&self) -> Option where A: Clone + FromPrimitive + Add + Div + Zero { let n_elements = self.len(); if n_elements == 0 { None } else { let n_elements = A::from_usize(n_elements).expect("Converting number of elements to `A` must not fail."); Some(self.sum() / n_elements) } } /// Return the product of all elements in the array. /// /// ``` /// use ndarray::arr2; /// /// let a = arr2(&[[1., 2.], /// [3., 4.]]); /// assert_eq!(a.product(), 24.); /// ``` pub fn product(&self) -> A where A: Clone + Mul + num_traits::One { if let Some(slc) = self.as_slice_memory_order() { return numeric_util::unrolled_fold(slc, A::one, A::mul); } let mut sum = A::one(); for row in self.rows() { if let Some(slc) = row.as_slice() { sum = sum * numeric_util::unrolled_fold(slc, A::one, A::mul); } else { sum = sum * row.iter().fold(A::one(), |acc, elt| acc * elt.clone()); } } sum } /// Return variance of elements in the array. /// /// The variance is computed using the [Welford one-pass /// algorithm](https://www.jstor.org/stable/1266577). /// /// The parameter `ddof` specifies the "delta degrees of freedom". For /// example, to calculate the population variance, use `ddof = 0`, or to /// calculate the sample variance, use `ddof = 1`. /// /// The variance is defined as: /// /// ```text /// 1 n /// variance = ―――――――― ∑ (xᵢ - x̅)² /// n - ddof i=1 /// ``` /// /// where /// /// ```text /// 1 n /// x̅ = ― ∑ xᵢ /// n i=1 /// ``` /// /// and `n` is the length of the array. /// /// **Panics** if `ddof` is less than zero or greater than `n` /// /// # Example /// /// ``` /// use ndarray::array; /// use approx::assert_abs_diff_eq; /// /// let a = array![1., -4.32, 1.14, 0.32]; /// let var = a.var(1.); /// assert_abs_diff_eq!(var, 6.7331, epsilon = 1e-4); /// ``` #[track_caller] #[cfg(feature = "std")] pub fn var(&self, ddof: A) -> A where A: Float + FromPrimitive { let zero = A::from_usize(0).expect("Converting 0 to `A` must not fail."); let n = A::from_usize(self.len()).expect("Converting length to `A` must not fail."); assert!( !(ddof < zero || ddof > n), "`ddof` must not be less than zero or greater than the length of \ the axis", ); let dof = n - ddof; let mut mean = A::zero(); let mut sum_sq = A::zero(); let mut i = 0; self.for_each(|&x| { let count = A::from_usize(i + 1).expect("Converting index to `A` must not fail."); let delta = x - mean; mean = mean + delta / count; sum_sq = (x - mean).mul_add(delta, sum_sq); i += 1; }); sum_sq / dof } /// Return standard deviation of elements in the array. /// /// The standard deviation is computed from the variance using /// the [Welford one-pass algorithm](https://www.jstor.org/stable/1266577). /// /// The parameter `ddof` specifies the "delta degrees of freedom". For /// example, to calculate the population standard deviation, use `ddof = 0`, /// or to calculate the sample standard deviation, use `ddof = 1`. /// /// The standard deviation is defined as: /// /// ```text /// ⎛ 1 n ⎞ /// stddev = sqrt ⎜ ―――――――― ∑ (xᵢ - x̅)²⎟ /// ⎝ n - ddof i=1 ⎠ /// ``` /// /// where /// /// ```text /// 1 n /// x̅ = ― ∑ xᵢ /// n i=1 /// ``` /// /// and `n` is the length of the array. /// /// **Panics** if `ddof` is less than zero or greater than `n` /// /// # Example /// /// ``` /// use ndarray::array; /// use approx::assert_abs_diff_eq; /// /// let a = array![1., -4.32, 1.14, 0.32]; /// let stddev = a.std(1.); /// assert_abs_diff_eq!(stddev, 2.59483, epsilon = 1e-4); /// ``` #[track_caller] #[cfg(feature = "std")] pub fn std(&self, ddof: A) -> A where A: Float + FromPrimitive { self.var(ddof).sqrt() } /// Return sum along `axis`. /// /// ``` /// use ndarray::{aview0, aview1, arr2, Axis}; /// /// let a = arr2(&[[1., 2., 3.], /// [4., 5., 6.]]); /// assert!( /// a.sum_axis(Axis(0)) == aview1(&[5., 7., 9.]) && /// a.sum_axis(Axis(1)) == aview1(&[6., 15.]) && /// /// a.sum_axis(Axis(0)).sum_axis(Axis(0)) == aview0(&21.) /// ); /// ``` /// /// **Panics** if `axis` is out of bounds. #[track_caller] pub fn sum_axis(&self, axis: Axis) -> Array where A: Clone + Zero + Add, D: RemoveAxis, { let min_stride_axis = self.dim.min_stride_axis(&self.strides); if axis == min_stride_axis { crate::Zip::from(self.lanes(axis)).map_collect(|lane| lane.sum()) } else { let mut res = Array::zeros(self.raw_dim().remove_axis(axis)); for subview in self.axis_iter(axis) { res = res + &subview; } res } } /// Return product along `axis`. /// /// The product of an empty array is 1. /// /// ``` /// use ndarray::{aview0, aview1, arr2, Axis}; /// /// let a = arr2(&[[1., 2., 3.], /// [4., 5., 6.]]); /// /// assert!( /// a.product_axis(Axis(0)) == aview1(&[4., 10., 18.]) && /// a.product_axis(Axis(1)) == aview1(&[6., 120.]) && /// /// a.product_axis(Axis(0)).product_axis(Axis(0)) == aview0(&720.) /// ); /// ``` /// /// **Panics** if `axis` is out of bounds. #[track_caller] pub fn product_axis(&self, axis: Axis) -> Array where A: Clone + One + Mul, D: RemoveAxis, { let min_stride_axis = self.dim.min_stride_axis(&self.strides); if axis == min_stride_axis { crate::Zip::from(self.lanes(axis)).map_collect(|lane| lane.product()) } else { let mut res = Array::ones(self.raw_dim().remove_axis(axis)); for subview in self.axis_iter(axis) { res = res * &subview; } res } } /// Return mean along `axis`. /// /// Return `None` if the length of the axis is zero. /// /// **Panics** if `axis` is out of bounds or if `A::from_usize()` /// fails for the axis length. /// /// ``` /// use ndarray::{aview0, aview1, arr2, Axis}; /// /// let a = arr2(&[[1., 2., 3.], /// [4., 5., 6.]]); /// assert!( /// a.mean_axis(Axis(0)).unwrap() == aview1(&[2.5, 3.5, 4.5]) && /// a.mean_axis(Axis(1)).unwrap() == aview1(&[2., 5.]) && /// /// a.mean_axis(Axis(0)).unwrap().mean_axis(Axis(0)).unwrap() == aview0(&3.5) /// ); /// ``` #[track_caller] pub fn mean_axis(&self, axis: Axis) -> Option> where A: Clone + Zero + FromPrimitive + Add + Div, D: RemoveAxis, { let axis_length = self.len_of(axis); if axis_length == 0 { None } else { let axis_length = A::from_usize(axis_length).expect("Converting axis length to `A` must not fail."); let sum = self.sum_axis(axis); Some(sum / aview0(&axis_length)) } } /// Return variance along `axis`. /// /// The variance is computed using the [Welford one-pass /// algorithm](https://www.jstor.org/stable/1266577). /// /// The parameter `ddof` specifies the "delta degrees of freedom". For /// example, to calculate the population variance, use `ddof = 0`, or to /// calculate the sample variance, use `ddof = 1`. /// /// The variance is defined as: /// /// ```text /// 1 n /// variance = ―――――――― ∑ (xᵢ - x̅)² /// n - ddof i=1 /// ``` /// /// where /// /// ```text /// 1 n /// x̅ = ― ∑ xᵢ /// n i=1 /// ``` /// /// and `n` is the length of the axis. /// /// **Panics** if `ddof` is less than zero or greater than `n`, if `axis` /// is out of bounds, or if `A::from_usize()` fails for any any of the /// numbers in the range `0..=n`. /// /// # Example /// /// ``` /// use ndarray::{aview1, arr2, Axis}; /// /// let a = arr2(&[[1., 2.], /// [3., 4.], /// [5., 6.]]); /// let var = a.var_axis(Axis(0), 1.); /// assert_eq!(var, aview1(&[4., 4.])); /// ``` #[track_caller] #[cfg(feature = "std")] pub fn var_axis(&self, axis: Axis, ddof: A) -> Array where A: Float + FromPrimitive, D: RemoveAxis, { let zero = A::from_usize(0).expect("Converting 0 to `A` must not fail."); let n = A::from_usize(self.len_of(axis)).expect("Converting length to `A` must not fail."); assert!( !(ddof < zero || ddof > n), "`ddof` must not be less than zero or greater than the length of \ the axis", ); let dof = n - ddof; let mut mean = Array::::zeros(self.dim.remove_axis(axis)); let mut sum_sq = Array::::zeros(self.dim.remove_axis(axis)); for (i, subview) in self.axis_iter(axis).enumerate() { let count = A::from_usize(i + 1).expect("Converting index to `A` must not fail."); azip!((mean in &mut mean, sum_sq in &mut sum_sq, &x in &subview) { let delta = x - *mean; *mean = *mean + delta / count; *sum_sq = (x - *mean).mul_add(delta, *sum_sq); }); } sum_sq.mapv_into(|s| s / dof) } /// Return standard deviation along `axis`. /// /// The standard deviation is computed from the variance using /// the [Welford one-pass algorithm](https://www.jstor.org/stable/1266577). /// /// The parameter `ddof` specifies the "delta degrees of freedom". For /// example, to calculate the population standard deviation, use `ddof = 0`, /// or to calculate the sample standard deviation, use `ddof = 1`. /// /// The standard deviation is defined as: /// /// ```text /// ⎛ 1 n ⎞ /// stddev = sqrt ⎜ ―――――――― ∑ (xᵢ - x̅)²⎟ /// ⎝ n - ddof i=1 ⎠ /// ``` /// /// where /// /// ```text /// 1 n /// x̅ = ― ∑ xᵢ /// n i=1 /// ``` /// /// and `n` is the length of the axis. /// /// **Panics** if `ddof` is less than zero or greater than `n`, if `axis` /// is out of bounds, or if `A::from_usize()` fails for any any of the /// numbers in the range `0..=n`. /// /// # Example /// /// ``` /// use ndarray::{aview1, arr2, Axis}; /// /// let a = arr2(&[[1., 2.], /// [3., 4.], /// [5., 6.]]); /// let stddev = a.std_axis(Axis(0), 1.); /// assert_eq!(stddev, aview1(&[2., 2.])); /// ``` #[track_caller] #[cfg(feature = "std")] pub fn std_axis(&self, axis: Axis, ddof: A) -> Array where A: Float + FromPrimitive, D: RemoveAxis, { self.var_axis(axis, ddof).mapv_into(|x| x.sqrt()) } } ndarray-0.16.1/src/numeric/mod.rs000064400000000000000000000000511046102023000147230ustar 00000000000000mod impl_numeric; mod impl_float_maths; ndarray-0.16.1/src/numeric_util.rs000064400000000000000000000071531046102023000152130ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::cmp; use crate::LinalgScalar; /// Fold over the manually unrolled `xs` with `f` pub fn unrolled_fold(mut xs: &[A], init: I, f: F) -> A where A: Clone, I: Fn() -> A, F: Fn(A, A) -> A, { // eightfold unrolled so that floating point can be vectorized // (even with strict floating point accuracy semantics) let mut acc = init(); let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = (init(), init(), init(), init(), init(), init(), init(), init()); while xs.len() >= 8 { p0 = f(p0, xs[0].clone()); p1 = f(p1, xs[1].clone()); p2 = f(p2, xs[2].clone()); p3 = f(p3, xs[3].clone()); p4 = f(p4, xs[4].clone()); p5 = f(p5, xs[5].clone()); p6 = f(p6, xs[6].clone()); p7 = f(p7, xs[7].clone()); xs = &xs[8..]; } acc = f(acc.clone(), f(p0, p4)); acc = f(acc.clone(), f(p1, p5)); acc = f(acc.clone(), f(p2, p6)); acc = f(acc.clone(), f(p3, p7)); // make it clear to the optimizer that this loop is short // and can not be autovectorized. for (i, x) in xs.iter().enumerate() { if i >= 7 { break; } acc = f(acc.clone(), x.clone()) } acc } /// Compute the dot product. /// /// `xs` and `ys` must be the same length pub fn unrolled_dot(xs: &[A], ys: &[A]) -> A where A: LinalgScalar { debug_assert_eq!(xs.len(), ys.len()); // eightfold unrolled so that floating point can be vectorized // (even with strict floating point accuracy semantics) let len = cmp::min(xs.len(), ys.len()); let mut xs = &xs[..len]; let mut ys = &ys[..len]; let mut sum = A::zero(); let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = (A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero(), A::zero()); while xs.len() >= 8 { p0 = p0 + xs[0] * ys[0]; p1 = p1 + xs[1] * ys[1]; p2 = p2 + xs[2] * ys[2]; p3 = p3 + xs[3] * ys[3]; p4 = p4 + xs[4] * ys[4]; p5 = p5 + xs[5] * ys[5]; p6 = p6 + xs[6] * ys[6]; p7 = p7 + xs[7] * ys[7]; xs = &xs[8..]; ys = &ys[8..]; } sum = sum + (p0 + p4); sum = sum + (p1 + p5); sum = sum + (p2 + p6); sum = sum + (p3 + p7); for (i, (&x, &y)) in xs.iter().zip(ys).enumerate() { if i >= 7 { break; } sum = sum + x * y; } sum } /// Compute pairwise equality /// /// `xs` and `ys` must be the same length pub fn unrolled_eq(xs: &[A], ys: &[B]) -> bool where A: PartialEq { debug_assert_eq!(xs.len(), ys.len()); // eightfold unrolled for performance (this is not done by llvm automatically) let len = cmp::min(xs.len(), ys.len()); let mut xs = &xs[..len]; let mut ys = &ys[..len]; while xs.len() >= 8 { if (xs[0] != ys[0]) | (xs[1] != ys[1]) | (xs[2] != ys[2]) | (xs[3] != ys[3]) | (xs[4] != ys[4]) | (xs[5] != ys[5]) | (xs[6] != ys[6]) | (xs[7] != ys[7]) { return false; } xs = &xs[8..]; ys = &ys[8..]; } for i in 0..xs.len() { if xs[i] != ys[i] { return false; } } true } ndarray-0.16.1/src/order.rs000064400000000000000000000047531046102023000136320ustar 00000000000000/// Array order /// /// Order refers to indexing order, or how a linear sequence is translated /// into a two-dimensional or multi-dimensional array. /// /// - `RowMajor` means that the index along the row is the most rapidly changing /// - `ColumnMajor` means that the index along the column is the most rapidly changing /// /// Given a sequence like: 1, 2, 3, 4, 5, 6 /// /// If it is laid it out in a 2 x 3 matrix using row major ordering, it results in: /// /// ```text /// 1 2 3 /// 4 5 6 /// ``` /// /// If it is laid using column major ordering, it results in: /// /// ```text /// 1 3 5 /// 2 4 6 /// ``` /// /// It can be seen as filling in "rows first" or "columns first". /// /// `Order` can be used both to refer to logical ordering as well as memory ordering or memory /// layout. The orderings have common short names, also seen in other environments, where /// row major is called "C" order (after the C programming language) and column major is called "F" /// or "Fortran" order. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Order { /// Row major or "C" order RowMajor, /// Column major or "F" order ColumnMajor, } impl Order { /// "C" is an alias for row major ordering pub const C: Order = Order::RowMajor; /// "F" (for Fortran) is an alias for column major ordering pub const F: Order = Order::ColumnMajor; /// Return true if input is Order::RowMajor, false otherwise #[inline] pub fn is_row_major(self) -> bool { match self { Order::RowMajor => true, Order::ColumnMajor => false, } } /// Return true if input is Order::ColumnMajor, false otherwise #[inline] pub fn is_column_major(self) -> bool { !self.is_row_major() } /// Return Order::RowMajor if the input is true, Order::ColumnMajor otherwise #[inline] pub fn row_major(row_major: bool) -> Order { if row_major { Order::RowMajor } else { Order::ColumnMajor } } /// Return Order::ColumnMajor if the input is true, Order::RowMajor otherwise #[inline] pub fn column_major(column_major: bool) -> Order { Self::row_major(!column_major) } /// Return the transpose: row major becomes column major and vice versa. #[inline] pub fn transpose(self) -> Order { match self { Order::RowMajor => Order::ColumnMajor, Order::ColumnMajor => Order::RowMajor, } } } ndarray-0.16.1/src/parallel/impl_par_methods.rs000064400000000000000000000171721046102023000176400ustar 00000000000000use crate::AssignElem; use crate::{Array, ArrayBase, DataMut, Dimension, IntoNdProducer, NdProducer, Zip}; use super::send_producer::SendProducer; use crate::parallel::par::ParallelSplits; use crate::parallel::prelude::*; use crate::partial::Partial; /// # Parallel methods /// /// These methods require crate feature `rayon`. impl ArrayBase where S: DataMut, D: Dimension, A: Send + Sync, { /// Parallel version of `map_inplace`. /// /// Modify the array in place by calling `f` by mutable reference on each element. /// /// Elements are visited in arbitrary order. pub fn par_map_inplace(&mut self, f: F) where F: Fn(&mut A) + Sync + Send { self.view_mut().into_par_iter().for_each(f) } /// Parallel version of `mapv_inplace`. /// /// Modify the array in place by calling `f` by **v**alue on each element. /// The array is updated with the new values. /// /// Elements are visited in arbitrary order. pub fn par_mapv_inplace(&mut self, f: F) where F: Fn(A) -> A + Sync + Send, A: Clone, { self.view_mut() .into_par_iter() .for_each(move |x| *x = f(x.clone())) } } // Zip const COLLECT_MAX_SPLITS: usize = 10; macro_rules! zip_impl { ($([$notlast:ident $($p:ident)*],)+) => { $( #[allow(non_snake_case)] impl Zip<($($p,)*), D> where $($p::Item : Send , )* $($p : Send , )* D: Dimension, $($p: NdProducer ,)* { /// The `par_for_each` method for `Zip`. /// /// This is a shorthand for using `.into_par_iter().for_each()` on /// `Zip`. /// /// Requires crate feature `rayon`. pub fn par_for_each(self, function: F) where F: Fn($($p::Item),*) + Sync + Send { self.into_par_iter().for_each(move |($($p,)*)| function($($p),*)) } expand_if!(@bool [$notlast] /// Map and collect the results into a new array, which has the same size as the /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. pub fn par_map_collect(self, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) -> Array where R: Send { let mut output = self.uninitialized_for_current_layout::(); let total_len = output.len(); // Create a parallel iterator that produces chunks of the zip with the output // array. It's crucial that both parts split in the same way, and in a way // so that the chunks of the output are still contig. // // Use a raw view so that we can alias the output data here and in the partial // result. let splits = unsafe { ParallelSplits { iter: self.and(SendProducer::new(output.raw_view_mut().cast::())), // Keep it from splitting the Zip down too small max_splits: COLLECT_MAX_SPLITS, } }; let collect_result = splits.map(move |zip| { // Apply the mapping function on this chunk of the zip // Create a partial result for the contiguous slice of data being written to unsafe { zip.collect_with_partial(&f) } }) .reduce(Partial::stub, Partial::try_merge); if std::mem::needs_drop::() { debug_assert_eq!(total_len, collect_result.len, "collect len is not correct, expected {}", total_len); assert!(collect_result.len == total_len, "Collect: Expected number of writes not completed"); } // Here the collect result is complete, and we release its ownership and transfer // it to the output array. collect_result.release_ownership(); unsafe { output.assume_init() } } /// Map and assign the results into the producer `into`, which should have the same /// size as the other inputs. /// /// The producer should have assignable items as dictated by the `AssignElem` trait, /// for example `&mut R`. pub fn par_map_assign_into(self, into: Q, f: impl Fn($($p::Item,)* ) -> R + Sync + Send) where Q: IntoNdProducer, Q::Item: AssignElem + Send, Q::Output: Send, { self.and(into) .par_for_each(move |$($p, )* output_| { output_.assign_elem(f($($p ),*)); }); } /// Parallel version of `fold`. /// /// Splits the producer in multiple tasks which each accumulate a single value /// using the `fold` closure. Those tasks are executed in parallel and their results /// are then combined to a single value using the `reduce` closure. /// /// The `identity` closure provides the initial values for each of the tasks and /// for the final reduction. /// /// This is a shorthand for calling `self.into_par_iter().fold(...).reduce(...)`. /// /// Note that it is often more efficient to parallelize not per-element but rather /// based on larger chunks of an array like generalized rows and operating on each chunk /// using a sequential variant of the accumulation. /// For example, sum each row sequentially and in parallel, taking advantage of locality /// and vectorization within each task, and then reduce their sums to the sum of the matrix. /// /// Also note that the splitting of the producer into multiple tasks is _not_ deterministic /// which needs to be considered when the accuracy of such an operation is analyzed. /// /// ## Examples /// /// ```rust /// use ndarray::{Array, Zip}; /// /// let a = Array::::ones((128, 1024)); /// let b = Array::::ones(128); /// /// let weighted_sum = Zip::from(a.rows()).and(&b).par_fold( /// || 0, /// |sum, row, factor| sum + row.sum() * factor, /// |sum, other_sum| sum + other_sum, /// ); /// /// assert_eq!(weighted_sum, a.len()); /// ``` pub fn par_fold(self, identity: ID, fold: F, reduce: R) -> T where ID: Fn() -> T + Send + Sync + Clone, F: Fn(T, $($p::Item),*) -> T + Send + Sync, R: Fn(T, T) -> T + Send + Sync, T: Send { self.into_par_iter() .fold(identity.clone(), move |accumulator, ($($p,)*)| { fold(accumulator, $($p),*) }) .reduce(identity, reduce) } ); } )+ }; } zip_impl! { [true P1], [true P1 P2], [true P1 P2 P3], [true P1 P2 P3 P4], [true P1 P2 P3 P4 P5], [false P1 P2 P3 P4 P5 P6], } ndarray-0.16.1/src/parallel/into_impls.rs000064400000000000000000000027021046102023000164600ustar 00000000000000use crate::{ArcArray, Array, ArrayView, ArrayViewMut, Dimension}; use super::prelude::IntoParallelIterator; use super::Parallel; /// Requires crate feature `rayon`. impl<'a, A, D> IntoParallelIterator for &'a Array where D: Dimension, A: Sync, { type Item = &'a A; type Iter = Parallel>; fn into_par_iter(self) -> Self::Iter { self.view().into_par_iter() } } // This is allowed: goes through `.view()` /// Requires crate feature `rayon`. impl<'a, A, D> IntoParallelIterator for &'a ArcArray where D: Dimension, A: Sync, { type Item = &'a A; type Iter = Parallel>; fn into_par_iter(self) -> Self::Iter { self.view().into_par_iter() } } /// Requires crate feature `rayon`. impl<'a, A, D> IntoParallelIterator for &'a mut Array where D: Dimension, A: Sync + Send, { type Item = &'a mut A; type Iter = Parallel>; fn into_par_iter(self) -> Self::Iter { self.view_mut().into_par_iter() } } // This is allowed: goes through `.view_mut()`, which is unique access /// Requires crate feature `rayon`. impl<'a, A, D> IntoParallelIterator for &'a mut ArcArray where D: Dimension, A: Sync + Send + Clone, { type Item = &'a mut A; type Iter = Parallel>; fn into_par_iter(self) -> Self::Iter { self.view_mut().into_par_iter() } } ndarray-0.16.1/src/parallel/mod.rs000064400000000000000000000105541046102023000150660ustar 00000000000000//! Parallelization features for ndarray. //! //! Parallelization features are based on the crate [rayon] and its parallel //! iterators. Ndarray implements the parallel iterable traits for arrays //! and array views, for some of its iterators and for [Zip]. //! There are also directly parallelized methods on arrays and on [Zip]. //! //! This requires the crate feature `rayon` to be enabled. //! //! The following types implement parallel iterators, accessed using these //! methods: //! //! - [`Array`], [`ArcArray`] `.par_iter()` and `.par_iter_mut()` //! - [`ArrayView`] `.into_par_iter()` //! - [`ArrayViewMut`] `.into_par_iter()` //! - [`AxisIter`], [`AxisIterMut`] `.into_par_iter()` //! - [`AxisChunksIter`], [`AxisChunksIterMut`] `.into_par_iter()` //! - [`Zip`] `.into_par_iter()` //! //! The following other parallelized methods exist: //! //! - [`ArrayBase::par_map_inplace()`] //! - [`ArrayBase::par_mapv_inplace()`] //! - [`Zip::par_for_each()`] (all arities) //! - [`Zip::par_map_collect()`] (all arities) //! - [`Zip::par_map_assign_into()`] (all arities) //! //! Note that you can use the parallel iterator for [Zip] to access all other //! rayon parallel iterator methods. //! //! Only the axis iterators are indexed parallel iterators, the rest are all //! “unindexed”. Use ndarray’s [Zip] for lock step parallel iteration of //! multiple arrays or producers at a time. //! //! For the unindexed parallel iterators, an inherent method [`with_min_len`](Parallel::with_min_len) //! is provided to limit the number of elements each parallel task processes in way that is //! similar to Rayon's [`IndexedParallelIterator::with_min_len`](rayon::prelude::IndexedParallelIterator::with_min_len). //! //! # Examples //! //! ## Arrays and array views //! //! Compute the exponential of each element in an array, parallelized. //! //! ``` //! use ndarray::Array2; //! use ndarray::parallel::prelude::*; //! //! let mut a = Array2::::zeros((128, 128)); //! //! // Parallel versions of regular array methods //! a.par_map_inplace(|x| *x = x.exp()); //! a.par_mapv_inplace(f64::exp); //! //! // You can also use the parallel iterator directly //! a.par_iter_mut().for_each(|x| *x = x.exp()); //! ``` //! //! ## Axis iterators //! //! Use the parallel `.axis_iter()` to compute the sum of each row. //! //! ``` //! use ndarray::Array; //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; //! //! let a = Array::linspace(0., 63., 64).into_shape_with_order((4, 16)).unwrap(); //! let mut sums = Vec::new(); //! a.axis_iter(Axis(0)) //! .into_par_iter() //! .map(|row| row.sum()) //! .collect_into_vec(&mut sums); //! //! assert_eq!(sums, [120., 376., 632., 888.]); //! ``` //! //! ## Axis chunks iterators //! //! Use the parallel `.axis_chunks_iter()` to process your data in chunks. //! //! ``` //! use ndarray::Array; //! use ndarray::Axis; //! use ndarray::parallel::prelude::*; //! //! let a = Array::linspace(0., 63., 64).into_shape_with_order((4, 16)).unwrap(); //! let mut shapes = Vec::new(); //! a.axis_chunks_iter(Axis(0), 3) //! .into_par_iter() //! .map(|chunk| chunk.shape().to_owned()) //! .collect_into_vec(&mut shapes); //! //! assert_eq!(shapes, [vec![3, 16], vec![1, 16]]); //! ``` //! //! ## Zip //! //! Use zip for lock step function application across several arrays //! //! ``` //! use ndarray::Array3; //! use ndarray::Zip; //! //! type Array3f64 = Array3; //! //! const N: usize = 128; //! let a = Array3f64::from_elem((N, N, N), 1.); //! let b = Array3f64::from_elem(a.dim(), 2.); //! let mut c = Array3f64::zeros(a.dim()); //! //! Zip::from(&mut c) //! .and(&a) //! .and(&b) //! .par_for_each(|c, &a, &b| { //! *c += a - b; //! }); //! ``` #[allow(unused_imports)] // used by rustdoc links use crate::iter::{AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut}; #[allow(unused_imports)] // used by rustdoc links use crate::{ArcArray, Array, ArrayBase, ArrayView, ArrayViewMut, Zip}; /// Into- traits for creating parallelized iterators and/or using [`par_azip!`] pub mod prelude { #[doc(no_inline)] pub use rayon::prelude::{ IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator, }; pub use super::par_azip; } pub use self::par::Parallel; pub use crate::par_azip; mod impl_par_methods; mod into_impls; mod par; mod send_producer; mod zipmacro; ndarray-0.16.1/src/parallel/par.rs000064400000000000000000000257621046102023000151000ustar 00000000000000use rayon::iter::plumbing::bridge; use rayon::iter::plumbing::bridge_unindexed; use rayon::iter::plumbing::Folder; use rayon::iter::plumbing::Producer; use rayon::iter::plumbing::ProducerCallback; use rayon::iter::plumbing::UnindexedProducer; use rayon::iter::plumbing::{Consumer, UnindexedConsumer}; use rayon::iter::IndexedParallelIterator; use rayon::iter::ParallelIterator; use rayon::prelude::IntoParallelIterator; use crate::iter::AxisChunksIter; use crate::iter::AxisChunksIterMut; use crate::iter::AxisIter; use crate::iter::AxisIterMut; use crate::split_at::SplitPreference; use crate::Dimension; use crate::{ArrayView, ArrayViewMut}; /// Parallel iterator wrapper. #[derive(Copy, Clone, Debug)] pub struct Parallel { iter: I, min_len: usize, } const DEFAULT_MIN_LEN: usize = 1; /// Parallel producer wrapper. #[derive(Copy, Clone, Debug)] struct ParallelProducer(I, usize); macro_rules! par_iter_wrapper { // thread_bounds are either Sync or Send + Sync ($iter_name:ident, [$($thread_bounds:tt)*]) => { /// Requires crate feature `rayon`. impl<'a, A, D> IntoParallelIterator for $iter_name<'a, A, D> where D: Dimension, A: $($thread_bounds)*, { type Item = ::Item; type Iter = Parallel; fn into_par_iter(self) -> Self::Iter { Parallel { iter: self, min_len: DEFAULT_MIN_LEN, } } } impl<'a, A, D> ParallelIterator for Parallel<$iter_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, { type Item = <$iter_name<'a, A, D> as Iterator>::Item; fn drive_unindexed(self, consumer: C) -> C::Result where C: UnindexedConsumer { bridge(self, consumer) } fn opt_len(&self) -> Option { Some(self.iter.len()) } } impl<'a, A, D> IndexedParallelIterator for Parallel<$iter_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, { fn with_producer(self, callback: Cb) -> Cb::Output where Cb: ProducerCallback { callback.callback(ParallelProducer(self.iter, self.min_len)) } fn len(&self) -> usize { ExactSizeIterator::len(&self.iter) } fn drive(self, consumer: C) -> C::Result where C: Consumer { bridge(self, consumer) } } impl<'a, A, D> IntoIterator for ParallelProducer<$iter_name<'a, A, D>> where D: Dimension, { type IntoIter = $iter_name<'a, A, D>; type Item = ::Item; fn into_iter(self) -> Self::IntoIter { self.0 } } // This is the real magic, I guess impl<'a, A, D> Producer for ParallelProducer<$iter_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, { type IntoIter = $iter_name<'a, A, D>; type Item = ::Item; fn into_iter(self) -> Self::IntoIter { self.0 } fn split_at(self, i: usize) -> (Self, Self) { let (a, b) = self.0.split_at(i); (ParallelProducer(a, self.1), ParallelProducer(b, self.1)) } } }; } par_iter_wrapper!(AxisIter, [Sync]); par_iter_wrapper!(AxisIterMut, [Send + Sync]); par_iter_wrapper!(AxisChunksIter, [Sync]); par_iter_wrapper!(AxisChunksIterMut, [Send + Sync]); macro_rules! par_iter_view_wrapper { // thread_bounds are either Sync or Send + Sync ($view_name:ident, [$($thread_bounds:tt)*]) => { /// Requires crate feature `rayon`. impl<'a, A, D> IntoParallelIterator for $view_name<'a, A, D> where D: Dimension, A: $($thread_bounds)*, { type Item = ::Item; type Iter = Parallel; fn into_par_iter(self) -> Self::Iter { Parallel { iter: self, min_len: DEFAULT_MIN_LEN, } } } impl<'a, A, D> ParallelIterator for Parallel<$view_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, { type Item = <$view_name<'a, A, D> as IntoIterator>::Item; fn drive_unindexed(self, consumer: C) -> C::Result where C: UnindexedConsumer { bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer) } fn opt_len(&self) -> Option { None } } impl<'a, A, D> Parallel<$view_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, { /// Sets the minimum number of elements desired to process in each job. This will not be /// split any smaller than this length, but of course a producer could already be smaller /// to begin with. /// /// ***Panics*** if `min_len` is zero. pub fn with_min_len(self, min_len: usize) -> Self { assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks."); Self { min_len, ..self } } } impl<'a, A, D> UnindexedProducer for ParallelProducer<$view_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, { type Item = <$view_name<'a, A, D> as IntoIterator>::Item; fn split(self) -> (Self, Option) { if self.0.len() <= self.1 { return (self, None) } let array = self.0; let max_axis = array.max_stride_axis(); let mid = array.len_of(max_axis) / 2; let (a, b) = array.split_at(max_axis, mid); (ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1))) } fn fold_with(self, folder: F) -> F where F: Folder, { Zip::from(self.0).fold_while(folder, |mut folder, elt| { folder = folder.consume(elt); if folder.full() { FoldWhile::Done(folder) } else { FoldWhile::Continue(folder) } }).into_inner() } } impl<'a, A, D> IntoIterator for ParallelProducer<$view_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, { type Item = <$view_name<'a, A, D> as IntoIterator>::Item; type IntoIter = <$view_name<'a, A, D> as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } }; } par_iter_view_wrapper!(ArrayView, [Sync]); par_iter_view_wrapper!(ArrayViewMut, [Sync + Send]); use crate::{FoldWhile, NdProducer, Zip}; macro_rules! zip_impl { ($([$($p:ident)*],)+) => { $( /// Requires crate feature `rayon`. #[allow(non_snake_case)] impl IntoParallelIterator for Zip<($($p,)*), D> where $($p::Item : Send , )* $($p : Send , )* D: Dimension, $($p: NdProducer ,)* { type Item = ($($p::Item ,)*); type Iter = Parallel; fn into_par_iter(self) -> Self::Iter { Parallel { iter: self, min_len: DEFAULT_MIN_LEN, } } } #[allow(non_snake_case)] impl ParallelIterator for Parallel> where $($p::Item : Send , )* $($p : Send , )* D: Dimension, $($p: NdProducer ,)* { type Item = ($($p::Item ,)*); fn drive_unindexed(self, consumer: Cons) -> Cons::Result where Cons: UnindexedConsumer { bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer) } fn opt_len(&self) -> Option { None } } #[allow(non_snake_case)] impl UnindexedProducer for ParallelProducer> where $($p : Send , )* $($p::Item : Send , )* D: Dimension, $($p: NdProducer ,)* { type Item = ($($p::Item ,)*); fn split(self) -> (Self, Option) { if self.0.size() <= self.1 { return (self, None) } let (a, b) = self.0.split(); (ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1))) } fn fold_with(self, folder: Fold) -> Fold where Fold: Folder, { self.0.fold_while(folder, |mut folder, $($p),*| { folder = folder.consume(($($p ,)*)); if folder.full() { FoldWhile::Done(folder) } else { FoldWhile::Continue(folder) } }).into_inner() } } )+ }; } zip_impl! { [P1], [P1 P2], [P1 P2 P3], [P1 P2 P3 P4], [P1 P2 P3 P4 P5], [P1 P2 P3 P4 P5 P6], } impl Parallel> where D: Dimension { /// Sets the minimum number of elements desired to process in each job. This will not be /// split any smaller than this length, but of course a producer could already be smaller /// to begin with. /// /// ***Panics*** if `min_len` is zero. pub fn with_min_len(self, min_len: usize) -> Self { assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks."); Self { min_len, ..self } } } /// A parallel iterator (unindexed) that produces the splits of the array /// or producer `P`. pub(crate) struct ParallelSplits

{ pub(crate) iter: P, pub(crate) max_splits: usize, } impl

ParallelIterator for ParallelSplits

where P: SplitPreference + Send { type Item = P; fn drive_unindexed(self, consumer: C) -> C::Result where C: UnindexedConsumer { bridge_unindexed(self, consumer) } fn opt_len(&self) -> Option { None } } impl

UnindexedProducer for ParallelSplits

where P: SplitPreference + Send { type Item = P; fn split(self) -> (Self, Option) { if self.max_splits == 0 || !self.iter.can_split() { return (self, None); } let (a, b) = self.iter.split(); ( ParallelSplits { iter: a, max_splits: self.max_splits - 1, }, Some(ParallelSplits { iter: b, max_splits: self.max_splits - 1, }), ) } fn fold_with(self, folder: Fold) -> Fold where Fold: Folder { folder.consume(self.iter) } } ndarray-0.16.1/src/parallel/send_producer.rs000064400000000000000000000036561046102023000171500ustar 00000000000000use crate::imp_prelude::*; use crate::{Layout, NdProducer}; use std::ops::{Deref, DerefMut}; /// An NdProducer that is unconditionally `Send`. #[repr(transparent)] pub(crate) struct SendProducer { inner: T, } impl SendProducer { /// Create an unconditionally `Send` ndproducer from the producer pub(crate) unsafe fn new(producer: T) -> Self { Self { inner: producer } } } unsafe impl

Send for SendProducer

{} impl

Deref for SendProducer

{ type Target = P; fn deref(&self) -> &P { &self.inner } } impl

DerefMut for SendProducer

{ fn deref_mut(&mut self) -> &mut P { &mut self.inner } } impl NdProducer for SendProducer

where P: NdProducer { type Item = P::Item; type Dim = P::Dim; type Ptr = P::Ptr; type Stride = P::Stride; private_impl! {} #[inline(always)] fn raw_dim(&self) -> Self::Dim { self.inner.raw_dim() } #[inline(always)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.inner.equal_dim(dim) } #[inline(always)] fn as_ptr(&self) -> Self::Ptr { self.inner.as_ptr() } #[inline(always)] fn layout(&self) -> Layout { self.inner.layout() } #[inline(always)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { self.inner.as_ref(ptr) } #[inline(always)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { self.inner.uget_ptr(i) } #[inline(always)] fn stride_of(&self, axis: Axis) -> Self::Stride { self.inner.stride_of(axis) } #[inline(always)] fn contiguous_stride(&self) -> Self::Stride { self.inner.contiguous_stride() } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let (a, b) = self.inner.split_at(axis, index); (Self { inner: a }, Self { inner: b }) } } ndarray-0.16.1/src/parallel/zipmacro.rs000064400000000000000000000030051046102023000161240ustar 00000000000000// Copyright 2017 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[macro_export] /// Parallelized array zip macro: lock step function application across several /// arrays and producers. /// /// This is a version of the [`azip`] macro that requires the crate feature /// `rayon` to be enabled. /// /// See the [`azip`] macro for more details about the macro syntax! /// /// This example: /// /// ```rust,ignore /// par_azip!((a in &mut a, &b in &b, &c in &c) { *a = b + c }) /// ``` /// /// Is equivalent to: /// /// ```rust,ignore /// Zip::from(&mut a).and(&b).and(&c).par_for_each(|a, &b, &c| { /// *a = b + c; /// }); /// ``` /// /// **Panics** if any of the arrays are not of the same shape. /// /// ## Examples /// /// ```rust /// use ndarray::Array2; /// use ndarray::parallel::par_azip; /// /// type M = Array2; /// /// let mut a = M::zeros((16, 16)); /// let b = M::from_elem(a.dim(), 1.); /// let c = M::from_elem(a.dim(), 2.); /// /// // Compute a simple ternary operation: /// // elementwise addition of b and c, stored in a /// /// par_azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); /// /// assert_eq!(a, &b + &c); /// ``` macro_rules! par_azip { ($($t:tt)*) => { $crate::azip!(@build par_for_each $($t)*) }; } ndarray-0.16.1/src/partial.rs000064400000000000000000000053551046102023000141520ustar 00000000000000// Copyright 2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::ptr; /// Partial is a partially written contiguous slice of data; /// it is the owner of the elements, but not the allocation, /// and will drop the elements on drop. #[must_use] pub(crate) struct Partial { /// Data pointer ptr: *mut T, /// Current length pub(crate) len: usize, } impl Partial { /// Create an empty partial for this data pointer /// /// ## Safety /// /// Unless ownership is released, the Partial acts as an owner of the slice of data (not the /// allocation); and will free the elements on drop; the pointer must be dereferenceable and /// the `len` elements following it valid. /// /// The Partial has an accessible length field which must only be modified in trusted code. pub(crate) unsafe fn new(ptr: *mut T) -> Self { Self { ptr, len: 0 } } #[cfg(feature = "rayon")] pub(crate) fn stub() -> Self { Self { len: 0, ptr: ptr::null_mut(), } } #[cfg(feature = "rayon")] pub(crate) fn is_stub(&self) -> bool { self.ptr.is_null() } /// Release Partial's ownership of the written elements, and return the current length pub(crate) fn release_ownership(mut self) -> usize { let ret = self.len; self.len = 0; ret } #[cfg(feature = "rayon")] /// Merge if they are in order (left to right) and contiguous. /// Skips merge if T does not need drop. pub(crate) fn try_merge(mut left: Self, right: Self) -> Self { if !std::mem::needs_drop::() { return left; } // Merge the partial collect results; the final result will be a slice that // covers the whole output. if left.is_stub() { right } else if left.ptr.wrapping_add(left.len) == right.ptr { left.len += right.release_ownership(); left } else { // failure to merge; this is a bug in collect, so we will never reach this debug_assert!(false, "Partial: failure to merge left and right parts"); left } } } unsafe impl Send for Partial where T: Send {} impl Drop for Partial { fn drop(&mut self) { if !self.ptr.is_null() { unsafe { ptr::drop_in_place(alloc::slice::from_raw_parts_mut(self.ptr, self.len)); } } } } ndarray-0.16.1/src/prelude.rs000064400000000000000000000031221046102023000141440ustar 00000000000000// Copyright 2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! ndarray prelude. //! //! This module contains the most used types, type aliases, traits, functions, //! and macros that you can import easily as a group. //! //! ``` //! use ndarray::prelude::*; //! //! # let _ = arr0(1); // use the import //! ``` #[doc(no_inline)] pub use crate::{ArcArray, Array, ArrayBase, ArrayView, ArrayViewMut, CowArray, RawArrayView, RawArrayViewMut}; #[doc(no_inline)] pub use crate::{Axis, Dim, Dimension}; #[doc(no_inline)] pub use crate::{Array0, Array1, Array2, Array3, Array4, Array5, Array6, ArrayD}; #[doc(no_inline)] pub use crate::{ArrayView0, ArrayView1, ArrayView2, ArrayView3, ArrayView4, ArrayView5, ArrayView6, ArrayViewD}; #[doc(no_inline)] pub use crate::{ ArrayViewMut0, ArrayViewMut1, ArrayViewMut2, ArrayViewMut3, ArrayViewMut4, ArrayViewMut5, ArrayViewMut6, ArrayViewMutD, }; #[doc(no_inline)] pub use crate::{Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; #[doc(no_inline)] pub use crate::{arr0, arr1, arr2, aview0, aview1, aview2, aview_mut1}; pub use crate::{array, azip, s}; #[doc(no_inline)] pub use crate::ShapeBuilder; #[doc(no_inline)] pub use crate::NewAxis; #[doc(no_inline)] pub use crate::AsArray; #[doc(no_inline)] #[cfg(feature = "std")] pub use crate::NdFloat; ndarray-0.16.1/src/private.rs000064400000000000000000000015171046102023000141640ustar 00000000000000//! The public parts of this private module are used to create traits //! that cannot be implemented outside of our own crate. This way we //! can feel free to extend those traits without worrying about it //! being a breaking change for other implementations. /// If this type is pub but not publicly reachable, third parties /// can't name it and can't implement traits using it. pub struct PrivateMarker; macro_rules! private_decl { () => { /// This trait is private to implement; this method exists to make it /// impossible to implement outside the crate. #[doc(hidden)] fn __private__(&self) -> crate::private::PrivateMarker; }; } macro_rules! private_impl { () => { fn __private__(&self) -> crate::private::PrivateMarker { crate::private::PrivateMarker } }; } ndarray-0.16.1/src/shape_builder.rs000064400000000000000000000123201046102023000153120ustar 00000000000000use crate::dimension::IntoDimension; use crate::order::Order; use crate::Dimension; /// A contiguous array shape of n dimensions. /// /// Either c- or f- memory ordered (*c* a.k.a *row major* is the default). #[derive(Copy, Clone, Debug)] pub struct Shape { /// Shape (axis lengths) pub(crate) dim: D, /// Strides can only be C or F here pub(crate) strides: Strides, } #[derive(Copy, Clone, Debug)] pub(crate) enum Contiguous {} impl Shape { pub(crate) fn is_c(&self) -> bool { matches!(self.strides, Strides::C) } } /// An array shape of n dimensions in c-order, f-order or custom strides. #[derive(Copy, Clone, Debug)] pub struct StrideShape { pub(crate) dim: D, pub(crate) strides: Strides, } impl StrideShape where D: Dimension { /// Return a reference to the dimension pub fn raw_dim(&self) -> &D { &self.dim } /// Return the size of the shape in number of elements pub fn size(&self) -> usize { self.dim.size() } } /// Stride description #[derive(Copy, Clone, Debug)] pub(crate) enum Strides { /// Row-major ("C"-order) C, /// Column-major ("F"-order) F, /// Custom strides Custom(D), } impl Strides { /// Return strides for `dim` (computed from dimension if c/f, else return the custom stride) pub(crate) fn strides_for_dim(self, dim: &D) -> D where D: Dimension { match self { Strides::C => dim.default_strides(), Strides::F => dim.fortran_strides(), Strides::Custom(c) => { debug_assert_eq!( c.ndim(), dim.ndim(), "Custom strides given with {} dimensions, expected {}", c.ndim(), dim.ndim() ); c } } } #[inline] pub(crate) fn is_custom(&self) -> bool { matches!(*self, Strides::Custom(_)) } } /// A trait for `Shape` and `D where D: Dimension` that allows /// customizing the memory layout (strides) of an array shape. /// /// This trait is used together with array constructor methods like /// `Array::from_shape_vec`. pub trait ShapeBuilder { type Dim: Dimension; type Strides; fn into_shape_with_order(self) -> Shape; fn f(self) -> Shape; fn set_f(self, is_f: bool) -> Shape; fn strides(self, strides: Self::Strides) -> StrideShape; } impl From for Shape where D: Dimension { /// Create a `Shape` from `dimension`, using the default memory layout. fn from(dimension: D) -> Shape { dimension.into_shape_with_order() } } impl From for StrideShape where D: Dimension, T: ShapeBuilder, { fn from(value: T) -> Self { let shape = value.into_shape_with_order(); let st = if shape.is_c() { Strides::C } else { Strides::F }; StrideShape { strides: st, dim: shape.dim, } } } impl ShapeBuilder for T where T: IntoDimension { type Dim = T::Dim; type Strides = T; fn into_shape_with_order(self) -> Shape { Shape { dim: self.into_dimension(), strides: Strides::C, } } fn f(self) -> Shape { self.set_f(true) } fn set_f(self, is_f: bool) -> Shape { self.into_shape_with_order().set_f(is_f) } fn strides(self, st: T) -> StrideShape { self.into_shape_with_order().strides(st.into_dimension()) } } impl ShapeBuilder for Shape where D: Dimension { type Dim = D; type Strides = D; fn into_shape_with_order(self) -> Shape { self } fn f(self) -> Self { self.set_f(true) } fn set_f(mut self, is_f: bool) -> Self { self.strides = if !is_f { Strides::C } else { Strides::F }; self } fn strides(self, st: D) -> StrideShape { StrideShape { dim: self.dim, strides: Strides::Custom(st), } } } impl Shape where D: Dimension { /// Return a reference to the dimension pub fn raw_dim(&self) -> &D { &self.dim } /// Return the size of the shape in number of elements pub fn size(&self) -> usize { self.dim.size() } } /// Array shape argument with optional order parameter /// /// Shape or array dimension argument, with optional [`Order`] parameter. /// /// This is an argument conversion trait that is used to accept an array shape and /// (optionally) an ordering argument. /// /// See for example [`.to_shape()`](crate::ArrayBase::to_shape). pub trait ShapeArg { type Dim: Dimension; fn into_shape_and_order(self) -> (Self::Dim, Option); } impl ShapeArg for T where T: IntoDimension { type Dim = T::Dim; fn into_shape_and_order(self) -> (Self::Dim, Option) { (self.into_dimension(), None) } } impl ShapeArg for (T, Order) where T: IntoDimension { type Dim = T::Dim; fn into_shape_and_order(self) -> (Self::Dim, Option) { (self.0.into_dimension(), Some(self.1)) } } ndarray-0.16.1/src/slice.rs000064400000000000000000000756041046102023000136210ustar 00000000000000// Copyright 2014-2016 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::dimension::slices_intersect; use crate::error::{ErrorKind, ShapeError}; #[cfg(doc)] use crate::s; use crate::{ArrayViewMut, DimAdd, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use std::convert::TryFrom; use std::fmt; use std::marker::PhantomData; use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; /// A slice (range with step size). /// /// `end` is an exclusive index. Negative `start` or `end` indexes are counted /// from the back of the axis. If `end` is `None`, the slice extends to the end /// of the axis. /// /// See also the [`s![]`](s!) macro. /// /// ## Examples /// /// `Slice::new(0, None, 1)` is the full range of an axis. It can also be /// created with `Slice::from(..)`. The Python equivalent is `[:]`. /// /// `Slice::new(a, b, 2)` is every second element from `a` until `b`. It can /// also be created with `Slice::from(a..b).step_by(2)`. The Python equivalent /// is `[a:b:2]`. /// /// `Slice::new(a, None, -1)` is every element, from `a` until the end, in /// reverse order. It can also be created with `Slice::from(a..).step_by(-1)`. /// The Python equivalent is `[a::-1]`. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct Slice { /// start index; negative are counted from the back of the axis pub start: isize, /// end index; negative are counted from the back of the axis; when not present /// the default is the full length of the axis. pub end: Option, /// step size in elements; the default is 1, for every element. pub step: isize, } impl Slice { /// Create a new `Slice` with the given extents. /// /// See also the `From` impls, converting from ranges; for example /// `Slice::from(i..)` or `Slice::from(j..k)`. /// /// `step` must be nonzero. /// (This method checks with a debug assertion that `step` is not zero.) pub fn new(start: isize, end: Option, step: isize) -> Slice { debug_assert_ne!(step, 0, "Slice::new: step must be nonzero"); Slice { start, end, step } } /// Create a new `Slice` with the given step size (multiplied with the /// previous step size). /// /// `step` must be nonzero. /// (This method checks with a debug assertion that `step` is not zero.) #[inline] pub fn step_by(self, step: isize) -> Self { debug_assert_ne!(step, 0, "Slice::step_by: step must be nonzero"); Slice { step: self.step * step, ..self } } } /// Token to represent a new axis in a slice description. /// /// See also the [`s![]`](s!) macro. #[derive(Clone, Copy, Debug)] pub struct NewAxis; /// A slice (range with step), an index, or a new axis token. /// /// See also the [`s![]`](s!) macro for a convenient way to create a /// `SliceInfo<[SliceInfoElem; n], Din, Dout>`. /// /// ## Examples /// /// `SliceInfoElem::Index(a)` is the index `a`. It can also be created with /// `SliceInfoElem::from(a)`. The Python equivalent is `[a]`. The macro /// equivalent is `s![a]`. /// /// `SliceInfoElem::Slice { start: 0, end: None, step: 1 }` is the full range /// of an axis. It can also be created with `SliceInfoElem::from(..)`. The /// Python equivalent is `[:]`. The macro equivalent is `s![..]`. /// /// `SliceInfoElem::Slice { start: a, end: Some(b), step: 2 }` is every second /// element from `a` until `b`. It can also be created with /// `SliceInfoElem::from(Slice::from(a..b).step_by(2))`. The Python equivalent /// is `[a:b:2]`. The macro equivalent is `s![a..b;2]`. /// /// `SliceInfoElem::Slice { start: a, end: None, step: -1 }` is every element, /// from `a` until the end, in reverse order. It can also be created with /// `SliceInfoElem::from(Slice::from(a..).step_by(-1))`. The Python equivalent /// is `[a::-1]`. The macro equivalent is `s![a..;-1]`. /// /// `SliceInfoElem::NewAxis` is a new axis of length 1. It can also be created /// with `SliceInfoElem::from(NewAxis)`. The Python equivalent is /// `[np.newaxis]`. The macro equivalent is `s![NewAxis]`. #[derive(Debug, PartialEq, Eq, Hash)] pub enum SliceInfoElem { /// A range with step size. `end` is an exclusive index. Negative `start` /// or `end` indexes are counted from the back of the axis. If `end` is /// `None`, the slice extends to the end of the axis. Slice { /// start index; negative are counted from the back of the axis start: isize, /// end index; negative are counted from the back of the axis; when not present /// the default is the full length of the axis. end: Option, /// step size in elements; the default is 1, for every element. step: isize, }, /// A single index. Index(isize), /// A new axis of length 1. NewAxis, } copy_and_clone! {SliceInfoElem} impl SliceInfoElem { /// Returns `true` if `self` is a `Slice` value. pub fn is_slice(&self) -> bool { matches!(self, SliceInfoElem::Slice { .. }) } /// Returns `true` if `self` is an `Index` value. pub fn is_index(&self) -> bool { matches!(self, SliceInfoElem::Index(_)) } /// Returns `true` if `self` is a `NewAxis` value. pub fn is_new_axis(&self) -> bool { matches!(self, SliceInfoElem::NewAxis) } } impl fmt::Display for SliceInfoElem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { SliceInfoElem::Index(index) => write!(f, "{}", index)?, SliceInfoElem::Slice { start, end, step } => { if start != 0 { write!(f, "{}", start)?; } write!(f, "..")?; if let Some(i) = end { write!(f, "{}", i)?; } if step != 1 { write!(f, ";{}", step)?; } } SliceInfoElem::NewAxis => write!(f, stringify!(NewAxis))?, } Ok(()) } } macro_rules! impl_slice_variant_from_range { ($self:ty, $constructor:path, $index:ty) => { impl From> for $self { #[inline] fn from(r: Range<$index>) -> $self { $constructor { start: r.start as isize, end: Some(r.end as isize), step: 1, } } } impl From> for $self { #[inline] fn from(r: RangeInclusive<$index>) -> $self { let end = *r.end() as isize; $constructor { start: *r.start() as isize, end: if end == -1 { None } else { Some(end + 1) }, step: 1, } } } impl From> for $self { #[inline] fn from(r: RangeFrom<$index>) -> $self { $constructor { start: r.start as isize, end: None, step: 1, } } } impl From> for $self { #[inline] fn from(r: RangeTo<$index>) -> $self { $constructor { start: 0, end: Some(r.end as isize), step: 1, } } } impl From> for $self { #[inline] fn from(r: RangeToInclusive<$index>) -> $self { let end = r.end as isize; $constructor { start: 0, end: if end == -1 { None } else { Some(end + 1) }, step: 1, } } } }; } impl_slice_variant_from_range!(Slice, Slice, isize); impl_slice_variant_from_range!(Slice, Slice, usize); impl_slice_variant_from_range!(Slice, Slice, i32); impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, isize); impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, usize); impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, i32); impl From for Slice { #[inline] fn from(_: RangeFull) -> Slice { Slice { start: 0, end: None, step: 1, } } } impl From for SliceInfoElem { #[inline] fn from(_: RangeFull) -> SliceInfoElem { SliceInfoElem::Slice { start: 0, end: None, step: 1, } } } impl From for SliceInfoElem { #[inline] fn from(s: Slice) -> SliceInfoElem { SliceInfoElem::Slice { start: s.start, end: s.end, step: s.step, } } } macro_rules! impl_sliceinfoelem_from_index { ($index:ty) => { impl From<$index> for SliceInfoElem { #[inline] fn from(r: $index) -> SliceInfoElem { SliceInfoElem::Index(r as isize) } } }; } impl_sliceinfoelem_from_index!(isize); impl_sliceinfoelem_from_index!(usize); impl_sliceinfoelem_from_index!(i32); impl From for SliceInfoElem { #[inline] fn from(_: NewAxis) -> SliceInfoElem { SliceInfoElem::NewAxis } } /// A type that can slice an array of dimension `D`. /// /// This trait is unsafe to implement because the implementation must ensure /// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are /// consistent with the `&[SliceInfoElem]` returned by `self.as_ref()` and that /// `self.as_ref()` always returns the same value when called multiple times. #[allow(clippy::missing_safety_doc)] // not implementable downstream pub unsafe trait SliceArg: AsRef<[SliceInfoElem]> { /// Dimensionality of the output array. type OutDim: Dimension; /// Returns the number of axes in the input array. fn in_ndim(&self) -> usize; /// Returns the number of axes in the output array. fn out_ndim(&self) -> usize; private_decl! {} } unsafe impl SliceArg for &T where T: SliceArg + ?Sized, D: Dimension, { type OutDim = T::OutDim; fn in_ndim(&self) -> usize { T::in_ndim(self) } fn out_ndim(&self) -> usize { T::out_ndim(self) } private_impl! {} } macro_rules! impl_slicearg_samedim { ($in_dim:ty) => { unsafe impl SliceArg<$in_dim> for SliceInfo where T: AsRef<[SliceInfoElem]>, Dout: Dimension, { type OutDim = Dout; fn in_ndim(&self) -> usize { self.in_ndim() } fn out_ndim(&self) -> usize { self.out_ndim() } private_impl! {} } }; } impl_slicearg_samedim!(Ix0); impl_slicearg_samedim!(Ix1); impl_slicearg_samedim!(Ix2); impl_slicearg_samedim!(Ix3); impl_slicearg_samedim!(Ix4); impl_slicearg_samedim!(Ix5); impl_slicearg_samedim!(Ix6); unsafe impl SliceArg for SliceInfo where T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { type OutDim = Dout; fn in_ndim(&self) -> usize { self.in_ndim() } fn out_ndim(&self) -> usize { self.out_ndim() } private_impl! {} } unsafe impl SliceArg for [SliceInfoElem] { type OutDim = IxDyn; fn in_ndim(&self) -> usize { self.iter().filter(|s| !s.is_new_axis()).count() } fn out_ndim(&self) -> usize { self.iter().filter(|s| !s.is_index()).count() } private_impl! {} } /// Represents all of the necessary information to perform a slice. /// /// The type `T` is typically `[SliceInfoElem; n]`, `&[SliceInfoElem]`, or /// `Vec`. The type `Din` is the dimension of the array to be /// sliced, and `Dout` is the output dimension after calling [`.slice()`]. Note /// that if `Din` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the /// `SliceInfo` instance can still be used to slice an array with dimension /// `IxDyn` as long as the number of axes matches. /// /// [`.slice()`]: crate::ArrayBase::slice #[derive(Debug)] pub struct SliceInfo { in_dim: PhantomData, out_dim: PhantomData, indices: T, } impl Deref for SliceInfo where Din: Dimension, Dout: Dimension, { type Target = T; fn deref(&self) -> &Self::Target { &self.indices } } fn check_dims_for_sliceinfo(indices: &[SliceInfoElem]) -> Result<(), ShapeError> where Din: Dimension, Dout: Dimension, { if let Some(in_ndim) = Din::NDIM { if in_ndim != indices.in_ndim() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } if let Some(out_ndim) = Dout::NDIM { if out_ndim != indices.out_ndim() { return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)); } } Ok(()) } impl SliceInfo where T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { /// Returns a new `SliceInfo` instance. /// /// **Note:** only unchecked for non-debug builds of `ndarray`. /// /// # Safety /// /// The caller must ensure that `in_dim` and `out_dim` are consistent with /// `indices` and that `indices.as_ref()` always returns the same value /// when called multiple times. #[doc(hidden)] pub unsafe fn new_unchecked( indices: T, in_dim: PhantomData, out_dim: PhantomData, ) -> SliceInfo { if cfg!(debug_assertions) { check_dims_for_sliceinfo::(indices.as_ref()) .expect("`Din` and `Dout` must be consistent with `indices`."); } SliceInfo { in_dim, out_dim, indices, } } /// Returns a new `SliceInfo` instance. /// /// Errors if `Din` or `Dout` is not consistent with `indices`. /// /// For common types, a safe alternative is to use `TryFrom` instead. /// /// # Safety /// /// The caller must ensure `indices.as_ref()` always returns the same value /// when called multiple times. pub unsafe fn new(indices: T) -> Result, ShapeError> { check_dims_for_sliceinfo::(indices.as_ref())?; Ok(SliceInfo { in_dim: PhantomData, out_dim: PhantomData, indices, }) } /// Returns the number of dimensions of the input array for /// [`.slice()`](crate::ArrayBase::slice). /// /// If `Din` is a fixed-size dimension type, then this is equivalent to /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `SliceInfoElem` elements. pub fn in_ndim(&self) -> usize { if let Some(ndim) = Din::NDIM { ndim } else { self.indices.as_ref().in_ndim() } } /// Returns the number of dimensions after calling /// [`.slice()`](crate::ArrayBase::slice) (including taking /// subviews). /// /// If `Dout` is a fixed-size dimension type, then this is equivalent to /// `Dout::NDIM.unwrap()`. Otherwise, the value is calculated by iterating /// over the `SliceInfoElem` elements. pub fn out_ndim(&self) -> usize { if let Some(ndim) = Dout::NDIM { ndim } else { self.indices.as_ref().out_ndim() } } } impl<'a, Din, Dout> TryFrom<&'a [SliceInfoElem]> for SliceInfo<&'a [SliceInfoElem], Din, Dout> where Din: Dimension, Dout: Dimension, { type Error = ShapeError; fn try_from(indices: &'a [SliceInfoElem]) -> Result, ShapeError> { unsafe { // This is okay because `&[SliceInfoElem]` always returns the same // value for `.as_ref()`. Self::new(indices) } } } impl TryFrom> for SliceInfo, Din, Dout> where Din: Dimension, Dout: Dimension, { type Error = ShapeError; fn try_from(indices: Vec) -> Result, Din, Dout>, ShapeError> { unsafe { // This is okay because `Vec` always returns the same value for // `.as_ref()`. Self::new(indices) } } } macro_rules! impl_tryfrom_array_for_sliceinfo { ($len:expr) => { impl TryFrom<[SliceInfoElem; $len]> for SliceInfo<[SliceInfoElem; $len], Din, Dout> where Din: Dimension, Dout: Dimension, { type Error = ShapeError; fn try_from( indices: [SliceInfoElem; $len], ) -> Result, ShapeError> { unsafe { // This is okay because `[SliceInfoElem; N]` always returns // the same value for `.as_ref()`. Self::new(indices) } } } }; } impl_tryfrom_array_for_sliceinfo!(0); impl_tryfrom_array_for_sliceinfo!(1); impl_tryfrom_array_for_sliceinfo!(2); impl_tryfrom_array_for_sliceinfo!(3); impl_tryfrom_array_for_sliceinfo!(4); impl_tryfrom_array_for_sliceinfo!(5); impl_tryfrom_array_for_sliceinfo!(6); impl_tryfrom_array_for_sliceinfo!(7); impl_tryfrom_array_for_sliceinfo!(8); impl AsRef<[SliceInfoElem]> for SliceInfo where T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { fn as_ref(&self) -> &[SliceInfoElem] { self.indices.as_ref() } } impl<'a, T, Din, Dout> From<&'a SliceInfo> for SliceInfo<&'a [SliceInfoElem], Din, Dout> where T: AsRef<[SliceInfoElem]>, Din: Dimension, Dout: Dimension, { fn from(info: &'a SliceInfo) -> SliceInfo<&'a [SliceInfoElem], Din, Dout> { SliceInfo { in_dim: info.in_dim, out_dim: info.out_dim, indices: info.indices.as_ref(), } } } impl Copy for SliceInfo where T: Copy, Din: Dimension, Dout: Dimension, { } impl Clone for SliceInfo where T: Clone, Din: Dimension, Dout: Dimension, { fn clone(&self) -> Self { SliceInfo { in_dim: PhantomData, out_dim: PhantomData, indices: self.indices.clone(), } } } /// Trait for determining dimensionality of input and output for [`s!`] macro. #[doc(hidden)] pub trait SliceNextDim { /// Number of dimensions that this slicing argument consumes in the input array. type InDim: Dimension; /// Number of dimensions that this slicing argument produces in the output array. type OutDim: Dimension; fn next_in_dim(&self, _: PhantomData) -> PhantomData<>::Output> where D: Dimension + DimAdd { PhantomData } fn next_out_dim(&self, _: PhantomData) -> PhantomData<>::Output> where D: Dimension + DimAdd { PhantomData } } macro_rules! impl_slicenextdim { (($($generics:tt)*), $self:ty, $in:ty, $out:ty) => { impl<$($generics)*> SliceNextDim for $self { type InDim = $in; type OutDim = $out; } }; } impl_slicenextdim!((), isize, Ix1, Ix0); impl_slicenextdim!((), usize, Ix1, Ix0); impl_slicenextdim!((), i32, Ix1, Ix0); impl_slicenextdim!((T), Range, Ix1, Ix1); impl_slicenextdim!((T), RangeInclusive, Ix1, Ix1); impl_slicenextdim!((T), RangeFrom, Ix1, Ix1); impl_slicenextdim!((T), RangeTo, Ix1, Ix1); impl_slicenextdim!((T), RangeToInclusive, Ix1, Ix1); impl_slicenextdim!((), RangeFull, Ix1, Ix1); impl_slicenextdim!((), Slice, Ix1, Ix1); impl_slicenextdim!((), NewAxis, Ix0, Ix1); /// Slice argument constructor. /// /// `s![]` takes a list of ranges/slices/indices/new-axes, separated by comma, /// with optional step sizes that are separated from the range by a semicolon. /// It is converted into a [`SliceInfo`] instance. /// /// Each range/slice/index uses signed indices, where a negative value is /// counted from the end of the axis. Step sizes are also signed and may be /// negative, but must not be zero. /// /// The syntax is `s![` *[ elem [, elem [ , ... ] ] ]* `]`, where *elem* is any /// of the following: /// /// * *index*: an index to use for taking a subview with respect to that axis. /// (The index is selected. The axis is removed except with /// [`.slice_collapse()`].) /// * *range*: a range with step size 1 to use for slicing that axis. /// * *range* `;` *step*: a range with step size *step* to use for slicing that axis. /// * *slice*: a [`Slice`] instance to use for slicing that axis. /// * *slice* `;` *step*: a range constructed from a [`Slice`] instance, /// multiplying the step size by *step*, to use for slicing that axis. /// * *new-axis*: a [`NewAxis`] instance that represents the creation of a new axis. /// (Except for [`.slice_collapse()`], which panics on [`NewAxis`] elements.) /// /// The number of *elem*, not including *new-axis*, must match the /// number of axes in the array. *index*, *range*, *slice*, *step*, and /// *new-axis* can be expressions. *index* must be of type `isize`, `usize`, or /// `i32`. *range* must be of type `Range`, `RangeTo`, `RangeFrom`, or /// `RangeFull` where `I` is `isize`, `usize`, or `i32`. *step* must be a type /// that can be converted to `isize` with the `as` keyword. /// /// For example, `s![0..4;2, 6, 1..5, NewAxis]` is a slice of the first axis /// for 0..4 with step size 2, a subview of the second axis at index 6, a slice /// of the third axis for 1..5 with default step size 1, and a new axis of /// length 1 at the end of the shape. The input array must have 3 dimensions. /// The resulting slice would have shape `[2, 4, 1]` for [`.slice()`], /// [`.slice_mut()`], and [`.slice_move()`], while [`.slice_collapse()`] would /// panic. Without the `NewAxis`, i.e. `s![0..4;2, 6, 1..5]`, /// [`.slice_collapse()`] would result in an array of shape `[2, 1, 4]`. /// /// [`.slice()`]: crate::ArrayBase::slice /// [`.slice_mut()`]: crate::ArrayBase::slice_mut /// [`.slice_move()`]: crate::ArrayBase::slice_move /// [`.slice_collapse()`]: crate::ArrayBase::slice_collapse /// /// See also [*Slicing*](crate::ArrayBase#slicing). /// /// # Example /// /// ``` /// use ndarray::{s, Array2, ArrayView2}; /// /// fn laplacian(v: &ArrayView2) -> Array2 { /// -4. * &v.slice(s![1..-1, 1..-1]) /// + v.slice(s![ ..-2, 1..-1]) /// + v.slice(s![1..-1, ..-2]) /// + v.slice(s![1..-1, 2.. ]) /// + v.slice(s![2.. , 1..-1]) /// } /// # fn main() { let _ = laplacian; } /// ``` /// /// # Negative *step* /// /// The behavior of negative *step* arguments is most easily understood with /// slicing as a two-step process: /// /// 1. First, perform a slice with *range*. /// /// 2. If *step* is positive, start with the front of the slice; if *step* is /// negative, start with the back of the slice. Then, add *step* until /// reaching the other end of the slice (inclusive). /// /// An equivalent way to think about step 2 is, "If *step* is negative, reverse /// the slice. Start at the front of the (possibly reversed) slice, and add /// *step.abs()* until reaching the back of the slice (inclusive)." /// /// For example, /// /// ``` /// # use ndarray::prelude::*; /// # /// # fn main() { /// let arr = array![0, 1, 2, 3]; /// assert_eq!(arr.slice(s![1..3;-1]), array![2, 1]); /// assert_eq!(arr.slice(s![1..;-2]), array![3, 1]); /// assert_eq!(arr.slice(s![0..4;-2]), array![3, 1]); /// assert_eq!(arr.slice(s![0..;-2]), array![3, 1]); /// assert_eq!(arr.slice(s![..;-2]), array![3, 1]); /// # } /// ``` #[macro_export] macro_rules! s( // convert a..b;c into @convert(a..b, c), final item (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr) => { match $r { r => { let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim); let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim); ( [$($stack)* $crate::s!(@convert r, $s)], in_dim, out_dim, ) } } }; // convert a..b into @convert(a..b), final item (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr) => { match $r { r => { let in_dim = $crate::SliceNextDim::next_in_dim(&r, $in_dim); let out_dim = $crate::SliceNextDim::next_out_dim(&r, $out_dim); ( [$($stack)* $crate::s!(@convert r)], in_dim, out_dim, ) } } }; // convert a..b;c into @convert(a..b, c), final item, trailing comma (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr ,) => { $crate::s![@parse $in_dim, $out_dim, [$($stack)*] $r;$s] }; // convert a..b into @convert(a..b), final item, trailing comma (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr ,) => { $crate::s![@parse $in_dim, $out_dim, [$($stack)*] $r] }; // convert a..b;c into @convert(a..b, c) (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr;$s:expr, $($t:tt)*) => { match $r { r => { $crate::s![@parse $crate::SliceNextDim::next_in_dim(&r, $in_dim), $crate::SliceNextDim::next_out_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r, $s),] $($t)* ] } } }; // convert a..b into @convert(a..b) (@parse $in_dim:expr, $out_dim:expr, [$($stack:tt)*] $r:expr, $($t:tt)*) => { match $r { r => { $crate::s![@parse $crate::SliceNextDim::next_in_dim(&r, $in_dim), $crate::SliceNextDim::next_out_dim(&r, $out_dim), [$($stack)* $crate::s!(@convert r),] $($t)* ] } } }; // empty call, i.e. `s![]` (@parse ::core::marker::PhantomData::<$crate::Ix0>, ::core::marker::PhantomData::<$crate::Ix0>, []) => { ( [], ::core::marker::PhantomData::<$crate::Ix0>, ::core::marker::PhantomData::<$crate::Ix0>, ) }; // Catch-all clause for syntax errors (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") }; // convert range/index/new-axis into SliceInfoElem (@convert $r:expr) => { <$crate::SliceInfoElem as ::core::convert::From<_>>::from($r) }; // convert range/index/new-axis and step into SliceInfoElem (@convert $r:expr, $s:expr) => { <$crate::SliceInfoElem as ::core::convert::From<_>>::from( <$crate::Slice as ::core::convert::From<_>>::from($r).step_by($s as isize) ) }; ($($t:tt)*) => { { let (indices, in_dim, out_dim) = $crate::s![@parse ::core::marker::PhantomData::<$crate::Ix0>, ::core::marker::PhantomData::<$crate::Ix0>, [] $($t)* ]; // Safety: The `s![@parse ...]` above always constructs the correct // values to meet the constraints of `SliceInfo::new_unchecked`. #[allow(unsafe_code)] unsafe { $crate::SliceInfo::new_unchecked(indices, in_dim, out_dim) } } }; ); /// Slicing information describing multiple mutable, disjoint slices. /// /// It's unfortunate that we need `'a` and `A` to be parameters of the trait, /// but they're necessary until Rust supports generic associated types. pub trait MultiSliceArg<'a, A, D> where A: 'a, D: Dimension, { /// The type of the slices created by `.multi_slice_move()`. type Output; /// Split the view into multiple disjoint slices. /// /// **Panics** if performing any individual slice panics or if the slices /// are not disjoint (i.e. if they intersect). fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output; private_decl! {} } impl<'a, A, D> MultiSliceArg<'a, A, D> for () where A: 'a, D: Dimension, { type Output = (); fn multi_slice_move(&self, _view: ArrayViewMut<'a, A, D>) -> Self::Output {} private_impl! {} } impl<'a, A, D, I0> MultiSliceArg<'a, A, D> for (I0,) where A: 'a, D: Dimension, I0: SliceArg, { type Output = (ArrayViewMut<'a, A, I0::OutDim>,); fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { (view.slice_move(&self.0),) } private_impl! {} } macro_rules! impl_multislice_tuple { ([$($but_last:ident)*] $last:ident) => { impl_multislice_tuple!(@def_impl ($($but_last,)* $last,), [$($but_last)*] $last); }; (@def_impl ($($all:ident,)*), [$($but_last:ident)*] $last:ident) => { impl<'a, A, D, $($all,)*> MultiSliceArg<'a, A, D> for ($($all,)*) where A: 'a, D: Dimension, $($all: SliceArg,)* { type Output = ($(ArrayViewMut<'a, A, $all::OutDim>,)*); fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { #[allow(non_snake_case)] let ($($all,)*) = self; let shape = view.raw_dim(); assert!(!impl_multislice_tuple!(@intersects_self &shape, ($($all,)*))); let raw_view = view.into_raw_view_mut(); unsafe { ( $(raw_view.clone().slice_move($but_last).deref_into_view_mut(),)* raw_view.slice_move($last).deref_into_view_mut(), ) } } private_impl! {} } }; (@intersects_self $shape:expr, ($head:expr,)) => { false }; (@intersects_self $shape:expr, ($head:expr, $($tail:expr,)*)) => { $(slices_intersect($shape, $head, $tail)) ||* || impl_multislice_tuple!(@intersects_self $shape, ($($tail,)*)) }; } impl_multislice_tuple!([I0] I1); impl_multislice_tuple!([I0 I1] I2); impl_multislice_tuple!([I0 I1 I2] I3); impl_multislice_tuple!([I0 I1 I2 I3] I4); impl_multislice_tuple!([I0 I1 I2 I3 I4] I5); impl<'a, A, D, T> MultiSliceArg<'a, A, D> for &T where A: 'a, D: Dimension, T: MultiSliceArg<'a, A, D>, { type Output = T::Output; fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output { T::multi_slice_move(self, view) } private_impl! {} } ndarray-0.16.1/src/split_at.rs000064400000000000000000000023041046102023000143240ustar 00000000000000use crate::imp_prelude::*; /// Arrays and similar that can be split along an axis pub(crate) trait SplitAt { fn split_at(self, axis: Axis, index: usize) -> (Self, Self) where Self: Sized; } pub(crate) trait SplitPreference: SplitAt { #[allow(dead_code)] // used only when Rayon support is enabled fn can_split(&self) -> bool; fn split_preference(&self) -> (Axis, usize); fn split(self) -> (Self, Self) where Self: Sized { let (axis, index) = self.split_preference(); self.split_at(axis, index) } } impl SplitAt for D where D: Dimension { fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { let mut d1 = self; let mut d2 = d1.clone(); let i = axis.index(); let len = d1[i]; d1[i] = index; d2[i] = len - index; (d1, d2) } } impl<'a, A, D> SplitAt for ArrayViewMut<'a, A, D> where D: Dimension { fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } } impl SplitAt for RawArrayViewMut where D: Dimension { fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } } ndarray-0.16.1/src/stacking.rs000064400000000000000000000143541046102023000143200ustar 00000000000000// Copyright 2014-2020 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::dimension; use crate::error::{from_kind, ErrorKind, ShapeError}; use crate::imp_prelude::*; /// Concatenate arrays along the given axis. /// /// ***Errors*** if the arrays have mismatching shapes, apart from along `axis`. /// (may be made more flexible in the future).
/// ***Errors*** if `arrays` is empty, if `axis` is out of bounds, /// if the result is larger than is possible to represent. /// /// ``` /// use ndarray::{arr2, Axis, concatenate}; /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( /// concatenate(Axis(0), &[a.view(), a.view()]) /// == Ok(arr2(&[[2., 2.], /// [3., 3.], /// [2., 2.], /// [3., 3.]])) /// ); /// ``` pub fn concatenate(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Clone, D: RemoveAxis, { if arrays.is_empty() { return Err(from_kind(ErrorKind::Unsupported)); } let mut res_dim = arrays[0].raw_dim(); if axis.index() >= res_dim.ndim() { return Err(from_kind(ErrorKind::OutOfBounds)); } let common_dim = res_dim.remove_axis(axis); if arrays .iter() .any(|a| a.raw_dim().remove_axis(axis) != common_dim) { return Err(from_kind(ErrorKind::IncompatibleShape)); } let stacked_dim = arrays.iter().fold(0, |acc, a| acc + a.len_of(axis)); res_dim.set_axis(axis, stacked_dim); let new_len = dimension::size_of_shape_checked(&res_dim)?; // start with empty array with precomputed capacity // append's handling of empty arrays makes sure `axis` is ok for appending res_dim.set_axis(axis, 0); let mut res = unsafe { // Safety: dimension is size 0 and vec is empty Array::from_shape_vec_unchecked(res_dim, Vec::with_capacity(new_len)) }; for array in arrays { res.append(axis, array.clone())?; } debug_assert_eq!(res.len_of(axis), stacked_dim); Ok(res) } /// Stack arrays along the new axis. /// /// ***Errors*** if the arrays have mismatching shapes. /// ***Errors*** if `arrays` is empty, if `axis` is out of bounds, /// if the result is larger than is possible to represent. /// /// ``` /// extern crate ndarray; /// /// use ndarray::{arr2, arr3, stack, Axis}; /// /// # fn main() { /// /// let a = arr2(&[[2., 2.], /// [3., 3.]]); /// assert!( /// stack(Axis(0), &[a.view(), a.view()]) /// == Ok(arr3(&[[[2., 2.], /// [3., 3.]], /// [[2., 2.], /// [3., 3.]]])) /// ); /// # } /// ``` pub fn stack(axis: Axis, arrays: &[ArrayView]) -> Result, ShapeError> where A: Clone, D: Dimension, D::Larger: RemoveAxis, { if arrays.is_empty() { return Err(from_kind(ErrorKind::Unsupported)); } let common_dim = arrays[0].raw_dim(); // Avoid panic on `insert_axis` call, return an Err instead of it. if axis.index() > common_dim.ndim() { return Err(from_kind(ErrorKind::OutOfBounds)); } let mut res_dim = common_dim.insert_axis(axis); if arrays.iter().any(|a| a.raw_dim() != common_dim) { return Err(from_kind(ErrorKind::IncompatibleShape)); } res_dim.set_axis(axis, arrays.len()); let new_len = dimension::size_of_shape_checked(&res_dim)?; // start with empty array with precomputed capacity // append's handling of empty arrays makes sure `axis` is ok for appending res_dim.set_axis(axis, 0); let mut res = unsafe { // Safety: dimension is size 0 and vec is empty Array::from_shape_vec_unchecked(res_dim, Vec::with_capacity(new_len)) }; for array in arrays { res.append(axis, array.clone().insert_axis(axis))?; } debug_assert_eq!(res.len_of(axis), arrays.len()); Ok(res) } /// Stack arrays along the new axis. /// /// Uses the [`stack()`] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// /// ***Panics*** if the `stack` function would return an error. /// /// ``` /// extern crate ndarray; /// /// use ndarray::{arr2, arr3, stack, Axis}; /// /// # fn main() { /// /// let a = arr2(&[[1., 2.], /// [3., 4.]]); /// assert_eq!( /// stack![Axis(0), a, a], /// arr3(&[[[1., 2.], /// [3., 4.]], /// [[1., 2.], /// [3., 4.]]]), /// ); /// assert_eq!( /// stack![Axis(1), a, a,], /// arr3(&[[[1., 2.], /// [1., 2.]], /// [[3., 4.], /// [3., 4.]]]), /// ); /// assert_eq!( /// stack![Axis(2), a, a], /// arr3(&[[[1., 1.], /// [2., 2.]], /// [[3., 3.], /// [4., 4.]]]), /// ); /// # } /// ``` #[macro_export] macro_rules! stack { ($axis:expr, $( $array:expr ),+ ,) => { $crate::stack!($axis, $($array),+) }; ($axis:expr, $( $array:expr ),+ ) => { $crate::stack($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() }; } /// Concatenate arrays along the given axis. /// /// Uses the [`concatenate()`] function, calling `ArrayView::from(&a)` on each /// argument `a`. /// /// ***Panics*** if the `concatenate` function would return an error. /// /// ``` /// extern crate ndarray; /// /// use ndarray::{arr2, concatenate, Axis}; /// /// # fn main() { /// /// let a = arr2(&[[1., 2.], /// [3., 4.]]); /// assert_eq!( /// concatenate![Axis(0), a, a], /// arr2(&[[1., 2.], /// [3., 4.], /// [1., 2.], /// [3., 4.]]), /// ); /// assert_eq!( /// concatenate![Axis(1), a, a,], /// arr2(&[[1., 2., 1., 2.], /// [3., 4., 3., 4.]]), /// ); /// # } /// ``` #[macro_export] macro_rules! concatenate { ($axis:expr, $( $array:expr ),+ ,) => { $crate::concatenate!($axis, $($array),+) }; ($axis:expr, $( $array:expr ),+ ) => { $crate::concatenate($axis, &[ $($crate::ArrayView::from(&$array) ),* ]).unwrap() }; } ndarray-0.16.1/src/tri.rs000064400000000000000000000240071046102023000133070ustar 00000000000000// Copyright 2014-2024 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use core::cmp::min; use num_traits::Zero; use crate::{ dimension::{is_layout_c, is_layout_f}, Array, ArrayBase, Axis, Data, Dimension, Zip, }; impl ArrayBase where S: Data, D: Dimension, A: Clone + Zero, { /// Upper triangular of an array. /// /// Return a copy of the array with elements below the *k*-th diagonal zeroed. /// For arrays with `ndim` exceeding 2, `triu` will apply to the final two axes. /// For 0D and 1D arrays, `triu` will return an unchanged clone. /// /// See also [`ArrayBase::tril`] /// /// ``` /// use ndarray::array; /// /// let arr = array![ /// [1, 2, 3], /// [4, 5, 6], /// [7, 8, 9] /// ]; /// assert_eq!( /// arr.triu(0), /// array![ /// [1, 2, 3], /// [0, 5, 6], /// [0, 0, 9] /// ] /// ); /// ``` pub fn triu(&self, k: isize) -> Array { if self.ndim() <= 1 { return self.to_owned(); } // Performance optimization for F-order arrays. // C-order array check prevents infinite recursion in edge cases like [[1]]. // k-size check prevents underflow when k == isize::MIN let n = self.ndim(); if is_layout_f(&self.dim, &self.strides) && !is_layout_c(&self.dim, &self.strides) && k > isize::MIN { let mut x = self.view(); x.swap_axes(n - 2, n - 1); let mut tril = x.tril(-k); tril.swap_axes(n - 2, n - 1); return tril; } let mut res = Array::zeros(self.raw_dim()); let ncols = self.len_of(Axis(n - 1)); let nrows = self.len_of(Axis(n - 2)); let indices = Array::from_iter(0..nrows); Zip::from(self.rows()) .and(res.rows_mut()) .and_broadcast(&indices) .for_each(|src, mut dst, row_num| { let mut lower = match k >= 0 { true => row_num.saturating_add(k as usize), // Avoid overflow false => row_num.saturating_sub(k.unsigned_abs()), // Avoid underflow, go to 0 }; lower = min(lower, ncols); dst.slice_mut(s![lower..]).assign(&src.slice(s![lower..])); }); res } /// Lower triangular of an array. /// /// Return a copy of the array with elements above the *k*-th diagonal zeroed. /// For arrays with `ndim` exceeding 2, `tril` will apply to the final two axes. /// For 0D and 1D arrays, `tril` will return an unchanged clone. /// /// See also [`ArrayBase::triu`] /// /// ``` /// use ndarray::array; /// /// let arr = array![ /// [1, 2, 3], /// [4, 5, 6], /// [7, 8, 9] /// ]; /// assert_eq!( /// arr.tril(0), /// array![ /// [1, 0, 0], /// [4, 5, 0], /// [7, 8, 9] /// ] /// ); /// ``` pub fn tril(&self, k: isize) -> Array { if self.ndim() <= 1 { return self.to_owned(); } // Performance optimization for F-order arrays. // C-order array check prevents infinite recursion in edge cases like [[1]]. // k-size check prevents underflow when k == isize::MIN let n = self.ndim(); if is_layout_f(&self.dim, &self.strides) && !is_layout_c(&self.dim, &self.strides) && k > isize::MIN { let mut x = self.view(); x.swap_axes(n - 2, n - 1); let mut tril = x.triu(-k); tril.swap_axes(n - 2, n - 1); return tril; } let mut res = Array::zeros(self.raw_dim()); let ncols = self.len_of(Axis(n - 1)); let nrows = self.len_of(Axis(n - 2)); let indices = Array::from_iter(0..nrows); Zip::from(self.rows()) .and(res.rows_mut()) .and_broadcast(&indices) .for_each(|src, mut dst, row_num| { // let row_num = i.into_dimension().last_elem(); let mut upper = match k >= 0 { true => row_num.saturating_add(k as usize).saturating_add(1), // Avoid overflow false => row_num.saturating_sub((k + 1).unsigned_abs()), // Avoid underflow }; upper = min(upper, ncols); dst.slice_mut(s![..upper]).assign(&src.slice(s![..upper])); }); res } } #[cfg(test)] mod tests { use core::isize; use crate::{array, dimension, Array0, Array1, Array2, Array3, ShapeBuilder}; use alloc::vec; #[test] fn test_keep_order() { let x = Array2::::ones((3, 3).f()); let res = x.triu(0); assert!(dimension::is_layout_f(&res.dim, &res.strides)); let res = x.tril(0); assert!(dimension::is_layout_f(&res.dim, &res.strides)); } #[test] fn test_0d() { let x = Array0::::ones(()); let res = x.triu(0); assert_eq!(res, x); let res = x.tril(0); assert_eq!(res, x); let x = Array0::::ones(().f()); let res = x.triu(0); assert_eq!(res, x); let res = x.tril(0); assert_eq!(res, x); } #[test] fn test_1d() { let x = array![1, 2, 3]; let res = x.triu(0); assert_eq!(res, x); let res = x.triu(0); assert_eq!(res, x); let x = Array1::::ones(3.f()); let res = x.triu(0); assert_eq!(res, x); let res = x.triu(0); assert_eq!(res, x); } #[test] fn test_2d() { let x = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; // Upper let res = x.triu(0); assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); // Lower let res = x.tril(0); assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); let x = Array2::from_shape_vec((3, 3).f(), vec![1, 4, 7, 2, 5, 8, 3, 6, 9]).unwrap(); // Upper let res = x.triu(0); assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); // Lower let res = x.tril(0); assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); } #[test] fn test_2d_single() { let x = array![[1]]; assert_eq!(x.triu(0), array![[1]]); assert_eq!(x.tril(0), array![[1]]); assert_eq!(x.triu(1), array![[0]]); assert_eq!(x.tril(1), array![[1]]); assert_eq!(x.triu(-1), array![[1]]); assert_eq!(x.tril(-1), array![[0]]); } #[test] fn test_3d() { let x = array![ [[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[10, 11, 12], [13, 14, 15], [16, 17, 18]], [[19, 20, 21], [22, 23, 24], [25, 26, 27]] ]; // Upper let res = x.triu(0); assert_eq!( res, array![ [[1, 2, 3], [0, 5, 6], [0, 0, 9]], [[10, 11, 12], [0, 14, 15], [0, 0, 18]], [[19, 20, 21], [0, 23, 24], [0, 0, 27]] ] ); // Lower let res = x.tril(0); assert_eq!( res, array![ [[1, 0, 0], [4, 5, 0], [7, 8, 9]], [[10, 0, 0], [13, 14, 0], [16, 17, 18]], [[19, 0, 0], [22, 23, 0], [25, 26, 27]] ] ); let x = Array3::from_shape_vec( (3, 3, 3).f(), vec![1, 10, 19, 4, 13, 22, 7, 16, 25, 2, 11, 20, 5, 14, 23, 8, 17, 26, 3, 12, 21, 6, 15, 24, 9, 18, 27], ) .unwrap(); // Upper let res = x.triu(0); assert_eq!( res, array![ [[1, 2, 3], [0, 5, 6], [0, 0, 9]], [[10, 11, 12], [0, 14, 15], [0, 0, 18]], [[19, 20, 21], [0, 23, 24], [0, 0, 27]] ] ); // Lower let res = x.tril(0); assert_eq!( res, array![ [[1, 0, 0], [4, 5, 0], [7, 8, 9]], [[10, 0, 0], [13, 14, 0], [16, 17, 18]], [[19, 0, 0], [22, 23, 0], [25, 26, 27]] ] ); } #[test] fn test_off_axis() { let x = array![ [[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[10, 11, 12], [13, 14, 15], [16, 17, 18]], [[19, 20, 21], [22, 23, 24], [25, 26, 27]] ]; let res = x.triu(1); assert_eq!( res, array![ [[0, 2, 3], [0, 0, 6], [0, 0, 0]], [[0, 11, 12], [0, 0, 15], [0, 0, 0]], [[0, 20, 21], [0, 0, 24], [0, 0, 0]] ] ); let res = x.triu(-1); assert_eq!( res, array![ [[1, 2, 3], [4, 5, 6], [0, 8, 9]], [[10, 11, 12], [13, 14, 15], [0, 17, 18]], [[19, 20, 21], [22, 23, 24], [0, 26, 27]] ] ); } #[test] fn test_odd_shape() { let x = array![[1, 2, 3], [4, 5, 6]]; let res = x.triu(0); assert_eq!(res, array![[1, 2, 3], [0, 5, 6]]); let res = x.tril(0); assert_eq!(res, array![[1, 0, 0], [4, 5, 0]]); let x = array![[1, 2], [3, 4], [5, 6]]; let res = x.triu(0); assert_eq!(res, array![[1, 2], [0, 4], [0, 0]]); let res = x.tril(0); assert_eq!(res, array![[1, 0], [3, 4], [5, 6]]); } #[test] fn test_odd_k() { let x = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; let z = Array2::zeros([3, 3]); assert_eq!(x.triu(isize::MIN), x); assert_eq!(x.tril(isize::MIN), z); assert_eq!(x.triu(isize::MAX), z); assert_eq!(x.tril(isize::MAX), x); } } ndarray-0.16.1/src/zip/mod.rs000064400000000000000000000772631046102023000141060ustar 00000000000000// Copyright 2017 bluss and ndarray developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #[macro_use] mod zipmacro; mod ndproducer; #[cfg(feature = "rayon")] use std::mem::MaybeUninit; use crate::imp_prelude::*; use crate::partial::Partial; use crate::AssignElem; use crate::IntoDimension; use crate::Layout; use crate::dimension; use crate::indexes::{indices, Indices}; use crate::split_at::{SplitAt, SplitPreference}; pub use self::ndproducer::{IntoNdProducer, NdProducer, Offset}; /// Return if the expression is a break value. macro_rules! fold_while { ($e:expr) => { match $e { FoldWhile::Continue(x) => x, x => return x, } }; } /// Broadcast an array so that it acts like a larger size and/or shape array. /// /// See [broadcasting](ArrayBase#broadcasting) for more information. trait Broadcast where E: IntoDimension { type Output: NdProducer; /// Broadcast the array to the new dimensions `shape`. /// /// ***Panics*** if broadcasting isn’t possible. #[track_caller] fn broadcast_unwrap(self, shape: E) -> Self::Output; private_decl! {} } /// Compute `Layout` hints for array shape dim, strides fn array_layout(dim: &D, strides: &D) -> Layout { let n = dim.ndim(); if dimension::is_layout_c(dim, strides) { // effectively one-dimensional => C and F layout compatible if n <= 1 || dim.slice().iter().filter(|&&len| len > 1).count() <= 1 { Layout::one_dimensional() } else { Layout::c() } } else if n > 1 && dimension::is_layout_f(dim, strides) { Layout::f() } else if n > 1 { if dim[0] > 1 && strides[0] == 1 { Layout::fpref() } else if dim[n - 1] > 1 && strides[n - 1] == 1 { Layout::cpref() } else { Layout::none() } } else { Layout::none() } } impl ArrayBase where S: RawData, D: Dimension, { pub(crate) fn layout_impl(&self) -> Layout { array_layout(&self.dim, &self.strides) } } impl<'a, A, D, E> Broadcast for ArrayView<'a, A, D> where E: IntoDimension, D: Dimension, { type Output = ArrayView<'a, A, E::Dim>; fn broadcast_unwrap(self, shape: E) -> Self::Output { #[allow(clippy::needless_borrow)] let res: ArrayView<'_, A, E::Dim> = (&self).broadcast_unwrap(shape.into_dimension()); unsafe { ArrayView::new(res.ptr, res.dim, res.strides) } } private_impl! {} } trait ZippableTuple: Sized { type Item; type Ptr: OffsetTuple + Copy; type Dim: Dimension; type Stride: Copy; fn as_ptr(&self) -> Self::Ptr; unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item; unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr; fn stride_of(&self, index: usize) -> Self::Stride; fn contiguous_stride(&self) -> Self::Stride; fn split_at(self, axis: Axis, index: usize) -> (Self, Self); } /// Lock step function application across several arrays or other producers. /// /// Zip allows matching several producers to each other elementwise and applying /// a function over all tuples of elements (one item from each input at /// a time). /// /// In general, the zip uses a tuple of producers /// ([`NdProducer`] trait) that all have to be of the /// same shape. The NdProducer implementation defines what its item type is /// (for example if it's a shared reference, mutable reference or an array /// view etc). /// /// If all the input arrays are of the same memory layout the zip performs much /// better and the compiler can usually vectorize the loop (if applicable). /// /// The order elements are visited is not specified. The producers don’t have to /// have the same item type. /// /// The `Zip` has two methods for function application: `for_each` and /// `fold_while`. The zip object can be split, which allows parallelization. /// A read-only zip object (no mutable producers) can be cloned. /// /// See also the [`azip!()`] which offers a convenient shorthand /// to common ways to use `Zip`. /// /// ``` /// use ndarray::Zip; /// use ndarray::Array2; /// /// type M = Array2; /// /// // Create four 2d arrays of the same size /// let mut a = M::zeros((64, 32)); /// let b = M::from_elem(a.dim(), 1.); /// let c = M::from_elem(a.dim(), 2.); /// let d = M::from_elem(a.dim(), 3.); /// /// // Example 1: Perform an elementwise arithmetic operation across /// // the four arrays a, b, c, d. /// /// Zip::from(&mut a) /// .and(&b) /// .and(&c) /// .and(&d) /// .for_each(|w, &x, &y, &z| { /// *w += x + y * z; /// }); /// /// // Example 2: Create a new array `totals` with one entry per row of `a`. /// // Use Zip to traverse the rows of `a` and assign to the corresponding /// // entry in `totals` with the sum across each row. /// // This is possible because the producer for `totals` and the row producer /// // for `a` have the same shape and dimensionality. /// // The rows producer yields one array view (`row`) per iteration. /// /// use ndarray::{Array1, Axis}; /// /// let mut totals = Array1::zeros(a.nrows()); /// /// Zip::from(&mut totals) /// .and(a.rows()) /// .for_each(|totals, row| *totals = row.sum()); /// /// // Check the result against the built in `.sum_axis()` along axis 1. /// assert_eq!(totals, a.sum_axis(Axis(1))); /// /// /// // Example 3: Recreate Example 2 using map_collect to make a new array /// /// let totals2 = Zip::from(a.rows()).map_collect(|row| row.sum()); /// /// // Check the result against the previous example. /// assert_eq!(totals, totals2); /// ``` #[derive(Debug, Clone)] #[must_use = "zipping producers is lazy and does nothing unless consumed"] pub struct Zip { parts: Parts, dimension: D, layout: Layout, /// The sum of the layout tendencies of the parts; /// positive for c- and negative for f-layout preference. layout_tendency: i32, } impl Zip<(P,), D> where D: Dimension, P: NdProducer, { /// Create a new `Zip` from the input array or other producer `p`. /// /// The Zip will take the exact dimension of `p` and all inputs /// must have the same dimensions (or be broadcast to them). pub fn from(p: IP) -> Self where IP: IntoNdProducer { let array = p.into_producer(); let dim = array.raw_dim(); let layout = array.layout(); Zip { dimension: dim, layout, parts: (array,), layout_tendency: layout.tendency(), } } } impl Zip<(Indices, P), D> where D: Dimension + Copy, P: NdProducer, { /// Create a new `Zip` with an index producer and the producer `p`. /// /// The Zip will take the exact dimension of `p` and all inputs /// must have the same dimensions (or be broadcast to them). /// /// *Note:* Indexed zip has overhead. pub fn indexed(p: IP) -> Self where IP: IntoNdProducer { let array = p.into_producer(); let dim = array.raw_dim(); Zip::from(indices(dim)).and(array) } } #[inline] fn zip_dimension_check(dimension: &D, part: &P) where D: Dimension, P: NdProducer, { ndassert!( part.equal_dim(dimension), "Zip: Producer dimension mismatch, expected: {:?}, got: {:?}", dimension, part.raw_dim() ); } impl Zip where D: Dimension { /// Return a the number of element tuples in the Zip pub fn size(&self) -> usize { self.dimension.size() } /// Return the length of `axis` /// /// ***Panics*** if `axis` is out of bounds. #[track_caller] fn len_of(&self, axis: Axis) -> usize { self.dimension[axis.index()] } fn prefer_f(&self) -> bool { !self.layout.is(Layout::CORDER) && (self.layout.is(Layout::FORDER) || self.layout_tendency < 0) } /// Return an *approximation* to the max stride axis; if /// component arrays disagree, there may be no choice better than the /// others. fn max_stride_axis(&self) -> Axis { let i = if self.prefer_f() { self.dimension .slice() .iter() .rposition(|&len| len > 1) .unwrap_or(self.dimension.ndim() - 1) } else { /* corder or default */ self.dimension .slice() .iter() .position(|&len| len > 1) .unwrap_or(0) }; Axis(i) } } impl Zip where D: Dimension { fn for_each_core(&mut self, acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { if self.dimension.ndim() == 0 { function(acc, unsafe { self.parts.as_ref(self.parts.as_ptr()) }) } else if self.layout.is(Layout::CORDER | Layout::FORDER) { self.for_each_core_contiguous(acc, function) } else { self.for_each_core_strided(acc, function) } } fn for_each_core_contiguous(&mut self, acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { debug_assert!(self.layout.is(Layout::CORDER | Layout::FORDER)); let size = self.dimension.size(); let ptrs = self.parts.as_ptr(); let inner_strides = self.parts.contiguous_stride(); unsafe { self.inner(acc, ptrs, inner_strides, size, &mut function) } } /// The innermost loop of the Zip for_each methods /// /// Run the fold while operation on a stretch of elements with constant strides /// /// `ptr`: base pointer for the first element in this stretch /// `strides`: strides for the elements in this stretch /// `len`: number of elements /// `function`: closure unsafe fn inner( &self, mut acc: Acc, ptr: P::Ptr, strides: P::Stride, len: usize, function: &mut F, ) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { let mut i = 0; while i < len { let p = ptr.stride_offset(strides, i); acc = fold_while!(function(acc, self.parts.as_ref(p))); i += 1; } FoldWhile::Continue(acc) } fn for_each_core_strided(&mut self, acc: Acc, function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { let n = self.dimension.ndim(); if n == 0 { panic!("Unreachable: ndim == 0 is contiguous") } if n == 1 || self.layout_tendency >= 0 { self.for_each_core_strided_c(acc, function) } else { self.for_each_core_strided_f(acc, function) } } // Non-contiguous but preference for C - unroll over Axis(ndim - 1) fn for_each_core_strided_c(&mut self, mut acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { let n = self.dimension.ndim(); let unroll_axis = n - 1; let inner_len = self.dimension[unroll_axis]; self.dimension[unroll_axis] = 1; let mut index_ = self.dimension.first_index(); let inner_strides = self.parts.stride_of(unroll_axis); // Loop unrolled over closest axis while let Some(index) = index_ { unsafe { let ptr = self.parts.uget_ptr(&index); acc = fold_while![self.inner(acc, ptr, inner_strides, inner_len, &mut function)]; } index_ = self.dimension.next_for(index); } FoldWhile::Continue(acc) } // Non-contiguous but preference for F - unroll over Axis(0) fn for_each_core_strided_f(&mut self, mut acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, P::Item) -> FoldWhile, P: ZippableTuple, { let unroll_axis = 0; let inner_len = self.dimension[unroll_axis]; self.dimension[unroll_axis] = 1; let index_ = self.dimension.first_index(); let inner_strides = self.parts.stride_of(unroll_axis); // Loop unrolled over closest axis if let Some(mut index) = index_ { loop { unsafe { let ptr = self.parts.uget_ptr(&index); acc = fold_while![self.inner(acc, ptr, inner_strides, inner_len, &mut function)]; } if !self.dimension.next_for_f(&mut index) { break; } } } FoldWhile::Continue(acc) } #[cfg(feature = "rayon")] pub(crate) fn uninitialized_for_current_layout(&self) -> Array, D> { let is_f = self.prefer_f(); Array::uninit(self.dimension.clone().set_f(is_f)) } } impl Zip<(P1, P2), D> where D: Dimension, P1: NdProducer, P1: NdProducer, { /// Debug assert traversal order is like c (including 1D case) // Method placement: only used for binary Zip at the moment. #[inline] pub(crate) fn debug_assert_c_order(self) -> Self { debug_assert!(self.layout.is(Layout::CORDER) || self.layout_tendency >= 0 || self.dimension.slice().iter().filter(|&&d| d > 1).count() <= 1, "Assertion failed: traversal is not c-order or 1D for \ layout {:?}, tendency {}, dimension {:?}", self.layout, self.layout_tendency, self.dimension); self } } /* trait Offset : Copy { unsafe fn offset(self, off: isize) -> Self; unsafe fn stride_offset(self, index: usize, stride: isize) -> Self { self.offset(index as isize * stride) } } impl Offset for *mut T { unsafe fn offset(self, off: isize) -> Self { self.offset(off) } } */ trait OffsetTuple { type Args; unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self; } impl OffsetTuple for *mut T { type Args = isize; unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self { self.offset(index as isize * stride) } } macro_rules! offset_impl { ($([$($param:ident)*][ $($q:ident)*],)+) => { $( #[allow(non_snake_case)] impl<$($param: Offset),*> OffsetTuple for ($($param, )*) { type Args = ($($param::Stride,)*); unsafe fn stride_offset(self, stride: Self::Args, index: usize) -> Self { let ($($param, )*) = self; let ($($q, )*) = stride; ($(Offset::stride_offset($param, $q, index),)*) } } )+ }; } offset_impl! { [A ][ a], [A B][ a b], [A B C][ a b c], [A B C D][ a b c d], [A B C D E][ a b c d e], [A B C D E F][ a b c d e f], } macro_rules! zipt_impl { ($([$($p:ident)*][ $($q:ident)*],)+) => { $( #[allow(non_snake_case)] impl),*> ZippableTuple for ($($p, )*) { type Item = ($($p::Item, )*); type Ptr = ($($p::Ptr, )*); type Dim = Dim; type Stride = ($($p::Stride,)* ); fn stride_of(&self, index: usize) -> Self::Stride { let ($(ref $p,)*) = *self; ($($p.stride_of(Axis(index)), )*) } fn contiguous_stride(&self) -> Self::Stride { let ($(ref $p,)*) = *self; ($($p.contiguous_stride(), )*) } fn as_ptr(&self) -> Self::Ptr { let ($(ref $p,)*) = *self; ($($p.as_ptr(), )*) } unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item { let ($(ref $q ,)*) = *self; let ($($p,)*) = ptr; ($($q.as_ref($p),)*) } unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr { let ($(ref $p,)*) = *self; ($($p.uget_ptr(i), )*) } fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { let ($($p,)*) = self; let ($($p,)*) = ( $($p.split_at(axis, index), )* ); ( ($($p.0,)*), ($($p.1,)*) ) } } )+ }; } zipt_impl! { [A ][ a], [A B][ a b], [A B C][ a b c], [A B C D][ a b c d], [A B C D E][ a b c d e], [A B C D E F][ a b c d e f], } macro_rules! map_impl { ($([$notlast:ident $($p:ident)*],)+) => { $( #[allow(non_snake_case)] impl Zip<($($p,)*), D> where D: Dimension, $($p: NdProducer ,)* { /// Apply a function to all elements of the input arrays, /// visiting elements in lock step. pub fn for_each(mut self, mut function: F) where F: FnMut($($p::Item),*) { self.for_each_core((), move |(), args| { let ($($p,)*) = args; FoldWhile::Continue(function($($p),*)) }); } /// Apply a fold function to all elements of the input arrays, /// visiting elements in lock step. /// /// # Example /// /// The expression `tr(AᵀB)` can be more efficiently computed as /// the equivalent expression `∑ᵢⱼ(A∘B)ᵢⱼ` (i.e. the sum of the /// elements of the entry-wise product). It would be possible to /// evaluate this expression by first computing the entry-wise /// product, `A∘B`, and then computing the elementwise sum of that /// product, but it's possible to do this in a single loop (and /// avoid an extra heap allocation if `A` and `B` can't be /// consumed) by using `Zip`: /// /// ``` /// use ndarray::{array, Zip}; /// /// let a = array![[1, 5], [3, 7]]; /// let b = array![[2, 4], [8, 6]]; /// /// // Without using `Zip`. This involves two loops and an extra /// // heap allocation for the result of `&a * &b`. /// let sum_prod_nonzip = (&a * &b).sum(); /// // Using `Zip`. This is a single loop without any heap allocations. /// let sum_prod_zip = Zip::from(&a).and(&b).fold(0, |acc, a, b| acc + a * b); /// /// assert_eq!(sum_prod_nonzip, sum_prod_zip); /// ``` pub fn fold(mut self, acc: Acc, mut function: F) -> Acc where F: FnMut(Acc, $($p::Item),*) -> Acc, { self.for_each_core(acc, move |acc, args| { let ($($p,)*) = args; FoldWhile::Continue(function(acc, $($p),*)) }).into_inner() } /// Apply a fold function to the input arrays while the return /// value is `FoldWhile::Continue`, visiting elements in lock step. /// pub fn fold_while(mut self, acc: Acc, mut function: F) -> FoldWhile where F: FnMut(Acc, $($p::Item),*) -> FoldWhile { self.for_each_core(acc, move |acc, args| { let ($($p,)*) = args; function(acc, $($p),*) }) } /// Tests if every element of the iterator matches a predicate. /// /// Returns `true` if `predicate` evaluates to `true` for all elements. /// Returns `true` if the input arrays are empty. /// /// Example: /// /// ``` /// use ndarray::{array, Zip}; /// let a = array![1, 2, 3]; /// let b = array![1, 4, 9]; /// assert!(Zip::from(&a).and(&b).all(|&a, &b| a * a == b)); /// ``` pub fn all(mut self, mut predicate: F) -> bool where F: FnMut($($p::Item),*) -> bool { !self.for_each_core((), move |_, args| { let ($($p,)*) = args; if predicate($($p),*) { FoldWhile::Continue(()) } else { FoldWhile::Done(()) } }).is_done() } /// Tests if at least one element of the iterator matches a predicate. /// /// Returns `true` if `predicate` evaluates to `true` for at least one element. /// Returns `false` if the input arrays are empty. /// /// Example: /// /// ``` /// use ndarray::{array, Zip}; /// let a = array![1, 2, 3]; /// let b = array![1, 4, 9]; /// assert!(Zip::from(&a).and(&b).any(|&a, &b| a == b)); /// assert!(!Zip::from(&a).and(&b).any(|&a, &b| a - 1 == b)); /// ``` pub fn any(mut self, mut predicate: F) -> bool where F: FnMut($($p::Item),*) -> bool { self.for_each_core((), move |_, args| { let ($($p,)*) = args; if predicate($($p),*) { FoldWhile::Done(()) } else { FoldWhile::Continue(()) } }).is_done() } expand_if!(@bool [$notlast] /// Include the producer `p` in the Zip. /// /// ***Panics*** if `p`’s shape doesn’t match the Zip’s exactly. #[track_caller] pub fn and

(self, p: P) -> Zip<($($p,)* P::Output, ), D> where P: IntoNdProducer, { let part = p.into_producer(); zip_dimension_check(&self.dimension, &part); self.build_and(part) } /// Include the producer `p` in the Zip. /// /// ## Safety /// /// The caller must ensure that the producer's shape is equal to the Zip's shape. /// Uses assertions when debug assertions are enabled. #[allow(unused)] pub(crate) unsafe fn and_unchecked

(self, p: P) -> Zip<($($p,)* P::Output, ), D> where P: IntoNdProducer, { #[cfg(debug_assertions)] { self.and(p) } #[cfg(not(debug_assertions))] { self.build_and(p.into_producer()) } } /// Include the producer `p` in the Zip, broadcasting if needed. /// /// If their shapes disagree, `rhs` is broadcast to the shape of `self`. /// /// ***Panics*** if broadcasting isn’t possible. #[track_caller] pub fn and_broadcast<'a, P, D2, Elem>(self, p: P) -> Zip<($($p,)* ArrayView<'a, Elem, D>, ), D> where P: IntoNdProducer, Item=&'a Elem>, D2: Dimension, { let part = p.into_producer().broadcast_unwrap(self.dimension.clone()); self.build_and(part) } fn build_and

(self, part: P) -> Zip<($($p,)* P, ), D> where P: NdProducer, { let part_layout = part.layout(); let ($($p,)*) = self.parts; Zip { parts: ($($p,)* part, ), layout: self.layout.intersect(part_layout), dimension: self.dimension, layout_tendency: self.layout_tendency + part_layout.tendency(), } } /// Map and collect the results into a new array, which has the same size as the /// inputs. /// /// If all inputs are c- or f-order respectively, that is preserved in the output. pub fn map_collect(self, f: impl FnMut($($p::Item,)* ) -> R) -> Array { self.map_collect_owned(f) } pub(crate) fn map_collect_owned(self, f: impl FnMut($($p::Item,)* ) -> R) -> ArrayBase where S: DataOwned { // safe because: all elements are written before the array is completed let shape = self.dimension.clone().set_f(self.prefer_f()); let output = >::build_uninit(shape, |output| { // Use partial to count the number of filled elements, and can drop the right // number of elements on unwinding (if it happens during apply/collect). unsafe { let output_view = output.into_raw_view_mut().cast::(); self.and(output_view) .collect_with_partial(f) .release_ownership(); } }); unsafe { output.assume_init() } } /// Map and assign the results into the producer `into`, which should have the same /// size as the other inputs. /// /// The producer should have assignable items as dictated by the `AssignElem` trait, /// for example `&mut R`. pub fn map_assign_into(self, into: Q, mut f: impl FnMut($($p::Item,)* ) -> R) where Q: IntoNdProducer, Q::Item: AssignElem { self.and(into) .for_each(move |$($p, )* output_| { output_.assign_elem(f($($p ),*)); }); } ); /// Split the `Zip` evenly in two. /// /// It will be split in the way that best preserves element locality. pub fn split(self) -> (Self, Self) { debug_assert_ne!(self.size(), 0, "Attempt to split empty zip"); debug_assert_ne!(self.size(), 1, "Attempt to split zip with 1 elem"); SplitPreference::split(self) } } expand_if!(@bool [$notlast] // For collect; Last producer is a RawViewMut #[allow(non_snake_case)] impl Zip<($($p,)* PLast), D> where D: Dimension, $($p: NdProducer ,)* PLast: NdProducer, { /// The inner workings of map_collect and par_map_collect /// /// Apply the function and collect the results into the output (last producer) /// which should be a raw array view; a Partial that owns the written /// elements is returned. /// /// Elements will be overwritten in place (in the sense of std::ptr::write). /// /// ## Safety /// /// The last producer is a RawArrayViewMut and must be safe to write into. /// The producer must be c- or f-contig and have the same layout tendency /// as the whole Zip. /// /// The returned Partial's proxy ownership of the elements must be handled, /// before the array the raw view points to realizes its ownership. pub(crate) unsafe fn collect_with_partial(self, mut f: F) -> Partial where F: FnMut($($p::Item,)* ) -> R { // Get the last producer; and make a Partial that aliases its data pointer let (.., ref output) = &self.parts; // debug assert that the output is contiguous in the memory layout we need if cfg!(debug_assertions) { let out_layout = output.layout(); assert!(out_layout.is(Layout::CORDER | Layout::FORDER)); assert!( (self.layout_tendency <= 0 && out_layout.tendency() <= 0) || (self.layout_tendency >= 0 && out_layout.tendency() >= 0), "layout tendency violation for self layout {:?}, output layout {:?},\ output shape {:?}", self.layout, out_layout, output.raw_dim()); } let mut partial = Partial::new(output.as_ptr()); // Apply the mapping function on this zip // if we panic with unwinding; Partial will drop the written elements. let partial_len = &mut partial.len; self.for_each(move |$($p,)* output_elem: *mut R| { output_elem.write(f($($p),*)); if std::mem::needs_drop::() { *partial_len += 1; } }); partial } } ); impl SplitPreference for Zip<($($p,)*), D> where D: Dimension, $($p: NdProducer ,)* { fn can_split(&self) -> bool { self.size() > 1 } fn split_preference(&self) -> (Axis, usize) { // Always split in a way that preserves layout (if any) let axis = self.max_stride_axis(); let index = self.len_of(axis) / 2; (axis, index) } } impl SplitAt for Zip<($($p,)*), D> where D: Dimension, $($p: NdProducer ,)* { fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { let (p1, p2) = self.parts.split_at(axis, index); let (d1, d2) = self.dimension.split_at(axis, index); (Zip { dimension: d1, layout: self.layout, parts: p1, layout_tendency: self.layout_tendency, }, Zip { dimension: d2, layout: self.layout, parts: p2, layout_tendency: self.layout_tendency, }) } } )+ }; } map_impl! { [true P1], [true P1 P2], [true P1 P2 P3], [true P1 P2 P3 P4], [true P1 P2 P3 P4 P5], [false P1 P2 P3 P4 P5 P6], } /// Value controlling the execution of `.fold_while` on `Zip`. #[derive(Debug, Copy, Clone)] pub enum FoldWhile { /// Continue folding with this value Continue(T), /// Fold is complete and will return this value Done(T), } impl FoldWhile { /// Return the inner value pub fn into_inner(self) -> T { match self { FoldWhile::Continue(x) | FoldWhile::Done(x) => x, } } /// Return true if it is `Done`, false if `Continue` pub fn is_done(&self) -> bool { match *self { FoldWhile::Continue(_) => false, FoldWhile::Done(_) => true, } } } ndarray-0.16.1/src/zip/ndproducer.rs000064400000000000000000000235501046102023000154620ustar 00000000000000use crate::imp_prelude::*; use crate::Layout; use crate::NdIndex; #[cfg(not(feature = "std"))] use alloc::vec::Vec; /// Argument conversion into a producer. /// /// Slices and vectors can be used (equivalent to 1-dimensional array views). /// /// This trait is like `IntoIterator` for `NdProducers` instead of iterators. pub trait IntoNdProducer { /// The element produced per iteration. type Item; /// Dimension type of the producer type Dim: Dimension; type Output: NdProducer; /// Convert the value into an `NdProducer`. fn into_producer(self) -> Self::Output; } impl

IntoNdProducer for P where P: NdProducer { type Item = P::Item; type Dim = P::Dim; type Output = Self; fn into_producer(self) -> Self::Output { self } } /// A producer of an n-dimensional set of elements; /// for example an array view, mutable array view or an iterator /// that yields chunks. /// /// Producers are used as a arguments to [`Zip`](crate::Zip) and /// [`azip!()`]. /// /// # Comparison to `IntoIterator` /// /// Most `NdProducers` are *iterable* (implement `IntoIterator`) but not directly /// iterators. This separation is needed because the producer represents /// a multidimensional set of items, it can be split along a particular axis for /// parallelization, and it has no fixed correspondence to a sequence. /// /// The natural exception is one dimensional producers, like `AxisIter`, which /// implement `Iterator` directly /// (`AxisIter` traverses a one dimensional sequence, along an axis, while /// *producing* multidimensional items). /// /// See also [`IntoNdProducer`] pub trait NdProducer { /// The element produced per iteration. type Item; // Internal use / Pointee type /// Dimension type type Dim: Dimension; // The pointer Ptr is used by an array view to simply point to the // current element. It doesn't have to be a pointer (see Indices). // Its main function is that it can be incremented with a particular // stride (= along a particular axis) #[doc(hidden)] /// Pointer or stand-in for pointer type Ptr: Offset; #[doc(hidden)] /// Pointer stride type Stride: Copy; #[doc(hidden)] fn layout(&self) -> Layout; /// Return the shape of the producer. fn raw_dim(&self) -> Self::Dim; #[doc(hidden)] fn equal_dim(&self, dim: &Self::Dim) -> bool { self.raw_dim() == *dim } #[doc(hidden)] fn as_ptr(&self) -> Self::Ptr; #[doc(hidden)] unsafe fn as_ref(&self, ptr: Self::Ptr) -> Self::Item; #[doc(hidden)] unsafe fn uget_ptr(&self, i: &Self::Dim) -> Self::Ptr; #[doc(hidden)] fn stride_of(&self, axis: Axis) -> ::Stride; #[doc(hidden)] fn contiguous_stride(&self) -> Self::Stride; #[doc(hidden)] fn split_at(self, axis: Axis, index: usize) -> (Self, Self) where Self: Sized; private_decl! {} } pub trait Offset: Copy { type Stride: Copy; unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self; private_decl! {} } impl Offset for *const T { type Stride = isize; unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { self.offset(s * (index as isize)) } private_impl! {} } impl Offset for *mut T { type Stride = isize; unsafe fn stride_offset(self, s: Self::Stride, index: usize) -> Self { self.offset(s * (index as isize)) } private_impl! {} } /// An array reference is an n-dimensional producer of element references /// (like ArrayView). impl<'a, A: 'a, S, D> IntoNdProducer for &'a ArrayBase where D: Dimension, S: Data, { type Item = &'a A; type Dim = D; type Output = ArrayView<'a, A, D>; fn into_producer(self) -> Self::Output { self.view() } } /// A mutable array reference is an n-dimensional producer of mutable element /// references (like ArrayViewMut). impl<'a, A: 'a, S, D> IntoNdProducer for &'a mut ArrayBase where D: Dimension, S: DataMut, { type Item = &'a mut A; type Dim = D; type Output = ArrayViewMut<'a, A, D>; fn into_producer(self) -> Self::Output { self.view_mut() } } /// A slice is a one-dimensional producer impl<'a, A: 'a> IntoNdProducer for &'a [A] { type Item = ::Item; type Dim = Ix1; type Output = ArrayView1<'a, A>; fn into_producer(self) -> Self::Output { <_>::from(self) } } /// A mutable slice is a mutable one-dimensional producer impl<'a, A: 'a> IntoNdProducer for &'a mut [A] { type Item = ::Item; type Dim = Ix1; type Output = ArrayViewMut1<'a, A>; fn into_producer(self) -> Self::Output { <_>::from(self) } } /// A one-dimensional array is a one-dimensional producer impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a [A; N] { type Item = ::Item; type Dim = Ix1; type Output = ArrayView1<'a, A>; fn into_producer(self) -> Self::Output { <_>::from(self) } } /// A mutable one-dimensional array is a mutable one-dimensional producer impl<'a, A: 'a, const N: usize> IntoNdProducer for &'a mut [A; N] { type Item = ::Item; type Dim = Ix1; type Output = ArrayViewMut1<'a, A>; fn into_producer(self) -> Self::Output { <_>::from(self) } } /// A Vec is a one-dimensional producer impl<'a, A: 'a> IntoNdProducer for &'a Vec { type Item = ::Item; type Dim = Ix1; type Output = ArrayView1<'a, A>; fn into_producer(self) -> Self::Output { <_>::from(self) } } /// A mutable Vec is a mutable one-dimensional producer impl<'a, A: 'a> IntoNdProducer for &'a mut Vec { type Item = ::Item; type Dim = Ix1; type Output = ArrayViewMut1<'a, A>; fn into_producer(self) -> Self::Output { <_>::from(self) } } impl<'a, A, D: Dimension> NdProducer for ArrayView<'a, A, D> { type Item = &'a A; type Dim = D; type Ptr = *mut A; type Stride = isize; private_impl! {} fn raw_dim(&self) -> Self::Dim { self.raw_dim() } fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } fn layout(&self) -> Layout { self.layout_impl() } unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { &*ptr } unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } #[inline(always)] fn contiguous_stride(&self) -> Self::Stride { 1 } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } } impl<'a, A, D: Dimension> NdProducer for ArrayViewMut<'a, A, D> { type Item = &'a mut A; type Dim = D; type Ptr = *mut A; type Stride = isize; private_impl! {} fn raw_dim(&self) -> Self::Dim { self.raw_dim() } fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } fn layout(&self) -> Layout { self.layout_impl() } unsafe fn as_ref(&self, ptr: *mut A) -> Self::Item { &mut *ptr } unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } #[inline(always)] fn contiguous_stride(&self) -> Self::Stride { 1 } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } } impl NdProducer for RawArrayView { type Item = *const A; type Dim = D; type Ptr = *const A; type Stride = isize; private_impl! {} fn raw_dim(&self) -> Self::Dim { self.raw_dim() } fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } fn as_ptr(&self) -> *const A { self.as_ptr() } fn layout(&self) -> Layout { self.layout_impl() } unsafe fn as_ref(&self, ptr: *const A) -> *const A { ptr } unsafe fn uget_ptr(&self, i: &Self::Dim) -> *const A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } #[inline(always)] fn contiguous_stride(&self) -> Self::Stride { 1 } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } } impl NdProducer for RawArrayViewMut { type Item = *mut A; type Dim = D; type Ptr = *mut A; type Stride = isize; private_impl! {} fn raw_dim(&self) -> Self::Dim { self.raw_dim() } fn equal_dim(&self, dim: &Self::Dim) -> bool { self.dim.equal(dim) } fn as_ptr(&self) -> *mut A { self.as_ptr() as _ } fn layout(&self) -> Layout { self.layout_impl() } unsafe fn as_ref(&self, ptr: *mut A) -> *mut A { ptr } unsafe fn uget_ptr(&self, i: &Self::Dim) -> *mut A { self.ptr.as_ptr().offset(i.index_unchecked(&self.strides)) } fn stride_of(&self, axis: Axis) -> isize { self.stride_of(axis) } #[inline(always)] fn contiguous_stride(&self) -> Self::Stride { 1 } fn split_at(self, axis: Axis, index: usize) -> (Self, Self) { self.split_at(axis, index) } } ndarray-0.16.1/src/zip/zipmacro.rs000064400000000000000000000107051046102023000151370ustar 00000000000000/// Array zip macro: lock step function application across several arrays and /// producers. /// /// This is a shorthand for [`Zip`](crate::Zip). /// /// This example: /// /// ```rust,ignore /// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); /// ``` /// /// Is equivalent to: /// /// ```rust,ignore /// Zip::from(&mut a).and(&b).and(&c).for_each(|a, &b, &c| { /// *a = b + c /// }); /// ``` /// /// The syntax is either /// /// `azip!((` *pat* `in` *expr* `,` *[* *pat* `in` *expr* `,` ... *]* `)` *body_expr* `)` /// /// or, to use `Zip::indexed` instead of `Zip::from`, /// /// `azip!((index` *pat* `,` *pat* `in` *expr* `,` *[* *pat* `in` *expr* `,` ... *]* `)` *body_expr* `)` /// /// The *expr* are expressions whose types must implement `IntoNdProducer`, the /// *pat* are the patterns of the parameters to the closure called by /// `Zip::for_each`, and *body_expr* is the body of the closure called by /// `Zip::for_each`. You can think of each *pat* `in` *expr* as being analogous to /// the `pat in expr` of a normal loop `for pat in expr { statements }`: a /// pattern, followed by `in`, followed by an expression that implements /// `IntoNdProducer` (analogous to `IntoIterator` for a `for` loop). /// /// **Panics** if any of the arrays are not of the same shape. /// /// ## Examples /// /// ```rust /// use ndarray::{azip, Array1, Array2, Axis}; /// /// type M = Array2; /// /// // Setup example arrays /// let mut a = M::zeros((16, 16)); /// let mut b = M::zeros(a.dim()); /// let mut c = M::zeros(a.dim()); /// /// // assign values /// b.fill(1.); /// for ((i, j), elt) in c.indexed_iter_mut() { /// *elt = (i + 10 * j) as f32; /// } /// /// // Example 1: Compute a simple ternary operation: /// // elementwise addition of b and c, stored in a /// azip!((a in &mut a, &b in &b, &c in &c) *a = b + c); /// /// assert_eq!(a, &b + &c); /// /// // Example 2: azip!() with index /// azip!((index (i, j), &b in &b, &c in &c) { /// a[[i, j]] = b - c; /// }); /// /// assert_eq!(a, &b - &c); /// /// /// // Example 3: azip!() on references /// // See the definition of the function below /// borrow_multiply(&mut a, &b, &c); /// /// assert_eq!(a, &b * &c); /// /// /// // Since this function borrows its inputs, the `IntoNdProducer` /// // expressions don't need to explicitly include `&mut` or `&`. /// fn borrow_multiply(a: &mut M, b: &M, c: &M) { /// azip!((a in a, &b in b, &c in c) *a = b * c); /// } /// /// /// // Example 4: using azip!() without dereference in pattern. /// // /// // Create a new array `totals` with one entry per row of `a`. /// // Use azip to traverse the rows of `a` and assign to the corresponding /// // entry in `totals` with the sum across each row. /// // /// // The row is an array view; it doesn't need to be dereferenced. /// let mut totals = Array1::zeros(a.nrows()); /// azip!((totals in &mut totals, row in a.rows()) *totals = row.sum()); /// /// // Check the result against the built in `.sum_axis()` along axis 1. /// assert_eq!(totals, a.sum_axis(Axis(1))); /// ``` #[macro_export] macro_rules! azip { // Indexed with a single producer // we allow an optional trailing comma after the producers in each rule. (@build $apply:ident (index $index:pat, $first_pat:pat in $first_prod:expr $(,)?) $body:expr) => { $crate::Zip::indexed($first_prod).$apply(|$index, $first_pat| $body) }; // Indexed with more than one producer (@build $apply:ident (index $index:pat, $first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),* $(,)?) $body:expr) => { $crate::Zip::indexed($first_prod) $(.and($prod))* .$apply(|$index, $first_pat, $($pat),*| $body) }; // Unindexed with a single producer (@build $apply:ident ($first_pat:pat in $first_prod:expr $(,)?) $body:expr) => { $crate::Zip::from($first_prod).$apply(|$first_pat| $body) }; // Unindexed with more than one producer (@build $apply:ident ($first_pat:pat in $first_prod:expr, $($pat:pat in $prod:expr),* $(,)?) $body:expr) => { $crate::Zip::from($first_prod) $(.and($prod))* .$apply(|$first_pat, $($pat),*| $body) }; // Unindexed with one or more producer, no loop body (@build $apply:ident $first_prod:expr $(, $prod:expr)* $(,)?) => { $crate::Zip::from($first_prod) $(.and($prod))* }; // catch-all rule (@build $($t:tt)*) => { compile_error!("Invalid syntax in azip!()") }; ($($t:tt)*) => { $crate::azip!(@build for_each $($t)*) }; } ndarray-0.16.1/tests/append.rs000064400000000000000000000310021046102023000143240ustar 00000000000000use ndarray::prelude::*; use ndarray::{ErrorKind, ShapeError}; #[test] fn push_row() { let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.push_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.push_column(aview1(&[1., 2.])), Ok(())); assert_eq!(a, array![[0., 1., 2., 3., 1.], [4., 5., 6., 7., 2.]]); } #[test] fn push_row_wrong_layout() { let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); assert_eq!(a.strides(), &[4, 1]); // Changing the memory layout to fit the next append let mut a2 = a.clone(); a2.push_column(aview1(&[1., 2.])).unwrap(); assert_eq!(a2, array![[0., 1., 2., 3., 1.], [4., 5., 6., 7., 2.]]); assert_eq!(a2.strides(), &[1, 2]); // Clone the array let mut dim = a.raw_dim(); dim[1] = 0; let mut b = Array::zeros(dim); b.append(Axis(1), a.view()).unwrap(); assert_eq!(b.push_column(aview1(&[1., 2.])), Ok(())); assert_eq!(b, array![[0., 1., 2., 3., 1.], [4., 5., 6., 7., 2.]]); } #[test] fn push_row_neg_stride_1() { let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); assert_eq!(a.strides(), &[4, 1]); a.invert_axis(Axis(0)); // Changing the memory layout to fit the next append let mut a2 = a.clone(); println!("a = {:?}", a); println!("a2 = {:?}", a2); a2.push_column(aview1(&[1., 2.])).unwrap(); assert_eq!(a2, array![[4., 5., 6., 7., 1.], [0., 1., 2., 3., 2.]]); assert_eq!(a2.strides(), &[1, 2]); a.invert_axis(Axis(1)); let mut a3 = a.clone(); a3.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a3, array![[7., 6., 5., 4.], [3., 2., 1., 0.], [4., 5., 6., 7.]]); assert_eq!(a3.strides(), &[4, 1]); a.invert_axis(Axis(0)); let mut a4 = a.clone(); a4.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a4, array![[3., 2., 1., 0.], [7., 6., 5., 4.], [4., 5., 6., 7.]]); assert_eq!(a4.strides(), &[4, -1]); } #[test] fn push_row_neg_stride_2() { let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); assert_eq!(a.strides(), &[4, 1]); a.invert_axis(Axis(1)); // Changing the memory layout to fit the next append let mut a2 = a.clone(); println!("a = {:?}", a); println!("a2 = {:?}", a2); a2.push_column(aview1(&[1., 2.])).unwrap(); assert_eq!(a2, array![[3., 2., 1., 0., 1.], [7., 6., 5., 4., 2.]]); assert_eq!(a2.strides(), &[1, 2]); a.invert_axis(Axis(0)); let mut a3 = a.clone(); a3.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a3, array![[7., 6., 5., 4.], [3., 2., 1., 0.], [4., 5., 6., 7.]]); assert_eq!(a3.strides(), &[4, 1]); a.invert_axis(Axis(1)); let mut a4 = a.clone(); a4.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a4, array![[4., 5., 6., 7.], [0., 1., 2., 3.], [4., 5., 6., 7.]]); assert_eq!(a4.strides(), &[4, 1]); } #[test] fn push_row_error() { let mut a = Array::zeros((3, 4)); assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.push_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.push_column(aview1(&[1., 2., 3.])), Ok(())); assert_eq!(a.t(), array![[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [1., 2., 3.]]); } #[test] fn push_row_existing() { let mut a = Array::zeros((1, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[3, 4]); assert_eq!(a, array![[0., 0., 0., 0.], [0., 1., 2., 3.], [4., 5., 6., 7.]]); assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.push_column(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); assert_eq!(a.push_column(aview1(&[1., 2., 3.])), Ok(())); assert_eq!(a, array![[0., 0., 0., 0., 1.], [0., 1., 2., 3., 2.], [4., 5., 6., 7., 3.]]); } #[test] fn push_row_col_len_1() { // Test appending 1 row and then cols from shape 1 x 1 let mut a = Array::zeros((1, 1)); a.push_row(aview1(&[1.])).unwrap(); // shape 2 x 1 a.push_column(aview1(&[2., 3.])).unwrap(); // shape 2 x 2 assert_eq!(a.push_row(aview1(&[1.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape))); //assert_eq!(a.push_row(aview1(&[1., 2.])), Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout))); a.push_column(aview1(&[4., 5.])).unwrap(); // shape 2 x 3 assert_eq!(a.shape(), &[2, 3]); assert_eq!(a, array![[0., 2., 4.], [1., 3., 5.]]); } #[test] fn push_column() { let mut a = Array::zeros((4, 0)); a.push_column(aview1(&[0., 1., 2., 3.])).unwrap(); a.push_column(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[4, 2]); assert_eq!(a.t(), array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); } #[test] fn append_array1() { let mut a = Array::zeros((0, 4)); a.append(Axis(0), aview2(&[[0., 1., 2., 3.]])).unwrap(); println!("{:?}", a); a.append(Axis(0), aview2(&[[4., 5., 6., 7.]])).unwrap(); println!("{:?}", a); //a.push_column(aview1(&[4., 5., 6., 7.])).unwrap(); //assert_eq!(a.shape(), &[4, 2]); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.]]); a.append(Axis(0), aview2(&[[5., 5., 4., 4.], [3., 3., 2., 2.]])) .unwrap(); println!("{:?}", a); assert_eq!(a, array![[0., 1., 2., 3.], [4., 5., 6., 7.], [5., 5., 4., 4.], [3., 3., 2., 2.]]); } #[test] fn append_array_3d() { let mut a = Array::zeros((0, 2, 2)); a.append(Axis(0), array![[[0, 1], [2, 3]]].view()).unwrap(); println!("{:?}", a); let aa = array![[[51, 52], [53, 54]], [[55, 56], [57, 58]]]; let av = aa.view(); println!("Send {:?} to append", av); a.append(Axis(0), av.clone()).unwrap(); a.swap_axes(0, 1); let aa = array![[[71, 72], [73, 74]], [[75, 76], [77, 78]]]; let mut av = aa.view(); av.swap_axes(0, 1); println!("Send {:?} to append", av); a.append(Axis(1), av.clone()).unwrap(); println!("{:?}", a); let aa = array![[[81, 82], [83, 84]], [[85, 86], [87, 88]]]; let mut av = aa.view(); av.swap_axes(0, 1); println!("Send {:?} to append", av); a.append(Axis(1), av).unwrap(); println!("{:?}", a); assert_eq!(a, array![[[0, 1], [51, 52], [55, 56], [71, 72], [75, 76], [81, 82], [85, 86]], [[2, 3], [53, 54], [57, 58], [73, 74], [77, 78], [83, 84], [87, 88]]]); } #[test] fn test_append_2d() { // create an empty array and append let mut a = Array::zeros((0, 4)); let ones = ArrayView::from(&[1.; 12]) .into_shape_with_order((3, 4)) .unwrap(); let zeros = ArrayView::from(&[0.; 8]) .into_shape_with_order((2, 4)) .unwrap(); a.append(Axis(0), ones).unwrap(); a.append(Axis(0), zeros).unwrap(); a.append(Axis(0), ones).unwrap(); println!("{:?}", a); assert_eq!(a.shape(), &[8, 4]); for (i, row) in a.rows().into_iter().enumerate() { let ones = i < 3 || i >= 5; assert!(row.iter().all(|&x| x == ones as i32 as f64), "failed on lane {}", i); } let mut a = Array::zeros((0, 4)); a = a.reversed_axes(); let ones = ones.reversed_axes(); let zeros = zeros.reversed_axes(); a.append(Axis(1), ones).unwrap(); a.append(Axis(1), zeros).unwrap(); a.append(Axis(1), ones).unwrap(); println!("{:?}", a); assert_eq!(a.shape(), &[4, 8]); for (i, row) in a.columns().into_iter().enumerate() { let ones = i < 3 || i >= 5; assert!(row.iter().all(|&x| x == ones as i32 as f64), "failed on lane {}", i); } } #[test] fn test_append_middle_axis() { // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 0, 2)); a.append( Axis(1), Array::from_iter(0..12) .into_shape_with_order((3, 2, 2)) .unwrap() .view(), ) .unwrap(); println!("{:?}", a); a.append( Axis(1), Array::from_iter(12..24) .into_shape_with_order((3, 2, 2)) .unwrap() .view(), ) .unwrap(); println!("{:?}", a); // ensure we can append to Axis(1) by letting it become outermost let mut a = Array::::zeros((3, 1, 2)); a.append( Axis(1), Array::from_iter(0..12) .into_shape_with_order((3, 2, 2)) .unwrap() .view(), ) .unwrap(); println!("{:?}", a); a.append( Axis(1), Array::from_iter(12..24) .into_shape_with_order((3, 2, 2)) .unwrap() .view(), ) .unwrap(); println!("{:?}", a); } #[test] fn test_append_zero_size() { { let mut a = Array::::zeros((0, 0)); a.append(Axis(0), aview2(&[[]])).unwrap(); a.append(Axis(0), aview2(&[[]])).unwrap(); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &[2, 0]); } { let mut a = Array::::zeros((0, 0)); a.append(Axis(1), ArrayView1::from(&[]).into_shape_with_order((0, 1)).unwrap()) .unwrap(); a.append(Axis(1), ArrayView1::from(&[]).into_shape_with_order((0, 1)).unwrap()) .unwrap(); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &[0, 2]); } } #[test] fn push_row_neg_stride_3() { let mut a = Array::zeros((0, 4)); a.push_row(aview1(&[0., 1., 2., 3.])).unwrap(); a.invert_axis(Axis(1)); a.push_row(aview1(&[4., 5., 6., 7.])).unwrap(); assert_eq!(a.shape(), &[2, 4]); assert_eq!(a, array![[3., 2., 1., 0.], [4., 5., 6., 7.]]); assert_eq!(a.strides(), &[4, -1]); } #[test] fn push_row_ignore_strides_length_one_axes() { let strides = &[0, 1, 10, 20]; for invert in &[vec![], vec![0], vec![1], vec![0, 1]] { for &stride0 in strides { for &stride1 in strides { let mut a = Array::from_shape_vec([1, 1].strides([stride0, stride1]), vec![0.]).unwrap(); for &ax in invert { a.invert_axis(Axis(ax)); } a.push_row(aview1(&[1.])).unwrap(); assert_eq!(a.shape(), &[2, 1]); assert_eq!(a, array![[0.], [1.]]); assert_eq!(a.stride_of(Axis(0)), 1); } } } } #[test] #[should_panic(expected = "IncompatibleShape")] fn zero_dimensional_error1() { let mut a = Array::zeros(()).into_dyn(); a.append(Axis(0), arr0(0).into_dyn().view()).unwrap(); } #[test] #[should_panic(expected = "IncompatibleShape")] fn zero_dimensional_error2() { let mut a = Array::zeros(()).into_dyn(); a.push(Axis(0), arr0(0).into_dyn().view()).unwrap(); } #[test] fn zero_dimensional_ok() { let mut a = Array::zeros(0); let one = aview0(&1); let two = aview0(&2); a.push(Axis(0), two).unwrap(); a.push(Axis(0), one).unwrap(); a.push(Axis(0), one).unwrap(); assert_eq!(a, array![2, 1, 1]); } ndarray-0.16.1/tests/array-construct.rs000064400000000000000000000165561046102023000162360ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use defmac::defmac; use ndarray::arr3; use ndarray::prelude::*; use ndarray::Zip; #[test] fn test_from_shape_fn() { let step = 3.1; let h = Array::from_shape_fn((5, 5), |(i, j)| f64::sin(i as f64 / step) * f64::cos(j as f64 / step)); assert_eq!(h.shape(), &[5, 5]); } #[test] fn test_dimension_zero() { let a: Array2 = Array2::from(vec![[], [], []]); assert_eq!((vec![0.; 0], None), a.into_raw_vec_and_offset()); let a: Array3 = Array3::from(vec![[[]], [[]], [[]]]); assert_eq!((vec![0.; 0], None), a.into_raw_vec_and_offset()); } #[test] #[cfg(feature = "approx")] fn test_arc_into_owned() { use approx::assert_abs_diff_ne; let a = Array2::from_elem((5, 5), 1.).into_shared(); let mut b = a.clone(); b.fill(0.); let mut c = b.into_owned(); c.fill(2.); // test that they are unshared assert_abs_diff_ne!(a, c, epsilon = 0.01); } #[test] fn test_arcarray_thread_safe() { fn is_send(_t: &T) {} fn is_sync(_t: &T) {} let a = Array2::from_elem((5, 5), 1.).into_shared(); is_send(&a); is_sync(&a); } #[test] fn test_from_fn_c0() { let a = Array::from_shape_fn((), |i| i); assert_eq!(a[()], ()); assert_eq!(a.len(), 1); assert_eq!(a.shape(), &[]); } #[test] fn test_from_fn_c1() { let a = Array::from_shape_fn(28, |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); } } #[test] fn test_from_fn_c() { let a = Array::from_shape_fn((4, 7), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); } } #[test] fn test_from_fn_c3() { let a = Array::from_shape_fn((4, 3, 7), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); } } #[test] fn test_from_fn_f0() { let a = Array::from_shape_fn(().f(), |i| i); assert_eq!(a[()], ()); assert_eq!(a.len(), 1); assert_eq!(a.shape(), &[]); } #[test] fn test_from_fn_f1() { let a = Array::from_shape_fn(28.f(), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); } } #[test] fn test_from_fn_f() { let a = Array::from_shape_fn((4, 7).f(), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); } } #[test] fn test_from_fn_f_with_zero() { defmac!(test_from_fn_f_with_zero shape => { let a = Array::from_shape_fn(shape.f(), |i| i); assert_eq!(a.len(), 0); assert_eq!(a.shape(), &shape); }); test_from_fn_f_with_zero!([0]); test_from_fn_f_with_zero!([0, 1]); test_from_fn_f_with_zero!([2, 0]); test_from_fn_f_with_zero!([0, 1, 2]); test_from_fn_f_with_zero!([2, 0, 1]); test_from_fn_f_with_zero!([1, 2, 0]); } #[test] fn test_from_fn_f3() { let a = Array::from_shape_fn((4, 2, 7).f(), |i| i); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt); } } #[test] fn deny_wraparound_from_vec() { let five = vec![0; 5]; let five_large = Array::from_shape_vec((3, 7, 29, 36760123, 823996703), five.clone()); println!("{:?}", five_large); assert!(five_large.is_err()); let six = Array::from_shape_vec(6, five.clone()); assert!(six.is_err()); } #[test] fn test_ones() { let mut a = Array::::zeros((2, 3, 4)); a.fill(1.0); let b = Array::::ones((2, 3, 4)); assert_eq!(a, b); } #[test] fn test_from_shape_empty_with_neg_stride() { // Issue #998, negative strides for an axis where it doesn't matter. let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let v = s[..12].to_vec(); let v_ptr = v.as_ptr(); let a = Array::from_shape_vec((2, 0, 2).strides((1, -4isize as usize, 2)), v).unwrap(); assert_eq!(a, arr3(&[[[0; 2]; 0]; 2])); assert_eq!(a.as_ptr(), v_ptr); } #[test] fn test_from_shape_with_neg_stride() { // Issue #998, negative strides for an axis where it doesn't matter. let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let v = s[..12].to_vec(); let v_ptr = v.as_ptr(); let a = Array::from_shape_vec((2, 1, 2).strides((1, -4isize as usize, 2)), v).unwrap(); assert_eq!(a, arr3(&[[[0, 2]], [[1, 3]]])); assert_eq!(a.as_ptr(), v_ptr); } #[test] fn test_from_shape_2_2_2_with_neg_stride() { // Issue #998, negative strides for an axis where it doesn't matter. let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let v = s[..12].to_vec(); let v_ptr = v.as_ptr(); let a = Array::from_shape_vec((2, 2, 2).strides((1, -4isize as usize, 2)), v).unwrap(); assert_eq!(a, arr3(&[[[4, 6], [0, 2]], [[5, 7], [1, 3]]])); assert_eq!(a.as_ptr(), v_ptr.wrapping_add(4)); } #[should_panic] #[test] fn deny_wraparound_zeros() { //2^64 + 5 = 18446744073709551621 = 3×7×29×36760123×823996703 (5 distinct prime factors) let _five_large = Array::::zeros((3, 7, 29, 36760123, 823996703)); } #[should_panic] #[test] fn deny_wraparound_reshape() { //2^64 + 5 = 18446744073709551621 = 3×7×29×36760123×823996703 (5 distinct prime factors) let five = Array::::zeros(5); let _five_large = five .into_shape_with_order((3, 7, 29, 36760123, 823996703)) .unwrap(); } #[should_panic] #[test] fn deny_wraparound_default() { let _five_large = Array::::default((3, 7, 29, 36760123, 823996703)); } #[should_panic] #[test] fn deny_wraparound_from_shape_fn() { let _five_large = Array::::from_shape_fn((3, 7, 29, 36760123, 823996703), |_| 0.); } #[should_panic] #[test] fn deny_wraparound_uninit() { let _five_large = Array::::uninit((3, 7, 29, 36760123, 823996703)); } #[should_panic] #[test] fn deny_slice_with_too_many_rows_to_arrayview2() { let _view = ArrayView2::from(&[[0u8; 0]; usize::MAX][..]); } #[should_panic] #[test] fn deny_slice_with_too_many_zero_sized_elems_to_arrayview2() { let _view = ArrayView2::from(&[[(); isize::MAX as usize]; isize::MAX as usize][..]); } #[should_panic] #[test] fn deny_slice_with_too_many_rows_to_arrayviewmut2() { let _view = ArrayViewMut2::from(&mut [[0u8; 0]; usize::MAX][..]); } #[should_panic] #[test] fn deny_slice_with_too_many_zero_sized_elems_to_arrayviewmut2() { let _view = ArrayViewMut2::from(&mut [[(); isize::MAX as usize]; isize::MAX as usize][..]); } #[test] fn maybe_uninit_1() { use std::mem::MaybeUninit; unsafe { // Array type Mat = Array; let mut a = Mat::uninit((10, 10)); a.mapv_inplace(|_| MaybeUninit::new(1.)); let a_init = a.assume_init(); assert_eq!(a_init, Array2::from_elem(a_init.dim(), 1.)); // ArcArray type ArcMat = ArcArray; let mut a = ArcMat::uninit((10, 10)); a.mapv_inplace(|_| MaybeUninit::new(1.)); let a2 = a.clone(); let a_init = a.assume_init(); assert_eq!(a_init, Array2::from_elem(a_init.dim(), 1.)); // ArrayView let av_init = a2.view().assume_init(); assert_eq!(av_init, Array2::from_elem(a_init.dim(), 1.)); // RawArrayViewMut let mut a = Mat::uninit((10, 10)); let v = a.raw_view_mut(); Zip::from(v).for_each(|ptr| *(*ptr).as_mut_ptr() = 1.); let u = a.raw_view_mut().assume_init(); Zip::from(u).for_each(|ptr| assert_eq!(*ptr, 1.)); } } ndarray-0.16.1/tests/array.rs000064400000000000000000002335061046102023000142100ustar 00000000000000#![allow(non_snake_case)] #![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use approx::assert_relative_eq; use defmac::defmac; #[allow(deprecated)] use itertools::{zip, Itertools}; use ndarray::indices; use ndarray::prelude::*; use ndarray::ErrorKind; use ndarray::{arr3, rcarr2}; use ndarray::{Slice, SliceInfo, SliceInfoElem}; use num_complex::Complex; use std::convert::TryFrom; macro_rules! assert_panics { ($body:expr) => { if let Ok(v) = ::std::panic::catch_unwind(|| $body) { panic!("assertion failed: should_panic; \ non-panicking result: {:?}", v); } }; ($body:expr, $($arg:tt)*) => { if let Ok(_) = ::std::panic::catch_unwind(|| $body) { panic!($($arg)*); } }; } #[test] fn test_matmul_arcarray() { let mut A = ArcArray::::zeros((2, 3)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; } let mut B = ArcArray::::zeros((3, 4)); for (i, elt) in B.iter_mut().enumerate() { *elt = i; } let c = A.dot(&B); println!("A = \n{:?}", A); println!("B = \n{:?}", B); println!("A x B = \n{:?}", c); unsafe { let result = ArcArray::from_shape_vec_unchecked((2, 4), vec![20, 23, 26, 29, 56, 68, 80, 92]); assert_eq!(c.shape(), result.shape()); assert!(c.iter().zip(result.iter()).all(|(a, b)| a == b)); assert!(c == result); } } #[allow(unused)] fn arrayview_shrink_lifetime<'a, 'b: 'a>(view: ArrayView1<'b, f64>) -> ArrayView1<'a, f64> { view.reborrow() } #[allow(unused)] fn arrayviewmut_shrink_lifetime<'a, 'b: 'a>(view: ArrayViewMut1<'b, f64>) -> ArrayViewMut1<'a, f64> { view.reborrow() } #[test] #[cfg(feature = "std")] fn test_mat_mul() { // smoke test, a big matrix multiplication of uneven size let (n, m) = (45, 33); let a = ArcArray::linspace(0., ((n * m) - 1) as f32, n as usize * m as usize) .into_shape_with_order((n, m)) .unwrap(); let b = ArcArray::eye(m); assert_eq!(a.dot(&b), a); let c = ArcArray::eye(n); assert_eq!(c.dot(&a), a); } #[deny(unsafe_code)] #[test] fn test_slice() { let mut A = ArcArray::::zeros((3, 4, 5)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; } let vi = A.slice(s![1.., ..;2, NewAxis, Slice::new(0, None, 2)]); assert_eq!(vi.shape(), &[2, 2, 1, 3]); let vi = A.slice(s![.., .., ..]); assert_eq!(vi.shape(), A.shape()); assert!(vi.iter().zip(A.iter()).all(|(a, b)| a == b)); } #[deny(unsafe_code)] #[test] fn test_slice_ix0() { let arr = arr0(5); assert_eq!(arr.slice(s![]), aview0(&5)); } #[test] fn test_slice_edge_cases() { let mut arr = Array3::::zeros((3, 4, 5)); arr.slice_collapse(s![0..0;-1, .., ..]); assert_eq!(arr.shape(), &[0, 4, 5]); let mut arr = Array2::::from_shape_vec((1, 1).strides((10, 1)), vec![5]).unwrap(); arr.slice_collapse(s![1..1, ..]); assert_eq!(arr.shape(), &[0, 1]); } #[test] fn test_slice_inclusive_range() { let arr = array![[1, 2, 3], [4, 5, 6]]; assert_eq!(arr.slice(s![1..=1, 1..=2]), array![[5, 6]]); assert_eq!(arr.slice(s![1..=-1, -2..=2;-1]), array![[6, 5]]); assert_eq!(arr.slice(s![0..=-1, 0..=2;2]), array![[1, 3], [4, 6]]); } /// Test that the compiler can infer a type for a sliced array from the /// arguments to `s![]`. /// /// This test relies on the fact that `.dot()` is implemented for both /// `ArrayView1` and `ArrayView2`, so the compiler needs to determine which /// type is the correct result for the `.slice()` call. #[test] fn test_slice_infer() { let a = array![1., 2.]; let b = array![[3., 4.], [5., 6.]]; b.slice(s![..-1, ..]).dot(&a); // b.slice(s![0, ..]).dot(&a); } #[test] fn test_slice_with_many_dim() { let mut A = ArcArray::::zeros(&[3, 1, 4, 1, 3, 2, 1][..]); for (i, elt) in A.iter_mut().enumerate() { *elt = i; } let vi = A.slice(s![..2, NewAxis, .., ..;2, NewAxis, ..1, ..1, 1.., ..]); let new_shape = &[2, 1, 1, 2, 1, 1, 1, 1, 1][..]; assert_eq!(vi.shape(), new_shape); let correct = array![ [A[&[0, 0, 0, 0, 0, 1, 0][..]], A[&[0, 0, 2, 0, 0, 1, 0][..]]], [A[&[1, 0, 0, 0, 0, 1, 0][..]], A[&[1, 0, 2, 0, 0, 1, 0][..]]] ] .into_shape_with_order(new_shape) .unwrap(); assert_eq!(vi, correct); let vi = A.slice(s![..2, 0, ..;2, 0, 0, 1, 0]); assert_eq!(vi.shape(), &[2, 2][..]); let correct = array![ [A[&[0, 0, 0, 0, 0, 1, 0][..]], A[&[0, 0, 2, 0, 0, 1, 0][..]]], [A[&[1, 0, 0, 0, 0, 1, 0][..]], A[&[1, 0, 2, 0, 0, 1, 0][..]]] ]; assert_eq!(vi, correct); } #[test] fn test_slice_range_variable() { let range = 1..4; let arr = array![0, 1, 2, 3, 4]; assert_eq!(arr.slice(s![range]), array![1, 2, 3]); } #[test] fn test_slice_args_eval_range_once() { let mut eval_count = 0; { let mut range = || { eval_count += 1; 1..4 }; let arr = array![0, 1, 2, 3, 4]; assert_eq!(arr.slice(s![range()]), array![1, 2, 3]); } assert_eq!(eval_count, 1); } #[test] fn test_slice_args_eval_step_once() { let mut eval_count = 0; { let mut step = || { eval_count += 1; -1 }; let arr = array![0, 1, 2, 3, 4]; assert_eq!(arr.slice(s![1..4;step()]), array![3, 2, 1]); } assert_eq!(eval_count, 1); } #[test] fn test_slice_array_fixed() { let mut arr = Array3::::zeros((5, 2, 5)); let info = s![1.., 1, NewAxis, ..;2]; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = s![1.., 1, ..;2]; arr.view().slice_collapse(info2); } #[test] fn test_slice_dyninput_array_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = s![1.., 1, NewAxis, ..;2]; arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = s![1.., 1, ..;2]; arr.view().slice_collapse(info2); } #[test] fn test_slice_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)); let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(NewAxis), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); } #[test] fn test_slice_dyninput_array_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = SliceInfo::<_, Ix3, IxDyn>::try_from([ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(NewAxis), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); } #[test] fn test_slice_dyninput_vec_fixed() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, Ix3, Ix3>::try_from(vec![ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(NewAxis), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, Ix2>::try_from(vec![ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); } #[test] fn test_slice_dyninput_vec_dyn() { let mut arr = Array3::::zeros((5, 2, 5)).into_dyn(); let info = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(NewAxis), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.slice(info); arr.slice_mut(info); arr.view().slice_move(info); let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from(vec![ SliceInfoElem::from(1..), SliceInfoElem::from(1), SliceInfoElem::from(Slice::from(..).step_by(2)), ]) .unwrap(); arr.view().slice_collapse(info2); } #[test] fn test_slice_with_subview_and_new_axis() { let mut arr = ArcArray::::zeros((3, 5, 4)); for (i, elt) in arr.iter_mut().enumerate() { *elt = i; } let vi = arr.slice(s![NewAxis, 1.., 2, ..;2]); assert_eq!(vi.shape(), &[1, 2, 2]); assert!(vi .iter() .zip( arr.index_axis(Axis(1), 2) .slice(s![1.., ..;2]) .insert_axis(Axis(0)) .iter() ) .all(|(a, b)| a == b)); let vi = arr.slice(s![1, NewAxis, 2, ..;2]); assert_eq!(vi.shape(), &[1, 2]); assert!(vi .iter() .zip( arr.index_axis(Axis(0), 1) .index_axis(Axis(0), 2) .slice(s![..;2]) .insert_axis(Axis(0)) .iter() ) .all(|(a, b)| a == b)); let vi = arr.slice(s![1, 2, 3]); assert_eq!(vi.shape(), &[]); assert_eq!(vi, Array0::from_elem((), arr[(1, 2, 3)])); } #[test] fn test_slice_collapse_with_indices() { let mut arr = ArcArray::::zeros((3, 5, 4)); for (i, elt) in arr.iter_mut().enumerate() { *elt = i; } { let mut vi = arr.view(); vi.slice_collapse(s![1.., 2, ..;2]); assert_eq!(vi.shape(), &[2, 1, 2]); assert!(vi .iter() .zip(arr.slice(s![1.., 2..3, ..;2]).iter()) .all(|(a, b)| a == b)); let mut vi = arr.view(); vi.slice_collapse(s![1, 2, ..;2]); assert_eq!(vi.shape(), &[1, 1, 2]); assert!(vi .iter() .zip(arr.slice(s![1..2, 2..3, ..;2]).iter()) .all(|(a, b)| a == b)); let mut vi = arr.view(); vi.slice_collapse(s![1, 2, 3]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), arr[(1, 2, 3)])); } // Do it to the ArcArray itself let elem = arr[(1, 2, 3)]; let mut vi = arr; vi.slice_collapse(s![1, 2, 3]); assert_eq!(vi.shape(), &[1, 1, 1]); assert_eq!(vi, Array3::from_elem((1, 1, 1), elem)); } #[test] #[should_panic] fn test_slice_collapse_with_newaxis() { let mut arr = Array2::::zeros((2, 3)); arr.slice_collapse(s![0, 0, NewAxis]); } #[test] fn test_multislice() { macro_rules! do_test { ($arr:expr, $($s:expr),*) => { { let arr = $arr; let copy = arr.clone(); assert_eq!( arr.multi_slice_mut(($($s,)*)), ($(copy.clone().slice_mut($s),)*) ); } }; } let mut arr = Array1::from_iter(0..48) .into_shape_with_order((8, 6)) .unwrap(); assert_eq!( (arr.clone().view_mut(),), arr.multi_slice_mut((s![.., ..],)), ); assert_eq!(arr.multi_slice_mut(()), ()); do_test!(&mut arr, s![0, ..]); do_test!(&mut arr, s![0, ..], s![1, ..]); do_test!(&mut arr, s![0, ..], s![-1, ..]); do_test!(&mut arr, s![0, ..], s![1.., ..]); do_test!(&mut arr, s![1, ..], s![..;2, ..]); do_test!(&mut arr, s![..2, ..], s![2.., ..]); do_test!(&mut arr, s![1..;2, ..], s![..;2, ..]); do_test!(&mut arr, s![..;-2, ..], s![..;2, ..]); do_test!(&mut arr, s![..;12, ..], s![3..;3, ..]); do_test!(&mut arr, s![3, ..], s![..-1;-2, ..]); do_test!(&mut arr, s![0, ..], s![1, ..], s![2, ..]); do_test!(&mut arr, s![0, ..], s![1, ..], s![2, ..], s![3, ..]); } #[test] fn test_multislice_intersecting() { assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![3, .., NewAxis], s![3, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![3, ..], s![3.., ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![3, ..], s![..;3, NewAxis, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![..;6, ..], s![3..;3, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![2, ..], s![..-1;-2, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![4, ..], s![3, ..], s![3, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![3, ..], s![4, ..], s![3, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![3, ..], s![3, ..], s![4, ..])); }); assert_panics!({ let mut arr = Array2::::zeros((8, 6)); arr.multi_slice_mut((s![3, ..], s![3, ..], s![4, ..], s![3, ..])); }); } #[should_panic] #[test] fn index_out_of_bounds() { let mut a = Array::::zeros((3, 4)); a[[3, 2]] = 1; } #[should_panic] #[test] fn slice_oob() { let a = ArcArray::::zeros((3, 4)); let _vi = a.slice(s![..10, ..]); } #[should_panic] #[test] fn slice_axis_oob() { let a = ArcArray::::zeros((3, 4)); let _vi = a.slice_axis(Axis(0), Slice::new(0, Some(10), 1)); } #[should_panic] #[test] fn slice_wrong_dim() { let a = ArcArray::::zeros(vec![3, 4, 5]); let _vi = a.slice(s![.., ..]); } #[test] fn test_index() { let mut A = ArcArray::::zeros((2, 3)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; } #[allow(deprecated)] for ((i, j), a) in zip(indices((2, 3)), &A) { assert_eq!(*a, A[[i, j]]); } let vi = A.slice(s![1.., ..;2]); let mut it = vi.iter(); #[allow(deprecated)] for ((i, j), x) in zip(indices((1, 2)), &mut it) { assert_eq!(*x, vi[[i, j]]); } assert!(it.next().is_none()); } #[test] fn test_index_arrays() { let a = Array1::from_iter(0..12); assert_eq!(a[1], a[[1]]); let v = a.view().into_shape_with_order((3, 4)).unwrap(); assert_eq!(a[1], v[[0, 1]]); let w = v.into_shape_with_order((2, 2, 3)).unwrap(); assert_eq!(a[1], w[[0, 0, 1]]); } #[test] #[allow(clippy::assign_op_pattern)] fn test_add() { let mut A = ArcArray::::zeros((2, 2)); for (i, elt) in A.iter_mut().enumerate() { *elt = i; } let B = A.clone(); A = A + &B; assert_eq!(A[[0, 0]], 0); assert_eq!(A[[0, 1]], 2); assert_eq!(A[[1, 0]], 4); assert_eq!(A[[1, 1]], 6); } #[test] fn test_multidim() { let mut mat = ArcArray::zeros(2 * 3 * 4 * 5 * 6) .into_shape_with_order((2, 3, 4, 5, 6)) .unwrap(); mat[(0, 0, 0, 0, 0)] = 22u8; { for (i, elt) in mat.iter_mut().enumerate() { *elt = i as u8; } } assert_eq!(mat.shape(), &[2, 3, 4, 5, 6]); } /* array([[[ 7, 6], [ 5, 4], [ 3, 2], [ 1, 0]], [[15, 14], [13, 12], [11, 10], [ 9, 8]]]) */ #[test] fn test_negative_stride_arcarray() { let mut mat = ArcArray::zeros((2, 4, 2)); mat[[0, 0, 0]] = 1.0f32; for (i, elt) in mat.iter_mut().enumerate() { *elt = i as f32; } { let vi = mat.slice(s![.., ..;-1, ..;-1]); assert_eq!(vi.shape(), &[2, 4, 2]); // Test against sequential iterator let seq = [7f32, 6., 5., 4., 3., 2., 1., 0., 15., 14., 13., 12., 11., 10., 9., 8.]; for (a, b) in vi.iter().zip(seq.iter()) { assert_eq!(*a, *b); } } { let vi = mat.slice(s![.., ..;-5, ..]); let seq = [6., 7., 14., 15.]; for (a, b) in vi.iter().zip(seq.iter()) { assert_eq!(*a, *b); } } } #[test] fn test_cow() { let mut mat = ArcArray::zeros((2, 2)); mat[[0, 0]] = 1; let n = mat.clone(); mat[[0, 1]] = 2; mat[[1, 0]] = 3; mat[[1, 1]] = 4; assert_eq!(mat[[0, 0]], 1); assert_eq!(mat[[0, 1]], 2); assert_eq!(n[[0, 0]], 1); assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); let mut rev = mat.into_shape_with_order(4).unwrap(); rev.slice_collapse(s![..;-1]); assert_eq!(rev[0], 4); assert_eq!(rev[1], 3); assert_eq!(rev[2], 2); assert_eq!(rev[3], 1); let before = rev.clone(); // mutation rev[0] = 5; assert_eq!(rev[0], 5); assert_eq!(rev[1], 3); assert_eq!(rev[2], 2); assert_eq!(rev[3], 1); assert_eq!(before[0], 4); assert_eq!(before[1], 3); assert_eq!(before[2], 2); assert_eq!(before[3], 1); } #[test] fn test_cow_shrink() { // A test for clone-on-write in the case that // mutation shrinks the array and gives it different strides // let mut mat = ArcArray::zeros((2, 3)); //mat.slice_collapse(s![.., ..;2]); mat[[0, 0]] = 1; let n = mat.clone(); mat[[0, 1]] = 2; mat[[0, 2]] = 3; mat[[1, 0]] = 4; mat[[1, 1]] = 5; mat[[1, 2]] = 6; assert_eq!(mat[[0, 0]], 1); assert_eq!(mat[[0, 1]], 2); assert_eq!(n[[0, 0]], 1); assert_eq!(n[[0, 1]], 0); assert_eq!(n.get((0, 1)), Some(&0)); // small has non-C strides this way let mut small = mat.into_shape_with_order(6).unwrap(); small.slice_collapse(s![4..;-1]); assert_eq!(small[0], 6); assert_eq!(small[1], 5); let before = small.clone(); // mutation // small gets back C strides in CoW. small[1] = 9; assert_eq!(small[0], 6); assert_eq!(small[1], 9); assert_eq!(before[0], 6); assert_eq!(before[1], 5); } #[test] #[cfg(feature = "std")] fn test_sub() { let mat = ArcArray::linspace(0., 15., 16) .into_shape_with_order((2, 4, 2)) .unwrap(); let s1 = mat.index_axis(Axis(0), 0); let s2 = mat.index_axis(Axis(0), 1); assert_eq!(s1.shape(), &[4, 2]); assert_eq!(s2.shape(), &[4, 2]); let n = ArcArray::linspace(8., 15., 8) .into_shape_with_order((4, 2)) .unwrap(); assert_eq!(n, s2); let m = ArcArray::from(vec![2., 3., 10., 11.]) .into_shape_with_order((2, 2)) .unwrap(); assert_eq!(m, mat.index_axis(Axis(1), 1)); } #[should_panic] #[test] #[cfg(feature = "std")] fn test_sub_oob_1() { let mat = ArcArray::linspace(0., 15., 16) .into_shape_with_order((2, 4, 2)) .unwrap(); mat.index_axis(Axis(0), 2); } #[test] #[cfg(feature = "approx")] fn test_select() { use approx::assert_abs_diff_eq; // test for 2-d array let x = arr2(&[[0., 1.], [1., 0.], [1., 0.], [1., 0.], [1., 0.], [0., 1.], [0., 1.]]); let r = x.select(Axis(0), &[1, 3, 5]); let c = x.select(Axis(1), &[1]); let r_target = arr2(&[[1., 0.], [1., 0.], [0., 1.]]); let c_target = arr2(&[[1., 0., 0., 0., 0., 1., 1.]]); assert_abs_diff_eq!(r, r_target); assert_abs_diff_eq!(c, c_target.t()); // test for 3-d array let y = arr3(&[[[1., 2., 3.], [1.5, 1.5, 3.]], [[1., 2., 8.], [1., 2.5, 3.]]]); let r = y.select(Axis(1), &[1]); let c = y.select(Axis(2), &[1]); let r_target = arr3(&[[[1.5, 1.5, 3.]], [[1., 2.5, 3.]]]); let c_target = arr3(&[[[2.], [1.5]], [[2.], [2.5]]]); assert_abs_diff_eq!(r, r_target); assert_abs_diff_eq!(c, c_target); } #[test] fn test_select_1d() { let x = arr1(&[0, 1, 2, 3, 4, 5, 6]); let r1 = x.select(Axis(0), &[1, 3, 4, 2, 2, 5]); assert_eq!(r1, arr1(&[1, 3, 4, 2, 2, 5])); // select nothing let r2 = x.select(Axis(0), &[]); assert_eq!(r2, arr1(&[])); // select nothing from empty let r3 = r2.select(Axis(0), &[]); assert_eq!(r3, arr1(&[])); } #[test] fn diag() { let d = arr2(&[[1., 2., 3.0f32]]).into_diag(); assert_eq!(d.dim(), 1); let a = arr2(&[[1., 2., 3.0f32], [0., 0., 0.]]); let d = a.view().into_diag(); assert_eq!(d.dim(), 2); let d = arr2::(&[[]]).into_diag(); assert_eq!(d.dim(), 0); let d = ArcArray::::zeros(()).into_diag(); assert_eq!(d.dim(), 1); } /// Check that the merged shape is correct. /// /// Note that this does not check the strides in the "merged" case! #[test] #[allow(clippy::cognitive_complexity)] fn merge_axes() { macro_rules! assert_merged { ($arr:expr, $slice:expr, $take:expr, $into:expr) => { let mut v = $arr.slice($slice); let merged_len = v.len_of(Axis($take)) * v.len_of(Axis($into)); assert!(v.merge_axes(Axis($take), Axis($into))); assert_eq!(v.len_of(Axis($take)), if merged_len == 0 { 0 } else { 1 }); assert_eq!(v.len_of(Axis($into)), merged_len); }; } macro_rules! assert_not_merged { ($arr:expr, $slice:expr, $take:expr, $into:expr) => { let mut v = $arr.slice($slice); let old_dim = v.raw_dim(); let old_strides = v.strides().to_owned(); assert!(!v.merge_axes(Axis($take), Axis($into))); assert_eq!(v.raw_dim(), old_dim); assert_eq!(v.strides(), &old_strides[..]); }; } let a = Array4::::zeros((3, 4, 5, 4)); assert_not_merged!(a, s![.., .., .., ..], 0, 0); assert_merged!(a, s![.., .., .., ..], 0, 1); assert_not_merged!(a, s![.., .., .., ..], 0, 2); assert_not_merged!(a, s![.., .., .., ..], 0, 3); assert_not_merged!(a, s![.., .., .., ..], 1, 0); assert_not_merged!(a, s![.., .., .., ..], 1, 1); assert_merged!(a, s![.., .., .., ..], 1, 2); assert_not_merged!(a, s![.., .., .., ..], 1, 3); assert_not_merged!(a, s![.., .., .., ..], 2, 1); assert_not_merged!(a, s![.., .., .., ..], 2, 2); assert_merged!(a, s![.., .., .., ..], 2, 3); assert_not_merged!(a, s![.., .., .., ..], 3, 0); assert_not_merged!(a, s![.., .., .., ..], 3, 1); assert_not_merged!(a, s![.., .., .., ..], 3, 2); assert_not_merged!(a, s![.., .., .., ..], 3, 3); assert_merged!(a, s![.., .., .., ..;2], 0, 1); assert_not_merged!(a, s![.., .., .., ..;2], 1, 0); assert_merged!(a, s![.., .., .., ..;2], 1, 2); assert_not_merged!(a, s![.., .., .., ..;2], 2, 1); assert_merged!(a, s![.., .., .., ..;2], 2, 3); assert_not_merged!(a, s![.., .., .., ..;2], 3, 2); assert_merged!(a, s![.., .., .., ..3], 0, 1); assert_not_merged!(a, s![.., .., .., ..3], 1, 0); assert_merged!(a, s![.., .., .., ..3], 1, 2); assert_not_merged!(a, s![.., .., .., ..3], 2, 1); assert_not_merged!(a, s![.., .., .., ..3], 2, 3); assert_merged!(a, s![.., .., ..;2, ..], 0, 1); assert_not_merged!(a, s![.., .., ..;2, ..], 1, 0); assert_not_merged!(a, s![.., .., ..;2, ..], 1, 2); assert_not_merged!(a, s![.., .., ..;2, ..], 2, 3); assert_merged!(a, s![.., ..;2, .., ..], 0, 1); assert_not_merged!(a, s![.., ..;2, .., ..], 1, 0); assert_not_merged!(a, s![.., ..;2, .., ..], 1, 2); assert_merged!(a, s![.., ..;2, .., ..], 2, 3); assert_not_merged!(a, s![.., ..;2, .., ..], 3, 2); let a = Array4::::zeros((3, 1, 5, 1).f()); assert_merged!(a, s![.., .., ..;2, ..], 0, 1); assert_merged!(a, s![.., .., ..;2, ..], 0, 3); assert_merged!(a, s![.., .., ..;2, ..], 1, 0); assert_merged!(a, s![.., .., ..;2, ..], 1, 1); assert_merged!(a, s![.., .., ..;2, ..], 1, 2); assert_merged!(a, s![.., .., ..;2, ..], 1, 3); assert_merged!(a, s![.., .., ..;2, ..], 2, 1); assert_merged!(a, s![.., .., ..;2, ..], 2, 3); assert_merged!(a, s![.., .., ..;2, ..], 3, 0); assert_merged!(a, s![.., .., ..;2, ..], 3, 1); assert_merged!(a, s![.., .., ..;2, ..], 3, 2); assert_merged!(a, s![.., .., ..;2, ..], 3, 3); let a = Array4::::zeros((3, 0, 5, 1)); assert_merged!(a, s![.., .., ..;2, ..], 0, 1); assert_merged!(a, s![.., .., ..;2, ..], 1, 1); assert_merged!(a, s![.., .., ..;2, ..], 2, 1); assert_merged!(a, s![.., .., ..;2, ..], 3, 1); assert_merged!(a, s![.., .., ..;2, ..], 1, 0); assert_merged!(a, s![.., .., ..;2, ..], 1, 2); assert_merged!(a, s![.., .., ..;2, ..], 1, 3); } #[test] fn swapaxes() { let mut a = arr2(&[[1., 2.], [3., 4.0f32]]); let b = arr2(&[[1., 3.], [2., 4.0f32]]); assert!(a != b); a.swap_axes(0, 1); assert_eq!(a, b); a.swap_axes(1, 1); assert_eq!(a, b); assert_eq!(a.as_slice_memory_order(), Some(&[1., 2., 3., 4.][..])); assert_eq!(b.as_slice_memory_order(), Some(&[1., 3., 2., 4.][..])); } #[test] fn permuted_axes() { let a = array![1].index_axis_move(Axis(0), 0); let permuted = a.view().permuted_axes([]); assert_eq!(a, permuted); let a = array![1]; let permuted = a.view().permuted_axes([0]); assert_eq!(a, permuted); let a = Array::from_iter(0..24) .into_shape_with_order((2, 3, 4)) .unwrap(); let permuted = a.view().permuted_axes([2, 1, 0]); for ((i0, i1, i2), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[(i2, i1, i0)]); } let permuted = a.view().into_dyn().permuted_axes(&[0, 2, 1][..]); for ((i0, i1, i2), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[&[i0, i2, i1][..]]); } let a = Array::from_iter(0..120) .into_shape_with_order((2, 3, 4, 5)) .unwrap(); let permuted = a.view().permuted_axes([1, 0, 3, 2]); for ((i0, i1, i2, i3), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[(i1, i0, i3, i2)]); } let permuted = a.view().into_dyn().permuted_axes(&[1, 2, 3, 0][..]); for ((i0, i1, i2, i3), elem) in a.indexed_iter() { assert_eq!(*elem, permuted[&[i1, i2, i3, i0][..]]); } } #[should_panic] #[test] fn permuted_axes_repeated_axis() { let a = Array::from_iter(0..24) .into_shape_with_order((2, 3, 4)) .unwrap(); a.view().permuted_axes([1, 0, 1]); } #[should_panic] #[test] fn permuted_axes_missing_axis() { let a = Array::from_iter(0..24) .into_shape_with_order((2, 3, 4)) .unwrap() .into_dyn(); a.view().permuted_axes(&[2, 0][..]); } #[should_panic] #[test] fn permuted_axes_oob() { let a = Array::from_iter(0..24) .into_shape_with_order((2, 3, 4)) .unwrap(); a.view().permuted_axes([1, 0, 3]); } #[test] fn standard_layout() { let mut a = arr2(&[[1., 2.], [3., 4.0]]); assert!(a.is_standard_layout()); a.swap_axes(0, 1); assert!(!a.is_standard_layout()); a.swap_axes(0, 1); assert!(a.is_standard_layout()); let x1 = a.index_axis(Axis(0), 0); assert!(x1.is_standard_layout()); let x2 = a.index_axis(Axis(1), 0); assert!(!x2.is_standard_layout()); let x3 = ArrayView1::from_shape(1.strides(2), &[1]).unwrap(); assert!(x3.is_standard_layout()); let x4 = ArrayView2::from_shape((0, 2).strides((0, 1)), &[1, 2]).unwrap(); assert!(x4.is_standard_layout()); } #[test] fn iter_size_hint() { let mut a = arr2(&[[1., 2.], [3., 4.]]); { let mut it = a.iter(); assert_eq!(it.size_hint(), (4, Some(4))); it.next(); assert_eq!(it.size_hint().0, 3); it.next(); assert_eq!(it.size_hint().0, 2); it.next(); assert_eq!(it.size_hint().0, 1); it.next(); assert_eq!(it.size_hint().0, 0); assert!(it.next().is_none()); assert_eq!(it.size_hint().0, 0); } a.swap_axes(0, 1); { let mut it = a.iter(); assert_eq!(it.size_hint(), (4, Some(4))); it.next(); assert_eq!(it.size_hint().0, 3); it.next(); assert_eq!(it.size_hint().0, 2); it.next(); assert_eq!(it.size_hint().0, 1); it.next(); assert_eq!(it.size_hint().0, 0); assert!(it.next().is_none()); assert_eq!(it.size_hint().0, 0); } } #[test] fn zero_axes() { let mut a = arr1::(&[]); for _ in a.iter() { panic!(); } a.map(|_| panic!()); a.map_inplace(|_| panic!()); a.for_each(|_| panic!()); println!("{:?}", a); let b = arr2::(&[[], [], [], []]); println!("{:?}\n{:?}", b.shape(), b); // we can even get a subarray of b let bsub = b.index_axis(Axis(0), 2); assert_eq!(bsub.dim(), 0); } #[test] fn equality() { let a = arr2(&[[1., 2.], [3., 4.]]); let mut b = arr2(&[[1., 2.], [2., 4.]]); assert!(a != b); b[(1, 0)] = 3.; assert!(a == b); // make sure we can compare different shapes without failure. let c = arr2(&[[1., 2.]]); assert!(a != c); } #[test] fn map1() { let a = arr2(&[[1., 2.], [3., 4.]]); let b = a.map(|&x| (x / 3.) as isize); assert_eq!(b, arr2(&[[0, 0], [1, 1]])); // test map to reference with array's lifetime. let c = a.map(|x| x); assert_eq!(a[(0, 0)], *c[(0, 0)]); } #[test] fn mapv_into_any_same_type() { let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; let a_plus_one: Array = array![[2., 3., 4.], [5., 6., 7.]]; assert_eq!(a.mapv_into_any(|a| a + 1.), a_plus_one); } #[test] fn mapv_into_any_diff_types() { let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; let a_even: Array = array![[false, true, false], [true, false, true]]; assert_eq!(a.mapv_into_any(|a| a.round() as i32 % 2 == 0), a_even); } #[test] fn as_slice_memory_order_mut_arcarray() { // Test that mutation breaks sharing for `ArcArray`. let a = rcarr2(&[[1., 2.], [3., 4.0f32]]); let mut b = a.clone(); for elt in b.as_slice_memory_order_mut().unwrap() { *elt = 0.; } assert!(a != b, "{:?} != {:?}", a, b); } #[test] fn as_slice_memory_order_mut_cowarray() { // Test that mutation breaks sharing for `CowArray`. let a = arr2(&[[1., 2.], [3., 4.0f32]]); let mut b = CowArray::from(a.view()); for elt in b.as_slice_memory_order_mut().unwrap() { *elt = 0.; } assert!(a != b, "{:?} != {:?}", a, b); } #[test] fn as_slice_memory_order_mut_contiguous_arcarray() { // Test that unsharing preserves the strides in the contiguous case for `ArcArray`. let a = rcarr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); let mut b = a.clone().slice_move(s![.., ..2]); assert_eq!(b.strides(), &[1, 2]); b.as_slice_memory_order_mut().unwrap(); assert_eq!(b.strides(), &[1, 2]); } #[test] fn as_slice_memory_order_mut_contiguous_cowarray() { // Test that unsharing preserves the strides in the contiguous case for `CowArray`. let a = arr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); let mut b = CowArray::from(a.slice(s![.., ..2])); assert!(b.is_view()); assert_eq!(b.strides(), &[1, 2]); b.as_slice_memory_order_mut().unwrap(); assert_eq!(b.strides(), &[1, 2]); } #[test] fn to_slice_memory_order() { for shape in vec![[2, 0, 3, 5], [2, 1, 3, 5], [2, 4, 3, 5]] { let data: Vec = (0..shape.iter().product()).collect(); let mut orig = Array1::from(data.clone()) .into_shape_with_order(shape) .unwrap(); for perm in vec![[0, 1, 2, 3], [0, 2, 1, 3], [2, 0, 1, 3]] { let mut a = orig.view_mut().permuted_axes(perm); assert_eq!(a.as_slice_memory_order().unwrap(), &data); assert_eq!(a.as_slice_memory_order_mut().unwrap(), &data); assert_eq!(a.view().to_slice_memory_order().unwrap(), &data); assert_eq!(a.view_mut().into_slice_memory_order().unwrap(), &data); } } } #[test] fn to_slice_memory_order_discontiguous() { let mut orig = Array3::::zeros([3, 2, 4]); assert!(orig .slice(s![.., 1.., ..]) .as_slice_memory_order() .is_none()); assert!(orig .slice_mut(s![.., 1.., ..]) .as_slice_memory_order_mut() .is_none()); assert!(orig .slice(s![.., 1.., ..]) .to_slice_memory_order() .is_none()); assert!(orig .slice_mut(s![.., 1.., ..]) .into_slice_memory_order() .is_none()); } #[test] fn array0_into_scalar() { // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); let a_ptr = a.as_ptr(); let (raw_vec, offset) = a.into_raw_vec_and_offset(); assert_ne!(a_ptr, raw_vec.as_ptr()); assert_eq!(offset, Some(2)); // `.into_scalar()` should still work correctly. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.into_scalar(), 6); // It should work for zero-size elements too. let a: Array0<()> = array![(), (), (), ()].index_axis_move(Axis(0), 2); assert_eq!(a.into_scalar(), ()); } #[test] fn array_view0_into_scalar() { // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); let a_ptr = a.as_ptr(); let (raw_vec, offset) = a.into_raw_vec_and_offset(); assert_ne!(a_ptr, raw_vec.as_ptr()); assert_eq!(offset, Some(2)); // `.into_scalar()` should still work correctly. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.view().into_scalar(), &6); // It should work for zero-size elements too. let a: Array0<()> = array![(), (), (), ()].index_axis_move(Axis(0), 2); assert_eq!(a.view().into_scalar(), &()); } #[test] fn array_view_mut0_into_scalar() { // With this kind of setup, the `Array`'s pointer is not the same as the // underlying `Vec`'s pointer. let a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_ne!(a.as_ptr(), a.into_raw_vec_and_offset().0.as_ptr()); // `.into_scalar()` should still work correctly. let mut a: Array0 = array![4, 5, 6, 7].index_axis_move(Axis(0), 2); assert_eq!(a.view_mut().into_scalar(), &6); // It should work for zero-size elements too. let mut a: Array0<()> = array![(), (), (), ()].index_axis_move(Axis(0), 2); assert_eq!(a.view_mut().into_scalar(), &()); } #[test] fn array1_into_raw_vec() { let data = vec![4, 5, 6, 7]; let array = Array::from(data.clone()); let (raw_vec, offset) = array.into_raw_vec_and_offset(); assert_eq!(data, raw_vec); assert_eq!(offset, Some(0)); } #[test] fn owned_array1() { let mut a = Array::from(vec![1, 2, 3, 4]); for elt in a.iter_mut() { *elt = 2; } for elt in a.iter() { assert_eq!(*elt, 2); } assert_eq!(a.shape(), &[4]); let mut a = Array::zeros((2, 2)); let mut b = ArcArray::zeros((2, 2)); a[(1, 1)] = 3; b[(1, 1)] = 3; assert_eq!(a, b); let c = a.clone(); let d1 = &a + &b; let d2 = a + b; assert!(c != d1); assert_eq!(d1, d2); } #[test] fn owned_array_with_stride() { let v: Vec<_> = (0..12).collect(); let dim = (2, 3, 2); let strides = (1, 4, 2); let a = Array::from_shape_vec(dim.strides(strides), v).unwrap(); assert_eq!(a.strides(), &[1, 4, 2]); } #[test] fn owned_array_discontiguous() { use std::iter::repeat; let v: Vec<_> = (0..12).flat_map(|x| repeat(x).take(2)).collect(); let dim = (3, 2, 2); let strides = (8, 4, 2); let a = Array::from_shape_vec(dim.strides(strides), v).unwrap(); assert_eq!(a.strides(), &[8, 4, 2]); println!("{:?}", a.iter().cloned().collect::>()); itertools::assert_equal(a.iter().cloned(), 0..12); } #[test] fn owned_array_discontiguous_drop() { use std::cell::RefCell; use std::collections::BTreeSet; use std::rc::Rc; struct InsertOnDrop(Rc>>, Option); impl Drop for InsertOnDrop { fn drop(&mut self) { let InsertOnDrop(ref set, ref mut value) = *self; set.borrow_mut().insert(value.take().expect("double drop!")); } } let set = Rc::new(RefCell::new(BTreeSet::new())); { let v: Vec<_> = (0..12) .map(|x| InsertOnDrop(set.clone(), Some(x))) .collect(); let mut a = Array::from_shape_vec((2, 6), v).unwrap(); // discontiguous and non-zero offset a.slice_collapse(s![.., 1..]); } // each item was dropped exactly once itertools::assert_equal(set.borrow().iter().cloned(), 0..12); } macro_rules! assert_matches { ($value:expr, $pat:pat) => { match $value { $pat => {} ref err => panic!( "assertion failed: `{}` matches `{}` found: {:?}", stringify!($value), stringify!($pat), err ), } }; } #[test] fn from_vec_dim_stride_empty_1d() { let empty: [f32; 0] = []; assert_matches!(Array::from_shape_vec(0.strides(1), empty.to_vec()), Ok(_)); } #[test] fn from_vec_dim_stride_0d() { let empty: [f32; 0] = []; let one = [1.]; let two = [1., 2.]; // too few elements assert_matches!( Array::from_shape_vec(().strides(()), empty.to_vec()), Err(_) ); // exact number of elements assert_matches!(Array::from_shape_vec(().strides(()), one.to_vec()), Ok(_)); // too many are ok assert_matches!(Array::from_shape_vec(().strides(()), two.to_vec()), Ok(_)); } #[test] fn from_vec_dim_stride_2d_1() { let two = [1., 2.]; let d = Ix2(2, 1); let s = d.default_strides(); assert_matches!(Array::from_shape_vec(d.strides(s), two.to_vec()), Ok(_)); } #[test] fn from_vec_dim_stride_2d_2() { let two = [1., 2.]; let d = Ix2(1, 2); let s = d.default_strides(); assert_matches!(Array::from_shape_vec(d.strides(s), two.to_vec()), Ok(_)); } #[test] fn from_vec_dim_stride_2d_3() { let a = arr3(&[[[1]], [[2]], [[3]]]); let d = a.raw_dim(); let s = d.default_strides(); assert_matches!( Array::from_shape_vec(d.strides(s), a.as_slice().unwrap().to_vec()), Ok(_) ); } #[test] fn from_vec_dim_stride_2d_4() { let a = arr3(&[[[1]], [[2]], [[3]]]); let d = a.raw_dim(); let s = d.fortran_strides(); assert_matches!( Array::from_shape_vec(d.strides(s), a.as_slice().unwrap().to_vec()), Ok(_) ); } #[test] fn from_vec_dim_stride_2d_5() { let a = arr3(&[[[1, 2, 3]]]); let d = a.raw_dim(); let s = d.fortran_strides(); assert_matches!( Array::from_shape_vec(d.strides(s), a.as_slice().unwrap().to_vec()), Ok(_) ); } #[test] fn from_vec_dim_stride_2d_6() { let a = [1., 2., 3., 4., 5., 6.]; let d = (2, 1, 1); let s = (2, 2, 1); assert_matches!(Array::from_shape_vec(d.strides(s), a.to_vec()), Ok(_)); let d = (1, 2, 1); let s = (2, 2, 1); assert_matches!(Array::from_shape_vec(d.strides(s), a.to_vec()), Ok(_)); } #[test] fn from_vec_dim_stride_2d_7() { // empty arrays can have 0 strides let a: [f32; 0] = []; // [[]] shape=[4, 0], strides=[0, 1] let d = (4, 0); let s = (0, 1); assert_matches!(Array::from_shape_vec(d.strides(s), a.to_vec()), Ok(_)); } #[test] fn from_vec_dim_stride_2d_8() { // strides of length 1 axes can be zero let a = [1.]; let d = (1, 1); let s = (0, 1); assert_matches!(Array::from_shape_vec(d.strides(s), a.to_vec()), Ok(_)); } #[test] fn from_vec_dim_stride_2d_rejects() { let two = [1., 2.]; let d = (2, 2); let s = (1, 0); assert_matches!(Array::from_shape_vec(d.strides(s), two.to_vec()), Err(_)); let d = (2, 2); let s = (0, 1); assert_matches!(Array::from_shape_vec(d.strides(s), two.to_vec()), Err(_)); } #[test] fn views() { let a = ArcArray::from(vec![1, 2, 3, 4]) .into_shape_with_order((2, 2)) .unwrap(); let b = a.view(); assert_eq!(a, b); assert_eq!(a.shape(), b.shape()); assert_eq!(a.clone() + a.clone(), &b + &b); assert_eq!(a.clone() + b, &b + &b); a.clone()[(0, 0)] = 99; assert_eq!(b[(0, 0)], 1); assert_eq!( a.view().into_iter().cloned().collect::>(), vec![1, 2, 3, 4] ); } #[test] fn view_mut() { let mut a = ArcArray::from(vec![1, 2, 3, 4]) .into_shape_with_order((2, 2)) .unwrap(); for elt in &mut a.view_mut() { *elt = 0; } assert_eq!(a, Array::zeros((2, 2))); { let mut b = a.view_mut(); b[(0, 0)] = 7; } assert_eq!(a[(0, 0)], 7); for elt in a.view_mut() { *elt = 2; } assert_eq!(a, ArcArray::from_elem((2, 2), 2)); } #[test] fn slice_mut() { let mut a = ArcArray::from(vec![1, 2, 3, 4]) .into_shape_with_order((2, 2)) .unwrap(); for elt in a.slice_mut(s![.., ..]) { *elt = 0; } assert_eq!(a, aview2(&[[0, 0], [0, 0]])); let mut b = arr2(&[[1, 2, 3], [4, 5, 6]]); let c = b.clone(); // make sure we can mutate b even if it has to be unshared first for elt in b.slice_mut(s![.., ..1]) { *elt = 0; } assert_eq!(b, aview2(&[[0, 2, 3], [0, 5, 6]])); assert!(c != b); for elt in b.slice_mut(s![.., ..;2]) { *elt = 99; } assert_eq!(b, aview2(&[[99, 2, 99], [99, 5, 99]])); } #[test] fn assign_ops() { let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = arr2(&[[1., 3.], [2., 4.]]); (*&mut a.view_mut()) += &b; assert_eq!(a, arr2(&[[2., 5.], [5., 8.]])); a -= &b; a -= &b; assert_eq!(a, arr2(&[[0., -1.,], [1., 0.]])); a += 1.; assert_eq!(a, arr2(&[[1., 0.,], [2., 1.]])); a *= 10.; a /= 5.; assert_eq!(a, arr2(&[[2., 0.,], [4., 2.]])); } #[test] fn aview() { let a = arr2(&[[1., 2., 3.], [4., 5., 6.]]); let data = [[1., 2., 3.], [4., 5., 6.]]; let b = aview2(&data); assert_eq!(a, b); assert_eq!(b.shape(), &[2, 3]); } #[test] fn aview_mut() { let mut data = [0; 16]; { let mut a = aview_mut1(&mut data).into_shape_with_order((4, 4)).unwrap(); { let mut slc = a.slice_mut(s![..2, ..;2]); slc += 1; } } assert_eq!(data, [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]); } #[test] fn transpose_view() { let a = arr2(&[[1, 2], [3, 4]]); let at = a.view().reversed_axes(); assert_eq!(at, arr2(&[[1, 3], [2, 4]])); let a = arr2(&[[1, 2, 3], [4, 5, 6]]); let at = a.view().reversed_axes(); assert_eq!(at, arr2(&[[1, 4], [2, 5], [3, 6]])); } #[test] fn transpose_view_mut() { let mut a = arr2(&[[1, 2], [3, 4]]); let mut at = a.view_mut().reversed_axes(); at[[0, 1]] = 5; assert_eq!(at, arr2(&[[1, 5], [2, 4]])); let mut a = arr2(&[[1, 2, 3], [4, 5, 6]]); let mut at = a.view_mut().reversed_axes(); at[[2, 1]] = 7; assert_eq!(at, arr2(&[[1, 4], [2, 5], [3, 7]])); } #[test] #[allow(clippy::cognitive_complexity)] fn insert_axis() { defmac!(test_insert orig, index, new => { let res = orig.insert_axis(Axis(index)); assert_eq!(res, new); assert!(res.is_standard_layout()); }); let v = 1; test_insert!(aview0(&v), 0, arr1(&[1])); assert!(::std::panic::catch_unwind(|| aview0(&v).insert_axis(Axis(1))).is_err()); test_insert!(arr1(&[1, 2, 3]), 0, arr2(&[[1, 2, 3]])); test_insert!(arr1(&[1, 2, 3]), 1, arr2(&[[1], [2], [3]])); assert!(::std::panic::catch_unwind(|| arr1(&[1, 2, 3]).insert_axis(Axis(2))).is_err()); test_insert!( arr2(&[[1, 2, 3], [4, 5, 6]]), 0, arr3(&[[[1, 2, 3], [4, 5, 6]]]) ); test_insert!( arr2(&[[1, 2, 3], [4, 5, 6]]), 1, arr3(&[[[1, 2, 3]], [[4, 5, 6]]]) ); test_insert!( arr2(&[[1, 2, 3], [4, 5, 6]]), 2, arr3(&[[[1], [2], [3]], [[4], [5], [6]]]) ); assert!( ::std::panic::catch_unwind(|| arr2(&[[1, 2, 3], [4, 5, 6]]).insert_axis(Axis(3))).is_err() ); test_insert!( Array3::::zeros((3, 4, 5)), 0, Array4::::zeros((1, 3, 4, 5)) ); test_insert!( Array3::::zeros((3, 4, 5)), 1, Array4::::zeros((3, 1, 4, 5)) ); test_insert!( Array3::::zeros((3, 4, 5)), 3, Array4::::zeros((3, 4, 5, 1)) ); assert!( ::std::panic::catch_unwind(|| Array3::::zeros((3, 4, 5)).insert_axis(Axis(4))).is_err() ); test_insert!( Array6::::zeros((2, 3, 4, 3, 2, 3)), 0, ArrayD::::zeros(vec![1, 2, 3, 4, 3, 2, 3]) ); test_insert!( Array6::::zeros((2, 3, 4, 3, 2, 3)), 3, ArrayD::::zeros(vec![2, 3, 4, 1, 3, 2, 3]) ); test_insert!( Array6::::zeros((2, 3, 4, 3, 2, 3)), 6, ArrayD::::zeros(vec![2, 3, 4, 3, 2, 3, 1]) ); assert!(::std::panic::catch_unwind( || Array6::::zeros((2, 3, 4, 3, 2, 3)).insert_axis(Axis(7)) ) .is_err()); test_insert!( ArrayD::::zeros(vec![3, 4, 5]), 0, ArrayD::::zeros(vec![1, 3, 4, 5]) ); test_insert!( ArrayD::::zeros(vec![3, 4, 5]), 1, ArrayD::::zeros(vec![3, 1, 4, 5]) ); test_insert!( ArrayD::::zeros(vec![3, 4, 5]), 3, ArrayD::::zeros(vec![3, 4, 5, 1]) ); assert!( ::std::panic::catch_unwind(|| ArrayD::::zeros(vec![3, 4, 5]).insert_axis(Axis(4))) .is_err() ); } #[test] fn insert_axis_f() { defmac!(test_insert_f orig, index, new => { let res = orig.insert_axis(Axis(index)); assert_eq!(res, new); assert!(res.t().is_standard_layout()); }); test_insert_f!( Array0::from_shape_vec(().f(), vec![1]).unwrap(), 0, arr1(&[1]) ); assert!( ::std::panic::catch_unwind(|| Array0::from_shape_vec(().f(), vec![1]) .unwrap() .insert_axis(Axis(1))) .is_err() ); test_insert_f!(Array1::::zeros((3).f()), 0, Array2::::zeros((1, 3))); test_insert_f!(Array1::::zeros((3).f()), 1, Array2::::zeros((3, 1))); assert!( ::std::panic::catch_unwind(|| Array1::::zeros((3).f()).insert_axis(Axis(2))).is_err() ); test_insert_f!( Array3::::zeros((3, 4, 5).f()), 1, Array4::::zeros((3, 1, 4, 5)) ); assert!( ::std::panic::catch_unwind(|| Array3::::zeros((3, 4, 5).f()).insert_axis(Axis(4))) .is_err() ); test_insert_f!( ArrayD::::zeros(vec![3, 4, 5].f()), 1, ArrayD::::zeros(vec![3, 1, 4, 5]) ); assert!(::std::panic::catch_unwind( || ArrayD::::zeros(vec![3, 4, 5].f()).insert_axis(Axis(4)) ) .is_err()); } #[test] fn insert_axis_view() { let a = array![[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]; assert_eq!( a.index_axis(Axis(1), 0).insert_axis(Axis(0)), array![[[1, 2], [5, 6], [9, 10]]] ); assert_eq!( a.index_axis(Axis(1), 0).insert_axis(Axis(1)), array![[[1, 2]], [[5, 6]], [[9, 10]]] ); assert_eq!( a.index_axis(Axis(1), 0).insert_axis(Axis(2)), array![[[1], [2]], [[5], [6]], [[9], [10]]] ); } #[test] fn arithmetic_broadcast() { let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = a.clone() * aview0(&1.); assert_eq!(a, b); a.swap_axes(0, 1); let b = a.clone() / aview0(&1.); assert_eq!(a, b); // reference let a = arr2(&[[2], [3], [4]]); let b = arr1(&[5, 6, 7]); assert_eq!(&a + &b, arr2(&[[7, 8, 9], [8, 9, 10], [9, 10, 11]])); assert_eq!( a.clone() - &b, arr2(&[[-3, -4, -5], [-2, -3, -4], [-1, -2, -3]]) ); assert_eq!( a.clone() * b.clone(), arr2(&[[10, 12, 14], [15, 18, 21], [20, 24, 28]]) ); assert_eq!(&b / a, arr2(&[[2, 3, 3], [1, 2, 2], [1, 1, 1]])); // Negative strides and non-contiguous memory let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; let s = Array3::from_shape_vec((2, 3, 2).strides((1, 4, 2)), s.to_vec()).unwrap(); let a = s.slice(s![..;-1,..;2,..]); let b = s.slice(s![..2, -1, ..]); let mut c = s.clone(); c.collapse_axis(Axis(2), 1); let c = c.slice(s![1,..;2,..]); assert_eq!( &a.to_owned() + &b, arr3(&[[[11, 15], [20, 24]], [[10, 14], [19, 23]]]) ); assert_eq!( &a + b.into_owned() + c, arr3(&[[[15, 19], [32, 36]], [[14, 18], [31, 35]]]) ); // shared array let sa = a.to_shared(); let sa2 = sa.to_shared(); let sb = b.to_shared(); let sb2 = sb.to_shared(); let sc = c.to_shared(); let sc2 = sc.into_shared(); assert_eq!( sa2 + &sb2 + sc2.into_owned(), arr3(&[[[15, 19], [32, 36]], [[14, 18], [31, 35]]]) ); // Same shape let a = s.slice(s![..;-1, ..;2, ..]); let b = s.slice(s![.., ..;2, ..]); assert_eq!(a.shape(), b.shape()); assert_eq!(&a + &b, arr3(&[[[3, 7], [19, 23]], [[3, 7], [19, 23]]])); } #[test] fn char_array() { // test compilation & basics of non-numerical array let cc = ArcArray::from_iter("alphabet".chars()) .into_shape_with_order((4, 2)) .unwrap(); assert!(cc.index_axis(Axis(1), 0) == ArcArray::from_iter("apae".chars())); } #[test] fn scalar_ops() { let a = Array::::zeros((5, 5)); let b = &a + 1; let c = (&a + &a + 2) - 3; println!("{:?}", b); println!("{:?}", c); let a = Array::::zeros((2, 2)); let b = (1. + a) * 3.; assert_eq!(b, arr2(&[[3., 3.], [3., 3.]])); let a = arr1(&[false, true, true]); let b = &a ^ true; let c = true ^ &a; assert_eq!(b, c); assert_eq!(true & &a, a); assert_eq!(b, arr1(&[true, false, false])); assert_eq!(true ^ &a, !a); let zero = Array::::zeros((2, 2)); let one = &zero + 1.; assert_eq!(0. * &one, zero); assert_eq!(&one * 0., zero); assert_eq!((&one + &one).sum(), 8.); assert_eq!(&one / 2., 0.5 * &one); assert_eq!(&one % 1., zero); let zero = Array::::zeros((2, 2)); let one = &zero + 1; assert_eq!(one.clone() << 3, 8 * &one); assert_eq!(3 << one.clone(), 6 * &one); assert_eq!(&one << 3, 8 * &one); assert_eq!(3 << &one, 6 * &one); } #[test] #[cfg(feature = "std")] fn split_at() { let mut a = arr2(&[[1., 2.], [3., 4.]]); { let (c0, c1) = a.view().split_at(Axis(1), 1); assert_eq!(c0, arr2(&[[1.], [3.]])); assert_eq!(c1, arr2(&[[2.], [4.]])); } { let (mut r0, mut r1) = a.view_mut().split_at(Axis(0), 1); r0[[0, 1]] = 5.; r1[[0, 0]] = 8.; } assert_eq!(a, arr2(&[[1., 5.], [8., 4.]])); let b = ArcArray::linspace(0., 59., 60) .into_shape_with_order((3, 4, 5)) .unwrap(); let (left, right) = b.view().split_at(Axis(2), 2); assert_eq!(left.shape(), [3, 4, 2]); assert_eq!(right.shape(), [3, 4, 3]); assert_eq!( left, arr3(&[ [[0., 1.], [5., 6.], [10., 11.], [15., 16.]], [[20., 21.], [25., 26.], [30., 31.], [35., 36.]], [[40., 41.], [45., 46.], [50., 51.], [55., 56.]] ]) ); // we allow for an empty right view when index == dim[axis] let (_, right) = b.view().split_at(Axis(1), 4); assert_eq!(right.shape(), [3, 0, 5]); } #[test] #[should_panic] fn deny_split_at_axis_out_of_bounds() { let a = arr2(&[[1., 2.], [3., 4.]]); a.view().split_at(Axis(2), 0); } #[test] #[should_panic] fn deny_split_at_index_out_of_bounds() { let a = arr2(&[[1., 2.], [3., 4.]]); a.view().split_at(Axis(1), 3); } #[test] #[cfg(feature = "std")] fn test_range() { let a = Array::range(0., 5., 1.); assert_eq!(a.len(), 5); assert_eq!(a[0], 0.); assert_eq!(a[4], 4.); let b = Array::range(0., 2.2, 1.); assert_eq!(b.len(), 3); assert_eq!(b[0], 0.); assert_eq!(b[2], 2.); let c = Array::range(0., 5., 2.); assert_eq!(c.len(), 3); assert_eq!(c[0], 0.); assert_eq!(c[1], 2.); assert_eq!(c[2], 4.); let d = Array::range(1.0, 2.2, 0.1); assert_eq!(d.len(), 13); assert_eq!(d[0], 1.); assert_eq!(d[10], 2.); assert_eq!(d[12], 2.2); let e = Array::range(1., 1., 1.); assert_eq!(e.len(), 0); assert!(e.is_empty()); } #[test] fn test_f_order() { // Test that arrays are logically equal in every way, // even if the underlying memory order is different let c = arr2(&[[1, 2, 3], [4, 5, 6]]); let mut f = Array::zeros(c.dim().f()); f.assign(&c); assert_eq!(f, c); assert_eq!(f.shape(), c.shape()); assert_eq!(c.strides(), &[3, 1]); assert_eq!(f.strides(), &[1, 2]); itertools::assert_equal(f.iter(), c.iter()); itertools::assert_equal(f.rows(), c.rows()); itertools::assert_equal(f.outer_iter(), c.outer_iter()); itertools::assert_equal(f.axis_iter(Axis(0)), c.axis_iter(Axis(0))); itertools::assert_equal(f.axis_iter(Axis(1)), c.axis_iter(Axis(1))); let dupc = &c + &c; let dupf = &f + &f; assert_eq!(dupc, dupf); } #[test] fn to_owned_memory_order() { // check that .to_owned() makes f-contiguous arrays out of f-contiguous // input. let c = arr2(&[[1, 2, 3], [4, 5, 6]]); let mut f = c.view(); // transposed array f.swap_axes(0, 1); let fo = f.to_owned(); assert_eq!(f, fo); assert_eq!(f.strides(), fo.strides()); // negated stride axis f.invert_axis(Axis(1)); let fo2 = f.to_owned(); assert_eq!(f, fo2); assert_eq!(f.strides(), fo2.strides()); } #[test] fn to_owned_neg_stride() { let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); c.slice_collapse(s![.., ..;-1]); let co = c.to_owned(); assert_eq!(c, co); assert_eq!(c.strides(), co.strides()); } #[test] fn discontiguous_owned_to_owned() { let mut c = arr2(&[[1, 2, 3], [4, 5, 6]]); c.slice_collapse(s![.., ..;2]); let co = c.to_owned(); assert_eq!(c.strides(), &[3, 2]); assert_eq!(co.strides(), &[2, 1]); assert_eq!(c, co); } #[test] fn map_memory_order() { let a = arr3(&[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [0, -1, -2]]]); let mut v = a.view(); v.swap_axes(0, 1); let amap = v.map(|x| *x >= 3); assert_eq!(amap.dim(), v.dim()); assert_eq!(amap.strides(), v.strides()); } #[test] fn map_mut_with_unsharing() { // Fortran-layout `ArcArray`. let a = rcarr2(&[[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]).reversed_axes(); assert_eq!(a.shape(), &[2, 5]); assert_eq!(a.strides(), &[1, 2]); assert_eq!( a.as_slice_memory_order(), Some(&[0, 5, 1, 6, 2, 7, 3, 8, 4, 9][..]) ); // Shared reference of a portion of `a`. let mut b = a.clone().slice_move(s![.., ..2]); assert_eq!(b.shape(), &[2, 2]); assert_eq!(b.strides(), &[1, 2]); assert_eq!(b.as_slice_memory_order(), Some(&[0, 5, 1, 6][..])); assert_eq!(b, array![[0, 1], [5, 6]]); // `.map_mut()` unshares the data. Earlier versions of `ndarray` failed // this assertion. See #1018. assert_eq!(b.map_mut(|&mut x| x + 10), array![[10, 11], [15, 16]]); // The strides should be preserved. assert_eq!(b.shape(), &[2, 2]); assert_eq!(b.strides(), &[1, 2]); } #[test] fn test_view_from_shape() { let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; let a = ArrayView::from_shape((2, 3, 2), &s).unwrap(); let mut answer = Array::from(s.to_vec()) .into_shape_with_order((2, 3, 2)) .unwrap(); assert_eq!(a, answer); // custom strides (row major) let a = ArrayView::from_shape((2, 3, 2).strides((6, 2, 1)), &s).unwrap(); assert_eq!(a, answer); // custom strides (col major) let a = ArrayView::from_shape((2, 3, 2).strides((1, 2, 6)), &s).unwrap(); assert_eq!(a, answer.t()); // negative strides let a = ArrayView::from_shape((2, 3, 2).strides((6, (-2isize) as usize, 1)), &s).unwrap(); answer.invert_axis(Axis(1)); assert_eq!(a, answer); } #[test] fn test_view_from_shape_allow_overlap() { let data = [0, 1, 2]; let view = ArrayView::from_shape((2, 3).strides((0, 1)), &data).unwrap(); assert_eq!(view, aview2(&[data; 2])); } #[test] fn test_view_mut_from_shape_deny_overlap() { let mut data = [0, 1, 2]; let result = ArrayViewMut::from_shape((2, 3).strides((0, 1)), &mut data); assert_matches!(result.map_err(|e| e.kind()), Err(ErrorKind::Unsupported)); } #[test] fn test_contiguous() { let c = arr3(&[[[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [7, 7, 7]]]); assert!(c.is_standard_layout()); assert!(c.as_slice_memory_order().is_some()); let v = c.slice(s![.., 0..1, ..]); assert!(!v.is_standard_layout()); assert!(!v.as_slice_memory_order().is_some()); let v = c.slice(s![1..2, .., ..]); assert!(v.is_standard_layout()); assert!(v.as_slice_memory_order().is_some()); let v = v.reversed_axes(); assert!(!v.is_standard_layout()); assert!(v.as_slice_memory_order().is_some()); let mut v = v.reversed_axes(); v.swap_axes(1, 2); assert!(!v.is_standard_layout()); assert!(v.as_slice_memory_order().is_some()); let a = Array::::zeros((20, 1)); let b = Array::::zeros((20, 1).f()); assert!(a.as_slice().is_some()); assert!(b.as_slice().is_some()); assert!(a.as_slice_memory_order().is_some()); assert!(b.as_slice_memory_order().is_some()); let a = a.t(); let b = b.t(); assert!(a.as_slice().is_some()); assert!(b.as_slice().is_some()); assert!(a.as_slice_memory_order().is_some()); assert!(b.as_slice_memory_order().is_some()); } #[test] fn test_contiguous_single_element() { assert_matches!(array![1].as_slice_memory_order(), Some(&[1])); let arr1 = array![1, 2, 3]; assert_matches!(arr1.slice(s![0..1]).as_slice_memory_order(), Some(&[1])); assert_matches!(arr1.slice(s![1..2]).as_slice_memory_order(), Some(&[2])); assert_matches!(arr1.slice(s![2..3]).as_slice_memory_order(), Some(&[3])); assert_matches!(arr1.slice(s![0..0]).as_slice_memory_order(), Some(&[])); let arr2 = array![[1, 2, 3], [4, 5, 6]]; assert_matches!(arr2.slice(s![.., 2..3]).as_slice_memory_order(), None); assert_matches!(arr2.slice(s![1, 2..3]).as_slice_memory_order(), Some(&[6])); } #[test] fn test_contiguous_neg_strides() { let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; let a = ArrayView::from_shape((2, 3, 2).strides((1, 4, 2)), &s).unwrap(); assert_eq!( a, arr3(&[[[0, 2], [4, 6], [8, 10]], [[1, 3], [5, 7], [9, 11]]]) ); assert!(a.as_slice_memory_order().is_some()); let mut b = a.slice(s![..;1, ..;-1, ..;-1]); assert_eq!( b, arr3(&[[[10, 8], [6, 4], [2, 0]], [[11, 9], [7, 5], [3, 1]]]) ); assert!(b.as_slice_memory_order().is_some()); b.swap_axes(1, 2); assert_eq!(b, arr3(&[[[10, 6, 2], [8, 4, 0]], [[11, 7, 3], [9, 5, 1]]])); assert!(b.as_slice_memory_order().is_some()); b.invert_axis(Axis(0)); assert_eq!(b, arr3(&[[[11, 7, 3], [9, 5, 1]], [[10, 6, 2], [8, 4, 0]]])); assert!(b.as_slice_memory_order().is_some()); let mut c = b.reversed_axes(); assert_eq!( c, arr3(&[[[11, 10], [9, 8]], [[7, 6], [5, 4]], [[3, 2], [1, 0]]]) ); assert!(c.as_slice_memory_order().is_some()); c.merge_axes(Axis(1), Axis(2)); assert_eq!(c, arr3(&[[[11, 10, 9, 8]], [[7, 6, 5, 4]], [[3, 2, 1, 0]]])); assert!(c.as_slice_memory_order().is_some()); let d = b.remove_axis(Axis(1)); assert_eq!(d, arr2(&[[11, 7, 3], [10, 6, 2]])); assert!(d.as_slice_memory_order().is_none()); let e = b.remove_axis(Axis(2)); assert_eq!(e, arr2(&[[11, 9], [10, 8]])); assert!(e.as_slice_memory_order().is_some()); let f = e.insert_axis(Axis(2)); assert_eq!(f, arr3(&[[[11], [9]], [[10], [8]]])); assert!(f.as_slice_memory_order().is_some()); let mut g = b.clone(); g.collapse_axis(Axis(1), 0); assert_eq!(g, arr3(&[[[11, 7, 3]], [[10, 6, 2]]])); assert!(g.as_slice_memory_order().is_none()); b.collapse_axis(Axis(2), 0); assert_eq!(b, arr3(&[[[11], [9]], [[10], [8]]])); assert!(b.as_slice_memory_order().is_some()); } #[test] fn test_swap() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); let b = a.clone(); for i in 0..a.nrows() { for j in i + 1..a.ncols() { a.swap((i, j), (j, i)); } } assert_eq!(a, b.t()); } #[test] fn test_uswap() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); let b = a.clone(); for i in 0..a.nrows() { for j in i + 1..a.ncols() { unsafe { a.uswap((i, j), (j, i)) }; } } assert_eq!(a, b.t()); } #[test] fn test_shape() { let data = [0, 1, 2, 3, 4, 5]; let a = Array::from_shape_vec((1, 2, 3), data.to_vec()).unwrap(); let b = Array::from_shape_vec((1, 2, 3).f(), data.to_vec()).unwrap(); let c = Array::from_shape_vec((1, 2, 3).strides((1, 3, 1)), data.to_vec()).unwrap(); println!("{:?}", a); println!("{:?}", b); println!("{:?}", c); assert_eq!(a.strides(), &[6, 3, 1]); assert_eq!(b.strides(), &[1, 1, 2]); assert_eq!(c.strides(), &[1, 3, 1]); } #[test] fn test_view_from_shape_ptr() { let data = [0, 1, 2, 3, 4, 5]; let view = unsafe { ArrayView::from_shape_ptr((2, 3), data.as_ptr()) }; assert_eq!(view, aview2(&[[0, 1, 2], [3, 4, 5]])); let mut data = data; let mut view = unsafe { ArrayViewMut::from_shape_ptr((2, 3), data.as_mut_ptr()) }; view[[1, 2]] = 6; assert_eq!(view, aview2(&[[0, 1, 2], [3, 4, 6]])); view[[0, 1]] = 0; assert_eq!(view, aview2(&[[0, 0, 2], [3, 4, 6]])); } #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] fn test_view_from_shape_ptr_deny_neg_strides() { let data = [0, 1, 2, 3, 4, 5]; let _view = unsafe { ArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) }; } #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] fn test_view_mut_from_shape_ptr_deny_neg_strides() { let mut data = [0, 1, 2, 3, 4, 5]; let _view = unsafe { ArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) }; } #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] fn test_raw_view_from_shape_ptr_deny_neg_strides() { let data = [0, 1, 2, 3, 4, 5]; let _view = unsafe { RawArrayView::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_ptr()) }; } #[should_panic(expected = "Unsupported")] #[cfg(debug_assertions)] #[test] fn test_raw_view_mut_from_shape_ptr_deny_neg_strides() { let mut data = [0, 1, 2, 3, 4, 5]; let _view = unsafe { RawArrayViewMut::from_shape_ptr((2, 3).strides((-3isize as usize, 1)), data.as_mut_ptr()) }; } #[test] fn test_raw_view_from_shape_allow_overlap() { let data = [0, 1, 2]; let view; unsafe { let raw_view = RawArrayView::from_shape_ptr((2, 3).strides((0, 1)), data.as_ptr()); view = raw_view.deref_into_view(); } assert_eq!(view, aview2(&[data, data])); } #[should_panic(expected = "strides must not allow any element")] #[cfg(debug_assertions)] #[test] fn test_raw_view_mut_from_shape_deny_overlap() { let mut data = [0, 1, 2]; unsafe { RawArrayViewMut::from_shape_ptr((2, 3).strides((0, 1)), data.as_mut_ptr()); } } #[test] fn test_default() { let a = as Default>::default(); assert_eq!(a, aview2(&[[0.0; 0]; 0])); #[derive(Default, Debug, PartialEq)] struct Foo(i32); let b = as Default>::default(); assert_eq!(b, arr0(Foo::default())); } #[test] fn test_default_ixdyn() { let a = as Default>::default(); let b = >::zeros(IxDyn(&[0])); assert_eq!(a, b); } #[test] fn test_map_axis() { let a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); let b = a.map_axis(Axis(0), |view| view.sum()); let answer1 = arr1(&[22, 26, 30]); assert_eq!(b, answer1); let c = a.map_axis(Axis(1), |view| view.sum()); let answer2 = arr1(&[6, 15, 24, 33]); assert_eq!(c, answer2); // Test zero-length axis case let arr = Array3::::zeros((3, 0, 4)); let mut counter = 0; let result = arr.map_axis(Axis(1), |x| { assert_eq!(x.shape(), &[0]); counter += 1; counter }); assert_eq!(result.shape(), &[3, 4]); itertools::assert_equal(result.iter().cloned().sorted(), 1..=3 * 4); let mut arr = Array3::::zeros((3, 0, 4)); let mut counter = 0; let result = arr.map_axis_mut(Axis(1), |x| { assert_eq!(x.shape(), &[0]); counter += 1; counter }); assert_eq!(result.shape(), &[3, 4]); itertools::assert_equal(result.iter().cloned().sorted(), 1..=3 * 4); } #[test] fn test_accumulate_axis_inplace_noop() { let mut a = Array2::::zeros((0, 3)); a.accumulate_axis_inplace(Axis(0), |&prev, curr| *curr += prev); assert_eq!(a, Array2::zeros((0, 3))); let mut a = Array2::::zeros((3, 1)); a.accumulate_axis_inplace(Axis(1), |&prev, curr| *curr += prev); assert_eq!(a, Array2::zeros((3, 1))); } #[rustfmt::skip] // Allow block array formatting #[test] fn test_accumulate_axis_inplace_nonstandard_layout() { let a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10,11,12]]); let mut a_t = a.clone().reversed_axes(); a_t.accumulate_axis_inplace(Axis(0), |&prev, curr| *curr += prev); assert_eq!(a_t, aview2(&[[1, 4, 7, 10], [3, 9, 15, 21], [6, 15, 24, 33]])); let mut a0 = a.clone(); a0.invert_axis(Axis(0)); a0.accumulate_axis_inplace(Axis(0), |&prev, curr| *curr += prev); assert_eq!(a0, aview2(&[[10, 11, 12], [17, 19, 21], [21, 24, 27], [22, 26, 30]])); let mut a1 = a.clone(); a1.invert_axis(Axis(1)); a1.accumulate_axis_inplace(Axis(1), |&prev, curr| *curr += prev); assert_eq!(a1, aview2(&[[3, 5, 6], [6, 11, 15], [9, 17, 24], [12, 23, 33]])); } #[test] fn test_to_vec() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.slice_collapse(s![..;-1, ..]); assert_eq!(a.row(3).to_vec(), vec![1, 2, 3]); assert_eq!(a.column(2).to_vec(), vec![12, 9, 6, 3]); a.slice_collapse(s![.., ..;-1]); assert_eq!(a.row(3).to_vec(), vec![3, 2, 1]); } #[test] fn test_array_clone_unalias() { let a = Array::::zeros((3, 3)); let mut b = a.clone(); b.fill(1); assert!(a != b); assert_eq!(a, Array::<_, _>::zeros((3, 3))); } #[test] fn test_array_clone_same_view() { let mut a = Array::from_iter(0..9) .into_shape_with_order((3, 3)) .unwrap(); a.slice_collapse(s![..;-1, ..;-1]); let b = a.clone(); assert_eq!(a, b); } #[test] fn test_array2_from_diag() { let diag = arr1(&[0, 1, 2]); let x = Array2::from_diag(&diag); let x_exp = arr2(&[[0, 0, 0], [0, 1, 0], [0, 0, 2]]); assert_eq!(x, x_exp); // check 0 length array let diag = Array1::::zeros(0); let x = Array2::from_diag(&diag); assert_eq!(x.ndim(), 2); assert_eq!(x.shape(), [0, 0]); } #[test] fn array_macros() { // array let a1 = array![1, 2, 3]; assert_eq!(a1, arr1(&[1, 2, 3])); let a2 = array![[1, 2], [3, 4], [5, 6]]; assert_eq!(a2, arr2(&[[1, 2], [3, 4], [5, 6]])); let a3 = array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; assert_eq!(a3, arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]])); let a4 = array![[[1, 2,], [3, 4,]], [[5, 6,], [7, 8,],],]; // trailing commas assert_eq!(a4, arr3(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]])); let s = String::from("abc"); let a2s = array![ [String::from("w"), s], [String::from("x"), String::from("y")] ]; assert_eq!(a2s[[0, 0]], "w"); assert_eq!(a2s[[0, 1]], "abc"); assert_eq!(a2s[[1, 0]], "x"); assert_eq!(a2s[[1, 1]], "y"); let empty1: Array = array![]; assert_eq!(empty1, array![]); let empty2: Array = array![[]]; assert_eq!(empty2, array![[]]); } #[cfg(test)] mod as_standard_layout_tests { use super::*; use ndarray::Data; use std::fmt::Debug; fn test_as_standard_layout_for(orig: ArrayBase) where S: Data, S::Elem: Clone + Debug + PartialEq, D: Dimension, { let orig_is_standard = orig.is_standard_layout(); let out = orig.as_standard_layout(); assert!(out.is_standard_layout()); assert_eq!(out, orig); assert_eq!(orig_is_standard, out.is_view()); } #[test] fn test_f_layout() { let shape = (2, 2).f(); let arr = Array::::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap(); assert!(!arr.is_standard_layout()); test_as_standard_layout_for(arr); } #[test] fn test_c_layout() { let arr = Array::::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); assert!(arr.is_standard_layout()); test_as_standard_layout_for(arr); } #[test] fn test_f_layout_view() { let shape = (2, 2).f(); let arr = Array::::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap(); let arr_view = arr.view(); assert!(!arr_view.is_standard_layout()); test_as_standard_layout_for(arr); } #[test] fn test_c_layout_view() { let arr = Array::::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); let arr_view = arr.view(); assert!(arr_view.is_standard_layout()); test_as_standard_layout_for(arr_view); } #[test] fn test_zero_dimensional_array() { let arr_view = ArrayView1::::from(&[]); assert!(arr_view.is_standard_layout()); test_as_standard_layout_for(arr_view); } #[test] fn test_custom_layout() { let shape = (1, 2, 3, 2).strides((12, 1, 2, 6)); let arr_data: Vec = (0..12).collect(); let arr = Array::::from_shape_vec(shape, arr_data).unwrap(); assert!(!arr.is_standard_layout()); test_as_standard_layout_for(arr); } } #[cfg(test)] mod array_cow_tests { use super::*; #[test] fn test_is_variant() { let arr: Array = array![[1, 2], [3, 4]]; let arr_cow = CowArray::::from(arr.view()); assert!(arr_cow.is_view()); assert!(!arr_cow.is_owned()); let arr_cow = CowArray::::from(arr); assert!(arr_cow.is_owned()); assert!(!arr_cow.is_view()); } fn run_with_various_layouts(mut f: impl FnMut(Array2)) { for all in vec![ Array2::from_shape_vec((7, 8), (0..7 * 8).collect()).unwrap(), Array2::from_shape_vec((7, 8).f(), (0..7 * 8).collect()).unwrap(), ] { f(all.clone()); f(all.clone().slice_move(s![.., 2..5])); f(all.clone().slice_move(s![3..5, 2..5])); f(all.clone().slice_move(s![.., ..;2])); f(all.clone().slice_move(s![..;3, ..])); f(all.clone().slice_move(s![.., ..;-1])); f(all.clone().slice_move(s![..;-2, ..;-1])); f(all.clone().slice_move(s![2..5;-2, 3..6])); f(all.clone().slice_move(s![2..5;-2, 3..6;-1])); } } #[test] fn test_element_mutation() { run_with_various_layouts(|arr: Array2| { let mut expected = arr.clone(); expected[(1, 1)] = 2; let mut arr_cow = CowArray::::from(arr.view()); arr_cow[(1, 1)] = 2; assert!(arr_cow.is_owned()); assert_eq!(arr_cow, expected); let ptr = arr.as_ptr(); let mut arr_cow = CowArray::::from(arr); assert_eq!(arr_cow.as_ptr(), ptr); arr_cow[(1, 1)] = 2; assert_eq!(arr_cow.as_ptr(), ptr); assert_eq!(arr_cow, expected); }); } #[test] fn test_clone() { run_with_various_layouts(|arr: Array2| { let arr_cow = CowArray::::from(arr.view()); let arr_cow_clone = arr_cow.clone(); assert!(arr_cow_clone.is_view()); assert_eq!(arr_cow, arr_cow_clone); assert_eq!(arr_cow.dim(), arr_cow_clone.dim()); assert_eq!(arr_cow.strides(), arr_cow_clone.strides()); let arr_cow = CowArray::::from(arr); let arr_cow_clone = arr_cow.clone(); assert!(arr_cow_clone.is_owned()); assert_eq!(arr_cow, arr_cow_clone); assert_eq!(arr_cow.dim(), arr_cow_clone.dim()); assert_eq!(arr_cow.strides(), arr_cow_clone.strides()); }); } #[test] fn test_clone_from() { fn assert_eq_contents_and_layout(arr1: &CowArray<'_, i32, Ix2>, arr2: &CowArray<'_, i32, Ix2>) { assert_eq!(arr1, arr2); assert_eq!(arr1.dim(), arr2.dim()); assert_eq!(arr1.strides(), arr2.strides()); } run_with_various_layouts(|arr: Array2| { run_with_various_layouts(|other_arr: Array2| { let arr_cow_src = CowArray::::from(arr.view()); let mut arr_cow_dst = CowArray::::from(other_arr.clone()); arr_cow_dst.clone_from(&arr_cow_src); assert!(arr_cow_dst.is_view()); assert_eq_contents_and_layout(&arr_cow_src, &arr_cow_dst); let arr_cow_src = CowArray::::from(arr.view()); let mut arr_cow_dst = CowArray::::from(other_arr.view()); arr_cow_dst.clone_from(&arr_cow_src); assert!(arr_cow_dst.is_view()); assert_eq_contents_and_layout(&arr_cow_src, &arr_cow_dst); let arr_cow_src = CowArray::::from(arr.clone()); let mut arr_cow_dst = CowArray::::from(other_arr.view()); arr_cow_dst.clone_from(&arr_cow_src); assert!(arr_cow_dst.is_owned()); assert_eq_contents_and_layout(&arr_cow_src, &arr_cow_dst); let arr_cow_src = CowArray::::from(arr.clone()); let mut arr_cow_dst = CowArray::::from(other_arr.clone()); arr_cow_dst.clone_from(&arr_cow_src); assert!(arr_cow_dst.is_owned()); assert_eq_contents_and_layout(&arr_cow_src, &arr_cow_dst); }); }); } #[test] fn test_into_owned() { run_with_various_layouts(|arr: Array2| { let before = CowArray::::from(arr.view()); let after = before.into_owned(); assert_eq!(arr, after); let before = CowArray::::from(arr.clone()); let ptr = before.as_ptr(); let after = before.into_owned(); assert_eq!(after.as_ptr(), ptr); assert_eq!(arr, after); }); } } #[test] fn test_remove_index() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); assert_eq!(a, array![[1, 2], [7, 8], [10,11]]); let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.invert_axis(Axis(0)); a.remove_index(Axis(0), 1); a.remove_index(Axis(1), 2); assert_eq!(a.shape(), &[3, 2]); assert_eq!(a, array![[10,11], [4, 5], [1, 2]]); a.remove_index(Axis(1), 1); assert_eq!(a.shape(), &[3, 1]); assert_eq!(a, array![[10], [4], [1]]); a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); assert_eq!(a, array![[], [], []]); } #[should_panic(expected = "must be less")] #[test] fn test_remove_index_oob1() { let mut a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]); a.remove_index(Axis(0), 4); } #[should_panic(expected = "must be less")] #[test] fn test_remove_index_oob2() { let mut a = array![[10], [4], [1]]; a.remove_index(Axis(1), 0); assert_eq!(a.shape(), &[3, 0]); assert_eq!(a, array![[], [], []]); a.remove_index(Axis(0), 1); // ok assert_eq!(a, array![[], []]); a.remove_index(Axis(1), 0); // oob } #[should_panic(expected = "index out of bounds")] #[test] fn test_remove_index_oob3() { let mut a = array![[10], [4], [1]]; a.remove_index(Axis(2), 0); } #[test] fn test_split_complex_view() { let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| Complex::::new(i as f32 * j as f32, k as f32)); let Complex { re, im } = a.view().split_complex(); assert_relative_eq!(re.sum(), 90.); assert_relative_eq!(im.sum(), 120.); } #[test] fn test_split_complex_view_roundtrip() { let a_re = Array3::from_shape_fn((3, 1, 5), |(i, j, _k)| i * j); let a_im = Array3::from_shape_fn((3, 1, 5), |(_i, _j, k)| k); let a = Array3::from_shape_fn((3, 1, 5), |(i, j, k)| Complex::new(a_re[[i, j, k]], a_im[[i, j, k]])); let Complex { re, im } = a.view().split_complex(); assert_eq!(a_re, re); assert_eq!(a_im, im); } #[test] fn test_split_complex_view_mut() { let eye_scalar = Array2::::eye(4); let eye_complex = Array2::>::eye(4); let mut a = Array2::>::zeros((4, 4)); let Complex { mut re, im } = a.view_mut().split_complex(); re.assign(&eye_scalar); assert_eq!(im.sum(), 0); assert_eq!(a, eye_complex); } #[test] fn test_split_complex_zerod() { let mut a = Array0::from_elem((), Complex::new(42, 32)); let Complex { re, im } = a.view().split_complex(); assert_eq!(re.get(()), Some(&42)); assert_eq!(im.get(()), Some(&32)); let cmplx = a.view_mut().split_complex(); cmplx.re.assign_to(cmplx.im); assert_eq!(a.get(()).unwrap().im, 42); } #[test] fn test_split_complex_permuted() { let a = Array3::from_shape_fn((3, 4, 5), |(i, j, k)| Complex::new(i * k + j, k)); let permuted = a.view().permuted_axes([1, 0, 2]); let Complex { re, im } = permuted.split_complex(); assert_eq!(re.get((3,2,4)).unwrap(), &11); assert_eq!(im.get((3,2,4)).unwrap(), &4); } #[test] fn test_split_complex_invert_axis() { let mut a = Array::from_shape_fn((2, 3, 2), |(i, j, k)| Complex::new(i as f64 + j as f64, i as f64 + k as f64)); a.invert_axis(Axis(1)); let cmplx = a.view().split_complex(); assert_eq!(cmplx.re, a.mapv(|z| z.re)); assert_eq!(cmplx.im, a.mapv(|z| z.im)); } ndarray-0.16.1/tests/assign.rs000064400000000000000000000174061046102023000143550ustar 00000000000000use ndarray::prelude::*; use std::sync::atomic::{AtomicUsize, Ordering}; #[test] fn assign() { let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = arr2(&[[1., 3.], [2., 4.]]); a.assign(&b); assert_eq!(a, b); /* Test broadcasting */ a.assign(&ArcArray::zeros(1)); assert_eq!(a, ArcArray::zeros((2, 2))); /* Test other type */ a.assign(&Array::from_elem((2, 2), 3.)); assert_eq!(a, ArcArray::from_elem((2, 2), 3.)); /* Test mut view */ let mut a = arr2(&[[1, 2], [3, 4]]); { let mut v = a.view_mut(); v.slice_collapse(s![..1, ..]); v.fill(0); } assert_eq!(a, arr2(&[[0, 0], [3, 4]])); } #[test] fn assign_to() { let mut a = arr2(&[[1., 2.], [3., 4.]]); let b = arr2(&[[0., 3.], [2., 0.]]); b.assign_to(&mut a); assert_eq!(a, b); } #[test] fn move_into_copy() { let a = arr2(&[[1., 2.], [3., 4.]]); let acopy = a.clone(); let mut b = Array::uninit(a.dim()); a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; assert_eq!(acopy, b); let a = arr2(&[[1., 2.], [3., 4.]]).reversed_axes(); let acopy = a.clone(); let mut b = Array::uninit(a.dim()); a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; assert_eq!(acopy, b); } #[test] fn move_into_owned() { // Test various memory layouts and holes while moving String elements. for &use_f_order in &[false, true] { for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert for &slice in &[false, true] { let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), |idx| format!("{:?}", idx)); if slice { a.slice_collapse(s![1..-1, ..;2]); } if invert_axis & 0b01 != 0 { a.invert_axis(Axis(0)); } if invert_axis & 0b10 != 0 { a.invert_axis(Axis(1)); } let acopy = a.clone(); let mut b = Array::uninit(a.dim()); a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; assert_eq!(acopy, b); } } } } #[test] fn move_into_slicing() { // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert let counter = DropCounter::default(); { let (m, n) = (5, 4); let mut a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); a.slice_collapse(s![1..-1, ..;2]); if invert_axis & 0b01 != 0 { a.invert_axis(Axis(0)); } if invert_axis & 0b10 != 0 { a.invert_axis(Axis(1)); } let mut b = Array::uninit(a.dim()); a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; let dropped_1 = total - (m - 2) * (n - 2); assert_eq!(counter.created(), total); assert_eq!(counter.dropped(), dropped_1); drop(b); } counter.assert_drop_count(); } } } #[test] fn move_into_diag() { // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); { let (m, n) = (5, 4); let a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); let a = a.into_diag(); let mut b = Array::uninit(a.dim()); a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; let dropped_1 = total - Ord::min(m, n); assert_eq!(counter.created(), total); assert_eq!(counter.dropped(), dropped_1); drop(b); } counter.assert_drop_count(); } } #[test] fn move_into_0dim() { // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); { let (m, n) = (5, 4); // slice into a 0-dim array let a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); let a = a.slice_move(s![2, 2]); assert_eq!(a.ndim(), 0); let mut b = Array::uninit(a.dim()); a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; let dropped_1 = total - 1; assert_eq!(counter.created(), total); assert_eq!(counter.dropped(), dropped_1); drop(b); } counter.assert_drop_count(); } } #[test] fn move_into_empty() { // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes). for &use_f_order in &[false, true] { let counter = DropCounter::default(); { let (m, n) = (5, 4); // slice into an empty array; let a = Array::from_shape_fn((m, n).set_f(use_f_order), |_idx| counter.element()); let a = a.slice_move(s![..0, 1..1]); assert!(a.is_empty()); let mut b = Array::uninit(a.dim()); a.move_into_uninit(b.view_mut()); let b = unsafe { b.assume_init() }; let total = m * n; let dropped_1 = total; assert_eq!(counter.created(), total); assert_eq!(counter.dropped(), dropped_1); drop(b); } counter.assert_drop_count(); } } #[test] fn move_into() { // Test various memory layouts and holes while moving String elements with move_into for &use_f_order in &[false, true] { for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert for &slice in &[false, true] { let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order), |idx| format!("{:?}", idx)); if slice { a.slice_collapse(s![1..-1, ..;2]); } if invert_axis & 0b01 != 0 { a.invert_axis(Axis(0)); } if invert_axis & 0b10 != 0 { a.invert_axis(Axis(1)); } let acopy = a.clone(); let mut b = Array::default(a.dim().set_f(!use_f_order ^ !slice)); a.move_into(&mut b); assert_eq!(acopy, b); } } } } /// This counter can create elements, and then count and verify /// the number of which have actually been dropped again. #[derive(Default)] struct DropCounter { created: AtomicUsize, dropped: AtomicUsize, } struct Element<'a>(&'a AtomicUsize); impl DropCounter { fn created(&self) -> usize { self.created.load(Ordering::Relaxed) } fn dropped(&self) -> usize { self.dropped.load(Ordering::Relaxed) } fn element(&self) -> Element<'_> { self.created.fetch_add(1, Ordering::Relaxed); Element(&self.dropped) } fn assert_drop_count(&self) { assert_eq!( self.created(), self.dropped(), "Expected {} dropped elements, but found {}", self.created(), self.dropped() ); } } impl<'a> Drop for Element<'a> { fn drop(&mut self) { self.0.fetch_add(1, Ordering::Relaxed); } } ndarray-0.16.1/tests/azip.rs000064400000000000000000000273011046102023000140270ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::prelude::*; use ndarray::Zip; use itertools::{assert_equal, cloned}; use std::mem::swap; #[test] fn test_azip1() { let mut a = Array::zeros(62); let mut x = 0; azip!((a in &mut a) { *a = x; x += 1; }); assert_equal(cloned(&a), 0..a.len()); } #[test] fn test_azip2() { let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn(a.dim(), |(i, j)| 1. / (i + 2 * j) as f32); azip!((a in &mut a, &b in &b) *a = b); assert_eq!(a, b); } #[test] fn test_azip2_1() { let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j) as f32); let b = b.slice(s![..;-1, 3..]); azip!((a in &mut a, &b in &b) *a = b); assert_eq!(a, b); } #[test] fn test_azip2_3() { let mut b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j) as f32); let mut c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); let a = b.clone(); azip!((b in &mut b, c in &mut c) swap(b, c)); assert_eq!(a, c); assert!(a != b); } #[test] #[cfg(feature = "approx")] fn test_zip_collect() { use approx::assert_abs_diff_eq; // test Zip::map_collect and that it preserves c/f layout. let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); { let a = Zip::from(&b).and(&c).map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } { let b = b.t(); let c = c.t(); let a = Zip::from(&b).and(&c).map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } } #[test] #[cfg(feature = "approx")] fn test_zip_assign_into() { use approx::assert_abs_diff_eq; let mut a = Array::::zeros((5, 10)); let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); Zip::from(&b).and(&c).map_assign_into(&mut a, |x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); } #[test] #[cfg(feature = "approx")] fn test_zip_assign_into_cell() { use approx::assert_abs_diff_eq; use std::cell::Cell; let a = Array::, _>::default((5, 10)); let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); Zip::from(&b).and(&c).map_assign_into(&a, |x, y| x + y); let a2 = a.mapv(|elt| elt.get()); assert_abs_diff_eq!(a2, &b + &c, epsilon = 1e-6); } #[test] fn test_zip_collect_drop() { use std::cell::RefCell; use std::panic; struct Recorddrop<'a>((usize, usize), &'a RefCell>); impl<'a> Drop for Recorddrop<'a> { fn drop(&mut self) { self.1.borrow_mut().push(self.0); } } #[derive(Copy, Clone)] enum Config { CC, CF, FF, } impl Config { fn a_is_f(self) -> bool { match self { Config::CC | Config::CF => false, _ => true, } } fn b_is_f(self) -> bool { match self { Config::CC => false, _ => true, } } } let test_collect_panic = |config: Config, will_panic: bool, slice: bool| { let mut inserts = RefCell::new(Vec::new()); let mut drops = RefCell::new(Vec::new()); let mut a = Array::from_shape_fn((5, 10).set_f(config.a_is_f()), |idx| idx); let mut b = Array::from_shape_fn((5, 10).set_f(config.b_is_f()), |_| 0); if slice { a = a.slice_move(s![.., ..-1]); b = b.slice_move(s![.., ..-1]); } let _result = panic::catch_unwind(panic::AssertUnwindSafe(|| { Zip::from(&a).and(&b).map_collect(|&elt, _| { if elt.0 > 3 && will_panic { panic!(); } inserts.borrow_mut().push(elt); Recorddrop(elt, &drops) }); })); println!("{:?}", inserts.get_mut()); println!("{:?}", drops.get_mut()); assert_eq!(inserts.get_mut().len(), drops.get_mut().len(), "Incorrect number of drops"); assert_eq!(inserts.get_mut(), drops.get_mut(), "Incorrect order of drops"); }; for &should_panic in &[true, false] { for &should_slice in &[false, true] { test_collect_panic(Config::CC, should_panic, should_slice); test_collect_panic(Config::CF, should_panic, should_slice); test_collect_panic(Config::FF, should_panic, should_slice); } } } #[test] fn test_azip_syntax_trailing_comma() { let mut b = Array::::zeros((5, 5)); let mut c = Array::::ones((5, 5)); let a = b.clone(); azip!((b in &mut b, c in &mut c, ) swap(b, c)); assert_eq!(a, c); assert!(a != b); } #[test] #[cfg(feature = "approx")] fn test_azip2_sum() { use approx::assert_abs_diff_eq; let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); for i in 0..2 { let ax = Axis(i); let mut b = Array::zeros(c.len_of(ax)); azip!((b in &mut b, c in c.axis_iter(ax)) *b = c.sum()); assert_abs_diff_eq!(b, c.sum_axis(Axis(1 - i)), epsilon = 1e-6); } } #[test] #[cfg(feature = "approx")] fn test_azip3_slices() { use approx::assert_abs_diff_eq; let mut a = [0.; 32]; let mut b = [0.; 32]; let mut c = [0.; 32]; for (i, elt) in b.iter_mut().enumerate() { *elt = i as f32; } azip!((a in &mut a[..], b in &b[..], c in &mut c[..]) { *a += b / 10.; *c = a.sin(); }); let res = Array::linspace(0., 3.1, 32).mapv_into(f32::sin); assert_abs_diff_eq!(res, ArrayView::from(&c), epsilon = 1e-4); } #[test] #[cfg(feature = "approx")] fn test_broadcast() { use approx::assert_abs_diff_eq; let n = 16; let mut a = Array::::zeros((n, n)); let mut b = Array::::from_elem((1, n), 1.); for ((i, j), elt) in b.indexed_iter_mut() { *elt /= 1. + (i + 2 * j) as f32; } let d = Array::from_elem((1, n), 1.); let e = Array::from_elem((), 2.); { let z = Zip::from(a.view_mut()) .and_broadcast(&b) .and_broadcast(&d) .and_broadcast(&e); z.for_each(|x, &y, &z, &w| *x = y + z + w); } let sum = &b + &d + &e; assert_abs_diff_eq!(a, sum.broadcast((n, n)).unwrap(), epsilon = 1e-4); } #[should_panic] #[test] fn test_zip_dim_mismatch_1() { let mut a = Array::zeros((5, 7)); let mut d = a.raw_dim(); d[0] += 1; let b = Array::from_shape_fn(d, |(i, j)| 1. / (i + 2 * j) as f32); azip!((a in &mut a, &b in &b) *a = b); } // Test that Zip handles memory layout correctly for // Zip::from(A).and(B) // where A is F-contiguous and B contiguous but neither F nor C contiguous. #[test] fn test_contiguous_but_not_c_or_f() { let a = Array::from_iter(0..27) .into_shape_with_order((3, 3, 3)) .unwrap(); // both F order let a = a.reversed_axes(); let mut b = a.clone(); assert_eq!(a.strides(), b.strides()); assert_eq!(a.strides(), &[1, 3, 9]); b.swap_axes(0, 1); // test single elem so that test keeps working if array `+` impl changes let correct = &a + &b; let correct_012 = a[[0, 1, 2]] + b[[0, 1, 2]]; let mut ans = Array::zeros(a.dim().f()); azip!((ans in &mut ans, &a in &a, &b in &b) *ans = a + b); println!("{:?}", a); println!("{:?}", b); println!("{:?}", ans); assert_eq!(ans[[0, 1, 2]], correct_012); assert_eq!(ans, correct); } #[test] fn test_clone() { let a = Array::from_iter(0..27) .into_shape_with_order((3, 3, 3)) .unwrap(); let z = Zip::from(&a).and(a.exact_chunks((1, 1, 1))); let w = z.clone(); let mut result = Vec::new(); z.for_each(|x, y| { result.push((x, y)); }); let mut i = 0; w.for_each(|x, y| { assert_eq!(result[i], (x, y)); i += 1; }); } #[test] fn test_indices_0() { let a1 = arr0(3); let mut count = 0; Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(i, ()); assert_eq!(*elt, 3); }); assert_eq!(count, 1); } #[test] fn test_indices_1() { let mut a1 = Array::default(12); for (i, elt) in a1.indexed_iter_mut() { *elt = i; } let mut count = 0; Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, a1.len()); let mut count = 0; let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len); } #[test] fn test_indices_2() { let mut a1 = Array::default((10, 12)); for (i, elt) in a1.indexed_iter_mut() { *elt = i; } let mut count = 0; azip!((index i, &a1 in &a1) { count += 1; assert_eq!(a1, i); }); assert_eq!(count, a1.len()); let mut count = 0; let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len); } #[test] fn test_indices_3() { let mut a1 = Array::default((4, 5, 6)); for (i, elt) in a1.indexed_iter_mut() { *elt = i; } let mut count = 0; Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, a1.len()); let mut count = 0; let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len); } #[test] fn test_indices_split_1() { for m in (0..4).chain(10..12) { for n in (0..4).chain(10..12) { let a1 = Array::::default((m, n)); if a1.len() <= 1 { continue; } let (a, b) = Zip::indexed(&a1).split(); let mut seen = Vec::new(); let mut ac = 0; a.for_each(|i, _| { ac += 1; seen.push(i); }); let mut bc = 0; b.for_each(|i, _| { bc += 1; seen.push(i); }); assert_eq!(a1.len(), ac + bc); seen.sort(); assert_eq!(seen.len(), a1.len()); seen.dedup(); assert_eq!(seen.len(), a1.len()); } } } #[test] fn test_zip_all() { let a = Array::::zeros(62); let b = Array::::ones(62); let mut c = Array::::ones(62); c[5] = 0.0; assert_eq!(true, Zip::from(&a).and(&b).all(|&x, &y| x + y == 1.0)); assert_eq!(false, Zip::from(&a).and(&b).all(|&x, &y| x == y)); assert_eq!(false, Zip::from(&a).and(&c).all(|&x, &y| x + y == 1.0)); } #[test] fn test_zip_all_empty_array() { let a = Array::::zeros(0); let b = Array::::ones(0); assert_eq!(true, Zip::from(&a).and(&b).all(|&_x, &_y| true)); assert_eq!(true, Zip::from(&a).and(&b).all(|&_x, &_y| false)); } ndarray-0.16.1/tests/broadcast.rs000064400000000000000000000050301046102023000150210ustar 00000000000000use ndarray::prelude::*; #[test] #[cfg(feature = "std")] fn broadcast_1() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); let a = ArcArray::linspace(0., 1., a_dim.size()) .into_shape_with_order(a_dim) .unwrap(); let b = ArcArray::linspace(0., 1., b_dim.size()) .into_shape_with_order(b_dim) .unwrap(); assert!(b.broadcast(a.dim()).is_some()); let c_dim = Dim([2, 1]); let c = ArcArray::linspace(0., 1., c_dim.size()) .into_shape_with_order(c_dim) .unwrap(); assert!(c.broadcast(1).is_none()); assert!(c.broadcast(()).is_none()); assert!(c.broadcast((2, 1)).is_some()); assert!(c.broadcast((2, 2)).is_some()); assert!(c.broadcast((32, 2, 1)).is_some()); assert!(c.broadcast((32, 1, 2)).is_none()); /* () can be broadcast to anything */ let z = ArcArray::::zeros(()); assert!(z.broadcast(()).is_some()); assert!(z.broadcast(1).is_some()); assert!(z.broadcast(3).is_some()); assert!(z.broadcast((7, 2, 9)).is_some()); } #[test] #[cfg(feature = "std")] fn test_add() { let a_dim = Dim([2, 4, 2, 2]); let b_dim = Dim([2, 1, 2, 1]); let mut a = ArcArray::linspace(0.0, 1., a_dim.size()) .into_shape_with_order(a_dim) .unwrap(); let b = ArcArray::linspace(0.0, 1., b_dim.size()) .into_shape_with_order(b_dim) .unwrap(); a += &b; let t = ArcArray::from_elem((), 1.0f32); a += &t; } #[test] #[should_panic] #[cfg(feature = "std")] fn test_add_incompat() { let a_dim = Dim([2, 4, 2, 2]); let mut a = ArcArray::linspace(0.0, 1., a_dim.size()) .into_shape_with_order(a_dim) .unwrap(); let incompat = ArcArray::from_elem(3, 1.0f32); a += &incompat; } #[test] fn test_broadcast() { let (_, n, k) = (16, 16, 16); let x1 = 1.; // b0 broadcast 1 -> n, k let x = Array::from(vec![x1]); let b0 = x.broadcast((n, k)).unwrap(); // b1 broadcast n -> n, k let b1 = Array::from_elem(n, x1); let b1 = b1.broadcast((n, k)).unwrap(); // b2 is n, k let b2 = Array::from_elem((n, k), x1); println!("b0=\n{:?}", b0); println!("b1=\n{:?}", b1); println!("b2=\n{:?}", b2); assert_eq!(b0, b1); assert_eq!(b0, b2); } #[test] fn test_broadcast_1d() { let n = 16; let x1 = 1.; // b0 broadcast 1 -> n let x = Array::from(vec![x1]); let b0 = x.broadcast(n).unwrap(); let b2 = Array::from_elem(n, x1); println!("b0=\n{:?}", b0); println!("b2=\n{:?}", b2); assert_eq!(b0, b2); } ndarray-0.16.1/tests/clone.rs000064400000000000000000000004451046102023000141640ustar 00000000000000use ndarray::arr2; #[test] fn test_clone_from() { let a = arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9]]); let b = arr2(&[[7, 7, 7]]); let mut c = b.clone(); c.clone_from(&a); assert_eq!(a, c); let mut bv = b.view(); bv.clone_from(&a.view()); assert_eq!(&a, &bv); } ndarray-0.16.1/tests/complex.rs000064400000000000000000000010711046102023000145270ustar 00000000000000use ndarray::Array; use ndarray::{arr1, arr2, Axis}; use num_complex::Complex; use num_traits::Num; fn c(re: T, im: T) -> Complex { Complex::new(re, im) } #[test] fn complex_mat_mul() { let a = arr2(&[[c(3., 4.), c(2., 0.)], [c(0., -2.), c(3., 0.)]]); let b = (&a * c(3., 0.)).map(|c| 5. * c / c.norm_sqr()); println!("{:>8.2}", b); let e = Array::eye(2); let r = a.dot(&e); println!("{}", a); assert_eq!(r, a); assert_eq!( a.mean_axis(Axis(0)).unwrap(), arr1(&[c(1.5, 1.), c(2.5, 0.)]) ); } ndarray-0.16.1/tests/dimension.rs000064400000000000000000000226711046102023000150560ustar 00000000000000#![allow(clippy::float_cmp)] use defmac::defmac; use ndarray::{arr2, ArcArray, Array, Axis, Dim, Dimension, IxDyn, RemoveAxis}; use std::hash::{Hash, Hasher}; #[test] fn insert_axis() { assert_eq!(Dim([]).insert_axis(Axis(0)), Dim([1])); assert_eq!(Dim([3]).insert_axis(Axis(0)), Dim([1, 3])); assert_eq!(Dim([3]).insert_axis(Axis(1)), Dim([3, 1])); assert_eq!(Dim([2, 3]).insert_axis(Axis(0)), Dim([1, 2, 3])); assert_eq!(Dim([2, 3]).insert_axis(Axis(1)), Dim([2, 1, 3])); assert_eq!(Dim([2, 3]).insert_axis(Axis(2)), Dim([2, 3, 1])); assert_eq!(Dim([2, 3, 4]).insert_axis(Axis(2)), Dim([2, 3, 1, 4])); assert_eq!( Dim([2, 3, 4, 5, 6, 7]).insert_axis(Axis(2)), Dim(vec![2, 3, 1, 4, 5, 6, 7]) ); assert_eq!(Dim(vec![]).insert_axis(Axis(0)), Dim(vec![1])); assert_eq!(Dim(vec![2, 3]).insert_axis(Axis(0)), Dim(vec![1, 2, 3])); assert_eq!(Dim(vec![2, 3]).insert_axis(Axis(1)), Dim(vec![2, 1, 3])); assert_eq!(Dim(vec![2, 3]).insert_axis(Axis(2)), Dim(vec![2, 3, 1])); assert_eq!( Dim(vec![2, 3, 4, 5, 6]).insert_axis(Axis(2)), Dim(vec![2, 3, 1, 4, 5, 6]) ); assert_eq!( Dim(vec![2, 3, 4, 5, 6, 7]).insert_axis(Axis(2)), Dim(vec![2, 3, 1, 4, 5, 6, 7]) ); } #[test] fn remove_axis() { assert_eq!(Dim([3]).remove_axis(Axis(0)), Dim([])); assert_eq!(Dim([1, 2]).remove_axis(Axis(0)), Dim([2])); assert_eq!(Dim([4, 5, 6]).remove_axis(Axis(1)), Dim([4, 6])); assert_eq!(Dim(vec![1, 2]).remove_axis(Axis(0)), Dim(vec![2])); assert_eq!(Dim(vec![4, 5, 6]).remove_axis(Axis(1)), Dim(vec![4, 6])); let a = ArcArray::::zeros((4, 5)); a.index_axis(Axis(1), 0); let a = ArcArray::::zeros(vec![4, 5, 6]); let _b = a .index_axis_move(Axis(1), 0) .to_shape((4, 6)) .unwrap() .to_shape(vec![2, 3, 4]) .unwrap(); } #[test] #[allow(clippy::eq_op)] fn dyn_dimension() { let a = arr2(&[[1., 2.], [3., 4.0]]) .into_shape_with_order(vec![2, 2]) .unwrap(); assert_eq!(&a - &a, Array::zeros(vec![2, 2])); assert_eq!(a[&[0, 0][..]], 1.); assert_eq!(a[[0, 0]], 1.); let mut dim = vec![1; 1024]; dim[16] = 4; dim[17] = 3; let z = Array::::zeros(dim.clone()); assert_eq!(z.shape(), &dim[..]); } #[test] fn dyn_insert() { let mut v = vec![2, 3, 4, 5]; let mut dim = Dim(v.clone()); defmac!(test_insert index => { dim = dim.insert_axis(Axis(index)); v.insert(index, 1); assert_eq!(dim.slice(), &v[..]); }); test_insert!(1); test_insert!(5); test_insert!(0); test_insert!(3); test_insert!(2); test_insert!(4); test_insert!(7); } #[test] fn dyn_remove() { let mut v = vec![1, 2, 3, 4, 5, 6, 7]; let mut dim = Dim(v.clone()); defmac!(test_remove index => { dim = dim.remove_axis(Axis(index)); v.remove(index); assert_eq!(dim.slice(), &v[..]); }); test_remove!(1); test_remove!(2); test_remove!(3); test_remove!(0); test_remove!(2); test_remove!(0); test_remove!(0); } #[test] fn fastest_varying_order() { let strides = Dim([2, 8, 4, 1]); let order = strides._fastest_varying_stride_order(); assert_eq!(order.slice(), &[3, 0, 2, 1]); let strides = Dim([-2isize as usize, 8, -4isize as usize, -1isize as usize]); let order = strides._fastest_varying_stride_order(); assert_eq!(order.slice(), &[3, 0, 2, 1]); assert_eq!(Dim([1, 3])._fastest_varying_stride_order(), Dim([0, 1])); assert_eq!( Dim([1, -3isize as usize])._fastest_varying_stride_order(), Dim([0, 1]) ); assert_eq!(Dim([7, 2])._fastest_varying_stride_order(), Dim([1, 0])); assert_eq!( Dim([-7isize as usize, 2])._fastest_varying_stride_order(), Dim([1, 0]) ); assert_eq!( Dim([6, 1, 3])._fastest_varying_stride_order(), Dim([1, 2, 0]) ); assert_eq!( Dim([-6isize as usize, 1, -3isize as usize])._fastest_varying_stride_order(), Dim([1, 2, 0]) ); // it's important that it produces distinct indices. Prefer the stable order // where 0 is before 1 when they are equal. assert_eq!(Dim([2, 2])._fastest_varying_stride_order(), [0, 1]); assert_eq!(Dim([2, 2, 1])._fastest_varying_stride_order(), [2, 0, 1]); assert_eq!( Dim([-2isize as usize, -2isize as usize, 3, 1, -2isize as usize]) ._fastest_varying_stride_order(), [3, 0, 1, 4, 2] ); } type ArrayF32 = Array; /* #[test] fn min_stride_axis() { let a = ArrayF32::zeros(10); assert_eq!(a.min_stride_axis(), Axis(0)); let a = ArrayF32::zeros((3, 3)); assert_eq!(a.min_stride_axis(), Axis(1)); assert_eq!(a.t().min_stride_axis(), Axis(0)); let a = ArrayF32::zeros(vec![3, 3]); assert_eq!(a.min_stride_axis(), Axis(1)); assert_eq!(a.t().min_stride_axis(), Axis(0)); let min_axis = a.axes().min_by_key(|t| t.2.abs()).unwrap().axis(); assert_eq!(min_axis, Axis(1)); let mut b = ArrayF32::zeros(vec![2, 3, 4, 5]); assert_eq!(b.min_stride_axis(), Axis(3)); for ax in 0..3 { b.swap_axes(3, ax); assert_eq!(b.min_stride_axis(), Axis(ax)); b.swap_axes(3, ax); } let a = ArrayF32::zeros((3, 3)); let v = a.broadcast((8, 3, 3)).unwrap(); assert_eq!(v.min_stride_axis(), Axis(0)); } */ #[test] fn max_stride_axis() { let a = ArrayF32::zeros(10); assert_eq!(a.max_stride_axis(), Axis(0)); let a = ArrayF32::zeros((3, 3)); assert_eq!(a.max_stride_axis(), Axis(0)); assert_eq!(a.t().max_stride_axis(), Axis(1)); let a = ArrayF32::zeros(vec![1, 3]); assert_eq!(a.max_stride_axis(), Axis(1)); let a = ArrayF32::zeros((1, 3)); assert_eq!(a.max_stride_axis(), Axis(1)); let a = ArrayF32::zeros(vec![3, 3]); assert_eq!(a.max_stride_axis(), Axis(0)); assert_eq!(a.t().max_stride_axis(), Axis(1)); let mut b = ArrayF32::zeros(vec![2, 3, 4, 5]); assert_eq!(b.max_stride_axis(), Axis(0)); for ax in 1..b.ndim() { b.swap_axes(0, ax); assert_eq!(b.max_stride_axis(), Axis(ax)); b.swap_axes(0, ax); } } #[test] fn test_indexing() { let mut x = Dim([1, 2]); assert_eq!(x[0], 1); assert_eq!(x[1], 2); x[0] = 7; assert_eq!(x, [7, 2]); } #[test] fn test_operations() { let mut x = Dim([1, 2]); let mut y = Dim([1, 1]); assert_eq!(x + y, [2, 3]); x += y; assert_eq!(x, [2, 3]); x *= 2; assert_eq!(x, [4, 6]); y[0] -= 1; assert_eq!(y, [0, 1]); } #[test] #[allow(clippy::cognitive_complexity)] fn test_hash() { fn calc_hash(value: &T) -> u64 { let mut hasher = std::collections::hash_map::DefaultHasher::new(); value.hash(&mut hasher); hasher.finish() } macro_rules! test_hash_eq { ($arr:expr) => { assert_eq!(calc_hash(&Dim($arr)), calc_hash(&Dim($arr))); assert_eq!(calc_hash(&Dim($arr)), calc_hash(&IxDyn(&$arr))); }; } macro_rules! test_hash_ne { ($arr1:expr, $arr2:expr) => { assert_ne!(calc_hash(&Dim($arr1)), calc_hash(&Dim($arr2))); assert_ne!(calc_hash(&Dim($arr1)), calc_hash(&IxDyn(&$arr2))); assert_ne!(calc_hash(&IxDyn(&$arr1)), calc_hash(&Dim($arr2))); }; } test_hash_eq!([]); test_hash_eq!([0]); test_hash_eq!([1]); test_hash_eq!([1, 2]); test_hash_eq!([3, 1, 2]); test_hash_eq!([3, 1, 4, 2]); test_hash_eq!([3, 1, 4, 2, 5]); test_hash_eq!([6, 3, 1, 4, 2, 5]); test_hash_ne!([0], [1]); test_hash_ne!([1, 2], [2, 1]); test_hash_ne!([3, 1, 2], [3, 1, 3]); test_hash_ne!([3, 1, 2, 4], [3, 1, 2, 3]); test_hash_ne!([3, 1, 2, 5, 4], [3, 1, 2, 4, 5]); test_hash_ne!([3, 1, 6, 2, 5, 4], [3, 1, 2, 4, 6, 5]); } #[test] fn test_generic_operations() { fn test_dim(d: &D) { let mut x = d.clone(); x[0] += 1; assert_eq!(x[0], 3); x += d; assert_eq!(x[0], 5); } test_dim(&Dim([2, 3, 4])); test_dim(&Dim(vec![2, 3, 4, 1])); test_dim(&Dim(2)); } #[test] fn test_array_view() { fn test_dim(d: &D) { assert_eq!(d.as_array_view().sum(), 7); assert_eq!(d.as_array_view().strides(), &[1]); } test_dim(&Dim([1, 2, 4])); test_dim(&Dim(vec![1, 1, 2, 3])); test_dim(&Dim(7)); } #[test] #[cfg(feature = "std")] #[allow(clippy::cognitive_complexity)] fn test_all_ndindex() { use ndarray::IntoDimension; macro_rules! ndindex { ($($i:expr),*) => { for &rev in &[false, true] { // rev is for C / F order let size = $($i *)* 1; let mut a = Array::linspace(0., (size - 1) as f64, size); if rev { a = a.reversed_axes(); } for (i, &elt) in a.indexed_iter() { let dim = i.into_dimension(); assert_eq!(elt, a[i]); assert_eq!(elt, a[dim]); } let dim = a.shape().to_vec(); let b = a.broadcast(dim).unwrap(); for (i, &elt) in b.indexed_iter() { let dim = i.into_dimension(); assert_eq!(elt, b[dim.slice()]); assert_eq!(elt, b[&dim]); assert_eq!(elt, b[dim]); } } }; } ndindex!(10); ndindex!(10, 4); ndindex!(10, 4, 3); ndindex!(10, 4, 3, 2); ndindex!(10, 4, 3, 2, 2); ndindex!(10, 4, 3, 2, 2, 2); } ndarray-0.16.1/tests/format.rs000064400000000000000000000026411046102023000143540ustar 00000000000000use ndarray::prelude::*; use ndarray::rcarr1; #[test] fn formatting() { let a = rcarr1::(&[1., 2., 3., 4.]); assert_eq!(format!("{}", a), "[1, 2, 3, 4]"); assert_eq!(format!("{:4}", a), "[ 1, 2, 3, 4]"); let a = a.into_shape_clone((4, 1, 1)).unwrap(); assert_eq!( format!("{}", a), "\ [[[1]], [[2]], [[3]], [[4]]]" ); assert_eq!( format!("{:4}", a), "\ [[[ 1]], [[ 2]], [[ 3]], [[ 4]]]", ); let a = a.into_shape_clone((2, 2)).unwrap(); assert_eq!( format!("{}", a), "\ [[1, 2], [3, 4]]" ); assert_eq!( format!("{:4}", a), "\ [[ 1, 2], [ 3, 4]]" ); let b = arr0::(3.5); assert_eq!(format!("{}", b), "3.5"); let s = format!("{:.3e}", aview1::(&[1.1, 2.2, 33., 440.])); assert_eq!(s, "[1.100e0, 2.200e0, 3.300e1, 4.400e2]"); let s = format!("{:02x}", aview1::(&[1, 0xff, 0xfe])); assert_eq!(s, "[01, ff, fe]"); } #[test] fn debug_format() { let a = Array2::::zeros((3, 4)); assert_eq!( format!("{:?}", a), "\ [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=Cc (0x5), const ndim=2" ); assert_eq!( format!("{:?}", a.into_dyn()), "\ [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], shape=[3, 4], strides=[4, 1], layout=Cc (0x5), dynamic ndim=2" ); } ndarray-0.16.1/tests/higher_order_f.rs000064400000000000000000000002431046102023000160260ustar 00000000000000use ndarray::prelude::*; #[test] #[should_panic] fn test_fold_axis_oob() { let a = arr2(&[[1., 2.], [3., 4.]]); a.fold_axis(Axis(2), 0., |x, y| x + y); } ndarray-0.16.1/tests/indices.rs000064400000000000000000000013201046102023000144730ustar 00000000000000use ndarray::indices_of; use ndarray::prelude::*; use ndarray::Order; #[test] fn test_ixdyn_index_iterate() { for &order in &[Order::C, Order::F] { let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = i + 10 * j + 100 * k; } let a = a.into_shape_with_order((dim, order)).unwrap(); println!("{:?}", a.dim()); let mut c = 0; for i in indices_of(&a) { let ans = i[0] + 10 * i[1] + 100 * i[2]; println!("{:?}", i); assert_eq!(a[i], ans); c += 1; } assert_eq!(c, a.len()); } } ndarray-0.16.1/tests/into-ixdyn.rs000064400000000000000000000010021046102023000151540ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::prelude::*; #[test] fn test_arr0_into_dyn() { assert!(arr0(1.234).into_dyn()[IxDyn(&[])] == 1.234); } #[test] fn test_arr2_into_arrd_nonstandard_strides() { let arr = Array2::from_shape_fn((12, 34).f(), |(i, j)| i * 34 + j).into_dyn(); let brr = ArrayD::from_shape_fn(vec![12, 34], |d| d[0] * 34 + d[1]); assert!(arr == brr); } ndarray-0.16.1/tests/iterator_chunks.rs000064400000000000000000000052041046102023000162660ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::prelude::*; #[test] #[cfg(feature = "std")] fn chunks() { use ndarray::NdProducer; let a = >::linspace(1., 100., 10 * 10) .into_shape_with_order((10, 10)) .unwrap(); let (m, n) = a.dim(); for i in 1..=m { for j in 1..=n { let c = a.exact_chunks((i, j)); let ly = n / j; for (index, elt) in c.into_iter().enumerate() { assert_eq!(elt.dim(), (i, j)); let cindex = (index / ly, index % ly); let cx = (cindex.0 * i) as isize; let cy = (cindex.1 * j) as isize; assert_eq!( elt, a.slice(s![cx.., cy..]) .slice(s![..i as isize, ..j as isize]) ); } let c = a.exact_chunks((i, j)); assert_eq!(c.into_iter().count(), (m / i) * (n / j)); let c = a.exact_chunks((i, j)); let (c1, c2) = c.split_at(Axis(0), (m / i) / 2); assert_eq!(c1.into_iter().count(), ((m / i) / 2) * (n / j)); assert_eq!(c2.into_iter().count(), (m / i - (m / i) / 2) * (n / j)); } } let c = a.exact_chunks((m + 1, n)); assert_eq!(c.raw_dim().size(), 0); assert_eq!(c.into_iter().count(), 0); } #[should_panic] #[test] fn chunks_different_size_1() { let a = Array::::zeros(vec![2, 3]); a.exact_chunks(vec![2]); } #[test] fn chunks_ok_size() { let mut a = Array::::zeros(vec![2, 3]); a.fill(1.); let mut c = 0; for elt in a.exact_chunks(vec![2, 1]) { assert!(elt.iter().all(|&x| x == 1.)); assert_eq!(elt.shape(), &[2, 1]); c += 1; } assert_eq!(c, 3); } #[should_panic] #[test] fn chunks_different_size_2() { let a = Array::::zeros(vec![2, 3]); a.exact_chunks(vec![2, 3, 4]); } #[test] fn chunks_mut() { let mut a = Array::zeros((7, 8)); for (i, mut chunk) in a.exact_chunks_mut((2, 3)).into_iter().enumerate() { chunk.fill(i); } println!("{:?}", a); let ans = array![ [0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0], [2, 2, 2, 3, 3, 3, 0, 0], [2, 2, 2, 3, 3, 3, 0, 0], [4, 4, 4, 5, 5, 5, 0, 0], [4, 4, 4, 5, 5, 5, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ]; assert_eq!(a, ans); } #[should_panic] #[test] fn chunks_different_size_3() { let mut a = Array::::zeros(vec![2, 3]); a.exact_chunks_mut(vec![2, 3, 4]); } ndarray-0.16.1/tests/iterators.rs000064400000000000000000000716611046102023000151100ustar 00000000000000#![allow(clippy::deref_addrof, clippy::unreadable_literal)] use ndarray::prelude::*; use ndarray::{arr3, indices, s, Slice, Zip}; use itertools::assert_equal; use itertools::enumerate; use std::cell::Cell; macro_rules! assert_panics { ($body:expr) => { if let Ok(v) = ::std::panic::catch_unwind(|| $body) { panic!("assertion failed: should_panic; \ non-panicking result: {:?}", v); } }; ($body:expr, $($arg:tt)*) => { if let Ok(_) = ::std::panic::catch_unwind(|| $body) { panic!($($arg)*); } }; } #[test] #[cfg(feature = "std")] fn double_ended() { let a = ArcArray::linspace(0., 7., 8); let mut it = a.iter().cloned(); assert_eq!(it.next(), Some(0.)); assert_eq!(it.next_back(), Some(7.)); assert_eq!(it.next(), Some(1.)); assert_eq!(it.rev().last(), Some(2.)); assert_equal(aview1(&[1, 2, 3]), &[1, 2, 3]); assert_equal(aview1(&[1, 2, 3]).into_iter().rev(), [1, 2, 3].iter().rev()); } #[test] fn double_ended_rows() { let a = ArcArray::from_iter(0..8).into_shape_clone((4, 2)).unwrap(); let mut row_it = a.rows().into_iter(); assert_equal(row_it.next_back().unwrap(), &[6, 7]); assert_equal(row_it.next().unwrap(), &[0, 1]); assert_equal(row_it.next_back().unwrap(), &[4, 5]); assert_equal(row_it.next_back().unwrap(), &[2, 3]); assert!(row_it.next().is_none()); assert!(row_it.next_back().is_none()); for (row, check) in a .rows() .into_iter() .rev() .zip(&[[6, 7], [4, 5], [2, 3], [0, 1]]) { assert_equal(row, check); } } #[test] fn iter_size_hint() { // Check that the size hint is correctly computed let a = ArcArray::from_iter(0..24) .into_shape_with_order((2, 3, 4)) .unwrap(); let mut data = [0; 24]; for (i, elt) in enumerate(&mut data) { *elt = i as i32; } assert_equal(&a, &data); let mut it = a.iter(); let mut ans = data.iter(); assert_eq!(it.len(), ans.len()); while ans.len() > 0 { assert_eq!(it.next(), ans.next()); assert_eq!(it.len(), ans.len()); } } #[test] #[cfg(feature = "std")] fn indexed() { let a = ArcArray::linspace(0., 7., 8); for (i, elt) in a.indexed_iter() { assert_eq!(i, *elt as usize); } let a = a.into_shape_with_order((2, 4, 1)).unwrap(); let (mut i, mut j, k) = (0, 0, 0); for (idx, elt) in a.indexed_iter() { assert_eq!(idx, (i, j, k)); j += 1; if j == 4 { j = 0; i += 1; } println!("{:?}", (idx, elt)); } } #[test] #[cfg(feature = "std")] fn as_slice() { use ndarray::Data; fn assert_slice_correct(v: &ArrayBase) where S: Data, D: Dimension, A: PartialEq + std::fmt::Debug, { let slc = v.as_slice(); assert!(slc.is_some()); let slc = slc.unwrap(); assert_eq!(v.len(), slc.len()); assert_equal(v.iter(), slc); } let a = ArcArray::linspace(0., 7., 8); let a = a.into_shape_with_order((2, 4, 1)).unwrap(); assert_slice_correct(&a); let a = a.into_shape_with_order((2, 4)).unwrap(); assert_slice_correct(&a); assert!(a.view().index_axis(Axis(1), 0).as_slice().is_none()); let v = a.view(); assert_slice_correct(&v); assert_slice_correct(&v.index_axis(Axis(0), 0)); assert_slice_correct(&v.index_axis(Axis(0), 1)); assert!(v.slice(s![.., ..1]).as_slice().is_none()); println!("{:?}", v.slice(s![..1;2, ..])); assert!(v.slice(s![..1;2, ..]).as_slice().is_some()); // `u` is contiguous, because the column stride of `2` doesn't matter // when the result is just one row anyway -- length of that dimension is 1 let u = v.slice(s![..1;2, ..]); println!("{:?}", u.shape()); println!("{:?}", u.strides()); println!("{:?}", v.slice(s![..1;2, ..])); assert!(u.as_slice().is_some()); assert_slice_correct(&u); let a = a.into_shape_with_order((8, 1)).unwrap(); assert_slice_correct(&a); let u = a.slice(s![..;2, ..]); println!( "u={:?}, shape={:?}, strides={:?}", u, u.shape(), u.strides() ); assert!(u.as_slice().is_none()); } #[test] fn inner_iter() { let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], // [[6, 7], // [8, 9], // ... assert_equal(a.rows(), vec![ aview1(&[0, 1]), aview1(&[2, 3]), aview1(&[4, 5]), aview1(&[6, 7]), aview1(&[8, 9]), aview1(&[10, 11]), ]); let mut b = ArcArray::zeros((2, 3, 2)); b.swap_axes(0, 2); b.assign(&a); assert_equal(b.rows(), vec![ aview1(&[0, 1]), aview1(&[2, 3]), aview1(&[4, 5]), aview1(&[6, 7]), aview1(&[8, 9]), aview1(&[10, 11]), ]); } #[test] fn inner_iter_corner_cases() { let a0 = ArcArray::::zeros(()); assert_equal(a0.rows(), vec![aview1(&[0])]); let a2 = ArcArray::::zeros((0, 3)); assert_equal(a2.rows(), vec![aview1(&[]); 0]); let a2 = ArcArray::::zeros((3, 0)); assert_equal(a2.rows(), vec![aview1(&[]); 3]); } #[test] fn inner_iter_size_hint() { // Check that the size hint is correctly computed let a = ArcArray::from_iter(0..24) .into_shape_with_order((2, 3, 4)) .unwrap(); let mut len = 6; let mut it = a.rows().into_iter(); assert_eq!(it.len(), len); while len > 0 { it.next(); len -= 1; assert_eq!(it.len(), len); } } #[allow(deprecated)] // into_outer_iter #[test] fn outer_iter() { let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], // [[6, 7], // [8, 9], // ... assert_equal(a.outer_iter(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut b = ArcArray::zeros((2, 3, 2)); b.swap_axes(0, 2); b.assign(&a); assert_equal(b.outer_iter(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut found_rows = Vec::new(); for sub in b.outer_iter() { for row in sub.into_outer_iter() { found_rows.push(row); } } assert_equal(a.rows(), found_rows.clone()); let mut found_rows_rev = Vec::new(); for sub in b.outer_iter().rev() { for row in sub.into_outer_iter().rev() { found_rows_rev.push(row); } } found_rows_rev.reverse(); assert_eq!(&found_rows, &found_rows_rev); // Test a case where strides are negative instead let mut c = ArcArray::zeros((2, 3, 2)); let mut cv = c.slice_mut(s![..;-1, ..;-1, ..;-1]); cv.assign(&a); assert_eq!(&a, &cv); assert_equal(cv.outer_iter(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut found_rows = Vec::new(); for sub in cv.outer_iter() { for row in sub.into_outer_iter() { found_rows.push(row); } } println!("{:#?}", found_rows); assert_equal(a.rows(), found_rows); } #[test] fn axis_iter() { let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], // [[6, 7], // [8, 9], // ... assert_equal(a.axis_iter(Axis(1)), vec![ a.index_axis(Axis(1), 0), a.index_axis(Axis(1), 1), a.index_axis(Axis(1), 2), ]); } #[test] fn axis_iter_split_at() { let a = Array::from_iter(0..5); let iter = a.axis_iter(Axis(0)); let all: Vec<_> = iter.clone().collect(); for mid in 0..=all.len() { let (left, right) = iter.clone().split_at(mid); assert_eq!(&all[..mid], &left.collect::>()[..]); assert_eq!(&all[mid..], &right.collect::>()[..]); } } #[test] fn axis_iter_split_at_partially_consumed() { let a = Array::from_iter(0..5); let mut iter = a.axis_iter(Axis(0)); while iter.next().is_some() { let remaining: Vec<_> = iter.clone().collect(); for mid in 0..=remaining.len() { let (left, right) = iter.clone().split_at(mid); assert_eq!(&remaining[..mid], &left.collect::>()[..]); assert_eq!(&remaining[mid..], &right.collect::>()[..]); } } } #[test] fn axis_iter_zip() { let a = Array::from_iter(0..5); let iter = a.axis_iter(Axis(0)); let mut b = Array::zeros(5); Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a, b); } #[test] fn axis_iter_zip_partially_consumed() { let a = Array::from_iter(0..5); let mut iter = a.axis_iter(Axis(0)); let mut consumed = 0; while iter.next().is_some() { consumed += 1; let mut b = Array::zeros(a.len() - consumed); Zip::from(&mut b) .and(iter.clone()) .for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } #[test] fn axis_iter_zip_partially_consumed_discontiguous() { let a = Array::from_iter(0..5); let mut iter = a.axis_iter(Axis(0)); let mut consumed = 0; while iter.next().is_some() { consumed += 1; let mut b = Array::zeros((a.len() - consumed) * 2); b.slice_collapse(s![..;2]); Zip::from(&mut b) .and(iter.clone()) .for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } #[test] fn outer_iter_corner_cases() { let a2 = ArcArray::::zeros((0, 3)); assert_equal(a2.outer_iter(), vec![aview1(&[]); 0]); let a2 = ArcArray::::zeros((3, 0)); assert_equal(a2.outer_iter(), vec![aview1(&[]); 3]); } #[allow(deprecated)] #[test] fn outer_iter_mut() { let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], // [[6, 7], // [8, 9], // ... let mut b = ArcArray::zeros((2, 3, 2)); b.swap_axes(0, 2); b.assign(&a); assert_equal(b.outer_iter_mut(), vec![a.index_axis(Axis(0), 0), a.index_axis(Axis(0), 1)]); let mut found_rows = Vec::new(); for sub in b.outer_iter_mut() { for row in sub.into_outer_iter() { found_rows.push(row); } } assert_equal(a.rows(), found_rows); } #[test] fn axis_iter_mut() { let a = ArcArray::from_iter(0..12); let a = a.into_shape_with_order((2, 3, 2)).unwrap(); // [[[0, 1], // [2, 3], // [4, 5]], // [[6, 7], // [8, 9], // ... let mut a = a.to_owned(); for mut subview in a.axis_iter_mut(Axis(1)) { subview[[0, 0]] = 42; } let b = arr3(&[[[42, 1], [42, 3], [42, 5]], [[6, 7], [8, 9], [10, 11]]]); assert_eq!(a, b); } #[test] fn axis_chunks_iter() { let a = ArcArray::from_iter(0..24); let a = a.into_shape_with_order((2, 6, 2)).unwrap(); let it = a.axis_chunks_iter(Axis(1), 2); assert_equal(it, vec![ arr3(&[[[0, 1], [2, 3]], [[12, 13], [14, 15]]]), arr3(&[[[4, 5], [6, 7]], [[16, 17], [18, 19]]]), arr3(&[[[8, 9], [10, 11]], [[20, 21], [22, 23]]]), ]); let a = ArcArray::from_iter(0..28); let a = a.into_shape_with_order((2, 7, 2)).unwrap(); let it = a.axis_chunks_iter(Axis(1), 2); assert_equal(it, vec![ arr3(&[[[0, 1], [2, 3]], [[14, 15], [16, 17]]]), arr3(&[[[4, 5], [6, 7]], [[18, 19], [20, 21]]]), arr3(&[[[8, 9], [10, 11]], [[22, 23], [24, 25]]]), arr3(&[[[12, 13]], [[26, 27]]]), ]); let it = a.axis_chunks_iter(Axis(1), 2).rev(); assert_equal(it, vec![ arr3(&[[[12, 13]], [[26, 27]]]), arr3(&[[[8, 9], [10, 11]], [[22, 23], [24, 25]]]), arr3(&[[[4, 5], [6, 7]], [[18, 19], [20, 21]]]), arr3(&[[[0, 1], [2, 3]], [[14, 15], [16, 17]]]), ]); let it = a.axis_chunks_iter(Axis(1), 7); assert_equal(it, vec![a.view()]); let it = a.axis_chunks_iter(Axis(1), 9); assert_equal(it, vec![a.view()]); } #[test] fn axis_iter_mut_split_at() { let mut a = Array::from_iter(0..5); let mut a_clone = a.clone(); let all: Vec<_> = a_clone.axis_iter_mut(Axis(0)).collect(); for mid in 0..=all.len() { let (left, right) = a.axis_iter_mut(Axis(0)).split_at(mid); assert_eq!(&all[..mid], &left.collect::>()[..]); assert_eq!(&all[mid..], &right.collect::>()[..]); } } #[test] fn axis_iter_mut_split_at_partially_consumed() { let mut a = Array::from_iter(0..5); for consumed in 1..=a.len() { for mid in 0..=(a.len() - consumed) { let mut a_clone = a.clone(); let remaining: Vec<_> = { let mut iter = a_clone.axis_iter_mut(Axis(0)); for _ in 0..consumed { iter.next(); } iter.collect() }; let (left, right) = { let mut iter = a.axis_iter_mut(Axis(0)); for _ in 0..consumed { iter.next(); } iter.split_at(mid) }; assert_eq!(&remaining[..mid], &left.collect::>()[..]); assert_eq!(&remaining[mid..], &right.collect::>()[..]); } } } #[test] fn axis_iter_mut_zip() { let orig = Array::from_iter(0..5); let mut cloned = orig.clone(); let iter = cloned.axis_iter_mut(Axis(0)); let mut b = Array::zeros(5); Zip::from(&mut b).and(iter).for_each(|b, mut a| { a[()] += 1; *b = a[()]; }); assert_eq!(cloned, b); assert_eq!(cloned, orig + 1); } #[test] fn axis_iter_mut_zip_partially_consumed() { let mut a = Array::from_iter(0..5); for consumed in 1..=a.len() { let remaining = a.len() - consumed; let mut iter = a.axis_iter_mut(Axis(0)); for _ in 0..consumed { iter.next(); } let mut b = Array::zeros(remaining); Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } #[test] fn axis_iter_mut_zip_partially_consumed_discontiguous() { let mut a = Array::from_iter(0..5); for consumed in 1..=a.len() { let remaining = a.len() - consumed; let mut iter = a.axis_iter_mut(Axis(0)); for _ in 0..consumed { iter.next(); } let mut b = Array::zeros(remaining * 2); b.slice_collapse(s![..;2]); Zip::from(&mut b).and(iter).for_each(|b, a| *b = a[()]); assert_eq!(a.slice(s![consumed..]), b); } } #[test] #[cfg(feature = "std")] fn axis_chunks_iter_corner_cases() { // examples provided by @bluss in PR #65 // these tests highlight corner cases of the axis_chunks_iter implementation // and enable checking if no pointer offsetting is out of bounds. However // checking the absence of of out of bounds offsetting cannot (?) be // done automatically, so one has to launch this test in a debugger. let a = ArcArray::::linspace(0., 7., 8) .into_shape_with_order((8, 1)) .unwrap(); let it = a.axis_chunks_iter(Axis(0), 4); assert_equal(it, vec![a.slice(s![..4, ..]), a.slice(s![4.., ..])]); let a = a.slice(s![..;-1,..]); let it = a.axis_chunks_iter(Axis(0), 8); assert_equal(it, vec![a.view()]); let it = a.axis_chunks_iter(Axis(0), 3); assert_equal(it, vec![ array![[7.], [6.], [5.]], array![[4.], [3.], [2.]], array![[1.], [0.]], ]); let b = ArcArray::::zeros((8, 2)); let a = b.slice(s![1..;2,..]); let it = a.axis_chunks_iter(Axis(0), 8); assert_equal(it, vec![a.view()]); let it = a.axis_chunks_iter(Axis(0), 1); assert_equal(it, vec![ArcArray::zeros((1, 2)); 4]); } #[test] fn axis_chunks_iter_zero_stride() { { // stride 0 case let b = Array::from(vec![0f32; 0]) .into_shape_with_order((5, 0, 3)) .unwrap(); let shapes: Vec<_> = b .axis_chunks_iter(Axis(0), 2) .map(|v| v.raw_dim()) .collect(); assert_eq!(shapes, vec![Ix3(2, 0, 3), Ix3(2, 0, 3), Ix3(1, 0, 3)]); } { // stride 0 case reverse let b = Array::from(vec![0f32; 0]) .into_shape_with_order((5, 0, 3)) .unwrap(); let shapes: Vec<_> = b .axis_chunks_iter(Axis(0), 2) .rev() .map(|v| v.raw_dim()) .collect(); assert_eq!(shapes, vec![Ix3(1, 0, 3), Ix3(2, 0, 3), Ix3(2, 0, 3)]); } // From issue #542, ZST element { let a = Array::from(vec![(); 3]); let chunks: Vec<_> = a.axis_chunks_iter(Axis(0), 2).collect(); assert_eq!(chunks, vec![a.slice(s![0..2]), a.slice(s![2..])]); } } #[should_panic] #[test] fn axis_chunks_iter_zero_chunk_size() { let a = Array::from_iter(0..5); a.axis_chunks_iter(Axis(0), 0); } #[test] fn axis_chunks_iter_zero_axis_len() { let a = Array::from_iter(0..0); assert!(a.axis_chunks_iter(Axis(0), 5).next().is_none()); } #[test] fn axis_chunks_iter_split_at() { let mut a = Array2::::zeros((11, 3)); a.iter_mut().enumerate().for_each(|(i, elt)| *elt = i); for source in &[ a.slice(s![..0, ..]), a.slice(s![..1, ..]), a.slice(s![..5, ..]), a.slice(s![..10, ..]), a.slice(s![..11, ..]), a.slice(s![.., ..0]), ] { let chunks_iter = source.axis_chunks_iter(Axis(0), 5); let all_chunks: Vec<_> = chunks_iter.clone().collect(); let n_chunks = chunks_iter.len(); assert_eq!(n_chunks, all_chunks.len()); for index in 0..=n_chunks { let (left, right) = chunks_iter.clone().split_at(index); assert_eq!(&all_chunks[..index], &left.collect::>()[..]); assert_eq!(&all_chunks[index..], &right.collect::>()[..]); } assert_panics!({ chunks_iter.split_at(n_chunks + 1); }); } } #[test] fn axis_chunks_iter_mut() { let a = ArcArray::from_iter(0..24); let mut a = a.into_shape_with_order((2, 6, 2)).unwrap(); let mut it = a.axis_chunks_iter_mut(Axis(1), 2); let mut col0 = it.next().unwrap(); col0[[0, 0, 0]] = 42; assert_eq!(col0, arr3(&[[[42, 1], [2, 3]], [[12, 13], [14, 15]]])); } #[should_panic] #[test] fn axis_chunks_iter_mut_zero_chunk_size() { let mut a = Array::from_iter(0..5); a.axis_chunks_iter_mut(Axis(0), 0); } #[test] fn axis_chunks_iter_mut_zero_axis_len() { let mut a = Array::from_iter(0..0); assert!(a.axis_chunks_iter_mut(Axis(0), 5).next().is_none()); } #[test] fn outer_iter_size_hint() { // Check that the size hint is correctly computed let a = ArcArray::from_iter(0..24) .into_shape_with_order((4, 3, 2)) .unwrap(); let mut len = 4; let mut it = a.outer_iter(); assert_eq!(it.len(), len); while len > 0 { it.next(); len -= 1; assert_eq!(it.len(), len); } // now try the double ended case let mut it = a.outer_iter(); it.next_back(); let mut len = 3; while len > 0 { it.next(); len -= 1; assert_eq!(it.len(), len); } let mut it = a.outer_iter(); it.next(); let mut len = 3; while len > 0 { it.next_back(); len -= 1; assert_eq!(it.len(), len); } } #[test] fn outer_iter_split_at() { let a = ArcArray::from_iter(0..30) .into_shape_with_order((5, 3, 2)) .unwrap(); let it = a.outer_iter(); let (mut itl, mut itr) = it.clone().split_at(2); assert_eq!(itl.next().unwrap()[[2, 1]], 5); assert_eq!(itl.next().unwrap()[[2, 1]], 11); assert_eq!(itl.next(), None); assert_eq!(itr.next().unwrap()[[2, 1]], 17); assert_eq!(itr.next().unwrap()[[2, 1]], 23); assert_eq!(itr.next().unwrap()[[2, 1]], 29); assert_eq!(itr.next(), None); // split_at on length should yield an empty iterator // on the right part let (_, mut itr) = it.split_at(5); assert_eq!(itr.next(), None); } #[test] #[should_panic] fn outer_iter_split_at_panics() { let a = ArcArray::from_iter(0..30) .into_shape_with_order((5, 3, 2)) .unwrap(); let it = a.outer_iter(); it.split_at(6); } #[test] fn outer_iter_mut_split_at() { let mut a = ArcArray::from_iter(0..30) .into_shape_with_order((5, 3, 2)) .unwrap(); { let it = a.outer_iter_mut(); let (mut itl, mut itr) = it.split_at(2); itl.next(); itl.next().unwrap()[[2, 1]] += 1; // now this value is 12 assert_eq!(itl.next(), None); itr.next(); itr.next(); itr.next().unwrap()[[2, 1]] -= 1; // now this value is 28 assert_eq!(itr.next(), None); } assert_eq!(a[[1, 2, 1]], 12); assert_eq!(a[[4, 2, 1]], 28); } #[test] fn iterators_are_send_sync() { // When the element type is Send + Sync, then the iterators and views // are too. fn _send_sync(_: &T) {} let mut a = ArcArray::from_iter(0..30) .into_shape_with_order((5, 3, 2)) .unwrap(); _send_sync(&a.view()); _send_sync(&a.view_mut()); _send_sync(&a.iter()); _send_sync(&a.iter_mut()); _send_sync(&a.indexed_iter()); _send_sync(&a.indexed_iter_mut()); _send_sync(&a.rows()); _send_sync(&a.rows_mut()); _send_sync(&a.outer_iter()); _send_sync(&a.outer_iter_mut()); _send_sync(&a.axis_iter(Axis(1))); _send_sync(&a.axis_iter_mut(Axis(1))); _send_sync(&a.axis_chunks_iter(Axis(1), 1)); _send_sync(&a.axis_chunks_iter_mut(Axis(1), 1)); _send_sync(&indices(a.dim())); _send_sync(&a.exact_chunks((1, 1, 1))); _send_sync(&a.exact_chunks_mut((1, 1, 1))); _send_sync(&a.exact_chunks((1, 1, 1)).into_iter()); _send_sync(&a.exact_chunks_mut((1, 1, 1)).into_iter()); } #[test] #[allow(clippy::unnecessary_fold)] fn test_fold() { let mut a = Array2::::default((20, 20)); a += 1; let mut iter = a.iter(); iter.next(); assert_eq!(iter.fold(0, |acc, &x| acc + x), a.sum() - 1); let mut a = Array0::::default(()); a += 1; assert_eq!(a.iter().fold(0, |acc, &x| acc + x), 1); } #[test] fn nth_back_examples() { let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); assert_eq!(a.iter().nth_back(0), Some(&a[a.len() - 1])); assert_eq!(a.iter().nth_back(1), Some(&a[a.len() - 2])); assert_eq!(a.iter().nth_back(a.len() - 2), Some(&a[1])); assert_eq!(a.iter().nth_back(a.len() - 1), Some(&a[0])); assert_eq!(a.iter().nth_back(a.len()), None); assert_eq!(a.iter().nth_back(a.len() + 1), None); assert_eq!(a.iter().nth_back(a.len() + 2), None); } #[test] fn nth_back_zero_n() { let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter1 = a.iter(); let mut iter2 = a.iter(); for _ in 0..(a.len() + 1) { assert_eq!(iter1.nth_back(0), iter2.next_back()); assert_eq!(iter1.len(), iter2.len()); } } #[test] fn nth_back_nonzero_n() { let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter1 = a.iter(); let mut iter2 = a.iter(); for _ in 0..(a.len() / 3 + 1) { assert_eq!(iter1.nth_back(2), { iter2.next_back(); iter2.next_back(); iter2.next_back() }); assert_eq!(iter1.len(), iter2.len()); } } #[test] fn nth_back_past_end() { let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter = a.iter(); assert_eq!(iter.nth_back(a.len()), None); assert_eq!(iter.next(), None); } #[test] fn nth_back_partially_consumed() { let mut a: Array1 = (0..256).collect(); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter = a.iter(); iter.next(); iter.next_back(); assert_eq!(iter.len(), a.len() - 2); assert_eq!(iter.nth_back(1), Some(&a[a.len() - 3])); assert_eq!(iter.len(), a.len() - 4); assert_eq!(iter.nth_back(a.len() - 6), Some(&a[2])); assert_eq!(iter.len(), 1); assert_eq!(iter.next(), Some(&a[1])); assert_eq!(iter.len(), 0); assert_eq!(iter.next(), None); assert_eq!(iter.next_back(), None); } #[test] fn test_rfold() { { let mut a = Array1::::default(256); a += 1; let mut iter = a.iter(); iter.next(); assert_eq!(iter.rfold(0, |acc, &x| acc + x), a.sum() - 1); } // Test strided arrays { let mut a = Array1::::default(256); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); a += 1; let mut iter = a.iter(); iter.next(); assert_eq!(iter.rfold(0, |acc, &x| acc + x), a.sum() - 1); } { let mut a = Array1::::default(256); a.slice_axis_inplace(Axis(0), Slice::new(0, None, -2)); a += 1; let mut iter = a.iter(); iter.next(); assert_eq!(iter.rfold(0, |acc, &x| acc + x), a.sum() - 1); } // Test order { let mut a = Array1::from_iter(0..20); a.slice_axis_inplace(Axis(0), Slice::new(0, None, 2)); let mut iter = a.iter(); iter.next(); let output = iter.rfold(Vec::new(), |mut acc, elt| { acc.push(*elt); acc }); assert_eq!( Array1::from(output), Array::from_iter((1..10).rev().map(|i| i * 2)) ); } } #[test] fn test_into_iter() { let a = Array1::from(vec![1, 2, 3, 4]); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 2, 3, 4]); } #[test] fn test_into_iter_2d() { let a = Array1::from(vec![1, 2, 3, 4]) .into_shape_with_order((2, 2)) .unwrap(); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 2, 3, 4]); let a = Array1::from(vec![1, 2, 3, 4]) .into_shape_with_order((2, 2)) .unwrap() .reversed_axes(); let v = a.into_iter().collect::>(); assert_eq!(v, [1, 3, 2, 4]); } #[test] fn test_into_iter_sliced() { let (m, n) = (4, 5); let drops = Cell::new(0); for i in 0..m - 1 { for j in 0..n - 1 { for i2 in i + 1..m { for j2 in j + 1..n { for invert in 0..3 { drops.set(0); let i = i as isize; let j = j as isize; let i2 = i2 as isize; let j2 = j2 as isize; let mut a = Array1::from_iter(0..(m * n) as i32) .mapv(|v| DropCount::new(v, &drops)) .into_shape_with_order((m, n)) .unwrap(); a.slice_collapse(s![i..i2, j..j2]); if invert < a.ndim() { a.invert_axis(Axis(invert)); } println!("{:?}, {:?}", i..i2, j..j2); println!("{:?}", a); let answer = a.iter().cloned().collect::>(); let v = a.into_iter().collect::>(); assert_eq!(v, answer); assert_eq!(drops.get(), m * n - v.len()); drop(v); assert_eq!(drops.get(), m * n); } } } } } } /// Helper struct that counts its drops Asserts that it's not dropped twice. Also global number of /// drops is counted in the cell. /// /// Compares equal by its "represented value". #[derive(Clone, Debug)] struct DropCount<'a> { value: i32, my_drops: usize, drops: &'a Cell, } impl PartialEq for DropCount<'_> { fn eq(&self, other: &Self) -> bool { self.value == other.value } } impl<'a> DropCount<'a> { fn new(value: i32, drops: &'a Cell) -> Self { DropCount { value, my_drops: 0, drops, } } } impl Drop for DropCount<'_> { fn drop(&mut self) { assert_eq!(self.my_drops, 0); self.my_drops += 1; self.drops.set(self.drops.get() + 1); } } #[test] fn test_impl_iter_compiles() { // Requires that the iterators are covariant in the element type // base case: std fn slice_iter_non_empty_indices<'s, 'a>(array: &'a Vec<&'s str>) -> impl Iterator + 'a { array .iter() .enumerate() .filter(|(_index, elem)| !elem.is_empty()) .map(|(index, _elem)| index) } let _ = slice_iter_non_empty_indices; // ndarray case fn array_iter_non_empty_indices<'s, 'a>(array: &'a Array<&'s str, Ix1>) -> impl Iterator + 'a { array .iter() .enumerate() .filter(|(_index, elem)| !elem.is_empty()) .map(|(index, _elem)| index) } let _ = array_iter_non_empty_indices; } ndarray-0.16.1/tests/ix0.rs000064400000000000000000000022431046102023000135620ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::Array; use ndarray::Ix0; use ndarray::ShapeBuilder; #[test] fn test_ix0() { let mut a = Array::zeros(Ix0()); assert_eq!(a[()], 0.); a[()] = 1.; assert_eq!(a[()], 1.); assert_eq!(a.len(), 1); assert!(!a.is_empty()); assert_eq!(a.as_slice().unwrap(), &[1.]); let mut a = Array::zeros(Ix0().f()); assert_eq!(a[()], 0.); a[()] = 1.; assert_eq!(a[()], 1.); assert_eq!(a.len(), 1); assert!(!a.is_empty()); assert_eq!(a.as_slice().unwrap(), &[1.]); } #[test] fn test_ix0_add() { let mut a = Array::zeros(Ix0()); a += 1.; assert_eq!(a[()], 1.); a += 2.; assert_eq!(a[()], 3.); } #[test] fn test_ix0_add_add() { let mut a = Array::zeros(Ix0()); a += 1.; let mut b = Array::zeros(Ix0()); b += 1.; a += &b; assert_eq!(a[()], 2.); } #[test] fn test_ix0_add_broad() { let mut b = Array::from(vec![5., 6.]); let mut a = Array::zeros(Ix0()); a += 1.; b += &a; assert_eq!(b[0], 6.); assert_eq!(b[1], 7.); } ndarray-0.16.1/tests/ixdyn.rs000064400000000000000000000102461046102023000142170ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::Array; use ndarray::IntoDimension; use ndarray::Ix3; use ndarray::Order; use ndarray::ShapeBuilder; #[test] fn test_ixdyn() { // check that we can use fixed size arrays for indexing let mut a = Array::zeros(vec![2, 3, 4]); a[[1, 1, 1]] = 1.; assert_eq!(a[[1, 1, 1]], 1.); } #[should_panic] #[test] fn test_ixdyn_wrong_dim() { // check that we can use but it panics at runtime, if number of axes is wrong let mut a = Array::zeros(vec![2, 3, 4]); a[[1, 1, 1]] = 1.; let _ = a[[0, 0]]; } #[test] fn test_ixdyn_out_of_bounds() { // check that we are out of bounds let a = Array::::zeros(vec![2, 3, 4]); let res = a.get([0, 3, 0]); assert_eq!(res, None); } #[test] fn test_ixdyn_iterate() { for &order in &[Order::C, Order::F] { let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); for (i, elt) in a.iter_mut().enumerate() { *elt = i; } println!("{:?}", a.dim()); let mut a = a.into_shape_with_order((dim, order)).unwrap(); println!("{:?}", a.dim()); let mut c = 0; for (i, elt) in a.iter_mut().enumerate() { assert_eq!(i, *elt); c += 1; } assert_eq!(c, a.len()); } } #[test] fn test_ixdyn_index_iterate() { for &order in &[Order::C, Order::F] { let mut a = Array::zeros((2, 3, 4).set_f(order.is_column_major())); let dim = a.shape().to_vec(); for ((i, j, k), elt) in a.indexed_iter_mut() { *elt = i + 10 * j + 100 * k; } let a = a.into_shape_with_order((dim, order)).unwrap(); println!("{:?}", a.dim()); let mut c = 0; for (i, elt) in a.indexed_iter() { assert_eq!(a[i], *elt); c += 1; } assert_eq!(c, a.len()); } } #[test] fn test_ixdyn_uget() { // check that we are out of bounds let mut a = Array::::zeros(vec![2, 3, 4]); a[[1, 2, 0]] = 1.; a[[1, 2, 1]] = 2.; a[[1, 2, 3]] = 7.; let mut x = Ix3(1, 2, 0); let step = Ix3(0, 0, 1); let mut sum = 0.; while let Some(&v) = a.get(x) { sum += v; x += step; } assert_eq!(sum, 10.); let mut x = Ix3(1, 2, 0); let mut sum = 0.; unsafe { for _ in 0..4 { sum += *a.uget(x); x += step; } } assert_eq!(sum, 10.); } #[test] fn test_0() { let mut a = Array::zeros(vec![]); let z = vec![].into_dimension(); assert_eq!(a[z.clone()], 0.); a[[]] = 1.; assert_eq!(a[[]], 1.); assert_eq!(a.len(), 1); assert!(!a.is_empty()); assert_eq!(a.as_slice().unwrap(), &[1.]); let mut a = Array::zeros(vec![].f()); assert_eq!(a[[]], 0.); a[[]] = 1.; assert_eq!(a[[]], 1.); assert_eq!(a.len(), 1); assert!(!a.is_empty()); assert_eq!(a.as_slice().unwrap(), &[1.]); } #[test] fn test_0_add() { let mut a = Array::zeros(vec![]); a += 1.; assert_eq!(a[[]], 1.); a += 2.; assert_eq!(a[[]], 3.); } #[test] fn test_0_add_add() { let mut a = Array::zeros(vec![]); a += 1.; let mut b = Array::zeros(vec![]); b += 1.; a += &b; assert_eq!(a[[]], 2.); } #[test] fn test_0_add_broad() { let mut b = Array::from(vec![5., 6.]); let mut a = Array::zeros(vec![]); a += 1.; b += &a; assert_eq!(b[0], 6.); assert_eq!(b[1], 7.); } #[test] #[cfg(feature = "std")] fn test_into_dimension() { use ndarray::{Ix0, Ix1, Ix2, IxDyn}; let a = Array::linspace(0., 41., 6 * 7) .into_shape_with_order((6, 7)) .unwrap(); let a2 = a.clone().into_shape_with_order(IxDyn(&[6, 7])).unwrap(); let b = a2.clone().into_dimensionality::().unwrap(); assert_eq!(a, b); assert!(a2.clone().into_dimensionality::().is_err()); assert!(a2.clone().into_dimensionality::().is_err()); assert!(a2.clone().into_dimensionality::().is_err()); let c = a2.clone().into_dimensionality::().unwrap(); assert_eq!(a2, c); } ndarray-0.16.1/tests/numeric.rs000064400000000000000000000204411046102023000145240ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use approx::assert_abs_diff_eq; use ndarray::{arr0, arr1, arr2, array, aview1, Array, Array1, Array2, Array3, Axis}; use std::f64; #[test] fn test_mean_with_nan_values() { let a = array![f64::NAN, 1.]; assert!(a.mean().unwrap().is_nan()); } #[test] fn test_mean_with_empty_array_of_floats() { let a: Array1 = array![]; assert!(a.mean().is_none()); } #[test] fn test_mean_with_array_of_floats() { let a: Array1 = array![ 0.99889651, 0.0150731, 0.28492482, 0.83819218, 0.48413156, 0.80710412, 0.41762936, 0.22879429, 0.43997224, 0.23831807, 0.02416466, 0.6269962, 0.47420614, 0.56275487, 0.78995021, 0.16060581, 0.64635041, 0.34876609, 0.78543249, 0.19938356, 0.34429457, 0.88072369, 0.17638164, 0.60819363, 0.250392, 0.69912532, 0.78855523, 0.79140914, 0.85084218, 0.31839879, 0.63381769, 0.22421048, 0.70760302, 0.99216018, 0.80199153, 0.19239188, 0.61356023, 0.31505352, 0.06120481, 0.66417377, 0.63608897, 0.84959691, 0.43599069, 0.77867775, 0.88267754, 0.83003623, 0.67016118, 0.67547638, 0.65220036, 0.68043427 ]; let exact_mean = 0.5475494054; assert_abs_diff_eq!(a.mean().unwrap(), exact_mean); } #[test] fn sum_mean_prod() { let a: Array2 = arr2(&[[1., 2.], [3., 4.]]); assert_eq!(a.sum_axis(Axis(0)), arr1(&[4., 6.])); assert_eq!(a.sum_axis(Axis(1)), arr1(&[3., 7.])); assert_eq!(a.product_axis(Axis(0)), arr1(&[3., 8.])); assert_eq!(a.product_axis(Axis(1)), arr1(&[2., 12.])); assert_eq!(a.mean_axis(Axis(0)), Some(arr1(&[2., 3.]))); assert_eq!(a.mean_axis(Axis(1)), Some(arr1(&[1.5, 3.5]))); assert_eq!(a.sum_axis(Axis(1)).sum_axis(Axis(0)), arr0(10.)); assert_eq!(a.product_axis(Axis(1)).product_axis(Axis(0)), arr0(24.)); assert_eq!(a.view().mean_axis(Axis(1)).unwrap(), aview1(&[1.5, 3.5])); assert_eq!(a.sum(), 10.); } #[test] fn sum_mean_prod_empty() { assert_eq!(Array3::::ones((2, 0, 3)).sum(), 0.); assert_eq!(Array3::::ones((2, 0, 3)).product(), 1.); assert_eq!(Array1::::ones(0).sum_axis(Axis(0)), arr0(0.)); assert_eq!(Array1::::ones(0).product_axis(Axis(0)), arr0(1.)); assert_eq!( Array3::::ones((2, 0, 3)).sum_axis(Axis(1)), Array::zeros((2, 3)), ); assert_eq!( Array3::::ones((2, 0, 3)).product_axis(Axis(1)), Array::ones((2, 3)), ); let a = Array1::::ones(0).mean_axis(Axis(0)); assert_eq!(a, None); let a = Array3::::ones((2, 0, 3)).mean_axis(Axis(1)); assert_eq!(a, None); } #[test] #[cfg(feature = "std")] fn var() { let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.var(0.), 5.049875, epsilon = 1e-8); } #[test] #[cfg(feature = "std")] #[should_panic] fn var_negative_ddof() { let a = array![1., 2., 3.]; a.var(-1.); } #[test] #[cfg(feature = "std")] #[should_panic] fn var_too_large_ddof() { let a = array![1., 2., 3.]; a.var(4.); } #[test] #[cfg(feature = "std")] fn var_nan_ddof() { let a = Array2::::zeros((2, 3)); let v = a.var(::std::f64::NAN); assert!(v.is_nan()); } #[test] #[cfg(feature = "std")] fn var_empty_arr() { let a: Array1 = array![]; assert!(a.var(0.0).is_nan()); } #[test] #[cfg(feature = "std")] fn std() { let a = array![1., -4.32, 1.14, 0.32]; assert_abs_diff_eq!(a.std(0.), 2.24719, epsilon = 1e-5); } #[test] #[cfg(feature = "std")] #[should_panic] fn std_negative_ddof() { let a = array![1., 2., 3.]; a.std(-1.); } #[test] #[cfg(feature = "std")] #[should_panic] fn std_too_large_ddof() { let a = array![1., 2., 3.]; a.std(4.); } #[test] #[cfg(feature = "std")] fn std_nan_ddof() { let a = Array2::::zeros((2, 3)); let v = a.std(::std::f64::NAN); assert!(v.is_nan()); } #[test] #[cfg(feature = "std")] fn std_empty_arr() { let a: Array1 = array![]; assert!(a.std(0.0).is_nan()); } #[test] #[cfg(feature = "approx")] fn var_axis() { use ndarray::{aview0, aview2}; let a = array![ [ [-9.76, -0.38, 1.59, 6.23], [-8.57, -9.27, 5.76, 6.01], [-9.54, 5.09, 3.21, 6.56], ], [ [8.23, -9.63, 3.76, -3.48], [-5.46, 5.86, -2.81, 1.35], [-1.08, 4.66, 8.34, -0.73], ], ]; assert_abs_diff_eq!( a.var_axis(Axis(0), 1.5), aview2(&[ [3.236401e+02, 8.556250e+01, 4.708900e+00, 9.428410e+01], [9.672100e+00, 2.289169e+02, 7.344490e+01, 2.171560e+01], [7.157160e+01, 1.849000e-01, 2.631690e+01, 5.314410e+01] ]), epsilon = 1e-4, ); assert_abs_diff_eq!( a.var_axis(Axis(1), 1.7), aview2(&[ [0.61676923, 80.81092308, 6.79892308, 0.11789744], [75.19912821, 114.25235897, 48.32405128, 9.03020513], ]), epsilon = 1e-8, ); assert_abs_diff_eq!( a.var_axis(Axis(2), 2.3), aview2(&[ [79.64552941, 129.09663235, 95.98929412], [109.64952941, 43.28758824, 36.27439706], ]), epsilon = 1e-8, ); let b = array![[1.1, 2.3, 4.7]]; assert_abs_diff_eq!( b.var_axis(Axis(0), 0.), aview1(&[0., 0., 0.]), epsilon = 1e-12 ); assert_abs_diff_eq!(b.var_axis(Axis(1), 0.), aview1(&[2.24]), epsilon = 1e-12); let c = array![[], []]; assert_eq!(c.var_axis(Axis(0), 0.), aview1(&[])); let d = array![1.1, 2.7, 3.5, 4.9]; assert_abs_diff_eq!(d.var_axis(Axis(0), 0.), aview0(&1.8875), epsilon = 1e-12); } #[test] #[cfg(feature = "approx")] fn std_axis() { use ndarray::aview2; let a = array![ [ [0.22935481, 0.08030619, 0.60827517, 0.73684379], [0.90339851, 0.82859436, 0.64020362, 0.2774583], [0.44485313, 0.63316367, 0.11005111, 0.08656246] ], [ [0.28924665, 0.44082454, 0.59837736, 0.41014531], [0.08382316, 0.43259439, 0.1428889, 0.44830176], [0.51529756, 0.70111616, 0.20799415, 0.91851457] ], ]; assert_abs_diff_eq!( a.std_axis(Axis(0), 1.5), aview2(&[ [0.05989184, 0.36051836, 0.00989781, 0.32669847], [0.81957535, 0.39599997, 0.49731472, 0.17084346], [0.07044443, 0.06795249, 0.09794304, 0.83195211], ]), epsilon = 1e-4, ); assert_abs_diff_eq!( a.std_axis(Axis(1), 1.7), aview2(&[ [0.42698655, 0.48139215, 0.36874991, 0.41458724], [0.26769097, 0.18941435, 0.30555015, 0.35118674], ]), epsilon = 1e-8, ); assert_abs_diff_eq!( a.std_axis(Axis(2), 2.3), aview2(&[ [0.41117907, 0.37130425, 0.35332388], [0.16905862, 0.25304841, 0.39978276], ]), epsilon = 1e-8, ); let b = array![[100000., 1., 0.01]]; assert_abs_diff_eq!( b.std_axis(Axis(0), 0.), aview1(&[0., 0., 0.]), epsilon = 1e-12, ); assert_abs_diff_eq!( b.std_axis(Axis(1), 0.), aview1(&[47140.214021552769]), epsilon = 1e-6, ); let c = array![[], []]; assert_eq!(c.std_axis(Axis(0), 0.), aview1(&[])); } #[test] #[should_panic] #[cfg(feature = "std")] fn var_axis_negative_ddof() { let a = array![1., 2., 3.]; a.var_axis(Axis(0), -1.); } #[test] #[should_panic] #[cfg(feature = "std")] fn var_axis_too_large_ddof() { let a = array![1., 2., 3.]; a.var_axis(Axis(0), 4.); } #[test] #[cfg(feature = "std")] fn var_axis_nan_ddof() { let a = Array2::::zeros((2, 3)); let v = a.var_axis(Axis(1), ::std::f64::NAN); assert_eq!(v.shape(), &[2]); v.mapv(|x| assert!(x.is_nan())); } #[test] #[cfg(feature = "std")] fn var_axis_empty_axis() { let a = Array2::::zeros((2, 0)); let v = a.var_axis(Axis(1), 0.); assert_eq!(v.shape(), &[2]); v.mapv(|x| assert!(x.is_nan())); } #[test] #[should_panic] #[cfg(feature = "std")] fn std_axis_bad_dof() { let a = array![1., 2., 3.]; a.std_axis(Axis(0), 4.); } #[test] #[cfg(feature = "std")] fn std_axis_empty_axis() { let a = Array2::::zeros((2, 0)); let v = a.std_axis(Axis(1), 0.); assert_eq!(v.shape(), &[2]); v.mapv(|x| assert!(x.is_nan())); } ndarray-0.16.1/tests/oper.rs000064400000000000000000000553541046102023000140420ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] #![cfg(feature = "std")] use ndarray::linalg::general_mat_mul; use ndarray::linalg::kron; use ndarray::prelude::*; #[cfg(feature = "approx")] use ndarray::Order; use ndarray::{rcarr1, rcarr2}; use ndarray::{Data, LinalgScalar}; use ndarray::{Ix, Ixs}; use ndarray_gen::array_builder::ArrayBuilder; use approx::assert_abs_diff_eq; use defmac::defmac; use num_traits::Num; use num_traits::Zero; fn test_oper(op: &str, a: &[f32], b: &[f32], c: &[f32]) { let aa = CowArray::from(arr1(a)); let bb = CowArray::from(arr1(b)); let cc = CowArray::from(arr1(c)); test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); let dim = (2, 2); let aa = aa.to_shape(dim).unwrap(); let bb = bb.to_shape(dim).unwrap(); let cc = cc.to_shape(dim).unwrap(); test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); let dim = (1, 2, 1, 2); let aa = aa.to_shape(dim).unwrap(); let bb = bb.to_shape(dim).unwrap(); let cc = cc.to_shape(dim).unwrap(); test_oper_arr::(op, aa.clone(), bb.clone(), cc.clone()); } fn test_oper_arr(op: &str, mut aa: CowArray, bb: CowArray, cc: CowArray) where D: Dimension { match op { "+" => { assert_eq!(&aa + &bb, cc); aa += &bb; assert_eq!(aa, cc); } "-" => { assert_eq!(&aa - &bb, cc); aa -= &bb; assert_eq!(aa, cc); } "*" => { assert_eq!(&aa * &bb, cc); aa *= &bb; assert_eq!(aa, cc); } "/" => { assert_eq!(&aa / &bb, cc); aa /= &bb; assert_eq!(aa, cc); } "%" => { assert_eq!(&aa % &bb, cc); aa %= &bb; assert_eq!(aa, cc); } "neg" => { assert_eq!(-&aa, cc); assert_eq!(-aa.into_owned(), cc); } _ => panic!(), } } #[test] fn operations() { test_oper("+", &[1.0, 2.0, 3.0, 4.0], &[0.0, 1.0, 2.0, 3.0], &[1.0, 3.0, 5.0, 7.0]); test_oper("-", &[1.0, 2.0, 3.0, 4.0], &[0.0, 1.0, 2.0, 3.0], &[1.0, 1.0, 1.0, 1.0]); test_oper("*", &[1.0, 2.0, 3.0, 4.0], &[0.0, 1.0, 2.0, 3.0], &[0.0, 2.0, 6.0, 12.0]); test_oper("/", &[1.0, 2.0, 3.0, 4.0], &[1.0, 1.0, 2.0, 3.0], &[1.0, 2.0, 3.0 / 2.0, 4.0 / 3.0]); test_oper("%", &[1.0, 2.0, 3.0, 4.0], &[1.0, 1.0, 2.0, 3.0], &[0.0, 0.0, 1.0, 1.0]); test_oper("neg", &[1.0, 2.0, 3.0, 4.0], &[1.0, 1.0, 2.0, 3.0], &[-1.0, -2.0, -3.0, -4.0]); } #[test] fn scalar_operations() { let a = arr0::(1.); let b = rcarr1::(&[1., 1.]); let c = rcarr2(&[[1., 1.], [1., 1.]]); { let mut x = a.clone(); let mut y = arr0(0.); x += 1.; y.fill(2.); assert_eq!(x, a + arr0(1.)); assert_eq!(x, y); } { let mut x = b.clone(); let mut y = rcarr1(&[0., 0.]); x += 1.; y.fill(2.); assert_eq!(x, b + arr0(1.)); assert_eq!(x, y); } { let mut x = c.clone(); let mut y = ArcArray::zeros((2, 2)); x += 1.; y.fill(2.); assert_eq!(x, c + arr0(1.)); assert_eq!(x, y); } } fn reference_dot<'a, V1, V2>(a: V1, b: V2) -> f32 where V1: AsArray<'a, f32>, V2: AsArray<'a, f32>, { let a = a.into(); let b = b.into(); a.iter() .zip(b.iter()) .fold(f32::zero(), |acc, (&x, &y)| acc + x * y) } #[test] fn dot_product() { let a = Array::range(0., 69., 1.); let b = &a * 2. - 7.; let dot = 197846.; assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); // test different alignments let max = 8 as Ixs; for i in 1..max { let a1 = a.slice(s![i..]); let b1 = b.slice(s![i..]); assert_abs_diff_eq!(a1.dot(&b1), reference_dot(&a1, &b1), epsilon = 1e-5); let a2 = a.slice(s![..-i]); let b2 = b.slice(s![i..]); assert_abs_diff_eq!(a2.dot(&b2), reference_dot(&a2, &b2), epsilon = 1e-5); } let a = a.map(|f| *f as f32); let b = b.map(|f| *f as f32); assert_abs_diff_eq!(a.dot(&b), dot as f32, epsilon = 1e-5); let max = 8 as Ixs; for i in 1..max { let a1 = a.slice(s![i..]); let b1 = b.slice(s![i..]); assert_abs_diff_eq!(a1.dot(&b1), reference_dot(&a1, &b1), epsilon = 1e-5); let a2 = a.slice(s![..-i]); let b2 = b.slice(s![i..]); assert_abs_diff_eq!(a2.dot(&b2), reference_dot(&a2, &b2), epsilon = 1e-5); } let a = a.map(|f| *f as i32); let b = b.map(|f| *f as i32); assert_eq!(a.dot(&b), dot as i32); } // test that we can dot product with a broadcast array #[test] fn dot_product_0() { let a = Array::range(0., 69., 1.); let x = 1.5; let b = aview0(&x); let b = b.broadcast(a.dim()).unwrap(); assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); // test different alignments let max = 8 as Ixs; for i in 1..max { let a1 = a.slice(s![i..]); let b1 = b.slice(s![i..]); assert_abs_diff_eq!(a1.dot(&b1), reference_dot(&a1, &b1), epsilon = 1e-5); let a2 = a.slice(s![..-i]); let b2 = b.slice(s![i..]); assert_abs_diff_eq!(a2.dot(&b2), reference_dot(&a2, &b2), epsilon = 1e-5); } } #[test] fn dot_product_neg_stride() { // test that we can dot with negative stride let a = Array::range(0., 69., 1.); let b = &a * 2. - 7.; for stride in -10..0 { // both negative let a = a.slice(s![..;stride]); let b = b.slice(s![..;stride]); assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); } for stride in -10..0 { // mixed let a = a.slice(s![..;-stride]); let b = b.slice(s![..;stride]); assert_abs_diff_eq!(a.dot(&b), reference_dot(&a, &b), epsilon = 1e-5); } } #[test] fn fold_and_sum() { let a = Array::linspace(0., 127., 128) .into_shape_with_order((8, 16)) .unwrap(); assert_abs_diff_eq!(a.fold(0., |acc, &x| acc + x), a.sum(), epsilon = 1e-5); // test different strides let max = 8 as Ixs; for i in 1..max { for j in 1..max { let a1 = a.slice(s![..;i, ..;j]); let mut sum = 0.; for elt in a1.iter() { sum += *elt; } assert_abs_diff_eq!(a1.fold(0., |acc, &x| acc + x), sum, epsilon = 1e-5); assert_abs_diff_eq!(sum, a1.sum(), epsilon = 1e-5); } } // skip a few elements let max = 8 as Ixs; for i in 1..max { for skip in 1..max { let a1 = a.slice(s![.., ..;i]); let mut iter1 = a1.iter(); for _ in 0..skip { iter1.next(); } let iter2 = iter1.clone(); let mut sum = 0.; for elt in iter1 { sum += *elt; } assert_abs_diff_eq!(iter2.fold(0., |acc, &x| acc + x), sum, epsilon = 1e-5); } } } #[test] fn product() { let a = Array::linspace(0.5, 2., 128) .into_shape_with_order((8, 16)) .unwrap(); assert_abs_diff_eq!(a.fold(1., |acc, &x| acc * x), a.product(), epsilon = 1e-5); // test different strides let max = 8 as Ixs; for i in 1..max { for j in 1..max { let a1 = a.slice(s![..;i, ..;j]); let mut prod = 1.; for elt in a1.iter() { prod *= *elt; } assert_abs_diff_eq!(a1.fold(1., |acc, &x| acc * x), prod, epsilon = 1e-5); assert_abs_diff_eq!(prod, a1.product(), epsilon = 1e-5); } } } fn range_mat(m: Ix, n: Ix) -> Array2 { ArrayBuilder::new((m, n)).build() } #[cfg(feature = "approx")] fn range1_mat64(m: Ix) -> Array1 { ArrayBuilder::new(m).build() } fn range_i32(m: Ix, n: Ix) -> Array2 { ArrayBuilder::new((m, n)).build() } // simple, slow, correct (hopefully) mat mul fn reference_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) -> Array2 where A: LinalgScalar, S: Data, S2: Data, { let ((m, k), (k2, n)) = (lhs.dim(), rhs.dim()); assert!(m.checked_mul(n).is_some()); assert_eq!(k, k2); let mut res_elems = Vec::::with_capacity(m * n); unsafe { res_elems.set_len(m * n); } let mut i = 0; let mut j = 0; for rr in &mut res_elems { unsafe { *rr = (0..k).fold(A::zero(), move |s, x| s + *lhs.uget((i, x)) * *rhs.uget((x, j))); } j += 1; if j == n { j = 0; i += 1; } } unsafe { ArrayBase::from_shape_vec_unchecked((m, n), res_elems) } } #[test] fn mat_mul() { let (m, n, k) = (8, 8, 8); let a = range_mat::(m, n); let b = range_mat::(n, k); let mut b = b / 4.; { let mut c = b.column_mut(0); c += 1.0; } let ab = a.dot(&b); let mut af = Array::zeros(a.dim().f()); let mut bf = Array::zeros(b.dim().f()); af.assign(&a); bf.assign(&b); assert_eq!(ab, a.dot(&bf)); assert_eq!(ab, af.dot(&b)); assert_eq!(ab, af.dot(&bf)); let (m, n, k) = (10, 5, 11); let a = range_mat::(m, n); let b = range_mat::(n, k); let mut b = b / 4.; { let mut c = b.column_mut(0); c += 1.0; } let ab = a.dot(&b); let mut af = Array::zeros(a.dim().f()); let mut bf = Array::zeros(b.dim().f()); af.assign(&a); bf.assign(&b); assert_eq!(ab, a.dot(&bf)); assert_eq!(ab, af.dot(&b)); assert_eq!(ab, af.dot(&bf)); let (m, n, k) = (10, 8, 1); let a = range_mat::(m, n); let b = range_mat::(n, k); let mut b = b / 4.; { let mut c = b.column_mut(0); c += 1.0; } let ab = a.dot(&b); let mut af = Array::zeros(a.dim().f()); let mut bf = Array::zeros(b.dim().f()); af.assign(&a); bf.assign(&b); assert_eq!(ab, a.dot(&bf)); assert_eq!(ab, af.dot(&b)); assert_eq!(ab, af.dot(&bf)); } // Check that matrix multiplication of contiguous matrices returns a // matrix with the same order #[test] fn mat_mul_order() { let (m, n, k) = (8, 8, 8); let a = range_mat::(m, n); let b = range_mat::(n, k); let mut af = Array::zeros(a.dim().f()); let mut bf = Array::zeros(b.dim().f()); af.assign(&a); bf.assign(&b); let cc = a.dot(&b); let ff = af.dot(&bf); assert_eq!(cc.strides()[1], 1); assert_eq!(ff.strides()[0], 1); } // test matrix multiplication shape mismatch #[test] #[should_panic] fn mat_mul_shape_mismatch() { let (m, k, k2, n) = (8, 8, 9, 8); let a = range_mat::(m, k); let b = range_mat::(k2, n); a.dot(&b); } // test matrix multiplication shape mismatch #[test] #[should_panic] fn mat_mul_shape_mismatch_2() { let (m, k, k2, n) = (8, 8, 8, 8); let a = range_mat::(m, k); let b = range_mat::(k2, n); let mut c = range_mat::(m, n + 1); general_mat_mul(1., &a, &b, 1., &mut c); } // Check that matrix multiplication // supports broadcast arrays. #[test] fn mat_mul_broadcast() { let (m, n, k) = (16, 16, 16); let a = range_mat::(m, n); let x1 = 1.; let x = Array::from(vec![x1]); let b0 = x.broadcast((n, k)).unwrap(); let b1 = Array::from_elem(n, x1); let b1 = b1.broadcast((n, k)).unwrap(); let b2 = Array::from_elem((n, k), x1); let c2 = a.dot(&b2); let c1 = a.dot(&b1); let c0 = a.dot(&b0); assert_eq!(c2, c1); assert_eq!(c2, c0); } // Check that matrix multiplication supports reversed axes #[test] fn mat_mul_rev() { let (m, n, k) = (16, 16, 16); let a = range_mat::(m, n); let b = range_mat::(n, k); let mut rev = Array::zeros(b.dim()); let mut rev = rev.slice_mut(s![..;-1, ..]); rev.assign(&b); println!("{:.?}", rev); let c1 = a.dot(&b); let c2 = a.dot(&rev); assert_eq!(c1, c2); } // Check that matrix multiplication supports arrays with zero rows or columns #[test] fn mat_mut_zero_len() { defmac!(mat_mul_zero_len range_mat_fn => { for n in 0..4 { for m in 0..4 { let a = range_mat_fn(m, n); let b = range_mat_fn(n, 0); assert_eq!(a.dot(&b), Array2::zeros((m, 0))); } for k in 0..4 { let a = range_mat_fn(0, n); let b = range_mat_fn(n, k); assert_eq!(a.dot(&b), Array2::zeros((0, k))); } } }); mat_mul_zero_len!(range_mat::); mat_mul_zero_len!(range_mat::); mat_mul_zero_len!(range_i32); } #[test] fn scaled_add() { let a = range_mat(16, 15); let mut b = range_mat(16, 15); b.mapv_inplace(f32::exp); let alpha = 0.2_f32; let mut c = a.clone(); c.scaled_add(alpha, &b); let d = alpha * &b + &a; assert_eq!(c, d); } #[cfg(feature = "approx")] #[test] fn scaled_add_2() { let beta = -2.3; let sizes = vec![ (4, 4, 1, 4), (8, 8, 1, 8), (17, 15, 17, 15), (4, 17, 4, 17), (17, 3, 1, 3), (19, 18, 19, 18), (16, 17, 16, 17), (15, 16, 15, 16), (67, 63, 1, 63), ]; // test different strides for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k, n, q) in &sizes { let mut a = range_mat::(m, k); let mut answer = a.clone(); let c = range_mat::(n, q); { let mut av = a.slice_mut(s![..;s1, ..;s2]); let c = c.slice(s![..;s1, ..;s2]); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); av.scaled_add(beta, &c); } approx::assert_relative_eq!(a, answer, epsilon = 1e-12, max_relative = 1e-7); } } } } #[cfg(feature = "approx")] #[test] fn scaled_add_3() { use approx::assert_relative_eq; use ndarray::{Slice, SliceInfo, SliceInfoElem}; use std::convert::TryFrom; let beta = -2.3; let sizes = vec![ (4, 4, 1, 4), (8, 8, 1, 8), (17, 15, 17, 15), (4, 17, 4, 17), (17, 3, 1, 3), (19, 18, 19, 18), (16, 17, 16, 17), (15, 16, 15, 16), (67, 63, 1, 63), ]; // test different strides for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k, n, q) in &sizes { let mut a = range_mat::(m, k); let mut answer = a.clone(); let cdim = if n == 1 { vec![q] } else { vec![n, q] }; let cslice: Vec = if n == 1 { vec![Slice::from(..).step_by(s2).into()] } else { vec![ Slice::from(..).step_by(s1).into(), Slice::from(..).step_by(s2).into(), ] }; let c = range_mat::(n, q).into_shape_with_order(cdim).unwrap(); { let mut av = a.slice_mut(s![..;s1, ..;s2]); let c = c.slice(SliceInfo::<_, IxDyn, IxDyn>::try_from(cslice).unwrap()); let mut answerv = answer.slice_mut(s![..;s1, ..;s2]); answerv += &(beta * &c); av.scaled_add(beta, &c); } assert_relative_eq!(a, answer, epsilon = 1e-12, max_relative = 1e-7); } } } } #[cfg(feature = "approx")] #[test] fn gen_mat_mul() { let alpha = -2.3; let beta = 3.14; let sizes = vec![ (4, 4, 4), (8, 8, 8), (17, 15, 16), (4, 17, 3), (17, 3, 22), (19, 18, 2), (16, 17, 15), (15, 16, 17), (67, 63, 62), ]; // test different strides for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k, n) in &sizes { let a = range_mat::(m, k); let b = range_mat::(k, n); let mut c = range_mat::(m, n); let mut answer = c.clone(); { let a = a.slice(s![..;s1, ..;s2]); let b = b.slice(s![..;s2, ..;s2]); let mut cv = c.slice_mut(s![..;s1, ..;s2]); let answer_part = alpha * reference_mat_mul(&a, &b) + beta * &cv; answer.slice_mut(s![..;s1, ..;s2]).assign(&answer_part); general_mat_mul(alpha, &a, &b, beta, &mut cv); } approx::assert_relative_eq!(c, answer, epsilon = 1e-12, max_relative = 1e-7); } } } } // Test y = A x where A is f-order #[cfg(feature = "approx")] #[test] fn gemm_64_1_f() { let a = range_mat::(64, 64).reversed_axes(); let (m, n) = a.dim(); // m x n times n x 1 == m x 1 let x = range_mat::(n, 1); let mut y = range_mat::(m, 1); let answer = reference_mat_mul(&a, &x) + &y; general_mat_mul(1.0, &a, &x, 1.0, &mut y); approx::assert_relative_eq!(y, answer, epsilon = 1e-12, max_relative = 1e-7); } #[test] fn gen_mat_mul_i32() { let alpha = -1; let beta = 2; let sizes = if cfg!(miri) { vec![(4, 4, 4), (4, 7, 3)] } else { vec![ (4, 4, 4), (8, 8, 8), (17, 15, 16), (4, 17, 3), (17, 3, 22), (19, 18, 2), (16, 17, 15), (15, 16, 17), (67, 63, 62), ] }; for &(m, k, n) in &sizes { let a = range_i32(m, k); let b = range_i32(k, n); let mut c = range_i32(m, n); let answer = alpha * reference_mat_mul(&a, &b) + beta * &c; general_mat_mul(alpha, &a, &b, beta, &mut c); assert_eq!(&c, &answer); } } #[cfg(feature = "approx")] #[test] fn gen_mat_vec_mul() { use approx::assert_relative_eq; use ndarray::linalg::general_mat_vec_mul; // simple, slow, correct (hopefully) mat mul fn reference_mat_vec_mul(lhs: &ArrayBase, rhs: &ArrayBase) -> Array1 where A: LinalgScalar, S: Data, S2: Data, { let ((m, _), k) = (lhs.dim(), rhs.dim()); reference_mat_mul( lhs, &rhs.as_standard_layout() .into_shape_with_order((k, 1)) .unwrap(), ) .into_shape_with_order(m) .unwrap() } let alpha = -2.3; let beta = 3.14; let sizes = vec![ (4, 4), (8, 8), (17, 15), (4, 17), (17, 3), (19, 18), (16, 17), (15, 16), (67, 63), ]; // test different strides for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, k) in &sizes { for order in [Order::C, Order::F] { let a = ArrayBuilder::new((m, k)).memory_order(order).build(); let (m, k) = a.dim(); let b = range1_mat64(k); let mut c = range1_mat64(m); let mut answer = c.clone(); { let a = a.slice(s![..;s1, ..;s2]); let b = b.slice(s![..;s2]); let mut cv = c.slice_mut(s![..;s1]); let answer_part = alpha * reference_mat_vec_mul(&a, &b) + beta * &cv; answer.slice_mut(s![..;s1]).assign(&answer_part); general_mat_vec_mul(alpha, &a, &b, beta, &mut cv); } assert_relative_eq!(c, answer, epsilon = 1e-12, max_relative = 1e-7); } } } } } #[cfg(feature = "approx")] #[test] fn vec_mat_mul() { use approx::assert_relative_eq; // simple, slow, correct (hopefully) mat mul fn reference_vec_mat_mul(lhs: &ArrayBase, rhs: &ArrayBase) -> Array1 where A: LinalgScalar, S: Data, S2: Data, { let (m, (_, n)) = (lhs.dim(), rhs.dim()); reference_mat_mul( &lhs.as_standard_layout() .into_shape_with_order((1, m)) .unwrap(), rhs, ) .into_shape_with_order(n) .unwrap() } let sizes = vec![ (4, 4), (8, 8), (17, 15), (4, 17), (17, 3), (19, 18), (16, 17), (15, 16), (67, 63), ]; // test different strides for &s1 in &[1, 2, -1, -2] { for &s2 in &[1, 2, -1, -2] { for &(m, n) in &sizes { for order in [Order::C, Order::F] { let b = ArrayBuilder::new((m, n)).memory_order(order).build(); let (m, n) = b.dim(); let a = range1_mat64(m); let mut c = range1_mat64(n); let mut answer = c.clone(); { let b = b.slice(s![..;s1, ..;s2]); let a = a.slice(s![..;s1]); let answer_part = reference_vec_mat_mul(&a, &b); answer.slice_mut(s![..;s2]).assign(&answer_part); c.slice_mut(s![..;s2]).assign(&a.dot(&b)); } assert_relative_eq!(c, answer, epsilon = 1e-12, max_relative = 1e-7); } } } } } #[test] fn kron_square_f64() { let a = arr2(&[[1.0, 0.0], [0.0, 1.0]]); let b = arr2(&[[0.0, 1.0], [1.0, 0.0]]); assert_eq!( kron(&a, &b), arr2(&[ [0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0] ]), ); assert_eq!( kron(&b, &a), arr2(&[ [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0] ]), ) } #[test] fn kron_square_i64() { let a = arr2(&[[1, 0], [0, 1]]); let b = arr2(&[[0, 1], [1, 0]]); assert_eq!( kron(&a, &b), arr2(&[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]), ); assert_eq!( kron(&b, &a), arr2(&[[0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0], [0, 1, 0, 0]]), ) } #[test] fn kron_i64() { let a = arr2(&[[1, 0]]); let b = arr2(&[[0, 1], [1, 0]]); let r = arr2(&[[0, 1, 0, 0], [1, 0, 0, 0]]); assert_eq!(kron(&a, &b), r); let a = arr2(&[[1, 0], [0, 0], [0, 1]]); let b = arr2(&[[0, 1], [1, 0]]); let r = arr2(&[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]); assert_eq!(kron(&a, &b), r); } ndarray-0.16.1/tests/par_azip.rs000064400000000000000000000032321046102023000146660ustar 00000000000000#![cfg(feature = "rayon")] #[cfg(feature = "approx")] use itertools::enumerate; use ndarray::parallel::prelude::*; use ndarray::prelude::*; use std::sync::atomic::{AtomicUsize, Ordering}; #[test] fn test_par_azip1() { let mut a = Array::zeros(62); let b = Array::from_elem(62, 42); par_azip!((a in &mut a) { *a = 42 }); assert_eq!(a, b); } #[test] fn test_par_azip2() { let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn(a.dim(), |(i, j)| 1. / (i + 2 * j) as f32); par_azip!((a in &mut a, &b in &b, ) *a = b ); assert_eq!(a, b); } #[test] #[cfg(feature = "approx")] fn test_par_azip3() { use approx::assert_abs_diff_eq; let mut a = [0.; 32]; let mut b = [0.; 32]; let mut c = [0.; 32]; for (i, elt) in enumerate(&mut b) { *elt = i as f32; } par_azip!((a in &mut a[..], &b in &b[..], c in &mut c[..]) { *a += b / 10.; *c = a.sin(); }); let res = Array::linspace(0., 3.1, 32).mapv_into(f32::sin); assert_abs_diff_eq!(res, ArrayView::from(&c), epsilon = 1e-4); } #[should_panic] #[test] fn test_zip_dim_mismatch_1() { let mut a = Array::zeros((5, 7)); let mut d = a.raw_dim(); d[0] += 1; let b = Array::from_shape_fn(d, |(i, j)| 1. / (i + 2 * j) as f32); par_azip!((a in &mut a, &b in &b) { *a = b; }); } #[test] fn test_indices_1() { let mut a1 = Array::default(12); for (i, elt) in a1.indexed_iter_mut() { *elt = i; } let count = AtomicUsize::new(0); par_azip!((index i, &elt in &a1) { count.fetch_add(1, Ordering::SeqCst); assert_eq!(elt, i); }); assert_eq!(count.load(Ordering::SeqCst), a1.len()); } ndarray-0.16.1/tests/par_rayon.rs000064400000000000000000000050311046102023000150520ustar 00000000000000#![cfg(feature = "rayon")] use ndarray::parallel::prelude::*; use ndarray::prelude::*; const M: usize = 1024 * 10; const N: usize = 100; const CHUNK_SIZE: usize = 100; const N_CHUNKS: usize = (M + CHUNK_SIZE - 1) / CHUNK_SIZE; #[test] fn test_axis_iter() { let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); } assert_eq!(a.axis_iter(Axis(0)).len(), M); let s: f64 = a.axis_iter(Axis(0)).into_par_iter().map(|x| x.sum()).sum(); println!("{:?}", a.slice(s![..10, ..5])); assert_eq!(s, a.sum()); } #[test] #[cfg(feature = "approx")] fn test_axis_iter_mut() { use approx::assert_abs_diff_eq; let mut a = Array::linspace(0., 1.0f64, M * N) .into_shape_with_order((M, N)) .unwrap(); let b = a.mapv(|x| x.exp()); a.axis_iter_mut(Axis(0)) .into_par_iter() .for_each(|mut v| v.mapv_inplace(|x| x.exp())); println!("{:?}", a.slice(s![..10, ..5])); assert_abs_diff_eq!(a, b, epsilon = 0.001); } #[test] fn test_regular_iter() { let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); } let s: f64 = a.view().into_par_iter().map(|&x| x).sum(); println!("{:?}", a.slice(s![..10, ..5])); assert_eq!(s, a.sum()); } #[test] fn test_regular_iter_collect() { let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); } let v = a.view().into_par_iter().map(|&x| x).collect::>(); assert_eq!(v.len(), a.len()); } #[test] fn test_axis_chunks_iter() { let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_chunks_iter_mut(Axis(0), CHUNK_SIZE).enumerate() { v.fill(i as _); } assert_eq!(a.axis_chunks_iter(Axis(0), CHUNK_SIZE).len(), N_CHUNKS); let s: f64 = a .axis_chunks_iter(Axis(0), CHUNK_SIZE) .into_par_iter() .map(|x| x.sum()) .sum(); println!("{:?}", a.slice(s![..10, ..5])); assert_eq!(s, a.sum()); } #[test] #[cfg(feature = "approx")] fn test_axis_chunks_iter_mut() { use approx::assert_abs_diff_eq; let mut a = Array::linspace(0., 1.0f64, M * N) .into_shape_with_order((M, N)) .unwrap(); let b = a.mapv(|x| x.exp()); a.axis_chunks_iter_mut(Axis(0), CHUNK_SIZE) .into_par_iter() .for_each(|mut v| v.mapv_inplace(|x| x.exp())); println!("{:?}", a.slice(s![..10, ..5])); assert_abs_diff_eq!(a, b, epsilon = 0.001); } ndarray-0.16.1/tests/par_zip.rs000064400000000000000000000064471046102023000145400ustar 00000000000000#![cfg(feature = "rayon")] use ndarray::prelude::*; use ndarray::Zip; const M: usize = 1024 * 10; const N: usize = 100; #[test] fn test_zip_1() { let mut a = Array2::::zeros((M, N)); Zip::from(&mut a).par_for_each(|x| *x = x.exp()); } #[test] fn test_zip_index_1() { let mut a = Array2::default((10, 10)); Zip::indexed(&mut a).par_for_each(|i, x| { *x = i; }); for (i, elt) in a.indexed_iter() { assert_eq!(*elt, i); } } #[test] fn test_zip_index_2() { let mut a = Array2::default((M, N)); Zip::indexed(&mut a).par_for_each(|i, x| { *x = i; }); for (i, elt) in a.indexed_iter() { assert_eq!(*elt, i); } } #[test] fn test_zip_index_3() { let mut a = Array::default((1, 2, 1, 2, 3)); Zip::indexed(&mut a).par_for_each(|i, x| { *x = i; }); for (i, elt) in a.indexed_iter() { assert_eq!(*elt, i); } } #[test] fn test_zip_index_4() { let mut a = Array2::zeros((M, N)); let mut b = Array2::zeros((M, N)); Zip::indexed(&mut a) .and(&mut b) .par_for_each(|(i, j), x, y| { *x = i; *y = j; }); for ((i, _), elt) in a.indexed_iter() { assert_eq!(*elt, i); } for ((_, j), elt) in b.indexed_iter() { assert_eq!(*elt, j); } } #[test] #[cfg(feature = "approx")] fn test_zip_collect() { use approx::assert_abs_diff_eq; // test Zip::map_collect and that it preserves c/f layout. let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); { let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } { let b = b.t(); let c = c.t(); let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } } #[test] #[cfg(feature = "approx")] fn test_zip_small_collect() { use approx::assert_abs_diff_eq; for m in 0..32 { for n in 0..16 { for &is_f in &[false, true] { let dim = (m, n).set_f(is_f); let b = Array::from_shape_fn(dim, |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn(dim, |(i, j)| f32::ln((1 + i + j) as f32)); { let a = Zip::from(&b).and(&c).par_map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); if m > 1 && n > 1 { assert_eq!(a.strides(), b.strides(), "Failure for {}x{} c/f: {:?}", m, n, is_f); } } } } } } #[test] #[cfg(feature = "approx")] fn test_zip_assign_into() { use approx::assert_abs_diff_eq; let mut a = Array::::zeros((M, N)); let b = Array::from_shape_fn((M, N), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((M, N), |(i, j)| f32::ln((1 + i + j) as f32)); Zip::from(&b) .and(&c) .par_map_assign_into(&mut a, |x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); } ndarray-0.16.1/tests/raw_views.rs000064400000000000000000000052321046102023000150710ustar 00000000000000use ndarray::prelude::*; use ndarray::Zip; use std::cell::Cell; #[test] fn raw_view_cast_cell() { // Test .cast() by creating an ArrayView> let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32); let answer = &a + 1.; { let raw_cell_view = a.raw_view_mut().cast::>(); let cell_view = unsafe { raw_cell_view.deref_into_view() }; Zip::from(cell_view).for_each(|elt| elt.set(elt.get() + 1.)); } assert_eq!(a, answer); } #[test] fn raw_view_cast_reinterpret() { // Test .cast() by reinterpreting u16 as [u8; 2] let a = Array::from_shape_fn((5, 5).f(), |(i, j)| (i as u16) << 8 | j as u16); let answer = a.mapv(u16::to_ne_bytes); let raw_view = a.raw_view().cast::<[u8; 2]>(); let view = unsafe { raw_view.deref_into_view() }; assert_eq!(view, answer); } #[test] fn raw_view_cast_zst() { struct Zst; let a = Array::<(), _>::default((250, 250)); let b: RawArrayView = a.raw_view().cast::(); assert_eq!(a.shape(), b.shape()); assert_eq!(a.as_ptr() as *const u8, b.as_ptr() as *const u8); } #[test] #[should_panic] fn raw_view_invalid_size_cast() { let data = [0i32; 16]; ArrayView::from(&data[..]).raw_view().cast::(); } #[test] #[should_panic] fn raw_view_mut_invalid_size_cast() { let mut data = [0i32; 16]; ArrayViewMut::from(&mut data[..]) .raw_view_mut() .cast::(); } #[test] fn raw_view_misaligned() { let data: [u16; 2] = [0x0011, 0x2233]; let ptr: *const u16 = data.as_ptr(); unsafe { let misaligned_ptr = (ptr as *const u8).add(1) as *const u16; RawArrayView::from_shape_ptr(1, misaligned_ptr); } } #[test] #[cfg(debug_assertions)] #[should_panic = "The pointer must be aligned."] fn raw_view_deref_into_view_misaligned() { fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> { let ptr: *const u16 = data.as_ptr(); unsafe { let misaligned_ptr = (ptr as *const u8).add(1) as *const u16; let raw_view = RawArrayView::from_shape_ptr(1, misaligned_ptr); raw_view.deref_into_view() } } let data: [u16; 2] = [0x0011, 0x2233]; misaligned_deref(&data); } #[test] #[cfg(debug_assertions)] #[should_panic = "Unsupported"] fn raw_view_negative_strides() { fn misaligned_deref(data: &[u16; 2]) -> ArrayView1<'_, u16> { let ptr: *const u16 = data.as_ptr(); unsafe { let raw_view = RawArrayView::from_shape_ptr(1.strides((-1isize) as usize), ptr); raw_view.deref_into_view() } } let data: [u16; 2] = [0x0011, 0x2233]; misaligned_deref(&data); } ndarray-0.16.1/tests/reserve.rs000064400000000000000000000032101046102023000145300ustar 00000000000000use ndarray::prelude::*; fn into_raw_vec_capacity(a: Array) -> usize { a.into_raw_vec_and_offset().0.capacity() } #[test] fn reserve_1d() { let mut a = Array1::::zeros((4,)); a.reserve(Axis(0), 1000).unwrap(); assert_eq!(a.shape(), &[4]); assert!(into_raw_vec_capacity(a) >= 1004); } #[test] fn reserve_3d() { let mut a = Array3::::zeros((0, 4, 8)); a.reserve(Axis(0), 10).unwrap(); assert_eq!(a.shape(), &[0, 4, 8]); assert!(into_raw_vec_capacity(a) >= 4 * 8 * 10); } #[test] fn reserve_empty_3d() { let mut a = Array3::::zeros((0, 0, 0)); a.reserve(Axis(0), 10).unwrap(); } #[test] fn reserve_3d_axis1() { let mut a = Array3::::zeros((2, 4, 8)); a.reserve(Axis(1), 10).unwrap(); assert!(into_raw_vec_capacity(a) >= 2 * 8 * 10); } #[test] fn reserve_3d_repeat() { let mut a = Array3::::zeros((2, 4, 8)); a.reserve(Axis(1), 10).unwrap(); a.reserve(Axis(2), 30).unwrap(); assert!(into_raw_vec_capacity(a) >= 2 * 4 * 30); } #[test] fn reserve_2d_with_data() { let mut a = array![[1, 2], [3, 4], [5, 6]]; a.reserve(Axis(1), 100).unwrap(); assert_eq!(a, array![[1, 2], [3, 4], [5, 6]]); assert!(into_raw_vec_capacity(a) >= 3 * 100); } #[test] fn reserve_2d_inverted_with_data() { let mut a = array![[1, 2], [3, 4], [5, 6]]; a.invert_axis(Axis(1)); assert_eq!(a, array![[2, 1], [4, 3], [6, 5]]); a.reserve(Axis(1), 100).unwrap(); assert_eq!(a, array![[2, 1], [4, 3], [6, 5]]); let (raw_vec, offset) = a.into_raw_vec_and_offset(); assert!(raw_vec.capacity() >= 3 * 100); assert_eq!(offset, Some(1)); } ndarray-0.16.1/tests/reshape.rs000064400000000000000000000234501046102023000145140ustar 00000000000000use ndarray::prelude::*; use itertools::enumerate; use ndarray::Order; #[test] fn reshape() { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let u = v.into_shape_with_order((3, 3)); assert!(u.is_err()); let u = v.into_shape_with_order((2, 2, 2)); assert!(u.is_ok()); let u = u.unwrap(); assert_eq!(u.shape(), &[2, 2, 2]); let s = u.into_shape_with_order((4, 2)).unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); } #[test] #[should_panic(expected = "IncompatibleShape")] fn reshape_error1() { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let _u = v.into_shape_with_order((2, 5)).unwrap(); } #[test] #[should_panic(expected = "IncompatibleLayout")] fn reshape_error2() { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let mut u = v.into_shape_with_order((2, 2, 2)).unwrap(); u.swap_axes(0, 1); let _s = u.into_shape_with_order((2, 4)).unwrap(); } #[test] fn reshape_f() { let mut u = Array::zeros((3, 4).f()); for (i, elt) in enumerate(u.as_slice_memory_order_mut().unwrap()) { *elt = i as i32; } let v = u.view(); println!("{:?}", v); // noop ok let v2 = v.into_shape_with_order(((3, 4), Order::F)); assert!(v2.is_ok()); assert_eq!(v, v2.unwrap()); let u = v.into_shape_with_order(((3, 2, 2), Order::F)); assert!(u.is_ok()); let u = u.unwrap(); println!("{:?}", u); assert_eq!(u.shape(), &[3, 2, 2]); let s = u.into_shape_with_order(((4, 3), Order::F)).unwrap(); println!("{:?}", s); assert_eq!(s.shape(), &[4, 3]); assert_eq!(s, aview2(&[[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]])); } #[test] fn to_shape_easy() { // 1D -> C -> C let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let u = v.to_shape(((3, 3), Order::RowMajor)); assert!(u.is_err()); let u = v.to_shape(((2, 2, 2), Order::C)); assert!(u.is_ok()); let u = u.unwrap(); assert!(u.is_view()); assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); let s = u.to_shape((4, 2)).unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); // 1D -> F -> F let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let u = v.to_shape(((3, 3), Order::ColumnMajor)); assert!(u.is_err()); let u = v.to_shape(((2, 2, 2), Order::ColumnMajor)); assert!(u.is_ok()); let u = u.unwrap(); assert!(u.is_view()); assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); let s = u.to_shape(((4, 2), Order::ColumnMajor)).unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); } #[test] fn to_shape_copy() { // 1D -> C -> F let v = ArrayView::from(&[1, 2, 3, 4, 5, 6, 7, 8]); let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap(); assert_eq!(u.shape(), &[4, 2]); assert_eq!(u, array![[1, 2], [3, 4], [5, 6], [7, 8]]); let u = u.to_shape(((2, 4), Order::ColumnMajor)).unwrap(); assert_eq!(u.shape(), &[2, 4]); assert_eq!(u, array![[1, 5, 2, 6], [3, 7, 4, 8]]); // 1D -> F -> C let v = ArrayView::from(&[1, 2, 3, 4, 5, 6, 7, 8]); let u = v.to_shape(((4, 2), Order::ColumnMajor)).unwrap(); assert_eq!(u.shape(), &[4, 2]); assert_eq!(u, array![[1, 5], [2, 6], [3, 7], [4, 8]]); let u = u.to_shape((2, 4)).unwrap(); assert_eq!(u.shape(), &[2, 4]); assert_eq!(u, array![[1, 5, 2, 6], [3, 7, 4, 8]]); } #[test] fn to_shape_add_axis() { // 1D -> C -> C let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap(); assert!(u.to_shape(((1, 4, 2), Order::RowMajor)).unwrap().is_view()); assert!(u.to_shape(((1, 4, 2), Order::ColumnMajor)).unwrap().is_view()); } #[test] fn to_shape_copy_stride() { let v = array![[1, 2, 3, 4], [5, 6, 7, 8]]; let vs = v.slice(s![.., ..3]); let lin1 = vs.to_shape(6).unwrap(); assert_eq!(lin1, array![1, 2, 3, 5, 6, 7]); assert!(lin1.is_owned()); let lin2 = vs.to_shape((6, Order::ColumnMajor)).unwrap(); assert_eq!(lin2, array![1, 5, 2, 6, 3, 7]); assert!(lin2.is_owned()); } #[test] fn to_shape_zero_len() { let v = array![[1, 2, 3, 4], [5, 6, 7, 8]]; let vs = v.slice(s![.., ..0]); let lin1 = vs.to_shape(0).unwrap(); assert_eq!(lin1, array![]); assert!(lin1.is_view()); } #[test] #[should_panic(expected = "IncompatibleShape")] fn to_shape_error1() { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let _u = v.to_shape((2, 5)).unwrap(); } #[test] #[should_panic(expected = "IncompatibleShape")] fn to_shape_error2() { // overflow let data = [3, 4, 5, 6, 7, 8]; let v = aview1(&data); let _u = v.to_shape((2, usize::MAX)).unwrap(); } #[test] fn to_shape_discontig() { for &create_order in &[Order::C, Order::F] { let a = Array::from_iter(0..64); let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap(); a1.slice_collapse(s![.., ..;2, ..]); // now shape (4, 2, 4) assert!(a1.as_slice_memory_order().is_none()); for &order in &[Order::C, Order::F] { let v1 = a1.to_shape(((2, 2, 2, 2, 2), order)).unwrap(); assert!(v1.is_view()); let v1 = a1.to_shape(((4, 1, 2, 1, 2, 2), order)).unwrap(); assert!(v1.is_view()); let v1 = a1.to_shape(((4, 2, 4), order)).unwrap(); assert!(v1.is_view()); let v1 = a1.to_shape(((8, 4), order)).unwrap(); assert_eq!(v1.is_view(), order == create_order && create_order == Order::C, "failed for {:?}, {:?}", create_order, order); let v1 = a1.to_shape(((4, 8), order)).unwrap(); assert_eq!(v1.is_view(), order == create_order && create_order == Order::F, "failed for {:?}, {:?}", create_order, order); let v1 = a1.to_shape((32, order)).unwrap(); assert!(!v1.is_view()); } } } #[test] fn to_shape_broadcast() { for &create_order in &[Order::C, Order::F] { let a = Array::from_iter(0..64); let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap(); a1.slice_collapse(s![.., ..1, ..]); // now shape (4, 1, 4) let v1 = a1.broadcast((4, 4, 4)).unwrap(); // Now shape (4, 4, 4) assert!(v1.as_slice_memory_order().is_none()); for &order in &[Order::C, Order::F] { let v2 = v1.to_shape(((2, 2, 2, 2, 2, 2), order)).unwrap(); assert_eq!(v2.strides(), match (create_order, order) { (Order::C, Order::C) => { &[32, 16, 0, 0, 2, 1] } (Order::C, Order::F) => { &[16, 32, 0, 0, 1, 2] } (Order::F, Order::C) => { &[2, 1, 0, 0, 32, 16] } (Order::F, Order::F) => { &[1, 2, 0, 0, 16, 32] } _other => unreachable!() }); let v2 = v1.to_shape(((4, 4, 4), order)).unwrap(); assert!(v2.is_view()); let v2 = v1.to_shape(((8, 8), order)).unwrap(); assert!(v2.is_owned()); } } } #[test] fn into_shape_with_order() { // 1D -> C -> C let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let u = v.into_shape_with_order(((3, 3), Order::RowMajor)); assert!(u.is_err()); let u = v.into_shape_with_order(((2, 2, 2), Order::C)); assert!(u.is_ok()); let u = u.unwrap(); assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); let s = u.into_shape_with_order((4, 2)).unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); // 1D -> F -> F let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = aview1(&data); let u = v.into_shape_with_order(((3, 3), Order::ColumnMajor)); assert!(u.is_err()); let u = v.into_shape_with_order(((2, 2, 2), Order::ColumnMajor)); assert!(u.is_ok()); let u = u.unwrap(); assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); let s = u .into_shape_with_order(((4, 2), Order::ColumnMajor)) .unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); } #[test] fn into_shape_clone() { // 1D -> C -> C { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = Array::from(data.to_vec()); let u = v.clone().into_shape_clone(((3, 3), Order::RowMajor)); assert!(u.is_err()); let u = v.clone().into_shape_clone(((2, 2, 2), Order::C)); assert!(u.is_ok()); let u = u.unwrap(); assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); let s = u.into_shape_clone((4, 2)).unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]])); let u = v.clone().into_shape_clone(((2, 2, 2), Order::F)); assert!(u.is_ok()); let u = u.unwrap(); assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); } // 1D -> F -> F { let data = [1, 2, 3, 4, 5, 6, 7, 8]; let v = Array::from(data.to_vec()); let u = v.clone().into_shape_clone(((3, 3), Order::ColumnMajor)); assert!(u.is_err()); let u = v.into_shape_clone(((2, 2, 2), Order::ColumnMajor)); assert!(u.is_ok()); let u = u.unwrap(); assert_eq!(u.shape(), &[2, 2, 2]); assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]); let s = u.into_shape_clone(((4, 2), Order::ColumnMajor)).unwrap(); assert_eq!(s.shape(), &[4, 2]); assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]); } } ndarray-0.16.1/tests/s.rs000064400000000000000000000006201046102023000133210ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::{s, Array}; #[test] fn test_s() { let a = Array::::zeros((3, 4)); let vi = a.slice(s![1.., ..;2]); assert_eq!(vi.shape(), &[2, 2]); // trailing comma let vi = a.slice(s![1.., ..;2, ]); assert_eq!(vi.shape(), &[2, 2]); } ndarray-0.16.1/tests/stacking.rs000064400000000000000000000040571046102023000146720ustar 00000000000000use ndarray::{arr2, arr3, aview1, aview2, concatenate, stack, Array2, Axis, ErrorKind, Ix1}; #[test] fn concatenating() { let a = arr2(&[[2., 2.], [3., 3.]]); let b = ndarray::concatenate(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.]])); let c = concatenate![Axis(0), a, b]; assert_eq!( c, arr2(&[[2., 2.], [3., 3.], [2., 2.], [3., 3.], [2., 2.], [3., 3.]]) ); let d = concatenate![Axis(0), a.row(0), &[9., 9.]]; assert_eq!(d, aview1(&[2., 2., 9., 9.])); let d = concatenate![Axis(1), a.row(0).insert_axis(Axis(1)), aview1(&[9., 9.]).insert_axis(Axis(1))]; assert_eq!(d, aview2(&[[2., 9.], [2., 9.]])); let d = concatenate![Axis(0), a.row(0).insert_axis(Axis(1)), aview1(&[9., 9.]).insert_axis(Axis(1))]; assert_eq!(d, aview2(&[[2.], [2.], [9.], [9.]])); let res = ndarray::concatenate(Axis(1), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); let res = ndarray::concatenate(Axis(2), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); let res: Result, _> = ndarray::concatenate(Axis(0), &[]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } #[test] fn stacking() { let a = arr2(&[[2., 2.], [3., 3.]]); let b = ndarray::stack(Axis(0), &[a.view(), a.view()]).unwrap(); assert_eq!(b, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); let c = stack![Axis(0), a, a]; assert_eq!(c, arr3(&[[[2., 2.], [3., 3.]], [[2., 2.], [3., 3.]]])); let c = arr2(&[[3., 2., 3.], [2., 3., 2.]]); let res = ndarray::stack(Axis(1), &[a.view(), c.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::IncompatibleShape); let res = ndarray::stack(Axis(3), &[a.view(), a.view()]); assert_eq!(res.unwrap_err().kind(), ErrorKind::OutOfBounds); let res: Result, _> = ndarray::stack::<_, Ix1>(Axis(0), &[]); assert_eq!(res.unwrap_err().kind(), ErrorKind::Unsupported); } ndarray-0.16.1/tests/views.rs000064400000000000000000000005201046102023000142130ustar 00000000000000use ndarray::prelude::*; use ndarray::Zip; #[test] fn cell_view() { let mut a = Array::from_shape_fn((10, 5), |(i, j)| (i * j) as f32); let answer = &a + 1.; { let cv1 = a.cell_view(); let cv2 = cv1; Zip::from(cv1).and(cv2).for_each(|a, b| a.set(b.get() + 1.)); } assert_eq!(a, answer); } ndarray-0.16.1/tests/windows.rs000064400000000000000000000256341046102023000145650ustar 00000000000000#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names )] use ndarray::prelude::*; use ndarray::{arr3, Zip}; // Edge Cases for Windows iterator: // // - window size is 0 // - what is the behaviour of the standard for this situation? // "Panics if size is 0." // - window size of 1 // - should a warning be printed? // - what is the behaviour of the standard for this situation? // => No overlapping for size-1-windows but normal behaviour besides that. // - window size bigger than actual array size // - ragged windows or panic? // - what is the behaviour of the standard for this situation? // "If the slice is shorter than size, the iterator returns no values." /// Test that verifies the `Windows` iterator panics on window sizes equal to zero. #[test] #[should_panic] fn windows_iterator_zero_size() { let a = Array::from_iter(10..37) .into_shape_with_order((3, 3, 3)) .unwrap(); a.windows(Dim((0, 0, 0))); } /// Test that verifies that no windows are yielded on oversized window sizes. #[test] fn windows_iterator_oversized() { let a = Array::from_iter(10..37) .into_shape_with_order((3, 3, 3)) .unwrap(); let mut iter = a.windows((4, 3, 2)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! assert_eq!(iter.next(), None); } /// Simple test for iterating 1d-arrays via `Windows`. #[test] fn windows_iterator_1d() { let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); itertools::assert_equal(a.windows(Dim(4)), vec![ arr1(&[10, 11, 12, 13]), arr1(&[11, 12, 13, 14]), arr1(&[12, 13, 14, 15]), arr1(&[13, 14, 15, 16]), arr1(&[14, 15, 16, 17]), arr1(&[15, 16, 17, 18]), arr1(&[16, 17, 18, 19]), ]); } /// Simple test for iterating 2d-arrays via `Windows`. #[test] fn windows_iterator_2d() { let a = Array::from_iter(10..30) .into_shape_with_order((5, 4)) .unwrap(); itertools::assert_equal(a.windows(Dim((3, 2))), vec![ arr2(&[[10, 11], [14, 15], [18, 19]]), arr2(&[[11, 12], [15, 16], [19, 20]]), arr2(&[[12, 13], [16, 17], [20, 21]]), arr2(&[[14, 15], [18, 19], [22, 23]]), arr2(&[[15, 16], [19, 20], [23, 24]]), arr2(&[[16, 17], [20, 21], [24, 25]]), arr2(&[[18, 19], [22, 23], [26, 27]]), arr2(&[[19, 20], [23, 24], [27, 28]]), arr2(&[[20, 21], [24, 25], [28, 29]]), ]); } /// Simple test for iterating 3d-arrays via `Windows`. #[test] fn windows_iterator_3d() { let a = Array::from_iter(10..37) .into_shape_with_order((3, 3, 3)) .unwrap(); itertools::assert_equal(a.windows(Dim((2, 2, 2))), vec![ arr3(&[[[10, 11], [13, 14]], [[19, 20], [22, 23]]]), arr3(&[[[11, 12], [14, 15]], [[20, 21], [23, 24]]]), arr3(&[[[13, 14], [16, 17]], [[22, 23], [25, 26]]]), arr3(&[[[14, 15], [17, 18]], [[23, 24], [26, 27]]]), arr3(&[[[19, 20], [22, 23]], [[28, 29], [31, 32]]]), arr3(&[[[20, 21], [23, 24]], [[29, 30], [32, 33]]]), arr3(&[[[22, 23], [25, 26]], [[31, 32], [34, 35]]]), arr3(&[[[23, 24], [26, 27]], [[32, 33], [35, 36]]]), ]); } /// Test that verifies the `Windows` iterator panics when stride has an axis equal to zero. #[test] #[should_panic] fn windows_iterator_stride_axis_zero() { let a = Array::from_iter(10..37) .into_shape_with_order((3, 3, 3)) .unwrap(); a.windows_with_stride((2, 2, 2), (0, 2, 2)); } /// Test that verifies that only first window is yielded when stride is oversized on every axis. #[test] fn windows_iterator_only_one_valid_window_for_oversized_stride() { let a = Array::from_iter(10..135) .into_shape_with_order((5, 5, 5)) .unwrap(); let mut iter = a.windows_with_stride((2, 2, 2), (8, 8, 8)).into_iter(); // (4,3,2) doesn't fit into (3,3,3) => oversized! itertools::assert_equal(iter.next(), Some(arr3(&[[[10, 11], [15, 16]], [[35, 36], [40, 41]]]))); } /// Simple test for iterating 1d-arrays via `Windows` with stride. #[test] fn windows_iterator_1d_with_stride() { let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); itertools::assert_equal(a.windows_with_stride(4, 2), vec![ arr1(&[10, 11, 12, 13]), arr1(&[12, 13, 14, 15]), arr1(&[14, 15, 16, 17]), arr1(&[16, 17, 18, 19]), ]); } /// Simple test for iterating 2d-arrays via `Windows` with stride. #[test] fn windows_iterator_2d_with_stride() { let a = Array::from_iter(10..30) .into_shape_with_order((5, 4)) .unwrap(); itertools::assert_equal(a.windows_with_stride((3, 2), (2, 1)), vec![ arr2(&[[10, 11], [14, 15], [18, 19]]), arr2(&[[11, 12], [15, 16], [19, 20]]), arr2(&[[12, 13], [16, 17], [20, 21]]), arr2(&[[18, 19], [22, 23], [26, 27]]), arr2(&[[19, 20], [23, 24], [27, 28]]), arr2(&[[20, 21], [24, 25], [28, 29]]), ]); } /// Simple test for iterating 3d-arrays via `Windows` with stride. #[test] fn windows_iterator_3d_with_stride() { let a = Array::from_iter(10..74) .into_shape_with_order((4, 4, 4)) .unwrap(); itertools::assert_equal(a.windows_with_stride((2, 2, 2), (2, 2, 2)), vec![ arr3(&[[[10, 11], [14, 15]], [[26, 27], [30, 31]]]), arr3(&[[[12, 13], [16, 17]], [[28, 29], [32, 33]]]), arr3(&[[[18, 19], [22, 23]], [[34, 35], [38, 39]]]), arr3(&[[[20, 21], [24, 25]], [[36, 37], [40, 41]]]), arr3(&[[[42, 43], [46, 47]], [[58, 59], [62, 63]]]), arr3(&[[[44, 45], [48, 49]], [[60, 61], [64, 65]]]), arr3(&[[[50, 51], [54, 55]], [[66, 67], [70, 71]]]), arr3(&[[[52, 53], [56, 57]], [[68, 69], [72, 73]]]), ]); } #[test] fn test_window_zip() { let a = Array::from_iter(0..64) .into_shape_with_order((4, 4, 4)) .unwrap(); for x in 1..4 { for y in 1..4 { for z in 1..4 { Zip::indexed(a.windows((x, y, z))).for_each(|(i, j, k), window| { let x = x as isize; let y = y as isize; let z = z as isize; let i = i as isize; let j = j as isize; let k = k as isize; assert_eq!(window, a.slice(s![i..i + x, j..j + y, k..k + z])); }) } } } } /// Test verifies that non existent Axis results in panic #[test] #[should_panic] fn axis_windows_outofbound() { let a = Array::from_iter(10..37) .into_shape_with_order((3, 3, 3)) .unwrap(); a.axis_windows(Axis(4), 2); } /// Test verifies that zero sizes results in panic #[test] #[should_panic] fn axis_windows_zero_size() { let a = Array::from_iter(10..37) .into_shape_with_order((3, 3, 3)) .unwrap(); a.axis_windows(Axis(0), 0); } /// Test verifies that over sized windows yield nothing #[test] fn axis_windows_oversized() { let a = Array::from_iter(10..37) .into_shape_with_order((3, 3, 3)) .unwrap(); let mut iter = a.axis_windows(Axis(2), 4).into_iter(); assert_eq!(iter.next(), None); } /// Simple test for iterating 1d-arrays via `Axis Windows`. #[test] fn test_axis_windows_1d() { let a = Array::from_iter(10..20).into_shape_with_order(10).unwrap(); itertools::assert_equal(a.axis_windows(Axis(0), 5), vec![ arr1(&[10, 11, 12, 13, 14]), arr1(&[11, 12, 13, 14, 15]), arr1(&[12, 13, 14, 15, 16]), arr1(&[13, 14, 15, 16, 17]), arr1(&[14, 15, 16, 17, 18]), arr1(&[15, 16, 17, 18, 19]), ]); } /// Simple test for iterating 2d-arrays via `Axis Windows`. #[test] fn test_axis_windows_2d() { let a = Array::from_iter(10..30) .into_shape_with_order((5, 4)) .unwrap(); itertools::assert_equal(a.axis_windows(Axis(0), 2), vec![ arr2(&[[10, 11, 12, 13], [14, 15, 16, 17]]), arr2(&[[14, 15, 16, 17], [18, 19, 20, 21]]), arr2(&[[18, 19, 20, 21], [22, 23, 24, 25]]), arr2(&[[22, 23, 24, 25], [26, 27, 28, 29]]), ]); } /// Simple test for iterating 3d-arrays via `Axis Windows`. #[test] fn test_axis_windows_3d() { let a = Array::from_iter(0..27) .into_shape_with_order((3, 3, 3)) .unwrap(); itertools::assert_equal(a.axis_windows(Axis(1), 2), vec![ arr3(&[ [[0, 1, 2], [3, 4, 5]], [[9, 10, 11], [12, 13, 14]], [[18, 19, 20], [21, 22, 23]], ]), arr3(&[ [[3, 4, 5], [6, 7, 8]], [[12, 13, 14], [15, 16, 17]], [[21, 22, 23], [24, 25, 26]], ]), ]); } #[test] fn tests_axis_windows_3d_zips_with_1d() { let a = Array::from_iter(0..27) .into_shape_with_order((3, 3, 3)) .unwrap(); let mut b = Array::zeros(2); Zip::from(b.view_mut()) .and(a.axis_windows(Axis(1), 2)) .for_each(|b, a| { *b = a.sum(); }); assert_eq!(b,arr1(&[207, 261])); } #[test] fn test_window_neg_stride() { let array = Array::from_iter(1..10) .into_shape_with_order((3, 3)) .unwrap(); // window neg/pos stride combinations // Make a 2 x 2 array of the windows of the 3 x 3 array // and compute test answers from here let mut answer = Array::from_iter(array.windows((2, 2)).into_iter().map(|a| a.to_owned())) .into_shape_with_order((2, 2)) .unwrap(); answer.invert_axis(Axis(1)); answer.map_inplace(|a| a.invert_axis(Axis(1))); itertools::assert_equal(array.slice(s![.., ..;-1]).windows((2, 2)), answer.iter()); answer.invert_axis(Axis(0)); answer.map_inplace(|a| a.invert_axis(Axis(0))); itertools::assert_equal(array.slice(s![..;-1, ..;-1]).windows((2, 2)), answer.iter()); answer.invert_axis(Axis(1)); answer.map_inplace(|a| a.invert_axis(Axis(1))); itertools::assert_equal(array.slice(s![..;-1, ..]).windows((2, 2)), answer.iter()); } #[test] fn test_windows_with_stride_on_inverted_axis() { let mut array = Array::from_iter(1..17) .into_shape_with_order((4, 4)) .unwrap(); // inverting axis results in negative stride array.invert_axis(Axis(0)); itertools::assert_equal(array.windows_with_stride((2, 2), (2, 2)), vec![ arr2(&[[13, 14], [9, 10]]), arr2(&[[15, 16], [11, 12]]), arr2(&[[5, 6], [1, 2]]), arr2(&[[7, 8], [3, 4]]), ]); array.invert_axis(Axis(1)); itertools::assert_equal(array.windows_with_stride((2, 2), (2, 2)), vec![ arr2(&[[16, 15], [12, 11]]), arr2(&[[14, 13], [10, 9]]), arr2(&[[8, 7], [4, 3]]), arr2(&[[6, 5], [2, 1]]), ]); } ndarray-0.16.1/tests/zst.rs000064400000000000000000000006211046102023000137000ustar 00000000000000use ndarray::arr2; use ndarray::ArcArray; #[test] fn test_swap() { let mut a = arr2(&[[(); 3]; 3]); let b = a.clone(); for i in 0..a.nrows() { for j in i + 1..a.ncols() { a.swap((i, j), (j, i)); } } assert_eq!(a, b.t()); } #[test] fn test() { let c = ArcArray::<(), _>::default((3, 4)); let mut d = c.clone(); for _ in d.iter_mut() {} }