diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a0d5fcd7..47cc2fb6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -29,6 +29,6 @@ updates: schedule: interval: daily - package-ecosystem: cargo - directory: /tensor + directory: /math schedule: - interval: daily \ No newline at end of file + interval: daily diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 6e4c0107..7a994225 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,4 +1,4 @@ -name: Clippy +name: clippy concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -6,16 +6,15 @@ concurrency: on: pull_request: - branches: [ main ] + branches: [ main, master ] + types: [ opened, reopened ] push: - branches: [ main ] - tags: [ nightly*, v*.*.* ] + branches: [ main, master ] + tags: [ v*, "*-nightly" ] release: types: [ created ] repository_dispatch: types: [ clippy ] - schedule: - - cron: 30 21 * * 0 # Every Sunday at 9:30PM UTC workflow_dispatch: permissions: @@ -29,22 +28,24 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Setup the toolchain - uses: actions-rs/toolchain@v1 + - + name: setup rust-toolchain + uses: actions-rust-lang/setup-rust-toolchain@v1 with: - profile: minimal - toolchain: stable - components: clippy - override: true - - name: Installation + cache-workspaces: true + components: clippy, rustfmt + - + name: setup cargo run: cargo install clippy-sarif sarif-fmt - - name: Analyze + - + name: Run rust-clippy run: cargo clippy --all-features --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt continue-on-error: true - - name: Upload analysis + - + name: Upload analysis uses: github/codeql-action/upload-sarif@v3 with: sarif_file: rust-clippy-results.sarif diff --git a/.github/workflows/crates.yml b/.github/workflows/crates.yml index 29bb6309..919b8df0 100644 --- a/.github/workflows/crates.yml +++ b/.github/workflows/crates.yml @@ -1,50 +1,71 @@ -name: Crates +name: crates.io concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false env: - BASENAME: ${{ github.event.repository.name }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} CARGO_TERM_COLOR: always on: release: - types: [ created ] + types: [ published ] repository_dispatch: types: [ publish ] workflow_dispatch: jobs: core: - name: Publish (core) runs-on: ubuntu-latest + strategy: + matrix: + features: [ core ] env: - CARGO_PACKAGE_NAME: ${{ github.event.repository.name }}-core + PACKAGE: ${{ github.event.repository.name }}-${{ matrix.features }} steps: - uses: actions/checkout@v4 - - name: Publish (${{ env.CARGO_PACKAGE_NAME }}) - run: cargo publish --all-features -v -p ${{ env.CARGO_PACKAGE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + - + name: rustup + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + cache-workspaces: true + - + name: publish (${{ env.PACKAGE }}) + run: cargo publish --all-features -v -p ${{ env.PACKAGE }} features: - name: Publish Features needs: core runs-on: ubuntu-latest strategy: matrix: - features: [ derive, graphs, macros, tensor ] + features: [ derive, graphs, macros, math ] env: - CARGO_PACKAGE_NAME: ${{ github.event.repository.name }}-${{ matrix.features }} + PACKAGE: ${{ github.event.repository.name }}-${{ matrix.features }} steps: - uses: actions/checkout@v4 - - name: Publish (${{ env.CARGO_PACKAGE_NAME }}) - run: cargo publish --all-features -v -p ${{ env.CARGO_PACKAGE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + - + name: rustup + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + cache-workspaces: true + - + name: publish (${{ env.PACKAGE }}) + run: cargo publish --all-features -v -p ${{ env.PACKAGE }} publish: env: - CARGO_PACKAGE_NAME: ${{ github.event.repository.name }} - name: Publish SDK - needs: features + PACKAGE: ${{ github.event.repository.name }} + needs: [ core, features ] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Publish (${{ env.CARGO_PACKAGE_NAME }}) - run: cargo publish --all-features -v -p ${{ env.CARGO_PACKAGE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file + - + name: rustup + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + cache-workspaces: true + - + name: publish (${{ env.PACKAGE }}) + run: cargo publish --all-features -v -p ${{ env.PACKAGE }} \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 89351f29..90e8850d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,79 +1,62 @@ -name: Rust +name: rust -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} +concurrency: cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref }} env: CARGO_TERM_COLOR: always on: + pull_request: + branches: [ main, master, ] + types: [ opened, synchronize, reopened ] push: - branches: [ main ] - release: - types: [ created ] + tags: [ v*, "*-nightly" ] repository_dispatch: types: [ rust ] - schedule: - - cron: 30 21 * * 0 # Every Sunday at 9:30pm UTC workflow_dispatch: -jobs: - builder: +permissions: write-all - name: Build +jobs: + workspace: strategy: matrix: - platform: [ macos-latest, ubuntu-latest, windows-latest ] + platform: [ ubuntu-latest ] + target: [ x86_64-unknown-linux-gnu ] toolchain: [ stable, nightly ] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v4 - - name: setup (langspace) - run: | - rustup default ${{ matrix.toolchain }} - rustup update - - name: Build - if: matrix.toolchain == 'stable' + - + name: rustup + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + cache-workspaces: true + target: ${{ matrix.target }} + toolchain: ${{ matrix.toolchain }} + - + name: build run: cargo build --all-features -r -v --workspace - - name: Build (nightly) - continue-on-error: true + - + name: test + run: cargo test --all-features -r -v --workspace + - + name: bench if: matrix.toolchain == 'nightly' - run: cargo build --all-features -r -v --workspace - - name: Cache build - id: cache-build + run: cargo bench -F full -v --workspace + - + name: cache uses: actions/cache@v4 with: + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} path: | ~/.cargo/registry ~/.cargo/git - target/release - key: ${{ matrix.toolchain }}-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - test: - name: Test - needs: [ builder ] - strategy: - matrix: - toolchain: [ stable, nightly ] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: setup (langspace) - run: | - rustup default ${{ matrix.toolchain }} - rustup update - - name: Test - run: cargo test --all-features -v --workspace - bench: - continue-on-error: true - name: Benchmark - needs: [ builder ] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: setup (langspace) - run: | - rustup default nightly - rustup update - - name: Bench - run: cargo bench --all --features full -v \ No newline at end of file + target + restore-keys: | + ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + ${{ runner.os }}-cargo- + ${{ runner.os }}- diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..de291ac8 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,11 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) +# and commit this file to your remote git repository to share the goodness with others. + +# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart + +tasks: + - init: cargo build + command: cargo watch -x run + + diff --git a/Cargo.toml b/Cargo.toml index baeb4e1c..84a0b650 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,38 +1,38 @@ [workspace.package] -authors = ["FL03 (https://github.com/FL03)"] +authors = ["FL03 "] categories = ["mathematics", "science"] -description = "Acme aims to provide a solid foundation for developing robust machine-learning systems in Rust." +description = "rsdiff aims to provide a solid foundation for developing robust machine-learning systems in Rust." edition = "2021" -homepage = "https://github.com/FL03/acme/wikis" +homepage = "https://github.com/FL03/rsdiff/wikis" keywords = ["acme", "autodiff", "mathematics", "tensor"] license = "Apache-2.0" -repository = "https://github.com/FL03/acme" +repository = "https://github.com/FL03/rsdiff" readme = "README.md" -version = "0.3.1" -# version = "0.3.1-nightly" +version = "0.0.1" +# version = "0.3.2-nightly" [workspace] default-members = [ - "acme" + "rsdiff" ] exclude = [ - "exp/container", ] members = [ - "acme", + "rsdiff", "core", "derive", "graphs", - "macros", - "tensor", + "macros", + "math", ] resolver = "2" [workspace.dependencies] paste = "1" +scsys = "0.2.1" strum = { features = ["derive"], version = "0.26" } smart-default = "0.7" diff --git a/acme/Cargo.toml b/acme/Cargo.toml deleted file mode 100644 index 24b2f736..00000000 --- a/acme/Cargo.toml +++ /dev/null @@ -1,126 +0,0 @@ -[package] -authors.workspace = true -categories.workspace = true -description.workspace = true -edition.workspace = true -homepage.workspace = true -keywords.workspace = true -license.workspace = true -name = "acme" -readme.workspace = true -repository.workspace = true -version.workspace = true - -[features] -default = [ - "core", - "tensor", -] - -full = [ - "core", - "derive", - "graph", - "serde", - "tensor", - "trace", -] - -core = [] - -derive = [ - "dep:acme-derive", - "macros" -] - -graph = [ - "dep:acme-graphs" -] - -io = [ - "acme-tensor/io" -] - -macros = [ - "dep:acme-macros" -] - -serde = [ - "acme-core/serde", - "acme-graphs/serde", - "acme-tensor/serde" -] - -std = [ - "acme-core/std", - "acme-tensor/std" -] - -tensor = [ - "dep:acme-tensor" -] - -trace = [ - "acme-core/trace", - "acme-graphs/trace", -] - - - - -[lib] -bench = true -crate-type = ["cdylib", "rlib"] -doctest = true -test = true - -[[bench]] -name = "tensor" -required-features = ["tensor"] - -[[example]] -doc = true -name = "autodiff" -required-features = ["macros"] - -[[example]] -name = "graph" -required-features = ["graph"] - -[[example]] -name = "macros" -required-features = ["macros"] - - -[[test]] -name = "macros" -required-features = ["macros"] - - -[build-dependencies] - -[dependencies] -acme-core = { path = "../core", version = "0.3.1" } -acme-derive = { optional = true, path = "../derive", version = "0.3.1" } -acme-graphs = { optional = true, path = "../graphs", version = "0.3.1" } -acme-macros = { optional = true, path = "../macros", version = "0.3.1" } -acme-tensor = { optional = true, path = "../tensor", version = "0.3.1" } -# acme-core = { path = "../core", version = "0.3.1-nightly" } -# acme-derive = { optional = true, path = "../derive", version = "0.3.1-nightly" } -# acme-graphs = { optional = true, path = "../graphs", version = "0.3.1-nightly" } -# acme-macros = { optional = true, path = "../macros", version = "0.3.1-nightly" } -# acme-tensor = { optional = true, path = "../tensor", version = "0.3.1-nightly" } - -[dev-dependencies] -approx = "0.5" -lazy_static = "1" -num = "0.4" -rand = "0.8" - -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - -[target.wasm32-unknown-unknown] - -[target.wasm32-wasi] diff --git a/acme/benches/tensor.rs b/acme/benches/tensor.rs deleted file mode 100644 index 4c996a40..00000000 --- a/acme/benches/tensor.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - Appellation: tensor - Contrib: FL03 -*/ -#![feature(test)] -extern crate acme; -extern crate test; - -use acme::prelude::{IntoShape, Shape, Tensor}; -use lazy_static::lazy_static; -use test::Bencher; - -lazy_static! { - static ref SHAPE_3D: Shape = SHAPE_3D_PATTERN.into_shape(); -} - -const SHAPE_3D_PATTERN: (usize, usize, usize) = (100, 10, 1); - -#[bench] -fn bench_iter(b: &mut Bencher) { - let shape = SHAPE_3D.clone(); - let n = shape.size(); - let tensor = Tensor::linspace(0f64, n as f64, n); - b.iter(|| tensor.iter().take(n)) -} - -#[bench] -fn bench_iter_rev(b: &mut Bencher) { - let shape = SHAPE_3D.clone(); - let n = shape.size(); - let tensor = Tensor::linspace(0f64, n as f64, n); - b.iter(|| tensor.iter().rev().take(n)) -} diff --git a/acme/examples/tensor.rs b/acme/examples/tensor.rs deleted file mode 100644 index d11ba925..00000000 --- a/acme/examples/tensor.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - Appellation: tensor - Contrib: FL03 -*/ -#![cfg(feature = "tensor")] - -extern crate acme; - -use acme::prelude::{Axis, BoxResult, IntoShape, Linspace, Matmul, Tensor}; - -fn main() -> BoxResult { - let shape = (3, 3); - - // tensor_iter_mut(shape)?; - axis_iter(shape, 0)?; - Ok(()) -} - -pub fn axis_iter(shape: impl IntoShape, axis: usize) -> BoxResult> { - let axis = Axis::new(axis); - let shape = shape.into_shape(); - let n = shape.size(); - let tensor = Tensor::linspace(0f64, n as f64, n).reshape(shape.clone())?; - - let mut res = Vec::new(); - for i in 0..tensor.shape()[axis] { - let mut tmp = Tensor::zeros(shape.ncols()); - for k in 0..shape.ncols() { - tmp[[k]] = tensor[[i, k]]; - } - res.push(tmp); - } - for i in res { - println!("{:?}", &i.to_vec()); - } - Ok(tensor) -} - -#[allow(dead_code)] -pub fn axis_iter_impl(shape: impl IntoShape, axis: usize) -> BoxResult> { - let axis = Axis::new(axis); - let shape = shape.into_shape(); - let n = shape.size(); - let tensor = Tensor::linspace(0f64, n as f64, n).reshape(shape.clone())?; - - let ns = tensor.layout().remove_axis(axis); - let mut res = Vec::new(); - for i in 0..tensor.shape()[axis] { - for j in ns.shape().iter().copied() { - let mut tmp = Tensor::zeros(j); - for k in 0..ns.shape()[j] { - tmp[[k]] = tensor[[i, k]]; - } - res.push(tmp); - } - } - for i in res { - println!("{:?}", &i.to_vec()); - } - Ok(tensor) -} - -pub fn example_matmul() -> BoxResult> { - let shape = (2, 3); - let tensor: Tensor = Tensor::linspace(1.0, 7.0, 6).reshape(shape)?; - let b = tensor.t(); - let c = tensor.matmul(&b); - println!("{:?}", &c); - Ok(c) -} - -pub fn tensor_iter_mut(shape: impl IntoShape) -> BoxResult> { - let shape = shape.into_shape(); - let n = shape.size(); - let exp = Vec::linspace(0f64, n as f64, n); - let mut tensor = Tensor::zeros(shape); - assert_ne!(&tensor, &exp); - for (elem, val) in tensor.iter_mut().zip(exp.iter()) { - *elem = *val; - } - assert_eq!(&tensor, &exp); - println!("{:?}", Vec::from_iter(&mut tensor.iter().rev())); - Ok(tensor) -} - -pub fn tensor_iter_mut_rev(shape: impl IntoShape) -> BoxResult> { - let shape = shape.into_shape(); - let n = shape.size(); - let exp = Vec::linspace(0f64, n as f64, n); - let mut tensor = Tensor::zeros(shape.clone()); - assert_ne!(&tensor, &exp); - for (elem, val) in tensor.iter_mut().rev().zip(exp.iter()) { - *elem = *val; - } - // assert_eq!(&tensor, &exp); - let sample = Tensor::linspace(0f64, n as f64, n).reshape(shape)?; - println!("*** Reversed ***"); - for i in sample.clone().iter().copied().rev() { - println!("{:?}", i); - } - - Ok(tensor) -} diff --git a/acme/src/lib.rs b/acme/src/lib.rs deleted file mode 100644 index 94120d85..00000000 --- a/acme/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -/* - Appellation: acme - Contrib: FL03 -*/ -//! # acme -//! -//! Acme is an autodifferentiaion library for Rust. It is designed to be a -//! flexible and powerful tool for building machine learning models and -//! other differentiable programs. -#![crate_name = "acme"] - -#[doc(inline)] -pub use acme_core::*; -#[cfg(feature = "derive")] -pub use acme_derive::*; -#[cfg(feature = "graph")] -#[doc(inline)] -pub use acme_graphs as graph; -#[cfg(feature = "macros")] -pub use acme_macros::*; -#[cfg(feature = "tensor")] -#[doc(inline)] -pub use acme_tensor as tensor; - -pub mod prelude { - #[cfg(feature = "tensor")] - #[doc(inline)] - pub use crate::tensor::prelude::*; - #[doc(inline)] - pub use acme_core::prelude::*; - #[cfg(feature = "derive")] - pub use acme_derive::*; - #[cfg(feature = "graph")] - #[doc(inline)] - pub use acme_graphs::prelude::*; - #[cfg(feature = "macros")] - pub use acme_macros::*; -} diff --git a/core/Cargo.toml b/core/Cargo.toml index 89f0cd08..5e2f4a9f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,7 +4,7 @@ description = "This is the core library for the Acme project. It contains the co edition.workspace = true homepage.workspace = true license.workspace = true -name = "acme-core" +name = "rsdiff-core" readme.workspace = true repository.workspace = true version.workspace = true @@ -14,6 +14,17 @@ default = [ "std", ] +full = [ + "default", + "approx", + "serde", + "trace" +] + +approx = [ + "dep:approx" +] + serde = [ "dep:serde", "serde-ext", @@ -29,6 +40,10 @@ trace = [ "dep:tracing", ] +wasi = [] + +wasm = [] + [lib] bench = false crate-type = ["cdylib", "rlib"] @@ -38,7 +53,9 @@ test = true [build-dependencies] [dependencies] +approx = { optional = true, version = "0.5" } num = "0.4" +paste.workspace = true serde = { optional = true, features = ["derive"], version = "1" } serde_json = { optional = true, version = "1" } smart-default.workspace = true @@ -46,6 +63,7 @@ strum.workspace = true tracing = { optional = true, version = "0.1" } [dev-dependencies] +approx = "0.5" lazy_static = "1" [package.metadata.docs.rs] diff --git a/core/src/ops/exp/operator.rs b/core/src/exp/operator.rs similarity index 100% rename from core/src/ops/exp/operator.rs rename to core/src/exp/operator.rs diff --git a/core/src/exp/structural.rs b/core/src/exp/structural.rs new file mode 100644 index 00000000..3de040dc --- /dev/null +++ b/core/src/exp/structural.rs @@ -0,0 +1,21 @@ +/* + Appellation: structural + Contrib: FL03 +*/ +use crate::ops::{Op, Params}; + +pub struct Adder { + args: BinaryArgs +} + +impl Adder { + pub fn new(lhs: A, rhs: B) -> Self { + Self { + args: BinaryArgs::new(lhs, rhs) + } + } +} + +pub trait StructuralFn { + type Output; +} \ No newline at end of file diff --git a/core/src/id/kinds/atomic.rs b/core/src/id/kinds/atomic.rs index 4535c2f2..79ceeebc 100644 --- a/core/src/id/kinds/atomic.rs +++ b/core/src/id/kinds/atomic.rs @@ -2,17 +2,13 @@ Appellation: atomic Contrib: FL03 */ -//! # Atomic Id -//! -//! -use crate::id::Identifier; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; - -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize,))] +use core::borrow::{Borrow, BorrowMut}; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + +/// +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[repr(C)] pub struct AtomicId(usize); @@ -51,6 +47,18 @@ impl AsMut for AtomicId { } } +impl Borrow for AtomicId { + fn borrow(&self) -> &usize { + &self.0 + } +} + +impl BorrowMut for AtomicId { + fn borrow_mut(&mut self) -> &mut usize { + &mut self.0 + } +} + impl Default for AtomicId { fn default() -> Self { Self::new() @@ -71,14 +79,6 @@ impl DerefMut for AtomicId { } } -impl Identifier for AtomicId {} - -impl core::fmt::Display for AtomicId { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "{}", self.0) - } -} - impl From for AtomicId { fn from(id: usize) -> Self { Self(id) @@ -90,3 +90,38 @@ impl From for usize { id.0 } } + +macro_rules! fmt_atomic { + ($($trait:ident($($fmt:tt)*)),*) => { + $( + fmt_atomic!(@impl $trait($($fmt)*)); + )* + }; + (@impl $trait:ident($($fmt:tt)*)) => { + impl core::fmt::$trait for AtomicId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, $($fmt)*, self.0) + } + } + }; +} + +fmt_atomic! { + Binary("{:b}"), + Debug("{:?}"), + Display("{}"), + LowerExp("{:e}"), + LowerHex("{:x}"), + Octal("{:o}"), + UpperExp("{:E}"), + UpperHex("{:X}") +} + +impl PartialEq for AtomicId +where + usize: PartialEq, +{ + fn eq(&self, other: &S) -> bool { + self.0.eq(other) + } +} diff --git a/core/src/id/id.rs b/core/src/id/kinds/indexed.rs similarity index 100% rename from core/src/id/id.rs rename to core/src/id/kinds/indexed.rs diff --git a/core/src/id/mod.rs b/core/src/id/mod.rs index 851978b5..ae214aea 100644 --- a/core/src/id/mod.rs +++ b/core/src/id/mod.rs @@ -2,46 +2,28 @@ Appellation: ids Contrib: FL03 */ -pub use self::{id::IndexId, kinds::*}; +pub use self::{kinds::*, traits::*}; -pub(crate) mod id; +pub(crate) mod traits; pub(crate) mod kinds { - pub use self::atomic::AtomicId; + pub use self::{atomic::AtomicId, indexed::IndexId}; - pub(crate) mod atomic; -} - -pub trait Identifier: Copy + Eq + Ord + ToString {} - -pub trait IntoId { - type Id: Identifier; - - fn into_id(self) -> Self::Id; -} - -pub trait Identifiable { - type Id: Identifier; - - fn id(&self) -> &Self::Id; - - fn id_mut(&mut self) -> &mut Self::Id; -} - -macro_rules! impl_identifier { - ($($t:ty),*) => { - $( - impl_identifier!(@loop $t); - )* - }; - (@loop $t:ty) => { - impl Identifier for $t {} - }; -} - -impl_identifier! { - u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize + pub mod atomic; + pub mod indexed; } #[cfg(test)] -mod tests {} +mod tests { + use super::traits::*; + use super::AtomicId; + + #[test] + fn test_id() { + let id = 0usize.get(); + assert_eq!(id, &0); + let atomic = AtomicId::new(); + let aid = Id::::get(&atomic); + assert_ne!(**aid, *id); + } +} diff --git a/core/src/id/traits.rs b/core/src/id/traits.rs new file mode 100644 index 00000000..78db85cf --- /dev/null +++ b/core/src/id/traits.rs @@ -0,0 +1,106 @@ +/* + Appellation: traits + Contrib: FL03 +*/ +use core::borrow::Borrow; + +/// An `Identifier` is a type that can be used as an identifier +pub trait Identifier: ToString { + private!(); +} + +/// The `Id` trait describes the behavior of a type that can be used as an id. +/// An `Id` is almost identical to an `Identifier`, but it is a trait that can be implemented for any type. +/// +pub trait Id +where + K: Identifier, +{ + type Q: Borrow; + + fn from_id(id: Self::Q) -> Self; + + fn get(&self) -> &Self::Q; +} + +pub trait Identifiable: Identify { + fn get_id(&self) -> &Self::Id; +} + +pub trait IdentifierExt: Identifier +where + Self: Copy + Eq + Ord + core::hash::Hash, +{ +} + +pub trait HashId: Identifier +where + Self: Eq + core::hash::Hash, +{ +} + +pub trait IntoId { + type Id: Identifier; + + fn into_id(self) -> Self::Id; +} + +pub trait Identify { + type Id: Identifier; + + fn id(&self) -> &Self::Id; +} + +pub trait IdentifyMut: Identify { + fn id_mut(&mut self) -> &mut Self::Id; +} + +/* + *********** impls *********** +*/ +impl Id for Q +where + K: Identifier, + Q: Borrow, +{ + type Q = Q; + + fn from_id(id: Self::Q) -> Self { + id + } + + fn get(&self) -> &Self::Q { + self + } +} + +impl Identify for S +where + S: Identifier, +{ + type Id = S; + + fn id(&self) -> &Self::Id { + self + } +} + +impl HashId for Id where Id: Eq + Identifier + core::hash::Hash {} + +impl IdentifierExt for Id where Id: Copy + Eq + Identifier + Ord + core::hash::Hash {} + +macro_rules! identifier { + ($($t:ty),*) => { + $( + identifier!(@impl $t); + )* + }; + (@impl $t:ty) => { + impl Identifier for $t { + seal!(); + } + }; +} + +identifier!(f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); +identifier!(bool, char, &str, String); diff --git a/core/src/lib.rs b/core/src/lib.rs index dbd557dc..b592d5a8 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,10 +1,10 @@ /* - Appellation: acme-core + Appellation: rsdiff-core Contrib: FL03 */ //! # Core //! -//! The core module provides the fundamental building blocks for the Acme library. +//! The core module provides the fundamental building blocks for the rsdiff library. //! One of the primary focuses of the library is to provide a set of primitives and utilities //! for interpreting various operations. The core module is the foundation for the rest of the //! library, and it is designed to be as lightweight as possible. @@ -12,6 +12,7 @@ #[cfg(not(feature = "std"))] extern crate alloc; +pub use self::traits::prelude::*; // pub use self::utils::*; #[macro_use] @@ -23,19 +24,25 @@ pub(crate) mod utils; pub mod error; pub mod id; -#[doc(hidden)] -pub mod math; #[macro_use] pub mod ops; -pub mod specs; +pub mod stores; +pub mod traits; pub mod types; +#[doc(hidden)] +pub mod exp { + //! # Experimental + pub mod operator; +} + #[doc(hidden)] pub mod prelude { pub use crate::error::*; pub use crate::id::*; pub use crate::nested; pub use crate::ops::prelude::*; - pub use crate::specs::prelude::*; + pub use crate::stores::prelude::*; + pub use crate::traits::prelude::*; pub use crate::types::*; } diff --git a/core/src/math/mod.rs b/core/src/math/mod.rs deleted file mode 100644 index 12dbfb85..00000000 --- a/core/src/math/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - Appellation: math - Contrib: FL03 -*/ -//! # Mathematics -//! -//! This module implements fundamental mathematical concepts and operations. -//! Each sub-module is dedicated to a specific branch of mathematics. -pub use self::props::*; - -pub(crate) mod props; - -pub mod linalg; - -pub trait Group {} - -#[cfg(test)] -mod tests {} diff --git a/core/src/ops/expr.rs b/core/src/ops/expr.rs index de58c1ed..3f9e271c 100644 --- a/core/src/ops/expr.rs +++ b/core/src/ops/expr.rs @@ -2,31 +2,43 @@ Appellation: expr Contrib: FL03 */ -use super::{BinaryOp, UnaryOp}; use crate::id::IndexId; +use crate::ops::{BinaryOp, NaryOp, TernaryOp, UnaryOp}; use crate::prelude::AnyBox; +use paste::paste; use strum::EnumIs; -#[doc(hidden)] +pub(crate) type BoxExpr = Box>; + #[derive(Clone, Debug, EnumIs, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Expr { - Binary(BinaryExpr), - Unary(UnaryExpr), + Binary(ExprBinary), + Nary(ExprNary), + Ternary(ExprTernary), + Unary(ExprUnary), Constant(V), Variable { id: IndexId, value: V }, } impl Expr { pub fn binary(lhs: Expr, rhs: Expr, op: BinaryOp) -> Self { - Self::Binary(BinaryExpr::new(lhs, rhs, op)) + Self::Binary(ExprBinary::new(lhs, rhs, op)) } pub fn constant(value: V) -> Self { Self::Constant(value) } - pub fn unary(arg: Expr, op: UnaryOp) -> Self { - Self::Unary(UnaryExpr::new(arg, op)) + pub fn nary(args: impl IntoIterator>, op: NaryOp) -> Self { + Self::Nary(ExprNary::new(args, op)) + } + + pub fn ternary(x: Expr, y: Expr, z: Expr, op: TernaryOp) -> Self { + Self::Ternary(ExprTernary::new(x, y, z, op)) + } + + pub fn unary(recv: Expr, op: UnaryOp) -> Self { + Self::Unary(ExprUnary::new(recv, op)) } pub fn variable(idx: K, value: V) -> Self { @@ -35,81 +47,93 @@ impl Expr { value, } } -} -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct BinaryExpr { - lhs: Box>, - op: BinaryOp, - rhs: Box>, + pub fn boxed(self) -> BoxExpr { + Box::new(self) + } } -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct UnaryExpr { - op: UnaryOp, - recv: Box>, -} +macro_rules! expr_variant { + ($variant:ident<$op:ty>($($param:ident),*)) => { + #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct $variant { + op: $op, + $($param: Box>),* + } -mod expr_impl { - use super::{BinaryExpr, Expr, UnaryExpr}; - use crate::ops::{BinaryOp, UnaryOp}; + impl $variant { + pub fn new($($param: Expr,)* op: $op,) -> Self { + Self { + op, + $($param: Box::new($param)),* + } + } - impl BinaryExpr { - pub fn new(lhs: Expr, rhs: Expr, op: BinaryOp) -> Self { - Self { - lhs: Box::new(lhs), - op, - rhs: Box::new(rhs), + pub fn op(&self) -> $op { + self.op } - } - pub fn lhs(&self) -> &Expr { - &self.lhs - } + pub fn op_mut(&mut self) -> &mut $op { + &mut self.op + } - pub fn lhs_mut(&mut self) -> &mut Expr { - &mut self.lhs + $( + pub fn $param(&self) -> &Expr { + &self.$param + } + )* + + paste! { + $( + pub fn [<$param _mut>](&mut self) -> &mut Expr { + &mut self.$param + } + )* + } } + }; - pub fn op(&self) -> BinaryOp { - self.op - } +} - pub fn op_mut(&mut self) -> &mut BinaryOp { - &mut self.op - } +expr_variant!(ExprBinary(lhs, rhs)); +expr_variant!(ExprTernary(x, y, z)); +expr_variant!(ExprUnary(recv)); - pub fn rhs(&self) -> &Expr { - &self.rhs - } +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ExprNary { + args: Vec>>, + op: NaryOp, +} - pub fn rhs_mut(&mut self) -> &mut Expr { - &mut self.rhs +impl ExprNary { + pub fn new(args: impl IntoIterator>, op: NaryOp) -> Self { + Self { + args: Vec::from_iter(args.into_iter().map(|i| i.boxed())), + op, } } - impl UnaryExpr { - pub fn new(recv: Expr, op: UnaryOp) -> Self { - Self { - recv: Box::new(recv), - op, - } - } + pub fn as_slice(&self) -> &[Box>] { + &self.args + } - pub fn op(&self) -> UnaryOp { - self.op - } + pub fn as_mut_slice(&mut self) -> &mut [Box>] { + &mut self.args + } - pub fn op_mut(&mut self) -> &mut UnaryOp { - &mut self.op - } + pub fn args(&self) -> &Vec>> { + &self.args + } - pub fn recv(&self) -> &Expr { - &self.recv - } + pub fn args_mut(&mut self) -> &mut Vec>> { + &mut self.args + } - pub fn recv_mut(&mut self) -> &mut Expr { - &mut self.recv - } + pub fn op(&self) -> NaryOp { + self.op + } + + pub fn op_mut(&mut self) -> &mut NaryOp { + &mut self.op } } diff --git a/core/src/ops/kinds/binary/arithmetic.rs b/core/src/ops/kinds/binary/arithmetic.rs index e7f110a4..2ff5cb52 100644 --- a/core/src/ops/kinds/binary/arithmetic.rs +++ b/core/src/ops/kinds/binary/arithmetic.rs @@ -75,38 +75,13 @@ macro_rules! impl_binary_assign { where A: $($p)::*, { - fn eval(&self, mut lhs: A, rhs: B) { - $($p)::*::$op(&mut lhs, rhs); + fn eval(&self, lhs: &mut A, rhs: B) { + $($p)::*::$op(lhs, rhs); } } }; } -impl_binary_op!( - Addition(core::ops::Add.add), - Division(core::ops::Div.div), - Multiplication(core::ops::Mul.mul), - Remainder(core::ops::Rem.rem), - Subtraction(core::ops::Sub.sub), - Power(num::traits::Pow.pow) -); - -impl_binary_assign!( - AddAssign(core::ops::AddAssign.add_assign), - DivAssign(core::ops::DivAssign.div_assign), - MulAssign(core::ops::MulAssign.mul_assign), - RemAssign(core::ops::RemAssign.rem_assign), - SubAssign(core::ops::SubAssign.sub_assign) -); - -// impl_binary_op!( -// (BitAnd, BitAnd. -// (BitOr, BitOr, |), -// (BitXor, BitXor, &|), -// (Shl, Shl, <<), -// (Shr, Shr, >>) -// ); - operations!(Arithmetic { Add(Addition): add, Div(Division): div, @@ -124,6 +99,23 @@ operations!(ArithmeticAssign { SubAssign(SubAssign): sub_assign }); +impl_binary_op!( + Addition(core::ops::Add.add), + Division(core::ops::Div.div), + Multiplication(core::ops::Mul.mul), + Remainder(core::ops::Rem.rem), + Subtraction(core::ops::Sub.sub), + Power(num::traits::Pow.pow) +); + +impl_binary_assign!( + AddAssign(core::ops::AddAssign.add_assign), + DivAssign(core::ops::DivAssign.div_assign), + MulAssign(core::ops::MulAssign.mul_assign), + RemAssign(core::ops::RemAssign.rem_assign), + SubAssign(core::ops::SubAssign.sub_assign) +); + impl Arithmetic { pub fn new(op: Arithmetic) -> Self { op @@ -221,7 +213,7 @@ impl BinaryAssignOp for ArithmeticAssign where A: NumAssignOps, { - fn eval(&self, lhs: A, rhs: B) { + fn eval(&self, lhs: &mut A, rhs: B) { self.assign_op().eval(lhs, rhs) } } diff --git a/core/src/ops/kinds/binary/mod.rs b/core/src/ops/kinds/binary/mod.rs index 85f73751..ce844a68 100644 --- a/core/src/ops/kinds/binary/mod.rs +++ b/core/src/ops/kinds/binary/mod.rs @@ -10,7 +10,7 @@ pub(crate) mod specs; use crate::ops::{Binary, Evaluate, Operand}; use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; +use strum::{AsRefStr, Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; impl Evaluate

for O where @@ -26,6 +26,7 @@ where } #[derive( + AsRefStr, Clone, Copy, Debug, diff --git a/core/src/ops/kinds/binary/specs.rs b/core/src/ops/kinds/binary/specs.rs index c541d128..a262828c 100644 --- a/core/src/ops/kinds/binary/specs.rs +++ b/core/src/ops/kinds/binary/specs.rs @@ -5,11 +5,6 @@ pub type BoxedBinOp = Box>; -pub trait BinaryOperand { - type Args: crate::ops::Params; - type Output; -} - pub trait BinOp { type Output; @@ -17,7 +12,7 @@ pub trait BinOp { } pub trait BinaryAssignOp { - fn eval(&self, lhs: A, rhs: B); + fn eval(&self, lhs: &mut A, rhs: B); } impl BinOp for S @@ -40,20 +35,35 @@ impl BinOp for Box> { } impl BinaryAssignOp for Box> { - fn eval(&self, lhs: A, rhs: B) { + fn eval(&self, lhs: &mut A, rhs: B) { self.as_ref().eval(lhs, rhs) } } -pub trait Logarithm { +pub trait Log { type Output; fn log(self, base: T) -> Self::Output; } macro_rules! impl_log { - ($t:ty) => { - impl Logarithm<$t> for $t { + ($($t:ty),*) => { + $( + impl_log!(@impl $t); + )* + }; + ($($call:ident<$out:ty>($t:ty)),*) => { + $( + impl_log!(@impl $call<$out>($t)); + )* + }; + ($call:ident<$out:ty>: $($t:ty),*) => { + $( + impl_log!(@impl $call<$out>($t)); + )* + }; + (@impl $t:ty) => { + impl Log<$t> for $t { type Output = $t; fn log(self, base: $t) -> Self::Output { @@ -61,33 +71,17 @@ macro_rules! impl_log { } } }; - (other $t:ty => $out:ty; $method:ident) => { - impl Logarithm<$t> for $t { + (@impl $call:ident<$out:ty>($t:ty)) => { + impl Log<$t> for $t { type Output = $out; fn log(self, base: $t) -> Self::Output { - self.$method(base) + self.$call(base) } } }; - (all [$($t:ty),*]) => { - $( - impl_log!($t); - )* - }; } -impl_log!(all [f32, f64]); - -impl_log!(other i8 => u32; ilog); -impl_log!(other i16 => u32; ilog); -impl_log!(other i32 => u32; ilog); -impl_log!(other i64 => u32; ilog); -impl_log!(other i128 => u32; ilog); -impl_log!(other isize => u32; ilog); -impl_log!(other u8 => u32; ilog); -impl_log!(other u16 => u32; ilog); -impl_log!(other u32 => u32; ilog); -impl_log!(other u64 => u32; ilog); -impl_log!(other u128 => u32; ilog); -impl_log!(other usize => u32; ilog); +impl_log!(f32, f64); + +impl_log!(ilog: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); diff --git a/core/src/ops/mod.rs b/core/src/ops/mod.rs index 86cb43d0..34f2b24a 100644 --- a/core/src/ops/mod.rs +++ b/core/src/ops/mod.rs @@ -45,12 +45,6 @@ pub(crate) mod traits { } } -#[doc(hidden)] -pub mod exp { - //! # Experimental - pub mod operator; -} - pub trait IntoOp where F: Operator, diff --git a/core/src/ops/traits/evaluate.rs b/core/src/ops/traits/evaluate.rs index 9f7dd1ad..26a69545 100644 --- a/core/src/ops/traits/evaluate.rs +++ b/core/src/ops/traits/evaluate.rs @@ -23,3 +23,19 @@ where fn grad(&self, args: Args) -> Self::Grad; } + +pub trait FnArgs { + + type Kind; +} + +pub struct Partial { + pub args: T, + pub func: F +} + +impl Partial where F: for <'a> Fn(&'a T) -> T { + pub fn new(args: T, func: F) -> Self { + Self { args, func } + } +} \ No newline at end of file diff --git a/core/src/ops/traits/operand.rs b/core/src/ops/traits/operand.rs index a72f6c0c..7e5571d6 100644 --- a/core/src/ops/traits/operand.rs +++ b/core/src/ops/traits/operand.rs @@ -63,7 +63,7 @@ where } macro_rules! impl_operand_ty { - ($($kind:ident),*) => { + ($($kind:ident),* $(,)?) => { $( impl_operand_ty!(@impl $kind); )* @@ -71,6 +71,7 @@ macro_rules! impl_operand_ty { (@impl $kind:ident) => { #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + #[repr(transparent)] pub struct $kind; impl OperandType for $kind { @@ -83,4 +84,4 @@ macro_rules! impl_operand_ty { }; } -impl_operand_ty!(Binary, Nary, Ternary, Unary); +impl_operand_ty!(Binary, Nary, Ternary, Unary,); diff --git a/core/src/specs/gradient.rs b/core/src/specs/gradient.rs deleted file mode 100644 index c1eededd..00000000 --- a/core/src/specs/gradient.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - Appellation: gradient - Contrib: FL03 -*/ -use crate::id::Identifiable; -use crate::specs::StoreExt; -pub trait Jacobian { - type Item; - - fn jacobian(&self) -> Self::Item; -} - -pub trait Partial { - type Output; - - fn partial(&self, args: T) -> Self::Output; -} - -pub trait Grad { - type Args: Identifiable; - type Gradient: StoreExt; - - fn grad(&self) -> Self::Gradient; - fn grad_at(&self, wrt: ::Id) -> Self::Args; -} - -pub trait Gradient { - type Gradient; - - fn grad(&self, args: T) -> Self::Gradient; -} - -pub trait IsDifferentiable { - /// Returns true if the function is differentiable. - fn differentiable(&self) -> bool; -} - -impl IsDifferentiable for S -where - S: Grad, -{ - fn differentiable(&self) -> bool { - true - } -} diff --git a/core/src/stores/mod.rs b/core/src/stores/mod.rs new file mode 100644 index 00000000..b4c6bdac --- /dev/null +++ b/core/src/stores/mod.rs @@ -0,0 +1,11 @@ +/* + Appellation: stores + Contrib: FL03 +*/ +pub use self::specs::*; + +pub(crate) mod specs; + +pub(crate) mod prelude { + pub use super::specs::*; +} diff --git a/core/src/specs/store.rs b/core/src/stores/specs.rs similarity index 82% rename from core/src/specs/store.rs rename to core/src/stores/specs.rs index 5b97e4bb..e91596c7 100644 --- a/core/src/specs/store.rs +++ b/core/src/stores/specs.rs @@ -2,6 +2,7 @@ Appellation: stores Contrib: FL03 */ +use crate::id::Identify; #[cfg(not(feature = "std"))] use alloc::collections::{btree_map, BTreeMap}; use core::borrow::Borrow; @@ -53,7 +54,8 @@ pub trait Store { pub trait StoreExt where - T: crate::id::Identifiable, + T: Identify, + ::Id: Copy, { type Store: Store; @@ -81,7 +83,8 @@ where impl StoreExt for S where S: Store, - T: crate::id::Identifiable, + T: Identify, + ::Id: Copy, { type Store = S; @@ -94,18 +97,8 @@ where } } -pub trait Cache { - fn get_or_insert_with(&mut self, key: K, f: F) -> &mut V - where - F: FnOnce() -> V; -} - -pub trait OrInsert { - fn or_insert(&mut self, key: K, value: V) -> &mut V; -} - macro_rules! entry { - ($($prefix:ident)::* -> $call:ident($($arg:tt),*)) => { + ($($prefix:ident)::*.$call:ident($($arg:tt),*)) => { $($prefix)::*::Entry::$call($($arg),*) }; @@ -113,28 +106,26 @@ macro_rules! entry { macro_rules! impl_entry { ($($prefix:ident)::* where $($preds:tt)* ) => { - impl<'a, K, V> Entry<'a> for $($prefix)::*::Entry<'a, K, V> where $($preds)* { type Key = K; type Value = V; fn key(&self) -> &Self::Key { - entry!($($prefix)::* -> key(self)) + entry!($($prefix)::*.key(self)) } fn or_insert(self, default: Self::Value) -> &'a mut Self::Value { - entry!($($prefix)::* -> or_insert(self, default)) + entry!($($prefix)::*.or_insert(self, default)) } } - }; } macro_rules! impl_store { - ($t:ty, where $($preds:tt)* ) => { + ($t:ty where $($rest:tt)*) => { - impl Store for $t where $($preds)* { + impl Store for $t where $($rest)* { fn get(&self, key: &K) -> Option<&V> { <$t>::get(self, &key) } @@ -158,6 +149,6 @@ macro_rules! impl_store { impl_entry!(btree_map where K: Ord); #[cfg(feature = "std")] impl_entry!(hash_map where K: Eq + core::hash::Hash); -impl_store!(BTreeMap, where K: Ord); +impl_store!(BTreeMap where K: Ord); #[cfg(feature = "std")] -impl_store!(HashMap, where K: Eq + core::hash::Hash); +impl_store!(HashMap where K: Eq + core::hash::Hash); diff --git a/core/src/specs/eval.rs b/core/src/traits/eval.rs similarity index 100% rename from core/src/specs/eval.rs rename to core/src/traits/eval.rs diff --git a/core/src/specs/mod.rs b/core/src/traits/mod.rs similarity index 59% rename from core/src/specs/mod.rs rename to core/src/traits/mod.rs index 005d9e92..08b81837 100644 --- a/core/src/specs/mod.rs +++ b/core/src/traits/mod.rs @@ -1,20 +1,21 @@ /* - Appellation: specs + Appellation: traits Contrib: FL03 */ +pub use self::{eval::*, propagate::*, scalar::Scalar}; -pub use self::{eval::*, gradient::*, prop::*, scalar::Scalar, store::*}; - -pub(crate) mod eval; -pub(crate) mod gradient; -pub(crate) mod prop; -pub(crate) mod scalar; -pub(crate) mod store; +pub mod eval; +pub mod propagate; +pub mod scalar; pub trait AsSlice { fn as_slice(&self) -> &[T]; } +pub trait AsSliceMut { + fn as_mut_slice(&mut self) -> &mut [T]; +} + impl AsSlice for S where S: AsRef<[T]>, @@ -24,26 +25,26 @@ where } } -pub trait AsSliceMut { - fn as_slice_mut(&mut self) -> &mut [T]; -} - impl AsSliceMut for S where S: AsMut<[T]>, { - fn as_slice_mut(&mut self) -> &mut [T] { + fn as_mut_slice(&mut self) -> &mut [T] { self.as_mut() } } +pub trait Gradient { + type Gradient; + + fn grad(&self, args: T) -> Self::Gradient; +} + pub(crate) mod prelude { pub use super::eval::*; - pub use super::gradient::*; - pub use super::prop::*; - pub use super::scalar::*; - pub use super::store::*; - pub use super::{AsSlice, AsSliceMut}; + pub use super::propagate::*; + pub use super::scalar::Scalar; + pub use super::{AsSlice, AsSliceMut, Gradient}; } #[cfg(test)] diff --git a/core/src/specs/prop.rs b/core/src/traits/propagate.rs similarity index 86% rename from core/src/specs/prop.rs rename to core/src/traits/propagate.rs index b10d6579..88b2df63 100644 --- a/core/src/specs/prop.rs +++ b/core/src/traits/propagate.rs @@ -13,6 +13,19 @@ pub trait Backward { fn backward(&self) -> Self::Output; } +pub trait Module: Forward + Backward { + type Config; + type Params; + + fn new(config: Self::Config) -> Self; + + fn config(&self) -> &Self::Config; + + fn config_mut(&mut self) -> &mut Self::Config; + + fn parameters(&self) -> Self::Params; +} + /// [Forward] describes an object capable of forward propagation. pub trait Forward { type Output; diff --git a/core/src/specs/scalar.rs b/core/src/traits/scalar.rs similarity index 100% rename from core/src/specs/scalar.rs rename to core/src/traits/scalar.rs diff --git a/core/src/types/variables.rs b/core/src/types/variables.rs index 781b5612..5e25fd4a 100644 --- a/core/src/types/variables.rs +++ b/core/src/types/variables.rs @@ -3,7 +3,7 @@ Contrib: FL03 */ use crate::prelude::{AtomicId, BinaryOp, Gradient, Op, UnaryOp}; -use crate::specs::{Eval, EvalMut, EvalOnce}; +use crate::{Eval, EvalMut, EvalOnce}; use core::borrow::{Borrow, BorrowMut}; use core::ops::{Neg, Not}; use num::{Num, One, Zero}; diff --git a/core/tests/utils.rs b/core/tests/utils.rs index c5dd6497..0a3e532b 100644 --- a/core/tests/utils.rs +++ b/core/tests/utils.rs @@ -4,9 +4,9 @@ */ #![cfg(test)] -extern crate acme_core as acme; +extern crate rsdiff_core as rsdiff; -use acme::nested; +use rsdiff::nested; #[test] fn test_nested() { diff --git a/derive/Cargo.toml b/derive/Cargo.toml index ec74f8a7..b088d980 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true -name = "acme-derive" +name = "rsdiff-derive" readme.workspace = true repository.workspace = true version.workspace = true diff --git a/derive/src/lib.rs b/derive/src/lib.rs index d246b84b..e6e63482 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,8 +1,8 @@ /* - Appellation: acme-derive + Appellation: rsdiff-derive Contrib: FL03 */ -//! # acme-derive +//! # rsdiff-derive //! //! extern crate proc_macro; diff --git a/exp/container/Cargo.toml b/exp/container/Cargo.toml deleted file mode 100644 index 0c14c93a..00000000 --- a/exp/container/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -authors = ["FL03 (https://github.com/FL03)"] -description = "acme-container" -edition = "2021" -name = "acme-container" -version = "0.1.0" - -[features] -default = [ - "std" -] - -std = [ - "acme/std", -] - -[lib] -bench = true -crate-type = ["cdylib", "rlib"] -doctest = true -test = true - -[build-dependencies] - -[dependencies] -num = "0.4" -rawpointer = "0.2.1" - -[dependencies.acme] -path = "../../acme" - -[dev-dependencies] diff --git a/exp/container/src/data/container.rs b/exp/container/src/data/container.rs deleted file mode 100644 index ec5bd7c8..00000000 --- a/exp/container/src/data/container.rs +++ /dev/null @@ -1,243 +0,0 @@ -/* - Appellation: container - Contrib: FL03 -*/ -use super::specs::{Data, DataOwned, RawData, RawDataMut}; -use super::{nonnull_from_vec_data, Container, SharedContainer}; -use crate::dim::can_index_slice; -use crate::iter::to_vec_mapped; -use acme::prelude::{IntoShape, IntoStride, Layout, Shape, Stride}; -use core::ptr::NonNull; -use core::slice; -use rawpointer::PointerExt; - -#[derive(Clone)] -pub struct ContainerBase -where - S: RawData, -{ - pub(crate) data: S, - pub(crate) layout: Layout, - pub(crate) ptr: NonNull, -} - -impl ContainerBase -where - S: RawData, -{ - #[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, - { - RawDataMut::try_ensure_unique(self); // for ArcArray - self.ptr.as_ptr() - } - pub fn as_slice_memory_order(&self) -> Option<&[A]> - where - S: Data, - { - if self.is_contiguous() { - let offset = self.layout.offset_from_low_addr_ptr_to_logical_ptr(); - unsafe { - Some(slice::from_raw_parts( - PointerExt::sub(self.ptr, offset).as_ptr(), - self.size(), - )) - } - } else { - None - } - } - /// Without any coping, turn the tensor into a shared tensor. - pub fn into_shared(self) -> SharedContainer - where - S: DataOwned, - { - let data = self.data.into_shared(); - // safe because: equivalent unmoved data, ptr and dims remain valid - unsafe { ContainerBase::from_data_ptr(data, self.ptr).with_layout(self.layout) } - } - /// Return true if the array is known to be contiguous. - pub fn is_contiguous(&self) -> bool { - self.layout().is_contiguous() - } - /// Return true if the array is known to be c-contiguous (Row Major) - pub fn is_standard_layout(&self) -> bool { - crate::dim::is_layout_c(self.layout()) - } - /// - pub fn iter(&self) -> slice::Iter<'_, A> - where - S: Data, - { - dbg!("Implement a custom iter for ContainerBase"); - self.as_slice_memory_order().unwrap().iter() - } - - pub fn layout(&self) -> &Layout { - &self.layout - } - - pub fn map<'a, B, F>(&'a self, f: F) -> Container - where - F: FnMut(&'a A) -> B, - A: 'a, - S: Data, - { - unsafe { - if let Some(slc) = self.as_slice_memory_order() { - ContainerBase::from_shape_trusted_iter_unchecked( - self.shape().as_slice(), - slc.iter(), - f, - ) - } else { - ContainerBase::from_shape_trusted_iter_unchecked(self.shape(), self.iter(), f) - } - } - } - - pub fn mapv(&self, mut f: F) -> Container - where - F: FnMut(A) -> B, - A: Clone, - S: Data, - { - self.map(move |x| f(x.clone())) - } - - pub fn shape(&self) -> &Shape { - self.layout().shape() - } - - pub fn stride(&self) -> &Stride { - self.layout().strides() - } - - /// Return the strides of the array as a slice. - pub fn strides(&self) -> &[isize] { - let s = self.stride().as_slice(); - // reinterpret unsigned integer as signed - unsafe { slice::from_raw_parts(s.as_ptr() as *const _, s.len()) } - } - - pub fn size(&self) -> usize { - self.layout.size() - } - - pub fn to_owned(&self) -> Container - where - A: Clone, - S: Data, - { - if let Some(slc) = self.as_slice_memory_order() { - unsafe { Container::from_shape_vec_unchecked(self.shape(), slc.to_vec()) } - } else { - self.map(A::clone) - } - } -} - -// Internal methods -impl ContainerBase -where - S: DataOwned + RawData, -{ - unsafe fn from_vec_dim_stride_unchecked( - dim: impl IntoShape, - strides: impl IntoStride, - mut v: Vec, - ) -> Self { - let layout = Layout::new(0, dim, strides); - // debug check for issues that indicates wrong use of this constructor - debug_assert!(can_index_slice(&v, layout.shape(), layout.strides()).is_ok()); - - let ptr = { - let tmp = nonnull_from_vec_data(&mut v); - PointerExt::add(tmp, layout.offset_from_low_addr_ptr_to_logical_ptr()) - }; - ContainerBase::from_data_ptr(DataOwned::new(v), ptr).with_layout(layout) - } - - /// 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: IntoShape, - I: ExactSizeIterator, - F: FnMut(I::Item) -> A, - { - let shape = shape.into_shape(); - let strides = crate::dim::default_strides(&shape); // shape.stride().strides_for_dim(&dim); - let v = to_vec_mapped(iter, map); - Self::from_vec_dim_stride_unchecked(shape, strides, v) - } -} - -impl ContainerBase -where - S: DataOwned, -{ - pub unsafe fn from_shape_vec_unchecked(shape: impl IntoShape, v: Vec) -> Self { - let shape = shape.into_shape(); - let strides = crate::default_strides(&shape); - Self::from_vec_dim_stride_unchecked(shape, strides, v) - } -} - -impl ContainerBase -where - S: RawData, -{ - pub(crate) unsafe fn from_data_ptr(data: S, ptr: NonNull) -> Self { - let tensor = Self { - data, - layout: Layout::contiguous(0), - ptr, - }; - debug_assert!(tensor.pointer_is_inbounds()); - tensor - } - - pub(crate) fn pointer_is_inbounds(&self) -> bool { - self.data._is_pointer_inbounds(self.as_ptr()) - } - - pub(crate) fn with_layout(self, layout: Layout) -> ContainerBase { - debug_assert_eq!(self.layout().size(), layout.size()); - - unsafe { self.with_layout_unchecked(layout) } - } - - pub(crate) unsafe fn with_layout_unchecked(self, layout: Layout) -> ContainerBase { - Self { - data: self.data, - layout, - ptr: self.ptr, - } - } -} diff --git a/exp/container/src/data/elem.rs b/exp/container/src/data/elem.rs deleted file mode 100644 index b2ec105d..00000000 --- a/exp/container/src/data/elem.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* - Appellation: elem - Contrib: FL03 -*/ -//! # Elements -//! -//! -use acme::prelude::R; - -pub trait Element { - type Elem; - - fn dtype(&self) -> R; -} diff --git a/exp/container/src/data/mod.rs b/exp/container/src/data/mod.rs deleted file mode 100644 index 0f283d30..00000000 --- a/exp/container/src/data/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* - Appellation: data - Contrib: FL03 -*/ -//! # Data -//! -//! -pub(crate) use self::utils::*; -pub use self::{container::*, specs::*}; - -pub(crate) mod container; -pub(crate) mod specs; - -pub mod elem; - -pub mod repr { - pub use self::{owned::OwnedRepr, shared::OwnedArcRepr, view::*}; - - pub(crate) mod owned; - pub(crate) mod shared; - #[allow(dead_code)] - pub(crate) mod view; -} - -pub type Container = ContainerBase>; - -pub type SharedContainer = ContainerBase>; - -pub type ContainerView<'a, A = f64> = ContainerBase>; - -pub type ContainerViewMut<'a, A = f64> = ContainerBase>; - -pub(crate) mod utils { - #[cfg(not(feature = "std"))] - use alloc::vec::Vec; - use core::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. - #[allow(dead_code)] - #[inline] - pub(crate) unsafe fn nonnull_debug_checked_from_ptr(ptr: *mut T) -> NonNull { - debug_assert!(!ptr.is_null()); - NonNull::new_unchecked(ptr) - } -} - -pub(crate) mod prelude { - pub use super::repr::*; - pub use super::specs::*; -} - -#[cfg(test)] -mod tests {} diff --git a/exp/container/src/data/repr/owned.rs b/exp/container/src/data/repr/owned.rs deleted file mode 100644 index 162b0a20..00000000 --- a/exp/container/src/data/repr/owned.rs +++ /dev/null @@ -1,257 +0,0 @@ -/* - Appellation: owned - Contrib: FL03 -*/ -use crate::data::repr::OwnedArcRepr; -use crate::data::utils::nonnull_from_vec_data; -use crate::data::{Container, ContainerBase, SharedContainer}; -use crate::data::{Data, DataMut, DataOwned, RawData, RawDataClone, RawDataMut, RawDataSubst}; -use core::mem::{self, ManuallyDrop, MaybeUninit}; -use core::ptr::NonNull; -use core::slice; -use rawpointer::PointerExt; -use std::sync::Arc; - -#[derive(Debug)] -#[repr(C)] -pub struct OwnedRepr { - capacity: usize, - len: usize, - ptr: NonNull, -} - -impl OwnedRepr { - /// Create an [OwnedRepr] from a [Vec] - pub fn from_vec(vec: Vec) -> Self { - let mut v = ManuallyDrop::new(vec); - - Self { - capacity: v.capacity(), - len: v.len(), - ptr: nonnull_from_vec_data(&mut v), - } - } - - pub fn as_ptr(&self) -> *const A { - self.ptr.as_ptr() - } - - pub fn as_ptr_mut(&mut self) -> *mut A { - self.ptr.as_ptr() - } - - pub const fn capacity(&self) -> usize { - self.capacity - } - - pub const fn len(&self) -> usize { - self.len - } -} - -// Internal methods -#[allow(dead_code)] -impl OwnedRepr { - pub(crate) fn as_nonnull_mut(&mut self) -> NonNull { - self.ptr - } - - pub(crate) fn as_slice(&self) -> &[A] { - unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } - } - - /// 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, - } - } - - pub(crate) fn into_vec(self) -> Vec { - ManuallyDrop::new(self).take_as_vec() - } - /// 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; - } - - pub(crate) fn take_as_vec(&mut self) -> Vec { - let capacity = self.capacity; - let len = self.len; - - self.capacity = 0; - self.len = 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 Data for OwnedRepr { - #[inline] - fn into_owned(self_: ContainerBase) -> Container - where - A: Clone, - { - self_ - } - - #[inline] - fn try_into_owned_nocopy( - self_: ContainerBase, - ) -> Result, ContainerBase> { - Ok(self_) - } - - fn to_shared(self_: &ContainerBase) -> SharedContainer - where - Self::Elem: Clone, - { - // clone to shared - self_.to_owned().into_shared() - } -} - -unsafe impl DataMut for OwnedRepr {} - -unsafe impl DataOwned for OwnedRepr { - type MaybeUninit = OwnedRepr>; - - fn new(elements: Vec) -> Self { - OwnedRepr::from(elements) - } - - fn into_shared(self) -> OwnedArcRepr { - OwnedArcRepr(Arc::new(self)) - } -} - -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 { - fn try_ensure_unique(_: &mut ContainerBase) - where - Self: Sized, - { - } - - fn try_is_unique(&mut self) -> Option { - Some(true) - } -} - -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 mem::size_of::() != 0 { - let our_off = - (ptr.as_ptr() as isize - self.as_ptr() as isize) / mem::size_of::() as isize; - new_ptr = PointerExt::offset(new_ptr, our_off); - } - (u, new_ptr) - } - - unsafe fn clone_from_with_ptr( - &mut self, - other: &Self, - ptr: NonNull, - ) -> NonNull { - let our_off = if mem::size_of::() != 0 { - (ptr.as_ptr() as isize - other.as_ptr() as isize) / mem::size_of::() as isize - } else { - 0 - }; - self.clone_from(other); - PointerExt::offset(self.as_nonnull_mut(), our_off) - } -} - -impl RawDataSubst for OwnedRepr { - type Output = OwnedRepr; - - unsafe fn data_subst(self) -> Self::Output { - self.data_subst() - } -} - -unsafe impl Send for OwnedRepr {} - -unsafe impl Sync for OwnedRepr {} - -impl From> for OwnedRepr { - fn from(vec: Vec) -> Self { - Self::from_vec(vec) - } -} diff --git a/exp/container/src/data/repr/shared.rs b/exp/container/src/data/repr/shared.rs deleted file mode 100644 index 5eb28a86..00000000 --- a/exp/container/src/data/repr/shared.rs +++ /dev/null @@ -1,140 +0,0 @@ -/* - Appellation: shared - Contrib: FL03 -*/ -use crate::data::repr::OwnedRepr; -use crate::data::specs::*; -use crate::data::{Container, ContainerBase, SharedContainer}; -#[cfg(not(feature = "std"))] -use alloc::sync::Arc; -use core::mem::MaybeUninit; -use core::ptr::NonNull; -use rawpointer::PointerExt; -#[cfg(feature = "std")] -use std::sync::Arc; - -#[derive(Debug)] -pub struct OwnedArcRepr(pub(crate) Arc>); - -impl Clone for OwnedArcRepr { - fn clone(&self) -> Self { - OwnedArcRepr(self.0.clone()) - } -} - -unsafe impl Data for OwnedArcRepr { - fn into_owned(self_: ContainerBase) -> crate::data::Container - where - Self::Elem: Clone, - { - // Self::ensure_unique(&mut self_); - let data = Arc::try_unwrap(self_.data.0).ok().unwrap(); - // safe because data is equivalent - unsafe { ContainerBase::from_data_ptr(data, self_.ptr).with_layout(self_.layout) } - } - - fn try_into_owned_nocopy( - self_: ContainerBase, - ) -> Result, ContainerBase> { - match Arc::try_unwrap(self_.data.0) { - Ok(owned_data) => unsafe { - // Safe because the data is equivalent. - Ok(ContainerBase::from_data_ptr(owned_data, self_.ptr).with_layout(self_.layout)) - }, - Err(arc_data) => unsafe { - // Safe because the data is equivalent; we're just - // reconstructing `self_`. - Err( - ContainerBase::from_data_ptr(OwnedArcRepr(arc_data), self_.ptr) - .with_layout(self_.layout), - ) - }, - } - } - - #[allow(clippy::wrong_self_convention)] - fn to_shared(self_: &ContainerBase) -> SharedContainer - where - Self::Elem: Clone, - { - // to shared using clone of OwnedArcRepr without clone of raw data. - self_.clone() - } -} - -unsafe impl DataMut for OwnedArcRepr where A: Clone {} - -unsafe impl DataOwned for OwnedArcRepr { - type MaybeUninit = OwnedArcRepr>; - - fn new(elements: Vec) -> Self { - OwnedArcRepr(Arc::new(OwnedRepr::from(elements))) - } - - fn into_shared(self) -> OwnedArcRepr { - self - } -} - -unsafe impl DataShared for OwnedArcRepr {} - -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! {} -} - -unsafe impl RawDataClone for OwnedArcRepr { - unsafe fn clone_with_ptr(&self, ptr: NonNull) -> (Self, NonNull) { - // pointer is preserved - (self.clone(), ptr) - } -} - -// NOTE: Copy on write -unsafe impl RawDataMut for OwnedArcRepr -where - A: Clone, -{ - fn try_ensure_unique(self_: &mut ContainerBase) - where - Self: Sized, - { - if Arc::get_mut(&mut self_.data.0).is_some() { - return; - } - if self_.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 = core::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 = PointerExt::offset(rvec.as_nonnull_mut(), our_off); - } - } - - fn try_is_unique(&mut self) -> Option { - Some(Arc::get_mut(&mut self.0).is_some()) - } -} - -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)) - } -} diff --git a/exp/container/src/data/repr/view.rs b/exp/container/src/data/repr/view.rs deleted file mode 100644 index d236601a..00000000 --- a/exp/container/src/data/repr/view.rs +++ /dev/null @@ -1,182 +0,0 @@ -/* - Appellation: view - Contrib: FL03 -*/ -use crate::data::specs::*; -use crate::data::{Container, ContainerBase, ContainerView, ContainerViewMut}; -use crate::iter::{BaseIter, ElementsBase, ElementsBaseMut, Iter, IterMut}; -use core::marker::PhantomData; -use core::ptr::NonNull; -use core::slice; - -/// 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 } - } -} - -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_: ContainerBase) -> Container - where - Self::Elem: Clone, - { - self_.to_owned() - } - - fn try_into_owned_nocopy( - self_: ContainerBase, - ) -> Result, ContainerBase> { - 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 ContainerBase) - where - Self: Sized, - { - } - - #[inline] - fn try_is_unique(&mut self) -> Option { - Some(true) - } -} - -unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { - fn into_owned(self_: ContainerBase) -> Container - where - Self::Elem: Clone, - { - self_.to_owned() - } - - fn try_into_owned_nocopy( - self_: ContainerBase, - ) -> Result, ContainerBase> { - Err(self_) - } -} - -unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {} - -impl<'a, A> ContainerView<'a, A> { - pub fn to_slice(&self) -> Option<&'a [A]> { - if self.is_standard_layout() { - unsafe { Some(slice::from_raw_parts(self.ptr.as_ptr(), self.size())) } - } else { - None - } - } -} - -// Internal Methods -impl<'a, A> ContainerView<'a, A> { - #[inline] - pub(crate) fn into_base_iter(self) -> BaseIter { - unsafe { - BaseIter::new( - self.ptr.as_ptr(), - self.shape().clone(), - self.stride().clone(), - ) - } - } - - #[inline] - pub(crate) fn into_elements_base(self) -> ElementsBase<'a, A> { - ElementsBase::new(self) - } - - pub(crate) fn into_iter_(self) -> Iter<'a, A> { - Iter::new(self) - } -} - -impl<'a, A> ContainerViewMut<'a, A> { - #[inline] - pub(crate) fn into_base_iter(self) -> BaseIter { - unsafe { - BaseIter::new( - self.ptr.as_ptr(), - self.shape().clone(), - self.stride().clone(), - ) - } - } - - #[inline] - pub(crate) fn into_elements_base(self) -> ElementsBaseMut<'a, A> { - ElementsBaseMut::new(self) - } - - pub(crate) fn into_iter_(self) -> IterMut<'a, A> { - IterMut::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.size())) } - } else { - Err(self) - } - } -} diff --git a/exp/container/src/data/specs.rs b/exp/container/src/data/specs.rs deleted file mode 100644 index 991a9fc1..00000000 --- a/exp/container/src/data/specs.rs +++ /dev/null @@ -1,159 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 -*/ -use crate::data::repr::OwnedArcRepr; -use crate::data::{Container, ContainerBase, SharedContainer}; -use core::mem::MaybeUninit; -use core::ptr::NonNull; - -/// Container representation trait. -/// -/// For a container 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_: ContainerBase) -> Container - where - Self::Elem: Clone; - - /// 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_: ContainerBase, - ) -> Result, ContainerBase>; - - /// 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_: &ContainerBase) -> SharedContainer - where - Self::Elem: Clone, - { - // clone to shared - self_.to_owned().into_shared() - } -} - -#[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 ContainerBase) - where - Self: Sized, - { - 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() - } -} - -#[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, without any copying. - #[doc(hidden)] - fn into_shared(self) -> OwnedArcRepr; -} - -/// 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 {} - -#[allow(clippy::missing_safety_doc)] -pub unsafe trait RawData: Sized { - 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 ContainerBase) - where - Self: Sized; - - /// 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 - } -} - -/// Raw Data Subsitution -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; -} diff --git a/exp/container/src/dim/dimension.rs b/exp/container/src/dim/dimension.rs deleted file mode 100644 index 8912ec08..00000000 --- a/exp/container/src/dim/dimension.rs +++ /dev/null @@ -1,6 +0,0 @@ -/* - Appellation: dimension - Contrib: FL03 -*/ - -pub struct Dim; diff --git a/exp/container/src/dim/mod.rs b/exp/container/src/dim/mod.rs deleted file mode 100644 index 0b1165b3..00000000 --- a/exp/container/src/dim/mod.rs +++ /dev/null @@ -1,210 +0,0 @@ -/* - Appellation: dim - Contrib: FL03 -*/ -//! # Dimension -//! - -pub use self::{dimension::Dim, utils::*}; - -pub(crate) mod dimension; - -use core::ops::IndexMut; - -pub trait IntoDimension { - type Dim: Dimension; - - fn into_dimension(self) -> Self::Dim; -} - -pub trait Dimension: IndexMut { - type Pattern; - - fn as_slice(&self) -> &[usize]; - /// Return the rank of the dimension; i.e. the number of axes. - fn rank(&self) -> usize; - /// Return the size of the dimension; i.e. the number of elements. - fn size(&self) -> usize; - - #[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.as_slice(), strides.as_slice()) { - offset += stride_offset(i, s); - } - offset - } -} - -pub(crate) mod utils { - use crate::index::{Ix, Ixs}; - use acme::prelude::{Layout, Shape, ShapeError, Stride}; - use core::mem; - - /// 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) - } - - pub(crate) fn default_strides(shape: &Shape) -> Stride { - // Compute default array strides - // Shape (a, b, c) => Give strides (b * c, c, 1) - let mut strides = Stride::zeros(shape.rank()); - // For empty arrays, use all zero strides. - if shape.iter().all(|&d| d != 0) { - let mut it = strides.as_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(shape.iter().rev()) { - cum_prod *= *dim; - *rs = cum_prod; - } - } - strides - } - - pub(crate) fn is_layout_c(layout: &Layout) -> bool { - if let 1 = *layout.rank() { - return layout.strides()[0] == 1 || layout.shape()[0] <= 1; - } - - for d in layout.shape().iter() { - 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!(layout.shape().iter().rev(), layout.strides().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 can_index_slice( - data: &[A], - shape: &Shape, - stride: &Stride, - ) -> Result<(), ShapeError> { - // Check conditions 1 and 2 and calculate `max_offset`. - let max_offset = max_abs_offset_check_overflow::(shape, stride)?; - can_index_slice_impl(max_offset, data.len(), shape, stride) - } - - fn can_index_slice_impl( - max_offset: usize, - data_len: usize, - dim: &Shape, - strides: &Stride, - ) -> Result<(), ShapeError> { - // Check condition 3. - let is_empty = dim.as_slice().iter().any(|&d| d == 0); - if is_empty && max_offset > data_len { - return Err(ShapeError::OutOfBounds); - } - if !is_empty && max_offset >= data_len { - return Err(ShapeError::OutOfBounds); - } - - // Check condition 4. - if !is_empty && dim_stride_overlap(dim, strides) { - return Err(ShapeError::Unsupported); - } - - Ok(()) - } - - pub fn dim_stride_overlap(dim: &Shape, strides: &Stride) -> bool { - let order = crate::_fastest_varying_stride_order(strides); - let mut sum_prev_offsets = 0; - for &index in order.as_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 - } - - pub fn max_abs_offset_check_overflow( - dim: &Shape, - strides: &Stride, - ) -> Result { - max_abs_offset_check_overflow_impl(mem::size_of::(), dim, strides) - } - - fn max_abs_offset_check_overflow_impl( - elem_size: usize, - dim: &Shape, - strides: &Stride, - ) -> Result { - // Condition 1. - if dim.rank() != strides.rank() { - return Err(ShapeError::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.as_slice(), strides.as_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(|| ShapeError::Overflow)?; - // Condition 2a. - if max_offset > isize::MAX as usize { - return Err(ShapeError::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(ShapeError::Overflow)?; - // Condition 2b. - if max_offset_bytes > isize::MAX as usize { - return Err(ShapeError::Overflow); - } - - Ok(max_offset) - } - - pub fn size_of_shape_checked(dim: &Shape) -> Result { - let size_nonzero = dim - .as_slice() - .iter() - .filter(|&&d| d != 0) - .try_fold(1usize, |acc, &d| acc.checked_mul(d)) - .ok_or(ShapeError::Overflow)?; - if size_nonzero > ::std::isize::MAX as usize { - Err(ShapeError::Overflow) - } else { - Ok(dim.size()) - } - } -} diff --git a/exp/container/src/index/mod.rs b/exp/container/src/index/mod.rs deleted file mode 100644 index d2c48ca3..00000000 --- a/exp/container/src/index/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -/* - Appellation: index - Contrib: FL03 -*/ -//! # Index -//! -//! -pub use self::slice::*; - -pub(crate) mod slice; - -pub type Ix = usize; - -pub type Ixs = isize; - -#[cfg(test)] -mod tests {} diff --git a/exp/container/src/index/slice.rs b/exp/container/src/index/slice.rs deleted file mode 100644 index 81a5fe5d..00000000 --- a/exp/container/src/index/slice.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - Appellation: slice - Contrib: FL03 -*/ -//! # Slice -//! -//! -use core::ops::{Range, RangeFrom}; -pub struct Slice { - pub start: isize, - pub end: Option, - pub step: isize, -} - -impl Slice { - pub fn new(start: isize, end: Option, step: isize) -> Self { - Self { start, end, step } - } -} - -impl From> for Slice { - fn from(range: Range) -> Self { - Self { - start: range.start, - end: Some(range.end), - step: 1, - } - } -} - -impl From> for Slice { - fn from(range: RangeFrom) -> Self { - Self { - start: range.start, - end: None, - step: 1, - } - } -} - -pub enum Slices { - Index(isize), - Slice(Slice), - NewAxis, -} diff --git a/exp/container/src/iter/axis.rs b/exp/container/src/iter/axis.rs deleted file mode 100644 index 1c8a56f5..00000000 --- a/exp/container/src/iter/axis.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - Appellation: axis - Contrib: FL03 -*/ -use crate::data::{ContainerBase, RawData}; -use crate::index::{Ix, Ixs}; -use acme::tensor::shape::{Axis, Layout}; - -pub struct AxisIter { - index: Ix, - end: Ix, - stride: Ixs, - inner_layout: Layout, - ptr: *mut A, -} - -impl AxisIter { - pub fn new(v: ContainerBase, axis: Axis) -> Self - where - S: RawData, - { - let stride = v.stride()[axis]; - let end = v.shape()[axis]; - // Self { - // index: 0, - // end, - // stride, - // inner_layout: layout.remove_axis(axis), - // ptr: v.as_mut_ptr(), - // } - unimplemented!() - } -} diff --git a/exp/container/src/iter/base.rs b/exp/container/src/iter/base.rs deleted file mode 100644 index c8af46da..00000000 --- a/exp/container/src/iter/base.rs +++ /dev/null @@ -1,199 +0,0 @@ -/* - Appellation: iter - Contrib: FL03 -*/ -use crate::data::{ContainerView, ContainerViewMut}; -use acme::prelude::{Shape, Stride}; -use core::marker::PhantomData; - -#[derive(Clone, Debug)] -pub enum ElementsRepr { - Slice(S), - Counted(C), -} - -/// Counted read only iterator -#[derive(Debug)] -pub struct ElementsBase<'a, A> { - inner: BaseIter, - _life: PhantomData<&'a A>, -} - -impl<'a, A> ElementsBase<'a, A> { - pub fn new(v: ContainerView<'a, A>) -> Self { - ElementsBase { - inner: v.into_base_iter(), - _life: PhantomData, - } - } -} - -impl<'a, A> Iterator for ElementsBase<'a, A> { - type Item = &'a A; - #[inline] - fn next(&mut self) -> Option<&'a A> { - self.inner.next().map(|p| unsafe { &*p }) - } - - 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)) } - } -} - -impl<'a, A> DoubleEndedIterator for ElementsBase<'a, A> { - #[inline] - fn next_back(&mut self) -> Option<&'a A> { - self.inner.next_back().map(|p| unsafe { &*p }) - } - - 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)) } - } -} - -impl<'a, A> ExactSizeIterator for ElementsBase<'a, A> { - fn len(&self) -> usize { - self.inner.len() - } -} - -/// An iterator over the elements of an array. -/// -/// Iterator element type is `&'a mut A`. -#[derive(Debug)] -pub struct ElementsBaseMut<'a, A> { - inner: BaseIter, - life: PhantomData<&'a mut A>, -} - -impl<'a, A> ElementsBaseMut<'a, A> { - pub fn new(v: ContainerViewMut<'a, A>) -> Self { - ElementsBaseMut { - inner: v.into_base_iter(), - life: PhantomData, - } - } -} - -impl<'a, A> Iterator for ElementsBaseMut<'a, A> { - type Item = &'a mut A; - #[inline] - fn next(&mut self) -> Option<&'a mut A> { - self.inner.next().map(|p| unsafe { &mut *p }) - } - - 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, &mut *ptr)) } - } -} - -impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut A> { - self.inner.next_back().map(|p| unsafe { &mut *p }) - } - - 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, &mut *ptr)) } - } -} - -impl<'a, A> ExactSizeIterator for ElementsBaseMut<'a, A> { - fn len(&self) -> usize { - self.inner.len() - } -} - -/// Base for iterators over all axes. -/// -/// Iterator element type is `*mut A`. -#[derive(Debug)] -pub struct BaseIter { - ptr: *mut A, - shape: Shape, - strides: Stride, - 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: *mut A, shape: Shape, strides: Stride) -> BaseIter { - BaseIter { - ptr, - index: shape.first_index(), - shape, - strides, - } - } -} - -impl Iterator for BaseIter { - type Item = *mut A; - - #[inline] - fn next(&mut self) -> Option<*mut A> { - let index = match self.index { - None => return None, - Some(ref ix) => ix.clone(), - }; - let offset = Shape::stride_offset(&index, &self.strides); - self.index = self.shape.next_for(index); - unsafe { Some(self.ptr.offset(offset)) } - } -} - -impl ExactSizeIterator for BaseIter { - fn len(&self) -> usize { - match self.index { - None => 0, - Some(ref ix) => { - let gone = crate::default_strides(&self.shape) - .as_slice() - .iter() - .zip(ix.as_slice().iter()) - .fold(0, |s, (&a, &b)| s + a * b); - self.shape.size() - gone - } - } - } -} - -impl DoubleEndedIterator for BaseIter { - #[inline] - fn next_back(&mut self) -> Option<*mut A> { - let index = match self.index.as_ref() { - None => return None, - Some(ix) => ix.clone(), - }; - self.shape[0] -= 1; - let offset = Shape::stride_offset(&self.shape, &self.strides); - if index == self.shape { - self.index = None; - } - - unsafe { Some(self.ptr.offset(offset)) } - } -} diff --git a/exp/container/src/iter/iterator.rs b/exp/container/src/iter/iterator.rs deleted file mode 100644 index 05245c75..00000000 --- a/exp/container/src/iter/iterator.rs +++ /dev/null @@ -1,149 +0,0 @@ -/* - Appellation: iterator - Contrib: FL03 -*/ -use super::base::*; -use crate::data::{ContainerView, ContainerViewMut}; -use core::slice::{Iter as SliceIter, IterMut as SliceIterMut}; -/// An iterator over the elements of a tensor. -/// -/// Iterator element type is `&'a A`. -/// -/// See [`.iter()`](ContainerBase::iter) for more information. - -#[derive(Debug)] -pub struct Iter<'a, A> { - inner: ElementsRepr, ElementsBase<'a, A>>, -} - -impl<'a, A> Iter<'a, A> { - pub(crate) fn new(self_: ContainerView<'a, A>) -> Self { - Iter { - inner: if let Some(slc) = self_.to_slice() { - ElementsRepr::Slice(slc.iter()) - } else { - ElementsRepr::Counted(self_.into_elements_base()) - }, - } - } -} - -impl<'a, A> Iterator for Iter<'a, A> { - type Item = &'a A; - #[inline] - fn next(&mut self) -> Option<&'a A> { - either_mut!(self.inner, iter => iter.next()) - } -} - -#[derive(Debug)] -pub struct IterMut<'a, A> { - inner: ElementsRepr, ElementsBaseMut<'a, A>>, -} - -impl<'a, A> IterMut<'a, A> { - pub(crate) fn new(self_: ContainerViewMut<'a, A>) -> Self { - IterMut { - inner: match self_.try_into_slice() { - Ok(x) => ElementsRepr::Slice(x.iter_mut()), - Err(self_) => ElementsRepr::Counted(self_.into_elements_base()), - }, - } - } -} -impl<'a, A> Iterator for IterMut<'a, A> { - 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> { - #[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> ExactSizeIterator for IterMut<'a, A> { - fn len(&self) -> usize { - either!(self.inner, ref iter => iter.len()) - } -} diff --git a/exp/container/src/iter/mod.rs b/exp/container/src/iter/mod.rs deleted file mode 100644 index 73489e23..00000000 --- a/exp/container/src/iter/mod.rs +++ /dev/null @@ -1,44 +0,0 @@ -/* - Appellation: iter - Contrib: FL03 -*/ -//! # Iter -//! -//! -// pub use self::{axis::*, iterator::*, position::*, utils::*}; -pub use self::{base::*, iterator::*, position::IndexIter, utils::*}; - -#[allow(dead_code, unused)] -pub(crate) mod axis; -pub(crate) mod base; -pub(crate) mod iterator; -pub(crate) mod position; - -pub(crate) mod utils { - use core::ptr; - - pub fn to_vec_mapped(iter: I, mut f: F) -> Vec - where - I: ExactSizeIterator, // + TrustedIterator - 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 - } -} - -#[cfg(test)] -mod tests {} diff --git a/exp/container/src/iter/position.rs b/exp/container/src/iter/position.rs deleted file mode 100644 index 9c6690b7..00000000 --- a/exp/container/src/iter/position.rs +++ /dev/null @@ -1,95 +0,0 @@ -/* - Appellation: position - Contrib: FL03 -*/ -use acme::prelude::{Layout, Shape, Stride}; - -/// -pub struct IndexIter<'a> { - next: Option, - position: Vec, - shape: &'a Shape, - stride: &'a Stride, -} - -impl<'a> IndexIter<'a> { - pub fn new(offset: usize, shape: &'a Shape, stride: &'a Stride) -> Self { - let elem_count: usize = shape.iter().product(); - let next = if elem_count == 0 { - None - } else { - // This applies to the scalar case. - Some(offset) - }; - Self { - next, - position: vec![0; *shape.rank()], - shape, - stride, - } - } - - pub(crate) fn index(&self, index: impl AsRef<[usize]>) -> usize { - index - .as_ref() - .iter() - .zip(self.stride.iter()) - .map(|(i, s)| i * s) - .sum() - } -} - -impl<'a> DoubleEndedIterator for IndexIter<'a> { - fn next_back(&mut self) -> Option { - let (pos, _idx) = self.next()?; - let position = self - .shape - .iter() - .zip(pos.iter()) - .map(|(s, p)| s - p) - .collect(); - let scope = self.index(&position); - println!("{:?}", &position); - Some((position, scope)) - // unimplemented!() - } -} - -impl<'a> Iterator for IndexIter<'a> { - type Item = (Vec, usize); - - fn next(&mut self) -> Option { - let scope = match self.next { - None => return None, - Some(storage_index) => storage_index, - }; - let mut updated = false; - let mut next = scope; - for ((multi_i, max_i), stride_i) in self - .position - .iter_mut() - .zip(self.shape.iter()) - .zip(self.stride.iter()) - .rev() - { - let next_i = *multi_i + 1; - if next_i < *max_i { - *multi_i = next_i; - updated = true; - next += stride_i; - break; - } else { - next -= *multi_i * stride_i; - *multi_i = 0 - } - } - self.next = if updated { Some(next) } else { None }; - Some((self.position.clone(), scope)) - } -} - -impl<'a> From<&'a Layout> for IndexIter<'a> { - fn from(layout: &'a Layout) -> Self { - Self::new(layout.offset(), layout.shape(), layout.strides()) - } -} diff --git a/exp/container/src/lib.rs b/exp/container/src/lib.rs deleted file mode 100644 index 96784a3a..00000000 --- a/exp/container/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -extern crate acme; -#[cfg(not(feature = "std"))] -extern crate alloc; - -#[allow(unused_imports)] -pub use self::utils::*; - -#[allow(unused_macros)] -#[macro_use] -pub(crate) mod macros; -#[macro_use] -pub(crate) mod seal; -#[macro_use] -pub(crate) mod utils; - -pub mod data; -pub mod dim; -pub mod index; -#[macro_use] -pub mod iter; - -pub mod prelude { - #[doc(inline)] - pub use crate::data::prelude::*; - #[doc(inline)] - pub use crate::iter::*; -} diff --git a/exp/container/src/macros.rs b/exp/container/src/macros.rs deleted file mode 100644 index d803aec7..00000000 --- a/exp/container/src/macros.rs +++ /dev/null @@ -1,22 +0,0 @@ -/* - Appellation: macros - Contrib: FL03 -*/ - -macro_rules! either { - ($value:expr, $inner:pat => $result:expr) => { - match $value { - crate::iter::ElementsRepr::Slice($inner) => $result, - crate::iter::ElementsRepr::Counted($inner) => $result, - } - }; -} - -macro_rules! either_mut { - ($value:expr, $inner:ident => $result:expr) => { - match $value { - crate::iter::ElementsRepr::Slice(ref mut $inner) => $result, - crate::iter::ElementsRepr::Counted(ref mut $inner) => $result, - } - }; -} diff --git a/exp/container/src/seal.rs b/exp/container/src/seal.rs deleted file mode 100644 index 1b6ae830..00000000 --- a/exp/container/src/seal.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - Appellation: seal - Contrib: FL03 -*/ -//! 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::seal::PrivateMarker; - }; -} - -macro_rules! private_impl { - () => { - fn __private__(&self) -> $crate::seal::PrivateMarker { - $crate::seal::PrivateMarker - } - }; -} diff --git a/exp/container/src/utils.rs b/exp/container/src/utils.rs deleted file mode 100644 index 0a0df888..00000000 --- a/exp/container/src/utils.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 -*/ -use acme::prelude::{Shape, Stride}; - -pub(crate) fn default_strides(shape: &Shape) -> Stride { - // Compute default array strides - // Shape (a, b, c) => Give strides (b * c, c, 1) - let mut strides = Stride::zeros(shape.rank()); - // For empty arrays, use all zero strides. - if shape.iter().all(|&d| d != 0) { - let mut it = strides.as_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(shape.iter().rev()) { - cum_prod *= *dim; - *rs = cum_prod; - } - } - strides -} - -pub(crate) fn _fastest_varying_stride_order(strides: &Stride) -> Stride { - let mut indices = strides.clone(); - for (i, elt) in indices.as_slice_mut().iter_mut().enumerate() { - *elt = i; - } - let strides = strides.as_slice(); - indices - .as_slice_mut() - .sort_by_key(|&i| (strides[i] as isize).abs()); - indices -} - -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 )*) - ) - }; -} diff --git a/exp/container/tests/default.rs b/exp/container/tests/default.rs deleted file mode 100644 index d1c044d2..00000000 --- a/exp/container/tests/default.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - Appellation: default - Contrib: FL03 -*/ -#![cfg(test)] - -fn addition(a: A, b: B) -> C -where - A: std::ops::Add, -{ - a + b -} - -#[test] -fn compiles() { - let result = addition(2, 2); - assert_eq!(result, 4); -} diff --git a/graphs/Cargo.toml b/graphs/Cargo.toml index d826762f..dc3bc4bf 100644 --- a/graphs/Cargo.toml +++ b/graphs/Cargo.toml @@ -5,7 +5,7 @@ description = "Computational graphs for Rust" edition.workspace = true homepage.workspace = true license.workspace = true -name = "acme-graphs" +name = "rsdiff-graphs" readme.workspace = true repository.workspace = true version.workspace = true @@ -21,7 +21,6 @@ full = [ "trace", ] -std = [] serde = [ "dep:serde", @@ -31,12 +30,24 @@ serde = [ serde-ext = [ "dep:serde_json", "petgraph/serde-1", - "acme-core/serde", + "rsdiff-core/serde", ] trace = [ "dep:tracing", - "acme-core/trace", + "rsdiff-core/trace", +] + +std = [ + "rsdiff-core/std", +] + +wasi = [ + "rsdiff-core/wasi", +] + +wasm = [ + "rsdiff-core/wasm", ] [lib] @@ -51,6 +62,7 @@ test = true [dependencies] num = "0.4" +paste = "1" petgraph = "0.6" serde = { optional = true, features = ["derive"], version = "1" } serde_json = { optional = true, version = "1" } @@ -58,10 +70,9 @@ smart-default.workspace = true strum.workspace = true tracing = { optional = true, version = "0.1" } -[dependencies.acme-core] +[dependencies.rsdiff-core] path = "../core" -version = "0.3.1" -# version = "0.3.1-nightly" +version = "0.0.1" [package.metadata.docs.rs] all-features = true diff --git a/graphs/src/cg/edge.rs b/graphs/src/cg/edge.rs index e19767c6..ab8fc345 100644 --- a/graphs/src/cg/edge.rs +++ b/graphs/src/cg/edge.rs @@ -3,8 +3,8 @@ Contrib: FL03 */ use crate::NodeIndex; -use acme::id::IndexId; -use acme::ops::Op; +use rsdiff::id::IndexId; +use rsdiff::ops::Op; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -14,10 +14,10 @@ pub struct Edge { } impl Edge { - pub fn new(args: impl IntoIterator, op: Op) -> Self { + pub fn new(args: impl IntoIterator, op: impl Into) -> Self { Self { args: Vec::from_iter(args.into_iter().map(IndexId::from_index)), - op, + op: op.into(), } } diff --git a/graphs/src/cg/graph.rs b/graphs/src/cg/graph.rs index 09ea2bcf..0bf63b87 100644 --- a/graphs/src/cg/graph.rs +++ b/graphs/src/cg/graph.rs @@ -2,9 +2,9 @@ Appellation: graph Contrib: FL03 */ -use super::{CGraph, Edge}; +use super::{CGraph, Edge, Node}; use crate::NodeIndex; -use acme::ops::BinaryOp; +use rsdiff::ops::{BinaryOp, Op, UnaryOp}; pub struct Graph { store: CGraph, @@ -17,10 +17,83 @@ impl Graph { } } - pub fn binary(&mut self, lhs: NodeIndex, rhs: NodeIndex, op: BinaryOp) { - let _a = &self.store[lhs]; - let _b = &self.store[rhs]; - let edge = Edge::new([lhs, rhs], op.into()); - self.store.add_edge(lhs, rhs, edge); + pub fn add_edge(&mut self, src: NodeIndex, to: NodeIndex, edge: Edge) { + self.store.add_edge(src, to, edge); } + + pub fn add_node(&mut self, weight: Node) -> NodeIndex { + self.store.add_node(weight) + } + + pub fn add_node_data(&mut self, weight: T) -> NodeIndex { + self.add_node(Node::new(weight, false)) + } + + pub fn add_node_param(&mut self, weight: T) -> NodeIndex { + self.add_node(Node::new(weight, true)) + } + + pub fn op(&mut self, args: Vec, res: T, op: impl Into) -> NodeIndex { + let dest = self.add_node_data(res); + let edge = Edge::new(args.clone(), op); + for arg in args { + self.add_edge(dest, arg, edge.clone()); + } + dest + } +} + +macro_rules! binary_op { + ($($($p:ident)::*.$call:ident),*) => { + $( + binary_op!(@impl $($p)::*.$call); + )* + }; + (std $($p:ident.$call:ident),*) => { + $( + binary_op!(@impl core::ops::$p.$call); + )* + }; + (@impl $($p:ident)::*.$call:ident) => { + pub fn $call(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex where T: $($p)::* { + let (a, b) = get!(self[lhs, rhs]); + + let res = $($p)::*::$call(*a.data(), *b.data()); + self.op(vec![lhs, rhs], res, BinaryOp::$call()) + } + }; +} + +macro_rules! unary_op { + ($($($p:ident)::*.$call:ident$(($($rest:tt)*))?),* $(,)?) => { + $( + unary_op!(@impl $($p)::*.$call$(($($rest)*))?); + + )* + }; + (core $($p:ident.$call:ident$(($($rest:tt)*))?),* $(,)?) => { + $( + unary_op!(@impl core::ops::$p.$call(where T: core::ops::$p)); + + )* + }; + (@impl $($p:ident)::*.$call:ident$(($($rest:tt)*))?) => { + pub fn $call(&mut self, recv: NodeIndex) -> NodeIndex $($($rest)*)? { + let (a,) = get!(self[recv]); + + let res = $($p)::*::$call(*a.data()); + self.op(vec![recv], res, UnaryOp::$call()) + } + }; +} + +impl Graph +where + T: Copy, +{ + binary_op!(std Add.add, Div.div, Mul.mul, Rem.rem, Sub.sub); + + binary_op!(num::traits::Pow.pow); + + unary_op!(core Neg.neg(), Not.not()); } diff --git a/graphs/src/cg/node.rs b/graphs/src/cg/node.rs index 856416f7..3d80dd4d 100644 --- a/graphs/src/cg/node.rs +++ b/graphs/src/cg/node.rs @@ -2,9 +2,9 @@ Appellation: node Contrib: FL03 */ -use acme::id::AtomicId; +use rsdiff::id::AtomicId; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Node { pub(crate) id: AtomicId, pub(crate) data: T, diff --git a/graphs/src/dcg/edge.rs b/graphs/src/dcg/edge.rs index 82053fbd..0d1faa2e 100644 --- a/graphs/src/dcg/edge.rs +++ b/graphs/src/dcg/edge.rs @@ -3,7 +3,7 @@ Contrib: FL03 */ use crate::NodeIndex; -use acme::id::IndexId; +use rsdiff::id::IndexId; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] diff --git a/graphs/src/dcg/graph.rs b/graphs/src/dcg/graph.rs index bdd7e452..0e6ebaea 100644 --- a/graphs/src/dcg/graph.rs +++ b/graphs/src/dcg/graph.rs @@ -8,28 +8,12 @@ use super::DynamicGraph; use crate::prelude::GraphResult as Result; use crate::NodeIndex; -use acme::ops::{Arithmetic, BinaryOp, Op, UnaryOp}; -use acme::prelude::Scalar; +use rsdiff::ops::{Arithmetic, BinaryOp, Op, UnaryOp}; +use rsdiff::prelude::Scalar; use core::ops::Index; use petgraph::algo::toposort; use std::collections::HashMap; -macro_rules! entry { - ($ctx:ident[$key:expr]) => { - entry!(@base $ctx[$key]).or_default() - }; - ($ctx:ident[$key:expr], $val:expr) => { - entry!(@base $ctx[$key].or_insert($val)) - }; - (@base $ctx:ident[$key:expr].$call:ident($val:expr)) => { - entry!($ctx[$key]).$call:ident($val) - }; - (@base $ctx:ident[$key:expr]) => { - $ctx.entry($key) - }; - -} - macro_rules! push { ($ctx:expr, $(($key:expr, $val:expr)),*) => { $(push!(@impl $ctx, $key, $val);)* diff --git a/graphs/src/dcg/mod.rs b/graphs/src/dcg/mod.rs index 1c06a0d6..342f3277 100644 --- a/graphs/src/dcg/mod.rs +++ b/graphs/src/dcg/mod.rs @@ -21,7 +21,7 @@ pub trait GraphData { impl GraphData for S where - S: acme::prelude::Scalar, + S: rsdiff::prelude::Scalar, { type Value = S; } diff --git a/graphs/src/dcg/node.rs b/graphs/src/dcg/node.rs index 3b5f7f41..54e3958a 100644 --- a/graphs/src/dcg/node.rs +++ b/graphs/src/dcg/node.rs @@ -3,8 +3,8 @@ Contrib: FL03 */ use crate::NodeIndex; -use acme::id::AtomicId; -use acme::ops::{BinaryOp, Op, UnaryOp}; +use rsdiff::id::AtomicId; +use rsdiff::ops::{BinaryOp, Op, UnaryOp}; #[derive(Clone, Debug)] pub enum Node { diff --git a/graphs/src/grad/store.rs b/graphs/src/grad/store.rs index abd8297f..53763d84 100644 --- a/graphs/src/grad/store.rs +++ b/graphs/src/grad/store.rs @@ -3,7 +3,7 @@ Contrib: FL03 */ use crate::NodeIndex; -use acme::prelude::Store; +use rsdiff::prelude::Store; use std::any::Any; use std::collections::btree_map::{BTreeMap, Entry}; use std::ops::{Index, IndexMut}; diff --git a/graphs/src/graph.rs b/graphs/src/graph.rs index fe6e8727..60018d9a 100644 --- a/graphs/src/graph.rs +++ b/graphs/src/graph.rs @@ -2,13 +2,9 @@ Appellation: graph Contrib: FL03 */ -//! # Graph -//! -//! A computational graph forms the backbone of automatic differentiation. Computational graphs are directed acyclic graphs (DAGs) -//! that represent any computation as a series of nodes and edges. -//! -//! In a dynamic computational graph (DCG), the graph considers the nodes to be tensors and the edges to be operations. -//! + +use crate::{DefaultIx, EdgeIndex, NodeIndex}; +use core::marker::PhantomData; pub trait GraphEntry { type Idx; @@ -26,10 +22,119 @@ pub trait ComputeGraph { fn add_edge( &mut self, - source: ::Idx, + src: ::Idx, target: ::Idx, weight: ::Weight, ) -> ::Idx; fn clear(&mut self); } + +pub struct Edge { + pub link: Link, + pub weight: Option, + _idx: PhantomData>, +} + +impl Edge { + pub fn new(src: NodeIndex, target: NodeIndex, weight: Option) -> Self { + Self { + link: Link::new(src, target), + weight, + _idx: PhantomData, + } + } + + pub fn from_link(link: Link) -> Self { + Self { + link, + weight: None, + _idx: PhantomData, + } + } + + pub fn unweighted(src: NodeIndex, target: NodeIndex) -> Self { + Self::new(src, target, None) + } + + pub fn weighted(src: NodeIndex, target: NodeIndex, weight: W) -> Self { + Self::new(src, target, Some(weight)) + } + + pub fn with_weight(self, weight: W) -> Self { + Self { + weight: Some(weight), + ..self + } + } + + pub fn is_unweighted(&self) -> bool { + self.weight.is_none() + } + + pub fn is_weighted(&self) -> bool { + self.weight.is_some() + } + + pub fn weight(&self) -> Option<&W> { + self.weight.as_ref() + } + + pub fn weight_mut(&mut self) -> Option<&mut W> { + self.weight.as_mut() + } +} + +impl GraphEntry for Edge { + type Idx = EdgeIndex; + type Weight = W; +} + +pub struct Link { + pub src: NodeIndex, + pub target: NodeIndex, +} + +impl Link { + pub fn new(src: NodeIndex, target: NodeIndex) -> Self { + Self { src, target } + } +} + +pub trait GraphEdge { + type Idx; + + fn src(&self) -> NodeIndex; + + fn target(&self) -> NodeIndex; +} + +impl GraphEdge for Link +where + Idx: Copy, +{ + type Idx = Idx; + + fn src(&self) -> NodeIndex { + self.src + } + + fn target(&self) -> NodeIndex { + self.target + } +} + +impl GraphEdge for Edge +where + Idx: Copy, +{ + type Idx = Idx; + + fn src(&self) -> NodeIndex { + self.link.src + } + + fn target(&self) -> NodeIndex { + self.link.target + } +} diff --git a/graphs/src/lib.rs b/graphs/src/lib.rs index bd2a8ccc..2090e1f3 100644 --- a/graphs/src/lib.rs +++ b/graphs/src/lib.rs @@ -1,17 +1,19 @@ /* - Appellation: acme-graphs + Appellation: rsdiff-graphs Contrib: FL03 */ -//! # acme-graphs +//! # rsdiff-graphs //! //! -extern crate acme_core as acme; +extern crate rsdiff_core as rsdiff; #[doc(inline)] pub use self::graph::*; pub(crate) mod graph; +#[macro_use] +pub(crate) mod macros; #[doc(hidden)] pub mod cg; @@ -21,8 +23,9 @@ pub mod grad; pub mod scg; pub use petgraph::graph::{EdgeIndex, GraphIndex, NodeIndex}; +pub use petgraph::stable_graph::DefaultIx; -pub(crate) type Id = acme::id::IndexId; +pub(crate) type Id = rsdiff::id::IndexId; #[doc(hidden)] pub mod prelude { diff --git a/graphs/src/macros.rs b/graphs/src/macros.rs new file mode 100644 index 00000000..1a0ab657 --- /dev/null +++ b/graphs/src/macros.rs @@ -0,0 +1,34 @@ +/* + Appellation: macros + Contrib: FL03 +*/ + +macro_rules! entry { + ($ctx:ident[$key:expr]) => { + entry!(@base $ctx[$key]).or_default() + }; + ($ctx:ident[$key:expr] or $val:expr) => { + entry!(@base $ctx[$key].or_insert($val)) + }; + (@base $ctx:ident[$key:expr].$call:ident($val:expr)) => { + entry!($ctx[$key]).$call:ident($val) + }; + (@base $ctx:ident[$key:expr]) => { + $ctx.entry($key) + }; + +} + +macro_rules! get { + ($self:ident[$($index:expr),* $(,)?]) => { + ( + $( + get!(@impl $self[$index]), + )* + ) + }; + (@impl $self:ident[$index:expr]) => { + &$self.store[$index] + }; + +} diff --git a/graphs/src/scg/graph.rs b/graphs/src/scg/graph.rs index 581f3599..12e26b08 100644 --- a/graphs/src/scg/graph.rs +++ b/graphs/src/scg/graph.rs @@ -4,7 +4,7 @@ */ use super::{Edge, Node, Operation}; use crate::prelude::GraphResult as Result; -use acme::ops::{Arithmetic, BinaryOp, Op, UnaryOp}; +use rsdiff::ops::{Arithmetic, BinaryOp, Op, UnaryOp}; use num::traits::{NumAssign, NumOps, Signed}; use petgraph::algo::toposort; use petgraph::prelude::{DiGraph, NodeIndex}; diff --git a/graphs/src/scg/node.rs b/graphs/src/scg/node.rs index 85b45994..e63525c9 100644 --- a/graphs/src/scg/node.rs +++ b/graphs/src/scg/node.rs @@ -7,8 +7,8 @@ //! A computational graph relies on weighted nodes to represent constants, operations, and variables. //! The edges connecting to any given node are considered to be inputs and help to determine the flow of information use crate::NodeIndex; -use acme::id::AtomicId; -use acme::ops::{Op, Operator}; +use rsdiff::id::AtomicId; +use rsdiff::ops::{Op, Operator}; use smart_default::SmartDefault; use strum::{Display, EnumCount, EnumIs, VariantNames}; diff --git a/graphs/tests/dcg.rs b/graphs/tests/dcg.rs index 9a8a5336..13dcc9ac 100644 --- a/graphs/tests/dcg.rs +++ b/graphs/tests/dcg.rs @@ -3,7 +3,7 @@ Contrib: FL03 */ #![cfg(test)] -extern crate acme_graphs as graphs; +extern crate rsdiff_graphs as graphs; use graphs::dcg::Dcg; diff --git a/graphs/tests/scg.rs b/graphs/tests/scg.rs index fea304b0..7ddc7825 100644 --- a/graphs/tests/scg.rs +++ b/graphs/tests/scg.rs @@ -3,7 +3,7 @@ Contrib: FL03 */ #![cfg(test)] -extern crate acme_graphs as graphs; +extern crate rsdiff_graphs as graphs; use graphs::scg::Scg; diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 9eab7382..13d4131a 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true -name = "acme-macros" +name = "rsdiff-macros" readme.workspace = true repository.workspace = true version.workspace = true diff --git a/macros/src/ast/ad.rs b/macros/src/ast/ad.rs index c3947107..4d4583b5 100644 --- a/macros/src/ast/ad.rs +++ b/macros/src/ast/ad.rs @@ -2,40 +2,51 @@ Appellation: ad Contrib: FL03 */ +use crate::handle::{expr, item}; use proc_macro2::TokenStream; use syn::parse::{Parse, ParseStream, Result}; use syn::{Expr, Ident, ItemFn, Token}; // #77 Try to integrate with the #[operator] macro by collecting the String created by invoking _lexical() -pub enum PartialFn { +pub enum Scope { Expr(Expr), Item(ItemFn), Verbatim(TokenStream), // Not considered } -impl Parse for PartialFn { +impl Scope { + pub fn handle(&self, args: &Ident) -> TokenStream { + match self { + Scope::Expr(scope) => expr::handle_expr(scope, args), + Scope::Item(scope) => item::handle_item_fn(scope, args), + Scope::Verbatim(_) => panic!("Custom functions not yet supported"), + } + } +} + +impl Parse for Scope { fn parse(input: ParseStream) -> Result { if let Ok(item) = input.parse() { Ok(Self::Item(item)) } else if let Ok(expr) = input.parse() { Ok(Self::Expr(expr)) } else { - Ok(PartialFn::Verbatim(input.parse()?)) + Ok(Scope::Verbatim(input.parse()?)) } } } pub struct AutodiffAst { - pub scope: PartialFn, + pub args: Ident, pub split: Token![:], - pub var: Ident, + pub scope: Scope, } impl Parse for AutodiffAst { fn parse(input: ParseStream) -> Result { - let var = input.parse()?; + let args = input.parse()?; let split = input.parse::()?; let scope = input.parse()?; - Ok(Self { scope, split, var }) + Ok(Self { args, split, scope }) } } diff --git a/macros/src/ast/grad.rs b/macros/src/ast/grad.rs index 3fb970d0..60ba3b28 100644 --- a/macros/src/ast/grad.rs +++ b/macros/src/ast/grad.rs @@ -2,9 +2,9 @@ Appellation: gradient Contrib: FL03 */ -#![allow(dead_code)] use proc_macro2::TokenStream; use syn::parse::{Parse, ParseStream, Result}; +use syn::Ident; use syn::{Attribute, ExprArray, ItemFn}; pub struct GradientAst { @@ -16,14 +16,6 @@ impl GradientAst { pub fn new(attrs: Vec, item: ItemFn) -> Self { Self { attrs, item } } - - pub fn attributes(&self) -> &[Attribute] { - &self.attrs - } - - pub fn item(&self) -> &ItemFn { - &self.item - } } impl Parse for GradientAst { @@ -34,8 +26,13 @@ impl Parse for GradientAst { } } +pub struct GradientAttr { + pub name: Option, +} + pub enum ExprGrad { Array(ExprArray), + Fn(ItemFn), Verbatim(TokenStream), } diff --git a/macros/src/ast/mod.rs b/macros/src/ast/mod.rs index ddb2eece..2a6163e8 100644 --- a/macros/src/ast/mod.rs +++ b/macros/src/ast/mod.rs @@ -15,15 +15,15 @@ use syn::parse::{Parse, ParseStream, Result}; use syn::Expr; pub struct BackendAst { - pub scope: Scope, + pub args: Args, pub span: Span, } impl Parse for BackendAst { fn parse(input: ParseStream) -> Result { - let scope = input.parse()?; + let args = input.parse()?; let span = Span::call_site(); - Ok(Self { scope, span }) + Ok(Self { args, span }) } } @@ -33,24 +33,24 @@ impl ToTokens for BackendAst { } } -pub enum Scope { +pub enum Args { Block(syn::Block), Expr(Expr), Item(syn::Item), Verbatim(TokenStream), } -impl Parse for Scope { +impl Parse for Args { fn parse(input: ParseStream) -> Result { if let Ok(block) = input.parse() { - Ok(Scope::Block(block)) + Ok(Args::Block(block)) } else if let Ok(item) = input.parse() { - Ok(Scope::Item(item)) + Ok(Args::Item(item)) } else if let Ok(expr) = input.parse() { - Ok(Scope::Expr(expr)) + Ok(Args::Expr(expr)) } else { dbg!("Currently not handled"); - Ok(Scope::Verbatim(input.parse()?)) + Ok(Args::Verbatim(input.parse()?)) } } } diff --git a/macros/src/ast/operator.rs b/macros/src/ast/operator.rs index e84bc5f7..a7b73f5c 100644 --- a/macros/src/ast/operator.rs +++ b/macros/src/ast/operator.rs @@ -4,15 +4,7 @@ */ use proc_macro2::Span; use syn::meta::ParseNestedMeta; -use syn::{Ident, Item, Result}; - -// pub fn from_proc_macro_attribute(args: TokenStream, item: TokenStream) -> OperatorAst { -// let mut attrs = OperatorAttr::new(); -// let op_parser = syn::meta::parser(|meta| attrs.parse(meta)); -// let _ = syn::parse_macro_input!(args with op_parser); -// let item = syn::parse_macro_input!(item as syn::Item); -// return OperatorAst::new(attrs, item); -// } +use syn::{Ident, Item}; pub struct OperatorAst { pub attrs: Option, @@ -32,19 +24,22 @@ impl OperatorAst { #[derive(Clone, Debug, Default)] pub struct OperatorAttr { - pub lexical: Option, + pub lex: Option, + pub params: Vec, } impl OperatorAttr { pub fn new() -> Self { - Self { lexical: None } + Self { + lex: None, + params: Vec::new(), + } } - pub fn parser(&mut self, meta: ParseNestedMeta) -> Result<()> { - if meta.path.is_ident("lexical") { - - let value: Ident = meta.value()?.parse()?; - self.lexical = Some(value); + pub fn parser(&mut self, meta: ParseNestedMeta) -> syn::Result<()> { + if meta.path.is_ident("lex") { + let value = meta.value()?.parse()?; + self.lex = Some(value); } else { return Err(meta.error("Unknown attribute")); } @@ -52,10 +47,10 @@ impl OperatorAttr { } pub fn is_lexical(&self) -> bool { - self.lexical.is_some() + self.lex.is_some() } pub fn set_lexical(&mut self, value: Option) { - self.lexical = value; + self.lex = value; } } diff --git a/macros/src/autodiff.rs b/macros/src/autodiff.rs index 781181e8..735153a0 100644 --- a/macros/src/autodiff.rs +++ b/macros/src/autodiff.rs @@ -2,18 +2,16 @@ Appellation: ad Contrib: FL03 */ -use crate::ast::ad::{AutodiffAst, PartialFn}; +use crate::ast::ad::{AutodiffAst, Scope}; use crate::handle::{expr, item}; use proc_macro2::TokenStream; pub fn impl_autodiff(partial: &AutodiffAst) -> TokenStream { - let AutodiffAst { - scope: expr, var, .. - } = partial; + let AutodiffAst { args, scope, .. } = partial; - match expr { - PartialFn::Expr(inner) => expr::handle_expr(inner, var), - PartialFn::Item(inner) => item::handle_item(&inner.clone().into(), var), - PartialFn::Verbatim(_inner) => panic!("Custom functions not yet supported"), + match scope { + Scope::Expr(inner) => expr::handle_expr(inner, args), + Scope::Item(inner) => item::handle_item(&inner.clone().into(), args), + Scope::Verbatim(_inner) => panic!("Custom functions not yet supported"), } } diff --git a/macros/src/error.rs b/macros/src/error.rs index 44f01bac..7ea04850 100644 --- a/macros/src/error.rs +++ b/macros/src/error.rs @@ -3,6 +3,7 @@ Contrib: FL03 */ +#[allow(dead_code)] #[derive(Clone, Debug)] pub enum Error { Ast(String), diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 08f3b2ad..b365193a 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,8 +1,8 @@ /* - Appellation: acme-macros + Appellation: rsdiff-macros Contrib: FL03 */ -//! # acme-macros +//! # rsdiff-macros //! //! extern crate proc_macro; @@ -29,7 +29,7 @@ use syn::parse_macro_input; /// ### Basic arithmetic /// /// ``` -/// extern crate acme_macros as macros; +/// extern crate rsdiff_macros as macros; /// /// use macros::autodiff; /// @@ -46,7 +46,7 @@ use syn::parse_macro_input; /// ### Trigonometric functions /// /// ``` -/// extern crate acme_macros as macros; +/// extern crate rsdiff_macros as macros; /// /// use macros::autodiff; /// @@ -76,7 +76,7 @@ pub fn operator(args: TokenStream, item: TokenStream) -> TokenStream { let op_parser = syn::meta::parser(|meta| attrs.parser(meta)); let _ = parse_macro_input!(args with op_parser); let item = parse_macro_input!(item as syn::Item); - let ast = ast::OperatorAst::new(Some(attrs), item); + let ast = ast::OperatorAst::new(Some(dbg!(attrs)), item); let result = operator::impl_operator(&ast); TokenStream::from(result) } @@ -92,7 +92,7 @@ pub(crate) mod kw { syn::custom_keyword!(tan); } +#[allow(unused)] pub(crate) mod primitives { pub type Result = std::result::Result; - pub type BoxError = Box; } diff --git a/macros/src/operator.rs b/macros/src/operator.rs index 35d1b00c..85c5412a 100644 --- a/macros/src/operator.rs +++ b/macros/src/operator.rs @@ -13,39 +13,36 @@ use syn::{Item, ItemFn, Lit, LitStr, Signature}; pub fn impl_operator(ast: &OperatorAst) -> TokenStream { let OperatorAst { attrs, item, .. } = ast; + let mut res = match item { Item::Fn(inner) => handle_operator_func(&inner), _ => panic!("Expected a function"), }; - - if let Some(attrs) = attrs { - let ext = handle_operator_attr(&attrs, &item); - res = quote! { - #res - #ext - }; + if let Some(attrs) = attrs { + let ext = handle_attr(&attrs, &item); + res.extend(ext); } res } -fn handle_operator_attr(attrs: &OperatorAttr, item: &Item) -> TokenStream { - let OperatorAttr { lexical } = attrs; +fn handle_attr(attrs: &OperatorAttr, item: &Item) -> TokenStream { + let OperatorAttr { lex, .. } = attrs; let item_tk = item.to_token_stream(); let item_str = item_tk.to_string(); let mut res = TokenStream::new(); - if let Some(lex) = lexical { - let lex_const = format_ident!("{}", lex.to_string().to_uppercase()); - let lex_func = format_ident!("{}", lex.to_string().to_lowercase()); + if let Some(l) = lex { + let constant = format_ident!("{}", l.to_string().to_uppercase()); + let function = format_ident!("{}", l.to_string().to_lowercase()); res = quote! { #res - pub const #lex_const: &str = #item_str; + pub const #constant: &str = #item_str; - pub fn #lex_func() -> String { + pub fn #function() -> String { #item_str.to_string() } - } + }; } res } diff --git a/macros/src/ops/binary.rs b/macros/src/ops/binary.rs index ca487ebb..fc0a4a0e 100644 --- a/macros/src/ops/binary.rs +++ b/macros/src/ops/binary.rs @@ -3,7 +3,7 @@ Contrib: FL03 */ use crate::handle::handle_expr; -use crate::BoxError; +use crate::Error; use proc_macro2::TokenStream; use quote::quote; use std::str::FromStr; @@ -26,7 +26,7 @@ pub enum BinaryOp { } impl BinaryOp { - pub fn from_binary(op: BinOp) -> Result { + pub fn from_binary(op: BinOp) -> crate::Result { use BinOp::*; match op { Add(_) | AddAssign(_) => Ok(Self::Add), @@ -73,7 +73,7 @@ impl core::fmt::Display for BinaryOp { } impl FromStr for BinaryOp { - type Err = BoxError; + type Err = Error; fn from_str(s: &str) -> Result { match s { @@ -110,7 +110,7 @@ impl Parse for BinaryOp { } impl TryFrom for BinaryOp { - type Error = BoxError; + type Error = Error; fn try_from(op: BinOp) -> Result { Self::from_binary(op) @@ -118,7 +118,7 @@ impl TryFrom for BinaryOp { } impl TryFrom for BinaryOp { - type Error = BoxError; + type Error = Error; fn try_from(ident: Ident) -> Result { Self::from_str(ident.to_string().as_str()) diff --git a/macros/src/utils.rs b/macros/src/utils.rs index e0af9a2c..f6012f5e 100644 --- a/macros/src/utils.rs +++ b/macros/src/utils.rs @@ -48,14 +48,6 @@ macro_rules! binary_expr { }; } -#[allow(dead_code)] -pub fn attrs_is_lexical(attrs: &Vec) -> bool { - attrs.iter().any(|attr| match &attr.meta { - syn::Meta::Path(inner) => inner.is_ident("lexical"), - _ => false, - }) -} - #[allow(dead_code)] pub fn fn_args_ident(arg: &syn::FnArg) -> Ident { use syn::Pat; diff --git a/macros/tests/arith.rs b/macros/tests/arith.rs index 7ed822b0..dc3101f9 100644 --- a/macros/tests/arith.rs +++ b/macros/tests/arith.rs @@ -4,9 +4,9 @@ */ #![cfg(test)] #![allow(unused_variables)] -extern crate acme_macros as acme; +extern crate rsdiff_macros as rsdiff; -use acme::autodiff; +use rsdiff::autodiff; #[test] fn test_add() { diff --git a/macros/tests/autodiff.rs b/macros/tests/autodiff.rs index be94cafa..5c6eea46 100644 --- a/macros/tests/autodiff.rs +++ b/macros/tests/autodiff.rs @@ -4,7 +4,7 @@ */ #![cfg(test)] #![allow(unused)] -extern crate acme_macros as macros; +extern crate rsdiff_macros as macros; use approx::assert_abs_diff_eq; use macros::autodiff; diff --git a/macros/tests/external.rs b/macros/tests/external.rs index dc130dca..7ce11153 100644 --- a/macros/tests/external.rs +++ b/macros/tests/external.rs @@ -4,7 +4,7 @@ */ #![cfg(test)] #![allow(unused)] -extern crate acme_macros as macros; +extern crate rsdiff_macros as macros; use defs::*; use macros::autodiff; diff --git a/math/Cargo.toml b/math/Cargo.toml new file mode 100644 index 00000000..61e370c3 --- /dev/null +++ b/math/Cargo.toml @@ -0,0 +1,75 @@ +[package] +authors.workspace = true +categories = ["mathematics"] +description.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "rsdiff-math" +repository.workspace = true +readme.workspace = true +version.workspace = true + +[features] +default = [ + "std" +] + +full = [ + "default", + "approx", + "serde", + "trace", +] + +approx = [ + "dep:approx", +] + +serde = [ + "dep:serde", +] + +trace = [ + "dep:tracing", +] + +std = [ + "rsdiff-core/std" +] + +wasi = [ + "rsdiff-core/wasi" +] + +wasm = [ + "rsdiff-core/wasm" +] + +[build-dependencies] + +[dependencies] +approx = { optional = true, version = "0.5" } +num = "0.4" +paste.workspace = true +serde = { optional = true, features = ["derive"], version = "1" } +smart-default.workspace = true +strum.workspace = true +tracing = { optional = true, version = "0.1" } + +[dependencies.rsdiff-core] +path = "../core" +version = "0.0.1" +# version = "0.3.2-nightly" + +[dev-dependencies] +approx = "0.5" + +[package.metadata.docs.rs] +all-features = true +rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/math/src/cluster/mod.rs b/math/src/cluster/mod.rs new file mode 100644 index 00000000..0e50fb59 --- /dev/null +++ b/math/src/cluster/mod.rs @@ -0,0 +1,4 @@ +/* + Appellation: cluster + Contrib: FL03 +*/ diff --git a/math/src/lib.rs b/math/src/lib.rs new file mode 100644 index 00000000..ce2eba0b --- /dev/null +++ b/math/src/lib.rs @@ -0,0 +1,25 @@ +/* + Appellation: acme-math + Contrib: FL03 +*/ + +#[cfg(not(feature = "std"))] +extern crate alloc; + +pub use self::traits::prelude::*; + +pub mod cluster; +pub mod linalg; +pub mod num; +pub mod props; +pub mod signal; +pub mod stats; +pub mod traits; + + + +#[doc(hidden)] +pub mod prelude { + pub use super::traits::prelude::*; + +} diff --git a/core/src/math/linalg/fields.rs b/math/src/linalg/fields.rs similarity index 100% rename from core/src/math/linalg/fields.rs rename to math/src/linalg/fields.rs diff --git a/core/src/math/linalg/mod.rs b/math/src/linalg/mod.rs similarity index 100% rename from core/src/math/linalg/mod.rs rename to math/src/linalg/mod.rs diff --git a/core/src/math/linalg/vs/mod.rs b/math/src/linalg/vs/mod.rs similarity index 85% rename from core/src/math/linalg/vs/mod.rs rename to math/src/linalg/vs/mod.rs index 4fd53b00..b965124b 100644 --- a/core/src/math/linalg/vs/mod.rs +++ b/math/src/linalg/vs/mod.rs @@ -2,7 +2,7 @@ Appellation: vs Contrib: FL03 */ -use crate::math::linalg::fields::Field; +use crate::linalg::fields::Field; pub trait VectorSpace { type Field: Field; diff --git a/math/src/num/mod.rs b/math/src/num/mod.rs new file mode 100644 index 00000000..c26ea46d --- /dev/null +++ b/math/src/num/mod.rs @@ -0,0 +1,35 @@ +/* + Appellation: num + Contrib: FL03 +*/ +pub use self::{traits::*, utils::*}; + +pub mod traits; + +pub(crate) mod utils { + use num::integer::Integer; + use num::traits::{Num, ToPrimitive, Unsigned}; + + pub fn harmonic(n: T) -> f64 + where + T: Integer + Num + ToPrimitive + Unsigned, + { + (1..=n.to_usize().unwrap()).fold(0f64, |acc, i| acc + i.to_f64().unwrap().recip()) + } +} + +#[cfg(test)] +mod tests { + use super::traits::NaturalNumber; + use approx::assert_relative_eq; + + #[test] + fn test_harmonic() { + assert_relative_eq!(1u8.harmonic(), 1.0); + assert_relative_eq!(2u16.harmonic(), super::harmonic(2u16)); + assert_relative_eq!(3u32.harmonic(), 11.0 / 6.0); + assert_relative_eq!(4u64.harmonic(), 25. / 12.); + assert_relative_eq!(5u128.harmonic(), 137. / 60.); + assert_relative_eq!(6usize.harmonic(), 2.4499999999999997); + } +} diff --git a/math/src/num/traits.rs b/math/src/num/traits.rs new file mode 100644 index 00000000..c64a6095 --- /dev/null +++ b/math/src/num/traits.rs @@ -0,0 +1,36 @@ +/* + Appellation: num + Contrib: FL03 +*/ +use paste::paste; + +pub trait NaturalNumber { + fn harmonic(&self) -> f64; +} + +macro_rules! natural { + ($($n:tt),*) => { + $(natural!(@impl $n);)* + }; + (@impl $n:tt) => { + paste! { + impl NaturalNumber for [] { + fn harmonic(&self) -> f64 { + let mut sum = 0.0; + for i in 1..=*self { + sum += (i as f64).recip(); + } + sum + } + } + + impl NaturalNumber for core::num::[] { + fn harmonic(&self) -> f64 { + self.get().harmonic() + } + } + } + }; +} + +natural!(8, 16, 32, 64, 128, size); diff --git a/core/src/math/props/mod.rs b/math/src/props/mod.rs similarity index 100% rename from core/src/math/props/mod.rs rename to math/src/props/mod.rs diff --git a/tensor/src/io/mod.rs b/math/src/signal/mod.rs similarity index 61% rename from tensor/src/io/mod.rs rename to math/src/signal/mod.rs index 9a895a5b..2e33dcf6 100644 --- a/tensor/src/io/mod.rs +++ b/math/src/signal/mod.rs @@ -1,4 +1,4 @@ /* - Appellation: io + Appellation: num Contrib: FL03 */ diff --git a/math/src/stats/mod.rs b/math/src/stats/mod.rs new file mode 100644 index 00000000..2e33dcf6 --- /dev/null +++ b/math/src/stats/mod.rs @@ -0,0 +1,4 @@ +/* + Appellation: num + Contrib: FL03 +*/ diff --git a/math/src/traits/mod.rs b/math/src/traits/mod.rs new file mode 100644 index 00000000..c8145bcb --- /dev/null +++ b/math/src/traits/mod.rs @@ -0,0 +1,15 @@ +/* + Appellation: traits + Contrib: FL03 +*/ + +pub trait Group {} + +pub(crate) mod prelude { + pub use super::Group; +} + +#[cfg(test)] +mod tests { + +} diff --git a/rsdiff/Cargo.toml b/rsdiff/Cargo.toml new file mode 100644 index 00000000..55297e9e --- /dev/null +++ b/rsdiff/Cargo.toml @@ -0,0 +1,122 @@ +[package] +authors.workspace = true +categories.workspace = true +description.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "rsdiff" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[features] +default = [ + "core", + "math", + "graph", +] + +full = [ + "default", + "derive", + "serde", + "trace", +] + +core = [] + +derive = [ + "dep:rsdiff-derive", + "macros" +] + +graph = [ + "dep:rsdiff-graphs" +] + +macros = [ + "dep:rsdiff-macros" +] + +math = [ + "dep:rsdiff-math" +] + +serde = [ + "rsdiff-core/serde", + "rsdiff-graphs/serde", +] + +std = [ + "rsdiff-core/std", + "rsdiff-graphs/std", +] + + +trace = [ + "rsdiff-core/trace", + "rsdiff-graphs/trace", +] + +wasi = [ + "rsdiff-core/wasi", + "rsdiff-graphs/wasi", +] + +wasm = [ + "rsdiff-core/wasm", + "rsdiff-graphs/wasm", +] + + + +[lib] +bench = true +crate-type = ["cdylib", "rlib"] +doctest = true +test = true + + +[[example]] +doc = true +name = "autodiff" +required-features = ["macros"] + +[[example]] +name = "graph" +required-features = ["graph"] + +[[example]] +name = "macros" +required-features = ["macros"] + + +[[test]] +name = "macros" +required-features = ["macros"] + + +[build-dependencies] + +[dependencies] +rsdiff-core = { path = "../core", version = "0.0.1" } +rsdiff-derive = { optional = true, path = "../derive", version = "0.0.1" } +rsdiff-graphs = { optional = true, path = "../graphs", version = "0.0.1" } +rsdiff-macros = { optional = true, path = "../macros", version = "0.0.1" } +rsdiff-math = { optional = true, path = "../math", version = "0.0.1" } + +[dev-dependencies] +approx = "0.5" +lazy_static = "1" +num = "0.4" +rand = "0.8" + +[package.metadata.docs.rs] +all-features = true +rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/acme/benches/default.rs b/rsdiff/benches/default.rs similarity index 100% rename from acme/benches/default.rs rename to rsdiff/benches/default.rs diff --git a/acme/examples/autodiff.rs b/rsdiff/examples/autodiff.rs similarity index 96% rename from acme/examples/autodiff.rs rename to rsdiff/examples/autodiff.rs index 340214b6..2738304d 100644 --- a/acme/examples/autodiff.rs +++ b/rsdiff/examples/autodiff.rs @@ -4,9 +4,9 @@ */ // #![cfg(feature = "macros")] #![allow(unused_variables)] -extern crate acme; +extern crate rsdiff; -use acme::autodiff; +use rsdiff::autodiff; macro_rules! format_exp { (symbolic: {exp: $ex:expr, vars: [$($var:ident),*] }) => { diff --git a/acme/examples/graph.rs b/rsdiff/examples/graph.rs similarity index 82% rename from acme/examples/graph.rs rename to rsdiff/examples/graph.rs index 44bc6ebe..204ea560 100644 --- a/acme/examples/graph.rs +++ b/rsdiff/examples/graph.rs @@ -4,10 +4,10 @@ */ #![cfg(feature = "graph")] -extern crate acme; +extern crate rsdiff; -use acme::graph::prelude::GraphResult; -use acme::graph::scg::Scg; +use rsdiff::graph::prelude::GraphResult; +use rsdiff::graph::scg::Scg; fn main() -> GraphResult<()> { let mut scg = Scg::new(); diff --git a/acme/examples/macros.rs b/rsdiff/examples/macros.rs similarity index 76% rename from acme/examples/macros.rs rename to rsdiff/examples/macros.rs index 3720ce78..d15a385b 100644 --- a/acme/examples/macros.rs +++ b/rsdiff/examples/macros.rs @@ -2,14 +2,13 @@ Appellation: autodiff Contrib: FL03 */ -extern crate acme; +extern crate rsdiff; -use acme::{autodiff, operator}; +use rsdiff::{autodiff, operator}; use num::Float; -fn main() -> acme::prelude::BoxResult { +fn main() -> rsdiff::prelude::BoxResult { let x = 5f64; - let lex = sigmoid_lex(); println!("{}", sigmoid(x)); println!("{}", sigmoid_lex()); let dx = sigmoid_prime(x); @@ -19,7 +18,7 @@ fn main() -> acme::prelude::BoxResult { Ok(()) } -#[operator(lexical = sigmoid_lex)] +#[operator(lex = sigmoid_lex)] pub fn sigmoid(x: T) -> T where T: Float, diff --git a/acme/examples/simple.rs b/rsdiff/examples/simple.rs similarity index 78% rename from acme/examples/simple.rs rename to rsdiff/examples/simple.rs index 0cd13623..fc02fe8d 100644 --- a/acme/examples/simple.rs +++ b/rsdiff/examples/simple.rs @@ -2,9 +2,9 @@ Appellation: simple Contrib: FL03 */ -extern crate acme; +extern crate rsdiff; -use acme::prelude::{nested, BoxResult}; +use rsdiff::prelude::{nested, BoxResult}; fn main() -> BoxResult { nested!( diff --git a/rsdiff/src/lib.rs b/rsdiff/src/lib.rs new file mode 100644 index 00000000..6ed2a69a --- /dev/null +++ b/rsdiff/src/lib.rs @@ -0,0 +1,39 @@ +/* + Appellation: rsdiff + Contrib: FL03 +*/ +//! # rsdiff +//! +//! rsdiff is an autodifferentiaion library for Rust. It is designed to be a +//! flexible and powerful tool for building machine learning models and +//! other differentiable programs. +#![crate_name = "rsdiff"] + +#[doc(inline)] +pub use rsdiff_core::*; +#[allow(unused_imports)] +#[cfg(feature = "derive")] +#[doc(inline)] +pub use rsdiff_derive::*; +#[cfg(feature = "graph")] +#[doc(inline)] +pub use rsdiff_graphs as graph; +#[cfg(feature = "macros")] +#[doc(inline)] +pub use rsdiff_macros::*; +#[cfg(feature = "math")] +#[doc(inline)] +pub use rsdiff_math as math; + +pub mod prelude { + pub use rsdiff_core::prelude::*; + #[allow(unused_imports)] + #[cfg(feature = "derive")] + pub use rsdiff_derive::*; + #[cfg(feature = "graph")] + pub use rsdiff_graphs::prelude::*; + #[cfg(feature = "macros")] + pub use rsdiff_macros::*; + #[cfg(feature = "math")] + pub use rsdiff_math::prelude::*; +} diff --git a/acme/tests/default.rs b/rsdiff/tests/default.rs similarity index 100% rename from acme/tests/default.rs rename to rsdiff/tests/default.rs diff --git a/acme/tests/macros.rs b/rsdiff/tests/macros.rs similarity index 97% rename from acme/tests/macros.rs rename to rsdiff/tests/macros.rs index 6331fdbb..f4d80eaf 100644 --- a/acme/tests/macros.rs +++ b/rsdiff/tests/macros.rs @@ -6,9 +6,9 @@ #![feature(fn_traits, tuple_trait, unboxed_closures)] #![cfg(all(test, feature = "macros"))] -extern crate acme; +extern crate rsdiff; -use acme::prelude::autodiff; +use rsdiff::prelude::autodiff; use utils::*; #[test] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..271800cb --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/tensor/Cargo.toml b/tensor/Cargo.toml deleted file mode 100644 index e0b06c7b..00000000 --- a/tensor/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -authors.workspace = true -categories.workspace = true -description = "A comprehensive tensor library for Rust with support for automatic-differentiation." -edition.workspace = true -homepage.workspace = true -keywords = ["acme", "autodiff", "data-structure", "tensor"] -license.workspace = true -name = "acme-tensor" -repository.workspace = true -readme.workspace = true -version.workspace = true - -[features] -default = [ - "std" -] - -io = [ - -] - -serde = [ - "dep:serde", - "serde-ext", -] - -serde-ext = [ - "acme-core/serde" -] - -std = [ - "acme-core/std", -] - -[build-dependencies] - -[dependencies] -num = "0.4" -rawpointer = "0.2" -serde = { optional = true, features = ["derive"], version = "1" } -strum = { features = ["derive"], version = "0.26" } - -[dependencies.acme-core] -path = "../core" -version = "0.3.1" -# version = "0.3.1-nightly" - -[dev-dependencies] -approx = "0.5" - -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - -[target.wasm32-unknown-unknown] - -[target.wasm32-wasi] \ No newline at end of file diff --git a/tensor/src/actions/create/arange.rs b/tensor/src/actions/create/arange.rs deleted file mode 100644 index 1bc7973d..00000000 --- a/tensor/src/actions/create/arange.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* - Appellation: arange - Contrib: FL03 -*/ -use super::utils::steps; -use num::traits::{FromPrimitive, Num, ToPrimitive}; - -pub struct Arange { - scope: usize, - start: T, - stop: T, - step: T, -} - -impl Arange { - pub fn new(start: T, stop: T, step: T) -> Self { - Self { - scope: 0, - start, - stop, - step, - } - } - - pub fn start(&self) -> &T { - &self.start - } - - pub fn stop(&self) -> &T { - &self.stop - } - - pub fn step(&self) -> &T { - &self.step - } - - pub fn steps(&self) -> usize - where - T: Copy + Num + ToPrimitive, - { - steps(self.start, self.stop, self.step) - } -} - -impl Iterator for Arange -where - T: Copy + FromPrimitive + Num + ToPrimitive, -{ - type Item = T; - - fn next(&mut self) -> Option { - if self.scope < self.steps() { - let value = self.start + self.step * T::from_usize(self.scope).unwrap(); - self.scope += 1; - Some(value) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_arange() { - let mut arange = Arange::new(0, 10, 2); - assert_eq!(arange.next(), Some(0)); - assert_eq!(arange.next(), Some(2)); - assert_eq!(arange.next(), Some(4)); - assert_eq!(arange.next(), Some(6)); - assert_eq!(arange.next(), Some(8)); - assert_eq!(arange.next(), None); - } -} diff --git a/tensor/src/actions/create/linspace.rs b/tensor/src/actions/create/linspace.rs deleted file mode 100644 index 8f245227..00000000 --- a/tensor/src/actions/create/linspace.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Appellation: linspace - Contrib: FL03 -*/ -use super::step_size; -use num::traits::{FromPrimitive, Num}; - -pub trait Linspace { - fn linspace(start: T, stop: T, steps: usize) -> Self; -} - -pub trait LinspaceExt: Linspace { - fn linspace_until(stop: T, steps: usize) -> Self; -} - -impl LinspaceExt for S -where - S: Linspace, - T: Default, -{ - fn linspace_until(stop: T, steps: usize) -> Self { - S::linspace(T::default(), stop, steps) - } -} - -impl Linspace for Vec -where - T: Copy + Default + FromPrimitive + Num + PartialOrd, -{ - fn linspace(start: T, stop: T, steps: usize) -> Self { - let step = step_size(start, stop, steps); - let mut vec = Vec::with_capacity(steps); - let mut value = start; - for _ in 0..steps { - vec.push(value); - value = value + step; - } - vec - } -} diff --git a/tensor/src/actions/create/mod.rs b/tensor/src/actions/create/mod.rs deleted file mode 100644 index b09dbcf0..00000000 --- a/tensor/src/actions/create/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - Appellation: create - Contrib: FL03 -*/ -pub use self::{arange::*, linspace::*, stack::*, utils::*}; - -pub(crate) mod arange; -pub(crate) mod linspace; -pub(crate) mod stack; - -pub(crate) mod utils { - use core::ops::{Div, Sub}; - use num::traits::{FromPrimitive, ToPrimitive}; - /// Calculate the step size for a given range and number of steps. - pub fn step_size(start: T, stop: T, steps: usize) -> T - where - T: FromPrimitive + Div + Sub, - { - (stop - start) / T::from_usize(steps).unwrap() - } - - pub fn steps(start: T, stop: T, step: T) -> usize - where - T: ToPrimitive + Div + Sub, - { - let steps = (stop - start) / step; - steps.to_usize().unwrap() - } -} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/actions/create/stack.rs b/tensor/src/actions/create/stack.rs deleted file mode 100644 index b07790a6..00000000 --- a/tensor/src/actions/create/stack.rs +++ /dev/null @@ -1,23 +0,0 @@ -/* - Appellation: stack - Contrib: FL03 -*/ -use crate::shape::Axis; - -pub trait Stack { - type Output; - - fn stack(&self, other: &T, along: Axis) -> Self::Output; -} - -pub trait Hstack { - type Output; - - fn hstack(&self, other: &T) -> Self::Output; -} - -pub trait Vstack { - type Output; - - fn vstack(&self, other: &T) -> Self::Output; -} diff --git a/tensor/src/actions/grad/mod.rs b/tensor/src/actions/grad/mod.rs deleted file mode 100644 index 18ae60e6..00000000 --- a/tensor/src/actions/grad/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -/* - Appellation: grad - Contrib: FL03 -*/ -//! # Gradient -//! -//! -pub use self::store::TensorGrad; - -pub(crate) mod store; - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/actions/grad/store.rs b/tensor/src/actions/grad/store.rs deleted file mode 100644 index 2bb37161..00000000 --- a/tensor/src/actions/grad/store.rs +++ /dev/null @@ -1,183 +0,0 @@ -/* - Appellation: store - Contrib: FL03 -*/ -use crate::prelude::TensorId; -use crate::TensorBase; -use acme::prelude::Store; -use core::borrow::{Borrow, BorrowMut}; -use core::ops::{Deref, DerefMut, Index, IndexMut}; -use std::collections::btree_map::{BTreeMap, Entry, Keys, Values}; - -#[derive(Clone, Debug)] -pub struct TensorGrad { - pub(crate) store: BTreeMap>, -} - -impl Default for TensorGrad { - fn default() -> Self { - Self::new() - } -} - -impl TensorGrad { - pub fn new() -> Self { - Self { - store: BTreeMap::new(), - } - } - /// Clears the store, removing all values. - pub fn clear(&mut self) { - self.store.clear() - } - /// Returns a reference to the value corresponding to the key. - pub fn entry(&mut self, key: TensorId) -> Entry<'_, TensorId, TensorBase> { - self.store.entry(key) - } - /// Returns a reference to the value corresponding to the key. - pub fn get_tensor(&self, item: &TensorBase) -> Option<&TensorBase> { - self.store.get(&item.id()) - } - /// Inserts a tensor into the store. - pub fn insert_tensor(&mut self, tensor: TensorBase) -> Option> { - self.insert(tensor.id(), tensor) - } - /// Returns true if the store contains no elements. - pub fn is_empty(&self) -> bool { - self.store.is_empty() - } - /// Returns an iterator over the store's keys - pub fn keys(&self) -> Keys<'_, TensorId, TensorBase> { - self.store.keys() - } - /// Returns the number of elements in the store. - pub fn len(&self) -> usize { - self.store.len() - } - /// If the store does not have a tensor with the given id, insert it. - /// Returns a mutable reference to the tensor. - pub fn or_insert(&mut self, tensor: TensorBase) -> &mut TensorBase { - self.entry(tensor.id()).or_insert(tensor) - } - /// If the store does not have a tensor with the given id, insert a tensor with the same shape - /// and dtype as the given tensor, where all elements are default. - pub fn or_insert_default(&mut self, tensor: &TensorBase) -> &mut TensorBase - where - T: Clone + Default, - { - self.entry(tensor.id()).or_insert(tensor.default_like()) - } - /// If the store does not have a tensor with the given id, insert a tensor with the same shape - /// and dtype as the given tensor, where all elements are zeros. - pub fn or_insert_zeros(&mut self, tensor: &TensorBase) -> &mut TensorBase - where - T: Clone + num::Zero, - { - self.entry(tensor.id()).or_insert(tensor.zeros_like()) - } - /// Remove an element from the store. - pub fn remove(&mut self, key: &TensorId) -> Option> { - self.store.remove(key) - } - /// Remove a tensor from the store. - pub fn remove_tensor(&mut self, tensor: &TensorBase) -> Option> { - self.remove(&tensor.id()) - } - - pub fn values(&self) -> Values<'_, TensorId, TensorBase> { - self.store.values() - } -} - -impl AsRef>> for TensorGrad { - fn as_ref(&self) -> &BTreeMap> { - &self.store - } -} - -impl AsMut>> for TensorGrad { - fn as_mut(&mut self) -> &mut BTreeMap> { - &mut self.store - } -} - -impl Borrow>> for TensorGrad { - fn borrow(&self) -> &BTreeMap> { - &self.store - } -} - -impl BorrowMut>> for TensorGrad { - fn borrow_mut(&mut self) -> &mut BTreeMap> { - &mut self.store - } -} - -impl Deref for TensorGrad { - type Target = BTreeMap>; - - fn deref(&self) -> &Self::Target { - &self.store - } -} - -impl DerefMut for TensorGrad { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.store - } -} - -impl Extend<(TensorId, TensorBase)> for TensorGrad { - fn extend)>>(&mut self, iter: I) { - self.store.extend(iter) - } -} - -impl FromIterator<(TensorId, TensorBase)> for TensorGrad { - fn from_iter)>>(iter: I) -> Self { - Self { - store: BTreeMap::from_iter(iter), - } - } -} - -impl Index<&TensorId> for TensorGrad { - type Output = TensorBase; - - fn index(&self, index: &TensorId) -> &Self::Output { - &self.store[index] - } -} - -impl IndexMut<&TensorId> for TensorGrad { - fn index_mut(&mut self, index: &TensorId) -> &mut Self::Output { - self.get_mut(index).expect("Tensor not found") - } -} - -impl IntoIterator for TensorGrad { - type Item = (TensorId, TensorBase); - type IntoIter = std::collections::btree_map::IntoIter>; - - fn into_iter(self) -> Self::IntoIter { - self.store.into_iter() - } -} - -impl Store> for TensorGrad { - fn get(&self, key: &TensorId) -> Option<&TensorBase> { - self.store.get(key) - } - - fn get_mut(&mut self, key: &TensorId) -> Option<&mut TensorBase> { - self.store.get_mut(key) - } - - fn insert(&mut self, key: TensorId, value: TensorBase) -> Option> { - self.store.insert(key, value) - } - - fn remove(&mut self, key: &TensorId) -> Option> { - self.remove(key) - } -} diff --git a/tensor/src/actions/index/mod.rs b/tensor/src/actions/index/mod.rs deleted file mode 100644 index 3fd00c10..00000000 --- a/tensor/src/actions/index/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Appellation: index - Contrib: FL03 -*/ -//! # Index -//! -//! -pub use self::slice::*; - -pub(crate) mod slice; - -/// A type alias for an unsigned integer used to index into a tensor. -pub type Ix = usize; -/// A type alias for a signed integer used to index into a tensor. -pub type Ixs = isize; - -pub trait TensorIndex {} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/actions/index/slice.rs b/tensor/src/actions/index/slice.rs deleted file mode 100644 index fdd3eb73..00000000 --- a/tensor/src/actions/index/slice.rs +++ /dev/null @@ -1,127 +0,0 @@ -/* - Appellation: slice - Contrib: FL03 -*/ -//! # Slice -//! -//! -use super::Ixs; - -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Slice { - pub start: Ixs, - pub end: Option, - pub step: Ixs, -} - -impl Slice { - pub fn new(start: Ixs, end: Option, step: Ixs) -> Self { - debug_assert_ne!(step, 0, "step must be non-zero"); - Self { start, end, step } - } - - pub fn start(&self) -> Ixs { - self.start - } - - pub fn end(&self) -> Option { - self.end - } - - pub fn step(&self) -> Ixs { - self.step - } -} - -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(rename_all = "snake_case") -)] -pub enum SliceType { - Index(Ixs), - Slice(Slice), - NewAxis, -} - -impl SliceType { - /// Create a new axis - pub fn new_axis() -> Self { - Self::NewAxis - } - /// Create a new index - pub fn index(index: Ixs) -> Self { - Self::Index(index) - } - /// Create a new slice - pub fn slice(start: Ixs, end: Option, step: Ixs) -> Self { - Self::Slice(Slice::new(start, end, step)) - } -} - -pub struct Slices { - slices: Vec, -} - -impl Slices { - pub fn new(slices: impl IntoIterator) -> Self { - Self { - slices: Vec::from_iter(slices), - } - } - - pub fn iter(&self) -> core::slice::Iter<'_, SliceType> { - self.slices.iter() - } -} - -macro_rules! impl_from_range { - ($self:ty, $constructor:expr, $idx:ty, [$($index:ty),*]) => { - $( - impl_from_range!($self, $constructor, $idx, $index); - )* - }; - ($self:ty, $constructor:expr, $idx:ty, $index:ty) => { - impl From> for $self { - #[inline] - fn from(r: core::ops::Range<$index>) -> $self { - $constructor(r.start as $idx, Some(r.end as $idx), 1) - } - } - - impl From> for $self { - #[inline] - fn from(r: core::ops::RangeFrom<$index>) -> $self { - $constructor(r.start as $idx, None, 1) - } - } - - impl From> for $self { - #[inline] - fn from(r: core::ops::RangeInclusive<$index>) -> $self { - let end = *r.end() as $idx; - $constructor(*r.start() as $idx, if end == -1 { None } else { Some(end + 1) }, 1) - } - } - - impl From> for $self { - #[inline] - fn from(r: core::ops::RangeTo<$index>) -> $self { - $constructor(0, Some(r.end as $idx), 1) - } - } - - impl From> for $self { - #[inline] - fn from(r: core::ops::RangeToInclusive<$index>) -> $self { - let end = r.end as $idx; - $constructor(0, if end == -1 { None } else { Some(end + 1) }, 1) - } - } - }; -} - -impl_from_range!(Slice, Slice::new, Ixs, [i32, isize, usize]); -impl_from_range!(SliceType, SliceType::slice, Ixs, [i32, isize, usize]); diff --git a/tensor/src/actions/iter/axis.rs b/tensor/src/actions/iter/axis.rs deleted file mode 100644 index 2f9e49e4..00000000 --- a/tensor/src/actions/iter/axis.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - Appellation: axis - Contrib: FL03 -*/ -use crate::index::{Ix, Ixs}; -use crate::shape::{Axis, Layout}; -use crate::TensorBase; -use core::ptr; - -pub struct AxisIter<'a, A> { - index: Ix, - end: Ix, - stride: Ixs, - inner_layout: Layout, - ptr: *const A, - tensor: TensorBase<&'a A>, -} - -impl<'a, A> AxisIter<'a, A> { - pub fn new(v: TensorBase<&'a A>, axis: Axis) -> Self { - let stride = v.strides()[axis] as isize; - let end = v.shape()[axis]; - Self { - index: 0, - end, - stride, - inner_layout: v.layout().remove_axis(axis), - ptr: unsafe { *v.as_ptr() }, - tensor: v, - } - } -} - -impl<'a, A> Iterator for AxisIter<'a, A> { - type Item = &'a A; - - fn next(&mut self) -> Option { - if self.index == self.end { - return None; - } - let ptr = unsafe { self.ptr.add(self.index) }; - self.index += self.stride as Ix; - unsafe { ptr.as_ref() } - } -} diff --git a/tensor/src/actions/iter/indexed.rs b/tensor/src/actions/iter/indexed.rs deleted file mode 100644 index b9a756eb..00000000 --- a/tensor/src/actions/iter/indexed.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - Appellation: indexed - Contrib: FL03 -*/ -use crate::iter::LayoutIter; -use crate::tensor::TensorBase; - -use super::Position; - -pub struct IndexedIter<'a, T: 'a> { - inner: LayoutIter, - scope: Option<&'a T>, - tensor: &'a TensorBase, -} - -impl<'a, T> IndexedIter<'a, T> { - pub fn new(tensor: &'a TensorBase) -> Self { - Self { - inner: tensor.layout().iter(), - scope: None, - tensor, - } - } -} - -impl<'a, T> Iterator for IndexedIter<'a, T> { - type Item = (&'a T, Position); - - fn next(&mut self) -> Option { - let pos = self.inner.next()?; - self.scope = self.tensor.get_by_index(pos.index()); - self.scope.map(|scope| (scope, pos)) - } -} - -impl<'a, T> DoubleEndedIterator for IndexedIter<'a, T> { - fn next_back(&mut self) -> Option { - let pos = self.inner.next_back()?; - self.scope = self.tensor.get_by_index(pos.index()); - self.scope.map(|scope| (scope, pos)) - } -} - -impl<'a, T> ExactSizeIterator for IndexedIter<'a, T> { - fn len(&self) -> usize { - self.tensor.size() - } -} diff --git a/tensor/src/actions/iter/iterator.rs b/tensor/src/actions/iter/iterator.rs deleted file mode 100644 index e58efa18..00000000 --- a/tensor/src/actions/iter/iterator.rs +++ /dev/null @@ -1,109 +0,0 @@ -/* - Appellation: iterator - Contrib: FL03 -*/ -use super::LayoutIter; -use crate::TensorBase; -use core::marker::PhantomData; -use core::ptr; - -/// An immutable iterator of the elements of a [tensor](crate::tensor::TensorBase) -/// Elements are visited in order, matching the layout of the tensor. -pub struct Iter<'a, T> { - inner: LayoutIter, - ptr: *const T, - tensor: TensorBase<&'a T>, -} - -impl<'a, T> Iter<'a, T> { - pub(crate) fn new(tensor: TensorBase<&'a T>) -> Self { - Self { - inner: tensor.layout().iter(), - ptr: unsafe { *tensor.as_ptr() }, - tensor, - } - } -} - -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option { - let pos = self.inner.next()?; - let item = self.tensor.get_by_index(pos.index())?; - self.ptr = ptr::from_ref(item); - unsafe { self.ptr.as_ref() } - } -} - -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - fn next_back(&mut self) -> Option { - let pos = self.inner.next_back()?; - let item = self.tensor.get_by_index(pos.index())?; - self.ptr = ptr::from_ref(item); - unsafe { self.ptr.as_ref() } - } -} - -impl<'a, T> ExactSizeIterator for Iter<'a, T> { - fn len(&self) -> usize { - self.tensor.size() - } -} - -impl<'a, T> From> for Iter<'a, T> { - fn from(tensor: TensorBase<&'a T>) -> Self { - Self::new(tensor) - } -} - -impl<'a, T> From<&'a TensorBase> for Iter<'a, T> { - fn from(tensor: &'a TensorBase) -> Self { - Self::new(tensor.view()) - } -} - -pub struct IterMut<'a, T: 'a> { - inner: LayoutIter, - ptr: *mut T, - tensor: TensorBase<&'a mut T>, - _marker: PhantomData<&'a mut T>, -} - -impl<'a, T> IterMut<'a, T> { - pub(crate) fn new(tensor: &'a mut TensorBase) -> Self { - Self { - inner: tensor.layout().iter(), - ptr: tensor.as_mut_ptr(), - tensor: tensor.view_mut(), - _marker: PhantomData, - } - } -} - -impl<'a, T> Iterator for IterMut<'a, T> { - type Item = &'a mut T; - - fn next(&mut self) -> Option { - let pos = self.inner.next()?; - let elem = self.tensor.get_mut_by_index(pos.index())?; - self.ptr = ptr::from_mut(elem); - unsafe { self.ptr.as_mut() } - } -} - -impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { - fn next_back(&mut self) -> Option { - let pos = self.inner.next_back()?; - let elem = self.tensor.get_mut_by_index(pos.index())?; - - self.ptr = ptr::from_mut(elem); - unsafe { self.ptr.as_mut() } - } -} - -impl<'a, T> ExactSizeIterator for IterMut<'a, T> { - fn len(&self) -> usize { - self.tensor.size() - } -} diff --git a/tensor/src/actions/iter/layout.rs b/tensor/src/actions/iter/layout.rs deleted file mode 100644 index 7250478a..00000000 --- a/tensor/src/actions/iter/layout.rs +++ /dev/null @@ -1,168 +0,0 @@ -/* - Appellation: layout - Contrib: FL03 -*/ -use crate::shape::Layout; - -#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize,))] -#[repr(C)] -pub struct Position { - pub(crate) index: usize, - pub(crate) position: Vec, -} - -/// An iterator over the positions of an n-dimensional tensor. -/// Each step yields a [position](Position) containing the current position -/// and corresponding index. -pub struct LayoutIter { - layout: Layout, - next: Option, - pos: Vec, -} - -impl LayoutIter { - pub(crate) fn new(layout: Layout) -> Self { - let next = if layout.size() == 0 { - None - } else { - // This applies to the scalar case. - Some(layout.offset()) - }; - let pos = vec![0; *layout.rank()]; - Self { next, layout, pos } - } - - pub(crate) fn index(&self, index: impl AsRef<[usize]>) -> usize { - self.layout.index(index) - } -} - -impl DoubleEndedIterator for LayoutIter { - fn next_back(&mut self) -> Option { - let Position { position, .. } = self.next()?; - let rev = self - .layout - .shape() - .get_final_position() - .iter() - .zip(position.iter()) - .map(|(s, p)| (s - p)) - .collect(); - let pos = Position::new(self.index(&rev), rev); - Some(pos) - } -} - -impl ExactSizeIterator for LayoutIter { - fn len(&self) -> usize { - self.layout.size() - } -} - -impl Iterator for LayoutIter { - type Item = Position; - - fn next(&mut self) -> Option { - let index = match self.next { - None => return None, - Some(i) => i, - }; - let cur = Position::new(index, self.pos.clone()); - let mut updated = false; - let mut next = index; - for ((i, j), s) in self - .pos - .iter_mut() - .zip(self.layout.shape.iter()) - .zip(self.layout.strides.iter()) - .rev() - { - let next_i = *i + 1; - if next_i < *j { - *i = next_i; - updated = true; - next += s; - break; - } else { - next -= *i * s; - *i = 0; - } - } - self.next = if updated { Some(next) } else { None }; - Some(cur) - } -} - -mod impl_position { - use super::Position; - use crate::shape::Layout; - - impl Position { - pub fn new(index: usize, position: Vec) -> Self { - Self { index, position } - } - - pub fn first(rank: usize) -> Self { - Self::new(0, vec![0; rank]) - } - /// Returns the index of the position. - pub fn index(&self) -> usize { - self.index - } - /// Given a particular layout, returns the next position. - pub fn next(&self, layout: &Layout) -> Option { - let mut position = self.position().to_vec(); - let mut updated = false; - let mut next = self.index(); - for ((i, j), s) in position - .iter_mut() - .zip(layout.shape().iter()) - .zip(layout.strides().iter()) - .rev() - { - let next_i = *i + 1; - if next_i < *j { - *i = next_i; - updated = true; - next += s; - break; - } else { - next -= *i * s; - *i = 0; - } - } - if updated { - Some(Self::new(next, position)) - } else { - None - } - } - /// Returns a reference to the position. - pub fn position(&self) -> &[usize] { - &self.position - } - /// Returns a mutable reference to the position. - pub fn position_mut(&mut self) -> &mut [usize] { - &mut self.position - } - } - - impl From<(usize, Vec)> for Position { - fn from((idx, pos): (usize, Vec)) -> Self { - Self::new(idx, pos) - } - } - - impl From<(Vec, usize)> for Position { - fn from((pos, idx): (Vec, usize)) -> Self { - Self::new(idx, pos) - } - } - - impl From for (usize, Vec) { - fn from(pos: Position) -> (usize, Vec) { - (pos.index, pos.position) - } - } -} diff --git a/tensor/src/actions/iter/mod.rs b/tensor/src/actions/iter/mod.rs deleted file mode 100644 index e3062b02..00000000 --- a/tensor/src/actions/iter/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - Appellation: iter - Contrib: FL03 -*/ -//! # Iter -//! -//! -pub use self::{indexed::*, iterator::*, layout::*, utils::*}; - -#[allow(dead_code, unused)] -pub(crate) mod axis; -pub(crate) mod indexed; -pub(crate) mod iterator; -pub(crate) mod layout; - -pub(crate) mod utils { - use core::ptr; - - pub(crate) fn zip(i: I, j: J) -> core::iter::Zip - where - I: IntoIterator, - J: IntoIterator, - { - i.into_iter().zip(j) - } - - pub fn to_vec_mapped(iter: I, mut f: F) -> Vec - where - I: ExactSizeIterator, // + TrustedIterator - 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 - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::shape::Layout; - - #[test] - fn test_to_vec_mapped() { - let v = [1, 2, 3, 4, 5]; - let f = |x| x * 2; - let res = to_vec_mapped(v.iter(), f); - assert_eq!(res, vec![2, 4, 6, 8, 10]); - } - - #[test] - fn test_position() { - let shape = (2, 2); - let layout = Layout::contiguous(shape); - let position = Position::new(1, vec![0, 1]); - assert_eq!(position.index(), 1); - assert_eq!( - position.next(&layout).unwrap(), - Position::new(2, vec![1, 0]) - ); - } -} diff --git a/tensor/src/backend/cpu/mod.rs b/tensor/src/backend/cpu/mod.rs deleted file mode 100644 index cfc95f37..00000000 --- a/tensor/src/backend/cpu/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: cpu - Contrib: FL03 -*/ -//! # CPU -//! -//! - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/backend/devices.rs b/tensor/src/backend/devices.rs deleted file mode 100644 index 79b28a63..00000000 --- a/tensor/src/backend/devices.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Appellation: devices - Contrib: FL03 -*/ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize,), - serde(rename_all = "lowercase", untagged) -)] -#[derive( - Clone, - Copy, - Debug, - Default, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - VariantNames, -)] -#[repr(C)] -#[strum(serialize_all = "lowercase")] -pub enum Device { - #[default] - CPU, - Cuda, -} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/backend/mod.rs b/tensor/src/backend/mod.rs deleted file mode 100644 index 75148ca8..00000000 --- a/tensor/src/backend/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - Appellation: backend - Contrib: FL03 -*/ -//! # Backend -//! -//! -pub use self::devices::*; - -pub(crate) mod devices; - -pub mod cpu; - -pub trait Backend {} - -pub trait BackendStorage { - type Backend: Backend; -} - -#[allow(unused_imports)] -pub(crate) mod prelude { - pub use super::devices::Device; - pub use super::{Backend, BackendStorage}; -} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/error.rs b/tensor/src/error.rs deleted file mode 100644 index 7ff3817e..00000000 --- a/tensor/src/error.rs +++ /dev/null @@ -1,104 +0,0 @@ -/* - Appellation: error - Contrib: FL03 -*/ -use crate::shape::error::ShapeError; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -pub type TensorResult = std::result::Result; - -#[derive( - Clone, Debug, Display, EnumCount, EnumIs, Eq, Hash, Ord, PartialEq, PartialOrd, VariantNames, -)] -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(rename_all = "snake_case", untagged) -)] -#[repr(usize)] -#[strum(serialize_all = "snake_case")] -pub enum TensorError { - Arithmetic(ArithmeticError), - Shape(ShapeError), - Singular, - NotScalar, - Unknown(String), -} - -unsafe impl Send for TensorError {} - -unsafe impl Sync for TensorError {} - -impl std::error::Error for TensorError {} - -impl From<&str> for TensorError { - fn from(error: &str) -> Self { - TensorError::Unknown(error.to_string()) - } -} - -impl From for TensorError { - fn from(error: String) -> Self { - TensorError::Unknown(error) - } -} - -#[derive( - Clone, - Copy, - Debug, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - VariantNames, -)] -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(rename_all = "snake_case", untagged) -)] -#[repr(usize)] -#[strum(serialize_all = "snake_case")] -pub enum ArithmeticError { - DivisionByZero, - Overflow, - Underflow, -} - -macro_rules! into_tensor_error { - ($(($error:ident => $kind:ident)),*) => { - $(into_tensor_error!($error => $kind);)* - }; - ($error:ident => $kind:ident) => { - impl From<$error> for TensorError { - fn from(error: $error) -> Self { - TensorError::$kind(error) - } - } - - impl TryFrom for $error { - type Error = TensorError; - - fn try_from(error: TensorError) -> TensorResult<$error> { - match error { - TensorError::$kind(error) => Ok(error), - error => Err(error), - } - } - } - }; -} - -into_tensor_error!( - (ArithmeticError => Arithmetic), - (ShapeError => Shape) -); diff --git a/tensor/src/fmt/mod.rs b/tensor/src/fmt/mod.rs deleted file mode 100644 index 4be68ca2..00000000 --- a/tensor/src/fmt/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: fmt - Contrib: FL03 -*/ -//! # Formatting -//! -//! - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/impls/create.rs b/tensor/src/impls/create.rs deleted file mode 100644 index 08458147..00000000 --- a/tensor/src/impls/create.rs +++ /dev/null @@ -1,149 +0,0 @@ -/* - Appellation: create - Contrib: FL03 -*/ -use crate::prelude::IntoShape; -use crate::tensor::{from_vec_with_kind, TensorBase}; -use num::traits::real::Real; -use num::traits::{FromPrimitive, NumAssign, One, Zero}; - -impl TensorBase -where - T: Clone, -{ - /// Create a new tensor, whose elements are set to their default value - /// from the current shape. - pub fn default_like(&self) -> Self - where - T: Default, - { - Self::fill(self.shape(), T::default()) - } - - /// Create an empty tensor from the given shape - pub fn empty(shape: impl IntoShape) -> Self - where - T: Default, - { - Self::fill(shape, T::default()) - } - /// Create a tensor, from the given shape, filled with the given value - pub fn fill(shape: impl IntoShape, value: T) -> Self { - let shape = shape.into_shape(); - let store = vec![value; shape.size()]; - from_vec_with_kind(false, shape, store) - } - /// Create a tensor, filled with some value, from the current shape - pub fn fill_like(&self, value: T) -> Self { - Self::fill(self.shape(), value) - } - pub fn like_with(&self, data: Vec) -> Self { - from_vec_with_kind(false, self.shape(), data) - } -} - -impl TensorBase -where - T: Copy + NumAssign + PartialOrd, -{ - /// Create a tensor within a range of values - pub fn arange(start: T, end: T, step: T) -> Self { - if T::is_zero(&step) { - panic!("step must be non-zero"); - } - let mut store = Vec::new(); - let mut value = start; - while value < end { - store.push(value); - value += step; - } - Self::from_vec(store) - } - /// Create an identity matrix of a certain size - pub fn eye(size: usize) -> Self { - let mut store = Vec::with_capacity(size * size); - for i in 0..size { - for j in 0..size { - store.push(if i == j { T::one() } else { T::zero() }); - } - } - Self::from_shape_vec((size, size), store) - } - /// Create a tensor with a certain number of elements, evenly spaced - /// between the provided start and end values - pub fn linspace(start: T, end: T, steps: usize) -> Self - where - T: FromPrimitive, - { - let step = (end - start) / T::from_usize(steps).unwrap(); - Self::arange(start, end, step) - } - - pub fn logspace(start: T, end: T, steps: usize) -> Self - where - T: Real, - { - let start = start.log2(); - let end = end.log2(); - let step = (end - start) / T::from(steps).unwrap(); - let mut store = Vec::with_capacity(steps); - let mut value: T = start; - for _ in 0..steps { - store.push(value.exp2()); - value += step; - } - from_vec_with_kind(false, (store.len(),), store) - } - - pub fn geomspace(start: T, end: T, steps: usize) -> Self - where - T: Real, - { - let start = start.log10(); - let end = end.log10(); - let step = (end - start) / T::from(steps).unwrap(); - let mut store = Vec::with_capacity(steps); - let mut value: T = start; - for _ in 0..steps { - store.push(value.exp()); - value += step; - } - from_vec_with_kind(false, (store.len(),), store) - } -} - -impl TensorBase -where - T: Clone + One, -{ - /// Create a tensor, filled with ones, from the given shape - pub fn ones(shape: impl IntoShape) -> Self { - Self::fill(shape, T::one()) - } - /// Create a tensor, filled with ones, from the shape of another tensor - pub fn ones_from(tensor: &TensorBase) -> Self { - Self::ones(tensor.shape()) - } - /// Create a tensor, filled with ones, from the shape of the tensor - pub fn ones_like(&self) -> Self { - Self::ones(self.shape()) - } -} - -impl TensorBase -where - T: Clone + Zero, -{ - /// Create a tensor, filled with zeros, from the given shape - pub fn zeros(shape: impl IntoShape) -> Self { - Self::fill(shape, T::zero()) - } - /// Create a tensor, filled with zeros, from the shape of another tensor - pub fn zeros_from(tensor: &TensorBase) -> Self { - Self::zeros(tensor.shape()) - } - /// Create a tensor, filled with zeros, from the shape of the tensor - pub fn zeros_like(&self) -> Self { - Self::zeros(self.shape()) - } -} diff --git a/tensor/src/impls/grad.rs b/tensor/src/impls/grad.rs deleted file mode 100644 index 4c23de37..00000000 --- a/tensor/src/impls/grad.rs +++ /dev/null @@ -1,185 +0,0 @@ -/* - Appellation: grad - Contrib: FL03 -*/ -use crate::actions::grad::TensorGrad; -use crate::prelude::{ScalarExt, TensorExpr, TensorId, TensorResult}; -use crate::TensorBase; -use acme::prelude::{Arithmetic, BinaryOp, Store, UnaryOp}; - -pub(crate) type Visited = std::collections::HashMap; - -macro_rules! entry { - ($ctx:expr, $entry:expr) => { - entry!($ctx, $entry, $entry.zeros_like()) - }; - ($ctx:expr, $entry:expr, $default:expr) => { - $ctx.entry($entry.id()).or_insert($default) - }; -} - -impl TensorBase -where - T: ScalarExt, -{ - /// toposort is a function which sorts the nodes of the op graph in topological order. - fn toposort(&self, reverse: bool) -> Vec<&TensorBase> { - // Here, the sorted nodes are passed as an owned value rather than as a mutable reference to workaround some lifetime limitations. - fn walk<'a, T>( - scope: &'a TensorBase, - nodes: Vec<&'a TensorBase>, - visited: &mut Visited, - ) -> (bool, Vec<&'a TensorBase>) { - if let Some(&tg) = visited.get(&scope.id()) { - return (tg, nodes); - } - // track the gradient of the current node - let mut track = false; - // recursively call on the children nodes - let mut nodes = if scope.is_variable() { - // Do not call recursively on the "leaf" nodes. - track = true; - nodes - } else if let Some(op) = scope.op().op() { - match op { - TensorExpr::Binary(lhs, rhs, _kind) => { - let (tg, nodes) = walk(lhs, nodes, visited); - track |= tg; - let (tg, nodes) = walk(rhs, nodes, visited); - track |= tg; - nodes - } - TensorExpr::Unary(a, _kind) => { - let (tg, nodes) = walk(a, nodes, visited); - track |= tg; - nodes - } - _ => nodes, - } - } else { - nodes - }; - visited.insert(scope.id(), track); - if track { - nodes.push(scope); - } - (track, nodes) - } - // walk through the dag - let (_tg, mut nodes) = walk(self, Vec::new(), &mut Visited::new()); - // reverse the nodes; if needed - if reverse { - nodes.reverse(); - } - // return the sorted nodes - nodes - } - /// Compute the gradient of the tensor - pub fn grad(&self) -> TensorResult> { - // get the sorted nodes - let sorted = self.toposort(true); - // initialize a new gradient store - let mut store = TensorGrad::new(); - // insert the gradient w.r.t. the current node - store.insert(self.id(), self.ones_like()); - - for node in sorted.iter() { - if node.is_variable() { - continue; - } - // get the gradient of the node - let grad = store.remove(&node.id()).expect("Gradient not found"); - // detach the gradient - let grad = grad.detach(); - // handle the different types of operations - if let Some(op) = &*node.op { - match op { - TensorExpr::Binary(lhs, rhs, kind) => match kind { - BinaryOp::Arith(inner) => match inner { - Arithmetic::Add(_) => { - *entry!(store, lhs) += &grad; - *entry!(store, rhs) += &grad; - } - Arithmetic::Div(_) => { - *entry!(store, lhs) += &grad / rhs.as_ref(); - *entry!(store, rhs) -= &grad * lhs.as_ref() / rhs.sqr(); - } - Arithmetic::Mul(_) => { - *entry!(store, lhs) += &grad * rhs.as_ref(); - *entry!(store, rhs) += &grad * lhs.as_ref(); - } - Arithmetic::Sub(_) => { - *entry!(store, lhs) += &grad; - *entry!(store, rhs) -= &grad; - } - _ => todo!(), - }, - _ => todo!(), - }, - TensorExpr::BinaryScalar(lhs, rhs, kind) => match kind { - BinaryOp::Arith(inner) => match inner { - Arithmetic::Add(_) => { - *entry!(store, lhs) += &grad; - } - Arithmetic::Div(_) => { - *entry!(store, lhs) += &grad / *rhs; - } - Arithmetic::Mul(_) => { - *entry!(store, lhs) += &grad * *rhs; - } - Arithmetic::Pow(_) => { - *entry!(store, lhs) += &grad * *rhs * lhs.pow(*rhs - T::one()); - } - Arithmetic::Sub(_) => { - *entry!(store, lhs) += &grad; - } - _ => todo!(), - }, - _ => todo!(), - }, - TensorExpr::Unary(val, kind) => match kind { - UnaryOp::Cos => { - *entry!(store, val) -= &grad * val.sin(); - } - UnaryOp::Cosh => { - *entry!(store, val) += &grad * val.sinh(); - } - UnaryOp::Exp => { - *entry!(store, val) += &grad * val.exp(); - } - UnaryOp::Neg => { - *entry!(store, val) -= &grad; - } - UnaryOp::Recip => { - *entry!(store, val) -= &grad / val.sqr(); - } - - UnaryOp::Sin => { - *entry!(store, val) += &grad * val.cos(); - } - UnaryOp::Sinh => { - *entry!(store, val) += &grad * val.cosh(); - } - UnaryOp::Sqrt => { - *entry!(store, val) += - &grad / (val.clone().sqrt() * T::from(2).unwrap()); - } - UnaryOp::Tan => { - *entry!(store, val) += &grad / val.clone().cos().sqr(); - } - - _ => {} - }, - TensorExpr::Sigmoid(val) => { - let tmp = val.detach(); - *entry!(store, val) += - &grad * tmp.sigmoid() * (tmp.ones_like() - tmp.sigmoid()); - } - _ => {} - } - } - } - - Ok(store) - } -} diff --git a/tensor/src/impls/iter.rs b/tensor/src/impls/iter.rs deleted file mode 100644 index 678feef3..00000000 --- a/tensor/src/impls/iter.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Appellation: iter - Contrib: FL03 -*/ -use crate::shape::Axis; -use crate::tensor::TensorBase; -use core::iter::{Product, Sum}; - -impl TensorBase -where - T: Copy, -{ - /// Compute the product of all elements in the tensor - pub fn product(&self) -> T - where - T: Product, - { - self.data().iter().copied().product() - } - #[doc(hidden)] - pub fn product_axis(&self, _axis: Axis) -> T { - unimplemented!("product_axis") - } - /// Compute the sum of all elements in the tensor - pub fn sum(&self) -> T - where - T: Sum, - { - self.data().iter().copied().sum() - } - #[doc(hidden)] - /// Compute the sum of all elements along the given axis - pub fn sum_axis(&self, _axis: Axis) -> T { - unimplemented!("sum_axis") - } -} - -impl IntoIterator for TensorBase { - type Item = T; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.data.into_iter() - } -} - -impl FromIterator for TensorBase { - fn from_iter>(iter: I) -> Self { - Self::from_vec(Vec::from_iter(iter)) - } -} diff --git a/tensor/src/impls/linalg.rs b/tensor/src/impls/linalg.rs deleted file mode 100644 index 66361552..00000000 --- a/tensor/src/impls/linalg.rs +++ /dev/null @@ -1,139 +0,0 @@ -/* - Appellation: linalg - Contrib: FL03 -*/ -//! Implementations for linear algebra operations. -//! -//! -use crate::linalg::{Inverse, Matmul}; -use crate::prelude::{Scalar, ShapeError, TensorError, TensorExpr, TensorResult}; -use crate::tensor::{self, TensorBase}; -use acme::prelude::{nested, UnaryOp}; -use num::traits::{Num, NumAssign}; - -fn inverse_impl(tensor: &TensorBase) -> TensorResult> -where - T: Copy + NumAssign + PartialOrd, -{ - let op = TensorExpr::unary(tensor.clone(), UnaryOp::Inv); - let n = tensor.nrows(); - - if !tensor.is_square() { - return Err(ShapeError::NotSquare.into()); // Matrix must be square for inversion - } - - let eye = TensorBase::eye(n); - - // Construct an augmented matrix by concatenating the original matrix with an identity matrix - let mut aug = TensorBase::zeros((n, 2 * n)); - // aug.slice_mut(s![.., ..cols]).assign(matrix); - for i in 0..n { - for j in 0..n { - aug[[i, j]] = tensor[[i, j]]; - } - for j in n..(2 * n) { - aug[[i, j]] = eye[[i, j - n]]; - } - } - - // Perform Gaussian elimination to reduce the left half to the identity matrix - for i in 0..n { - let pivot = aug[[i, i]]; - - if pivot == T::zero() { - return Err(TensorError::Singular); // Matrix is singular - } - - for j in 0..(2 * n) { - aug[[i, j]] /= pivot; - } - - for j in 0..n { - if i != j { - let am = aug.clone(); - let factor = aug[[j, i]]; - for k in 0..(2 * n) { - aug[[j, k]] -= factor * am[[i, k]]; - } - } - } - } - - // Extract the inverted matrix from the augmented matrix - let mut inv = tensor.zeros_like().with_op(op.into()); - for i in 0..n { - for j in 0..n { - inv[[i, j]] = aug[[i, j + n]]; - } - } - - Ok(inv.to_owned()) -} - -impl TensorBase -where - T: Copy, -{ - /// Creates a new tensor containing only the diagonal elements of the original tensor. - pub fn diag(&self) -> Self { - let n = self.nrows(); - Self::from_shape_iter(self.shape().diag(), (0..n).map(|i| self[vec![i; n]])) - } - /// Find the inverse of the tensor - /// - /// # Errors - /// - /// Returns an error if the matrix is not square or if the matrix is singular. - /// - pub fn inv(&self) -> TensorResult - where - T: NumAssign + PartialOrd, - { - inverse_impl(self) - } - /// Compute the trace of the matrix. - /// The trace of a matrix is the sum of the diagonal elements. - pub fn trace(&self) -> TensorResult - where - T: Num, - { - if !self.is_square() { - return Err(ShapeError::NotSquare.into()); - } - let n = self.nrows(); - let trace = (0..n).fold(T::zero(), |acc, i| acc + self[[i, i]]); - Ok(trace) - } -} - -impl Inverse for TensorBase -where - T: Copy + Num + NumAssign + PartialOrd, -{ - type Output = TensorResult; - - fn inv(&self) -> Self::Output { - inverse_impl(self) - } -} - -impl Matmul> for TensorBase -where - T: Scalar, -{ - type Output = Self; - - fn matmul(&self, other: &Self) -> Self { - let sc = |m: usize, n: usize| m * self.ncols() + n; - let oc = |m: usize, n: usize| m * other.ncols() + n; - - let shape = self.shape().matmul_shape(other.shape()).unwrap(); - let mut result = vec![T::zero(); shape.size()]; - - nested!(i in 0..self.nrows() => j in 0..other.ncols() => k in 0..self.ncols() => { - result[oc(i, j)] += self.data[sc(i, k)] * other.data[oc(k, j)] - }); - let op = TensorExpr::matmul(self.clone(), other.clone()); - tensor::from_vec_with_op(false, op, shape, result) - } -} diff --git a/tensor/src/impls/num.rs b/tensor/src/impls/num.rs deleted file mode 100644 index c38ab5c2..00000000 --- a/tensor/src/impls/num.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Appellation: num - Contrib: FL03 -*/ -use crate::prelude::Scalar; -use crate::tensor::TensorBase; -use num::traits::{Num, One, Zero}; - -impl Num for TensorBase -where - T: Scalar + Num, -{ - type FromStrRadixErr = T::FromStrRadixErr; - - fn from_str_radix(str: &str, radix: u32) -> Result { - T::from_str_radix(str, radix).map(Self::from_scalar) - } -} - -impl One for TensorBase -where - T: Copy + One, -{ - fn one() -> Self { - Self::from_scalar(T::one()) - } -} - -impl Zero for TensorBase -where - T: Copy + Zero, -{ - fn zero() -> Self { - Self::from_scalar(T::zero()) - } - - fn is_zero(&self) -> bool { - self.data().iter().all(|x| x.is_zero()) - } -} diff --git a/tensor/src/impls/ops/binary.rs b/tensor/src/impls/ops/binary.rs deleted file mode 100644 index 59ca8a9c..00000000 --- a/tensor/src/impls/ops/binary.rs +++ /dev/null @@ -1,356 +0,0 @@ -/* - Appellation: arith - Contrib: FL03 -*/ -use crate::prelude::{Scalar, TensorExpr}; -use crate::tensor::{from_vec_with_op, TensorBase}; -use acme::ops::binary::BinaryOp; -use core::ops; -use num::traits::float::{Float, FloatCore}; -use num::traits::Pow; - -#[allow(dead_code)] -pub(crate) fn broadcast_scalar_op( - lhs: &TensorBase, - rhs: &TensorBase, - op: BinaryOp, - f: F, -) -> TensorBase -where - F: Fn(T, T) -> T, - T: Copy + Default, -{ - let mut lhs = lhs.clone(); - let mut rhs = rhs.clone(); - if lhs.is_scalar() { - lhs = lhs.broadcast(rhs.shape()); - } - if rhs.is_scalar() { - rhs = rhs.broadcast(lhs.shape()); - } - let shape = lhs.shape().clone(); - let store = lhs - .data() - .iter() - .zip(rhs.data().iter()) - .map(|(a, b)| f(*a, *b)) - .collect(); - let op = TensorExpr::binary(lhs, rhs, op); - from_vec_with_op(false, op, shape, store) -} - -fn check_shapes_or_scalar(lhs: &TensorBase, rhs: &TensorBase) -where - T: Clone + Default, -{ - let is_scalar = lhs.is_scalar() || rhs.is_scalar(); - debug_assert!( - is_scalar || lhs.shape() == rhs.shape(), - "Shape Mismatch: {:?} != {:?}", - lhs.shape(), - rhs.shape() - ); -} - -macro_rules! check { - (ne: $lhs:expr, $rhs:expr) => { - if $lhs != $rhs { - panic!("Shape Mismatch: {:?} != {:?}", $lhs, $rhs); - } - }; -} - -impl TensorBase -where - T: Scalar, -{ - pub fn apply_binary(&self, other: &Self, op: BinaryOp) -> Self { - check_shapes_or_scalar(self, other); - let shape = self.shape(); - let store = self - .data() - .iter() - .zip(other.data().iter()) - .map(|(a, b)| *a + *b) - .collect(); - let op = TensorExpr::binary(self.clone(), other.clone(), op); - from_vec_with_op(false, op, shape, store) - } - - pub fn apply_binaryf(&self, other: &Self, op: BinaryOp, f: F) -> Self - where - F: Fn(T, T) -> T, - { - check_shapes_or_scalar(self, other); - let shape = self.shape(); - let store = self - .data() - .iter() - .zip(other.data().iter()) - .map(|(a, b)| f(*a, *b)) - .collect(); - let op = TensorExpr::binary(self.clone(), other.clone(), op); - from_vec_with_op(false, op, shape, store) - } -} - -impl TensorBase { - pub fn pow(&self, exp: T) -> Self - where - T: Copy + Pow, - { - let shape = self.shape(); - let store = self.data().iter().copied().map(|a| a.pow(exp)).collect(); - let op = TensorExpr::binary_scalar(self.clone(), exp, BinaryOp::pow()); - from_vec_with_op(false, op, shape, store) - } - - pub fn powf(&self, exp: T) -> Self - where - T: Float, - { - let shape = self.shape(); - let store = self.data().iter().copied().map(|a| a.powf(exp)).collect(); - let op = TensorExpr::binary_scalar(self.clone(), exp, BinaryOp::pow()); - from_vec_with_op(false, op, shape, store) - } - - pub fn powi(&self, exp: i32) -> Self - where - T: FloatCore, - { - let shape = self.shape(); - let store = self.data().iter().copied().map(|a| a.powi(exp)).collect(); - let op = TensorExpr::binary_scalar(self.clone(), T::from(exp).unwrap(), BinaryOp::pow()); - from_vec_with_op(false, op, shape, store) - } -} - -// impl TensorBase where T: ComplexFloat + Scalar, Real = T> { - -// pub fn powc(&self, exp: ::Complex) -> TensorBase<::Complex> { -// let shape = self.shape(); -// let store = self.data().iter().copied().map(|a| Scalar::powc(a, exp)).collect(); -// let op = TensorExpr::binary_scalar_c(self.clone(), exp, BinaryOp::Pow); -// TensorBase { -// id: TensorId::new(), -// data: store, -// kind: TensorKind::default(), -// layout: Layout::contiguous(shape), -// op: BackpropOp::new(op) -// } -// } -// } - -impl Pow for TensorBase -where - T: Copy + Pow, -{ - type Output = Self; - - fn pow(self, exp: T) -> Self::Output { - let shape = self.shape().clone(); - let store = self.data().iter().map(|a| a.pow(exp)).collect(); - let op = TensorExpr::binary_scalar(self, exp, BinaryOp::pow()); - from_vec_with_op(false, op, shape, store) - } -} - -impl<'a, T> Pow for &'a TensorBase -where - T: Copy + Pow, -{ - type Output = TensorBase; - - fn pow(self, exp: T) -> Self::Output { - let shape = self.shape().clone(); - let store = self.data().iter().map(|a| a.pow(exp)).collect(); - let op = TensorExpr::binary_scalar(self.clone(), exp, BinaryOp::pow()); - from_vec_with_op(false, op, shape, store) - } -} - -macro_rules! impl_binary_op { - ($(($trait:ident, $method:ident, $op:tt)),*) => { - $( impl_binary_op!($trait, $method, $op); )* - }; - ($trait:ident, $method:ident, $op:tt) => { - impl_binary_op!(scalar: $trait, $method, $op); - impl_binary_op!(tensor: $trait, $method, $op); - }; - (scalar: $trait:ident, $method:ident, $op:tt) => { - - impl ops::$trait for TensorBase - where - T: Copy + ops::$trait, - { - type Output = Self; - - fn $method(self, other: T) -> Self::Output { - let shape = self.shape().clone(); - let store = self.data().iter().map(|a| *a $op other).collect(); - let op = TensorExpr::binary_scalar(self, other, BinaryOp::$method()); - from_vec_with_op(false, op, shape, store) - } - } - - impl<'a, T> ops::$trait for &'a TensorBase - where - T: Copy + ops::$trait, - { - type Output = TensorBase; - - fn $method(self, other: T) -> Self::Output { - let shape = self.shape().clone(); - let store = self.data().iter().map(|a| *a $op other).collect(); - let op = TensorExpr::binary_scalar(self.clone(), other, BinaryOp::$method()); - from_vec_with_op(false, op, shape, store) - } - } - }; - (tensor: $trait:ident, $method:ident, $op:tt) => { - impl ops::$trait for TensorBase - where - T: Copy + ops::$trait, - { - type Output = Self; - - fn $method(self, other: Self) -> Self::Output { - check!(ne: self.shape(), other.shape()); - let shape = self.shape().clone(); - let store = self.data().iter().zip(other.data().iter()).map(|(a, b)| *a $op *b).collect(); - let op = TensorExpr::binary(self, other, BinaryOp::$method()); - from_vec_with_op(false, op, shape, store) - } - } - - impl<'a, T> ops::$trait<&'a TensorBase> for TensorBase - where - T: Copy + ops::$trait, - { - type Output = TensorBase; - - fn $method(self, other: &'a TensorBase) -> Self::Output { - if self.shape() != other.shape() { - panic!("shapes must be equal"); - } - let shape = self.shape().clone(); - let store = self.data().iter().zip(other.data().iter()).map(|(a, b)| *a $op *b).collect(); - let op = TensorExpr::binary(self, other.clone(), BinaryOp::$method()); - from_vec_with_op(false, op, shape, store) - } - } - - impl<'a, T> ops::$trait> for &'a TensorBase - where - T: Copy + ops::$trait, - { - type Output = TensorBase; - - fn $method(self, other: TensorBase) -> Self::Output { - if self.shape() != other.shape() { - panic!("shapes must be equal"); - } - let shape = self.shape().clone(); - let store = self.data().iter().zip(other.data().iter()).map(|(a, b)| *a $op *b).collect(); - let op = TensorExpr::binary(self.clone(), other, BinaryOp::$method()); - from_vec_with_op(false, op, shape, store) - } - } - - impl<'a, 'b, T> ops::$trait<&'b TensorBase> for &'a TensorBase - where - T: Copy + ops::$trait, - { - type Output = TensorBase; - - fn $method(self, other: &'b TensorBase) -> Self::Output { - if self.shape() != other.shape() { - panic!("shapes must be equal"); - } - let shape = self.shape().clone(); - let store = self.data().iter().zip(other.data().iter()).map(|(a, b)| *a $op *b).collect(); - let op = TensorExpr::binary(self.clone(), other.clone(), BinaryOp::$method()); - from_vec_with_op(false, op, shape, store) - } - } - }; - -} - -macro_rules! impl_assign_op { - ($trait:ident, $method:ident, $constructor:ident, $inner:ident, $op:tt) => { - impl core::ops::$trait for TensorBase - where - T: Copy + core::ops::$inner, - { - fn $method(&mut self, other: Self) { - check!(ne: self.shape(), other.shape()); - let shape = self.shape().clone(); - let store = self.data().iter().zip(other.data().iter()).map(|(a, b)| *a $op *b).collect(); - let op = TensorExpr::binary(self.clone(), other, BinaryOp::$constructor()); - - *self = from_vec_with_op(false, op, shape, store); - } - } - - impl<'a, T> core::ops::$trait<&'a TensorBase> for TensorBase - where - T: Copy + core::ops::$inner, - { - fn $method(&mut self, other: &'a TensorBase) { - check!(ne: self.shape(), other.shape()); - let shape = self.shape().clone(); - let store = self.data().iter().zip(other.data().iter()).map(|(a, b)| *a $op *b).collect(); - let op = TensorExpr::binary(self.clone(), other.clone(), BinaryOp::$constructor()); - - *self = from_vec_with_op(false, op, shape, store); - } - } - }; - -} - -macro_rules! impl_binary_method { - ($method:ident, $f:expr) => { - pub fn $method(&self, other: &Self) -> Self { - $f(self, other) - } - - }; - (scalar: $variant:tt, $method:ident, $op:tt) => { - pub fn $method(&self, other: T) -> Self { - let shape = self.shape(); - let store = self.data().iter().map(| elem | *elem $op other).collect(); - let op = TensorExpr::binary_scalar(self.clone(), other, BinaryOp::$variant()); - from_vec_with_op(false, op, shape, store) - } - - }; - (tensor: $method:ident, $op:tt) => { - pub fn $method(&self, other: &Self) -> Self { - check!(ne: self.shape(), other.shape()); - let shape = self.shape(); - let store = self.data().iter().zip(other.data().iter()).map(|(a, b)| *a $op *b).collect(); - let op = TensorExpr::binary(self.clone(), other.clone(), BinaryOp::$method()); - from_vec_with_op(false, op, shape, store) - } - - }; -} - -impl_binary_op!((Add, add, +), (Div, div, /), (Mul, mul, *), (Rem, rem, %), (Sub, sub, -)); - -impl_assign_op!(AddAssign, add_assign, add, Add, +); -impl_assign_op!(DivAssign, div_assign, div, Div, /); -impl_assign_op!(MulAssign, mul_assign, mul, Mul, *); -impl_assign_op!(RemAssign, rem_assign, rem, Rem, %); -impl_assign_op!(SubAssign, sub_assign, sub, Sub, -); - -impl TensorBase -where - T: Scalar, -{ - impl_binary_method!(tensor: add, +); - impl_binary_method!(scalar: add, add_scalar, +); -} diff --git a/tensor/src/impls/ops/unary.rs b/tensor/src/impls/ops/unary.rs deleted file mode 100644 index 82e8ca63..00000000 --- a/tensor/src/impls/ops/unary.rs +++ /dev/null @@ -1,120 +0,0 @@ -/* - Appellation: arith - Contrib: FL03 -*/ -use crate::prelude::{Scalar, ScalarExt, TensorExpr}; -use crate::tensor::*; -use acme::ops::unary::UnaryOp; -use core::ops; - -impl ops::Neg for TensorBase -where - T: Copy + ops::Neg, -{ - type Output = Self; - - fn neg(self) -> Self::Output { - let shape = self.shape().clone(); - let store = self.data().iter().map(|a| (*a).neg()).collect(); - let op = TensorExpr::unary(self, UnaryOp::Neg); - from_vec_with_op(false, op, shape, store) - } -} - -impl<'a, T> ops::Neg for &'a TensorBase -where - T: Copy + ops::Neg, -{ - type Output = TensorBase; - - fn neg(self) -> Self::Output { - let shape = self.shape(); - let store = self.data().iter().map(|a| (*a).neg()).collect(); - let op = TensorExpr::unary(self.clone(), UnaryOp::Neg); - from_vec_with_op(false, op, shape, store) - } -} - -impl ops::Not for TensorBase -where - T: Copy + ops::Not, -{ - type Output = Self; - - fn not(self) -> Self::Output { - let shape = self.shape().clone(); - let store = self.data().iter().map(|a| (*a).not()).collect(); - let op = TensorExpr::unary(self, UnaryOp::Not); - from_vec_with_op(false, op, shape, store) - } -} - -impl<'a, T> ops::Not for &'a TensorBase -where - T: Copy + ops::Not, -{ - type Output = TensorBase; - - fn not(self) -> Self::Output { - let shape = self.shape(); - let store = self.data.iter().copied().map(|a| !a).collect(); - let op = TensorExpr::unary(self.clone(), UnaryOp::Not); - from_vec_with_op(false, op, shape, store) - } -} - -macro_rules! impl_unary_op { - ($variant:ident, $method:ident) => { - pub fn $method(&self) -> Self { - let shape = self.shape(); - let store = self.data().iter().copied().map(|v| v.$method()).collect(); - let op = TensorExpr::unary(self.clone(), UnaryOp::$variant); - from_vec_with_op(false, op, shape, store) - } - }; - (custom $variant:ident, $method:ident, $f:expr) => { - pub fn $method(self) -> Self { - let shape = self.shape().clone(); - let store = self.store.iter().copied().map($f).collect(); - let op = TensorExpr::unary(self, UnaryOp::$variant); - from_vec_with_op(false, op, shape, store) - } - }; -} - -impl TensorBase -where - T: Scalar, -{ - pub fn abs(&self) -> TensorBase<::Real> - where - T: Scalar, - { - let shape = self.shape(); - let store = self.data.iter().copied().map(Scalar::abs).collect(); - let op = TensorExpr::unary(self.clone(), UnaryOp::Abs); - from_vec_with_op(false, op, shape, store) - } - - pub fn sigmoid(&self) -> TensorBase - where - T: ScalarExt, - { - let op = TensorExpr::sigmoid(self.clone()); - let shape = self.shape(); - let store = self.iter().copied().map(ScalarExt::sigmoid).collect(); - from_vec_with_op(false, op, shape, store) - } - - impl_unary_op!(Cos, cos); - impl_unary_op!(Cosh, cosh); - impl_unary_op!(Exp, exp); - impl_unary_op!(Ln, ln); - impl_unary_op!(Recip, recip); - impl_unary_op!(Sin, sin); - impl_unary_op!(Sinh, sinh); - impl_unary_op!(Square, sqr); - impl_unary_op!(Sqrt, sqrt); - impl_unary_op!(Tan, tan); - impl_unary_op!(Tanh, tanh); -} diff --git a/tensor/src/impls/reshape.rs b/tensor/src/impls/reshape.rs deleted file mode 100644 index f90f0862..00000000 --- a/tensor/src/impls/reshape.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* - Appellation: reshape - Contrib: FL03 -*/ -use crate::prelude::{TensorExpr, TensorId, TensorResult}; -use crate::shape::{Axis, IntoShape, ShapeError}; -use crate::tensor::TensorBase; - -impl TensorBase -where - T: Clone, -{ - /// coerce the tensor to act like a larger shape. - /// This method doesn't change the underlying data, but it does change the layout. - pub fn broadcast(&self, shape: impl IntoShape) -> Self { - let layout = self.layout().broadcast_as(shape).unwrap(); - let op = TensorExpr::broadcast(self.clone(), layout.shape().clone()); - Self { - id: TensorId::new(), - kind: self.kind(), - layout, - op: op.into(), - data: self.data().clone(), - } - } - #[doc(hidden)] - pub fn pad(&self, shape: impl IntoShape, _with: T) -> Self { - let shape = shape.into_shape(); - - let _diff = *self.shape().rank() - *shape.rank(); - - todo!() - } - /// Swap two axes in the tensor. - pub fn swap_axes(&self, swap: Axis, with: Axis) -> Self { - let op = TensorExpr::swap_axes(self.clone(), swap, with); - - let layout = self.layout().clone().swap_axes(swap, with); - let shape = self.layout.shape(); - let mut data = self.data.to_vec(); - - for i in 0..shape[swap] { - for j in 0..shape[with] { - let scope = self.layout.index([i, j]); - let target = layout.index([j, i]); - data[target] = self.data()[scope].clone(); - } - } - - TensorBase { - id: TensorId::new(), - kind: self.kind, - layout, - op: op.into(), - data: data.clone(), - } - } - /// Transpose the tensor. - pub fn t(&self) -> Self { - let op = TensorExpr::transpose(self.clone()); - - let layout = self.layout().clone().reverse_axes(); - TensorBase { - id: TensorId::new(), - kind: self.kind(), - layout, - op: op.into(), - data: self.data().clone(), - } - } - /// Reshape the tensor - /// returns an error if the new shape specifies a different number of elements. - pub fn reshape(self, shape: impl IntoShape) -> TensorResult { - let shape = shape.into_shape(); - if self.size() != shape.size() { - return Err(ShapeError::MismatchedElements.into()); - } - - let mut tensor = self; - - tensor.layout.reshape(shape); - - Ok(tensor) - } -} diff --git a/tensor/src/lib.rs b/tensor/src/lib.rs deleted file mode 100644 index d5fa8b38..00000000 --- a/tensor/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* - Appellation: acme-tensor - Contrib: FL03 -*/ -//! # Tensor -//! -//! This library implements a tensor data structure with support for automatic differentiation. -//! -#[cfg(not(feature = "std"))] -extern crate alloc; - -extern crate acme_core as acme; - -#[doc(inline)] -pub use self::{actions::*, tensor::*, utils::*}; - -#[allow(unused)] -#[macro_use] -pub(crate) mod seal; -pub(crate) mod tensor; -#[macro_use] -pub(crate) mod utils; - -#[doc(hidden)] -pub mod backend; -pub mod error; -#[doc(hidden)] -pub mod fmt; -#[cfg(feature = "io")] -pub mod io; -pub mod linalg; -pub mod ops; -pub mod shape; -pub mod specs; -pub mod stats; -pub mod types; - -pub(crate) mod actions { - - pub mod create; - pub mod grad; - pub mod index; - pub mod iter; -} - -mod impls { - mod ops { - mod binary; - mod unary; - } - mod create; - mod grad; - mod iter; - mod linalg; - mod num; - mod reshape; -} - -pub type Tensor = tensor::TensorBase; - -pub mod prelude { - pub use crate::actions::{create::*, grad::*, index::*, iter::*}; - pub use crate::error::*; - pub use crate::linalg::prelude::*; - pub use crate::ops::*; - pub use crate::shape::prelude::*; - pub use crate::specs::prelude::*; - pub use crate::stats::prelude::*; - pub use crate::types::prelude::*; - pub use crate::utils::*; - pub use crate::Tensor; - pub(crate) use acme::prelude::Scalar; -} diff --git a/tensor/src/linalg/mod.rs b/tensor/src/linalg/mod.rs deleted file mode 100644 index e29f38f0..00000000 --- a/tensor/src/linalg/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -/* - Appellation: linalg - Contrib: FL03 -*/ -//! # Linear Algebra -//! -//! -pub use self::{specs::*, uplo::UPLO}; - -pub(crate) mod specs; -pub(crate) mod uplo; - -pub mod solve; -pub mod tri; - -pub(crate) mod prelude { - pub use super::specs::{Inverse, Matmul}; - pub use super::uplo::UPLO; -} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/linalg/solve/mod.rs b/tensor/src/linalg/solve/mod.rs deleted file mode 100644 index 8e6d7a59..00000000 --- a/tensor/src/linalg/solve/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* - Appellation: solve - Contrib: FL03 -*/ -//! # Solvers -//! -//! diff --git a/tensor/src/linalg/specs.rs b/tensor/src/linalg/specs.rs deleted file mode 100644 index c7ec2cbd..00000000 --- a/tensor/src/linalg/specs.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 -*/ -use core::iter::Sum; -use core::ops::{Add, Mul}; - -/// [Affine] describes a type of geometric transformation which preserves -/// lines and parallelisms. -/// -/// ### General Formula -/// f(x) = A * x + b -pub trait Affine { - type Output; - - fn affine(&self, mul: A, add: B) -> Self::Output; -} - -impl Affine for S -where - S: Clone + Mul, - C: Add, -{ - type Output = C; - - fn affine(&self, mul: A, add: B) -> Self::Output { - self.clone() * mul + add - } -} - -/// Inversion -/// -/// The inverse of a matrix is a matrix that, when multiplied with the original matrix, gives the -/// identity matrix. -pub trait Inverse { - type Output; - - fn inv(&self) -> Self::Output; -} - -impl Inverse for S -where - S: Clone + num::traits::Inv, -{ - type Output = T; - - fn inv(&self) -> Self::Output { - self.clone().inv() - } -} - -/// Matrix multiplication -pub trait Matmul { - type Output; - - fn matmul(&self, rhs: &Rhs) -> Self::Output; -} - -impl Matmul> for Vec -where - A: Clone + Mul, - B: Clone, - C: Sum, -{ - type Output = C; - - fn matmul(&self, rhs: &Vec) -> Self::Output { - self.iter() - .cloned() - .zip(rhs.iter().cloned()) - .map(|(a, b)| a * b) - .sum() - } -} - -// impl_matmul!(Vec, Vec, Vec); diff --git a/tensor/src/linalg/tri/mod.rs b/tensor/src/linalg/tri/mod.rs deleted file mode 100644 index ccfe535e..00000000 --- a/tensor/src/linalg/tri/mod.rs +++ /dev/null @@ -1,50 +0,0 @@ -/* - Appellation: tri - Contrib: FL03 -*/ -pub use self::utils::*; - -pub(crate) mod utils { - use crate::linalg::UPLO; - use crate::tensor::TensorBase; - use num::Zero; - - pub fn triangular(a: &TensorBase, uplo: UPLO) -> TensorBase - where - T: Clone + Zero, - { - debug_assert!(a.is_square(), "Tensor must be square."); - match uplo { - UPLO::Upper => triu(a), - UPLO::Lower => tril(a), - } - } - - /// Returns the lower triangular portion of a matrix. - pub fn tril(a: &TensorBase) -> TensorBase - where - T: Clone + Zero, - { - debug_assert!(a.is_square(), "Matrix must be square."); - let mut out = a.clone(); - for i in 0..a.shape()[0] { - for j in i + 1..a.shape()[1] { - out[[i, j]] = T::zero(); - } - } - out - } - /// Returns the upper triangular portion of a matrix. - pub fn triu(a: &TensorBase) -> TensorBase - where - T: Clone + Zero, - { - let mut out = a.clone(); - for i in 0..a.shape()[0] { - for j in 0..i { - out[[i, j]] = T::zero(); - } - } - out - } -} diff --git a/tensor/src/linalg/uplo.rs b/tensor/src/linalg/uplo.rs deleted file mode 100644 index 3aec3fbf..00000000 --- a/tensor/src/linalg/uplo.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Appellation: kinds - Contrib: FL03 -*/ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize,), - serde(rename_all = "lowercase", untagged) -)] -#[derive( - Clone, - Copy, - Debug, - Default, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - VariantNames, -)] -#[repr(usize)] -#[strum(serialize_all = "lowercase")] -pub enum UPLO { - Lower, - #[default] - Upper, -} - -impl UPLO { - pub fn lower() -> Self { - Self::Lower - } - - pub fn upper() -> Self { - Self::Upper - } -} - -unsafe impl Send for UPLO {} - -unsafe impl Sync for UPLO {} diff --git a/tensor/src/ops/backprop.rs b/tensor/src/ops/backprop.rs deleted file mode 100644 index 1388c053..00000000 --- a/tensor/src/ops/backprop.rs +++ /dev/null @@ -1,105 +0,0 @@ -/* - Appellation: backprop - Contrib: FL03 -*/ -use super::TensorExpr; -use crate::TensorBase; -use acme::prelude::BinaryOp; -use core::borrow::Borrow; -use core::ops::{Deref, DerefMut}; - -pub trait TensorOp { - type Output; - - fn name(&self) -> &str; -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct BackpropOp(Option>); - -impl BackpropOp { - pub fn new(op: TensorExpr) -> Self { - BackpropOp(Some(op)) - } - - pub fn none() -> Self { - BackpropOp(None) - } - - pub fn binary(lhs: TensorBase, rhs: TensorBase, kind: BinaryOp) -> Self { - BackpropOp(Some(TensorExpr::binary(lhs, rhs, kind))) - } - - pub fn is_none(&self) -> bool { - self.0.is_none() - } - - pub fn op(&self) -> Option<&TensorExpr> { - self.0.as_ref() - } - - pub fn op_mut(&mut self) -> Option<&mut TensorExpr> { - self.0.as_mut() - } - - pub fn into_inner(self) -> Option> { - self.0 - } - - pub fn take(&mut self) -> Option> { - self.0.take() - } - - pub fn view(&self) -> BackpropOp<&A, &B> { - BackpropOp(self.0.as_ref().map(|op| op.view())) - } - - pub fn view_mut(&mut self) -> BackpropOp<&mut A, &mut B> { - BackpropOp(self.0.as_mut().map(|op| op.view_mut())) - } -} - -impl Borrow>> for BackpropOp { - fn borrow(&self) -> &Option> { - &self.0 - } -} - -impl Default for BackpropOp { - fn default() -> Self { - Self::none() - } -} - -impl Deref for BackpropOp { - type Target = Option>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for BackpropOp { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl From>> for BackpropOp { - fn from(op: Option>) -> Self { - BackpropOp(op) - } -} - -impl From> for BackpropOp { - fn from(op: TensorExpr) -> Self { - BackpropOp(Some(op)) - } -} - -impl From> for Option> { - fn from(op: BackpropOp) -> Option> { - op.into_inner() - } -} diff --git a/tensor/src/ops/kinds/reshape.rs b/tensor/src/ops/kinds/reshape.rs deleted file mode 100644 index 8a243129..00000000 --- a/tensor/src/ops/kinds/reshape.rs +++ /dev/null @@ -1,137 +0,0 @@ -/* - Appellation: reshape - Contrib: FL03 -*/ -use crate::ops::{BoxTensor, TensorExpr}; -use crate::shape::{Axis, Shape}; -use crate::tensor::TensorBase; -use strum::{Display, EnumCount, EnumDiscriminants, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive(Clone, Debug, EnumDiscriminants, Eq, Hash, PartialEq)] -#[repr(C)] -#[strum_discriminants(derive( - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Hash, - Ord, - PartialOrd, - VariantNames -))] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(rename_all = "snake_case"), - strum(serialize_all = "snake_case"), - strum_discriminants(derive(serde::Deserialize, serde::Serialize)) -)] -#[strum_discriminants(name(ReshapeOp))] -pub enum ReshapeExpr { - Broadcast { - recv: BoxTensor, - shape: Shape, - }, - Reshape { - recv: BoxTensor, - shape: Shape, - }, - Swap { - recv: BoxTensor, - a: usize, - b: usize, - }, - SwapAxis { - recv: BoxTensor, - a: Axis, - b: Axis, - }, - Transpose { - recv: BoxTensor, - }, -} - -impl ReshapeExpr { - pub fn broadcast(recv: TensorBase, shape: Shape) -> Self { - Self::Broadcast { - recv: recv.boxed(), - shape, - } - } - - pub fn reshape(recv: TensorBase, shape: Shape) -> Self { - Self::Reshape { - recv: recv.boxed(), - shape, - } - } - - pub fn swap(recv: TensorBase, a: usize, b: usize) -> Self { - Self::Swap { - recv: recv.boxed(), - a, - b, - } - } - - pub fn swap_axes(recv: TensorBase, a: Axis, b: Axis) -> Self { - Self::SwapAxis { - recv: recv.boxed(), - a, - b, - } - } - - pub fn transpose(recv: TensorBase) -> Self { - Self::Transpose { recv: recv.boxed() } - } - - pub fn recv(&self) -> &BoxTensor { - match self { - Self::Broadcast { recv, .. } => recv, - Self::Reshape { recv, .. } => recv, - Self::Swap { recv, .. } => recv, - Self::SwapAxis { recv, .. } => recv, - Self::Transpose { recv } => recv, - } - } - - pub fn recv_mut(&mut self) -> &mut BoxTensor { - match self { - Self::Broadcast { recv, .. } => recv, - Self::Reshape { recv, .. } => recv, - Self::Swap { recv, .. } => recv, - Self::SwapAxis { recv, .. } => recv, - Self::Transpose { recv } => recv, - } - } - - pub fn view(&self) -> ReshapeExpr<&T> { - match self { - Self::Broadcast { recv, shape } => ReshapeExpr::broadcast(recv.view(), shape.clone()), - Self::Reshape { recv, shape } => ReshapeExpr::reshape(recv.view(), shape.clone()), - Self::Swap { recv, a, b } => ReshapeExpr::swap(recv.view(), *a, *b), - Self::SwapAxis { recv, a, b } => ReshapeExpr::swap_axes(recv.view(), *a, *b), - Self::Transpose { recv } => ReshapeExpr::transpose(recv.view()), - } - } - - pub fn view_mut(&mut self) -> ReshapeExpr<&mut T> { - match self { - Self::Broadcast { recv, shape } => { - ReshapeExpr::broadcast(recv.view_mut(), shape.clone()) - } - Self::Reshape { recv, shape } => ReshapeExpr::reshape(recv.view_mut(), shape.clone()), - Self::Swap { recv, a, b } => ReshapeExpr::swap(recv.view_mut(), *a, *b), - Self::SwapAxis { recv, a, b } => ReshapeExpr::swap_axes(recv.view_mut(), *a, *b), - Self::Transpose { recv } => ReshapeExpr::transpose(recv.view_mut()), - } - } -} - -impl From> for TensorExpr { - fn from(expr: ReshapeExpr) -> Self { - TensorExpr::Shape(expr) - } -} diff --git a/tensor/src/ops/mod.rs b/tensor/src/ops/mod.rs deleted file mode 100644 index bb0297ab..00000000 --- a/tensor/src/ops/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -/* - Appellation: ops - Contrib: FL03 -*/ -pub use self::{backprop::*, kinds::*, op::*}; - -pub(crate) mod backprop; -pub(crate) mod op; - -pub(crate) mod kinds { - pub use self::reshape::*; - - pub(crate) mod reshape; -} - -pub trait BaseOperation { - type Output; - - fn name(&self) -> &str; -} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/ops/op.rs b/tensor/src/ops/op.rs deleted file mode 100644 index bc47dd53..00000000 --- a/tensor/src/ops/op.rs +++ /dev/null @@ -1,119 +0,0 @@ -/* - Appellation: kinds - Contrib: FL03 -*/ -use crate::ops::kinds::reshape::*; -use crate::shape::{Axis, Shape}; -use crate::tensor::TensorBase; -use acme::prelude::{BinaryOp, UnaryOp}; -use num::Complex; - -pub type BoxTensor = Box>; - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -#[non_exhaustive] -pub enum TensorExpr { - Binary(BoxTensor, BoxTensor, BinaryOp), - BinaryScalar(BoxTensor, B, BinaryOp), - Unary(BoxTensor, UnaryOp), - Matmul(BoxTensor, BoxTensor), - Sigmoid(BoxTensor), - Shape(ReshapeExpr), -} - -impl TensorExpr { - pub fn binary(lhs: TensorBase, rhs: TensorBase, op: BinaryOp) -> Self { - Self::Binary(lhs.boxed(), rhs.boxed(), op) - } - - pub fn binary_scalar(lhs: TensorBase, rhs: B, op: BinaryOp) -> Self { - Self::BinaryScalar(Box::new(lhs), rhs, op) - } - - pub fn binary_scalar_c( - lhs: TensorBase, - rhs: Complex, - op: BinaryOp, - ) -> TensorExpr> { - TensorExpr::BinaryScalar(Box::new(lhs), rhs, op) - } - - pub fn broadcast(tensor: TensorBase, shape: Shape) -> Self { - Self::shape(ReshapeExpr::broadcast(tensor, shape)) - } - - pub fn matmul(lhs: TensorBase, rhs: TensorBase) -> Self { - Self::Matmul(Box::new(lhs), Box::new(rhs)) - } - - pub fn reshape(tensor: TensorBase, shape: Shape) -> Self { - Self::shape(ReshapeExpr::reshape(tensor, shape)) - } - - pub fn shape(expr: ReshapeExpr) -> Self { - Self::Shape(expr) - } - - pub fn sigmoid(tensor: TensorBase) -> Self { - Self::Sigmoid(Box::new(tensor)) - } - - pub fn swap_axes(tensor: TensorBase, swap: Axis, with: Axis) -> Self { - Self::shape(ReshapeExpr::swap_axes(tensor, swap, with)) - } - - pub fn transpose(tensor: TensorBase) -> Self { - Self::Shape(ReshapeExpr::transpose(tensor)) - } - - pub fn unary(tensor: TensorBase, op: UnaryOp) -> Self { - Self::Unary(Box::new(tensor), op) - } - - pub fn lhs(self) -> Option> { - match self { - Self::Binary(lhs, _, _) => Some(*lhs), - Self::BinaryScalar(lhs, _, _) => Some(*lhs), - Self::Unary(lhs, _) => Some(*lhs), - Self::Matmul(lhs, _) => Some(*lhs), - _ => None, - } - } - - pub fn rhs(self) -> Option> { - match self { - Self::Binary(_, rhs, _) => Some(*rhs), - Self::BinaryScalar(_, scalar, _) => Some(TensorBase::from_scalar(scalar)), - Self::Matmul(_, rhs) => Some(*rhs), - _ => None, - } - } - pub fn view(&self) -> TensorExpr<&A, &B> { - match self { - TensorExpr::Binary(lhs, rhs, op) => TensorExpr::binary(lhs.view(), rhs.view(), *op), - TensorExpr::BinaryScalar(lhs, rhs, op) => { - TensorExpr::binary_scalar(lhs.view(), rhs, *op) - } - TensorExpr::Unary(tensor, op) => TensorExpr::unary(tensor.view(), *op), - TensorExpr::Matmul(lhs, rhs) => TensorExpr::matmul(lhs.view(), rhs.view()), - TensorExpr::Sigmoid(tensor) => TensorExpr::sigmoid(tensor.view()), - TensorExpr::Shape(inner) => TensorExpr::Shape(inner.view()), - } - } - pub fn view_mut(&mut self) -> TensorExpr<&mut A, &mut B> { - match self { - TensorExpr::Binary(lhs, rhs, op) => { - TensorExpr::binary(lhs.view_mut(), rhs.view_mut(), *op) - } - - TensorExpr::BinaryScalar(lhs, rhs, op) => { - TensorExpr::binary_scalar(lhs.view_mut(), rhs, *op) - } - TensorExpr::Unary(tensor, op) => TensorExpr::unary(tensor.view_mut(), *op), - TensorExpr::Matmul(lhs, rhs) => TensorExpr::matmul(lhs.view_mut(), rhs.view_mut()), - TensorExpr::Sigmoid(tensor) => TensorExpr::sigmoid(tensor.view_mut()), - TensorExpr::Shape(inner) => TensorExpr::Shape(inner.view_mut()), - } - } -} diff --git a/tensor/src/seal.rs b/tensor/src/seal.rs deleted file mode 100644 index 1b6ae830..00000000 --- a/tensor/src/seal.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - Appellation: seal - Contrib: FL03 -*/ -//! 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::seal::PrivateMarker; - }; -} - -macro_rules! private_impl { - () => { - fn __private__(&self) -> $crate::seal::PrivateMarker { - $crate::seal::PrivateMarker - } - }; -} diff --git a/tensor/src/shape/axis.rs b/tensor/src/shape/axis.rs deleted file mode 100644 index 882cb0bf..00000000 --- a/tensor/src/shape/axis.rs +++ /dev/null @@ -1,135 +0,0 @@ -/* - Appellation: axis - Contrib: FL03 -*/ -use core::ops::Deref; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -pub trait IntoAxis { - fn into_axis(self) -> Axis; -} - -impl IntoAxis for usize { - fn into_axis(self) -> Axis { - Axis::new(self) - } -} - -/// An [Axis] is used to represent a dimension in a tensor. -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -pub struct Axis(pub(crate) usize); - -impl Axis { - pub fn new(axis: usize) -> Self { - Self(axis) - } - - pub fn into_inner(self) -> usize { - self.0 - } - - pub fn axis(&self) -> usize { - self.0 - } - - pub fn dec(&self) -> Axis { - self - 1 - } - - pub fn inc(&self) -> Axis { - self + 1 - } -} - -impl AsRef for Axis { - fn as_ref(&self) -> &usize { - &self.0 - } -} - -impl Deref for Axis { - type Target = usize; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::fmt::Display for Axis { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for Axis { - fn from(axis: usize) -> Self { - Axis(axis) - } -} - -impl From for usize { - fn from(axis: Axis) -> Self { - axis.0 - } -} - -macro_rules! impl_std_ops { - ($(($trait:tt, $method:ident, $e:tt)),*) => { - $( - impl_std_ops!($trait, $method, $e); - )* - }; - ($trait:tt, $method:ident, $e:tt) => { - impl core::ops::$trait for Axis { - type Output = Axis; - - fn $method(self, rhs: usize) -> Self::Output { - Axis(self.0 $e rhs) - } - } - - impl<'a> core::ops::$trait for &'a Axis { - type Output = Axis; - - fn $method(self, rhs: usize) -> Self::Output { - Axis(self.0 $e rhs) - } - } - - impl core::ops::$trait for Axis { - type Output = Axis; - - fn $method(self, rhs: Axis) -> Self::Output { - Axis(self.0 $e rhs.0) - } - } - - impl<'a> core::ops::$trait for &'a Axis { - type Output = Axis; - - fn $method(self, rhs: Axis) -> Self::Output { - Axis(self.0 $e rhs.0) - } - } - - impl<'a> core::ops::$trait<&'a Axis> for Axis { - type Output = Axis; - - fn $method(self, rhs: &'a Axis) -> Self::Output { - Axis(self.0 $e rhs.0) - } - } - - impl<'a> core::ops::$trait<&'a Axis> for &'a Axis { - type Output = Axis; - - fn $method(self, rhs: &'a Axis) -> Self::Output { - Axis(self.0 $e rhs.0) - } - } - }; -} - -impl_std_ops!((Add, add, +), (Sub, sub, -), (Mul, mul, *), (Div, div, /), (Rem, rem, %)); diff --git a/tensor/src/shape/dim/dimension.rs b/tensor/src/shape/dim/dimension.rs deleted file mode 100644 index 8912ec08..00000000 --- a/tensor/src/shape/dim/dimension.rs +++ /dev/null @@ -1,6 +0,0 @@ -/* - Appellation: dimension - Contrib: FL03 -*/ - -pub struct Dim; diff --git a/tensor/src/shape/dim/mod.rs b/tensor/src/shape/dim/mod.rs deleted file mode 100644 index 5f3b4165..00000000 --- a/tensor/src/shape/dim/mod.rs +++ /dev/null @@ -1,162 +0,0 @@ -/* - Appellation: dim - Contrib: FL03 -*/ -//! # Dimension -//! - -pub use self::{dimension::Dim, utils::*}; - -pub(crate) mod dimension; - -use super::Rank; - -pub trait IntoDimension { - type Dim: Dimension; - - fn into_dimension(self) -> Self::Dim; -} - -pub trait Dimension { - type Pattern; - - fn as_slice(&self) -> &[usize]; - /// Return the [rank](super::Rank) of the dimension; i.e. the number of axes. - fn rank(&self) -> Rank; - /// Return the size of the dimension; i.e. the number of elements. - fn size(&self) -> usize; - - #[doc(hidden)] - /// Return stride offset for index. - fn stride_offset(index: &Self, strides: &Self) -> isize { - izip!(index.as_slice(), strides.as_slice()) - .fold(0, |acc, (&i, &s)| acc + stride_offset(i, s)) - } -} - -pub(crate) mod utils { - use crate::index::{Ix, Ixs}; - use crate::shape::{Shape, ShapeError, Stride}; - use core::mem; - - pub fn can_index_slice( - data: &[A], - shape: &Shape, - stride: &Stride, - ) -> Result<(), ShapeError> { - // Check conditions 1 and 2 and calculate `max_offset`. - let max_offset = max_abs_offset_check_overflow::(shape, stride)?; - can_index_slice_impl(max_offset, data.len(), shape, stride) - } - - pub fn dim_stride_overlap(dim: &Shape, strides: &Stride) -> bool { - let order = strides._fastest_varying_stride_order(); - let mut sum_prev_offsets = 0; - for &index in order.as_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 - } - - pub fn max_abs_offset_check_overflow( - dim: &Shape, - strides: &Stride, - ) -> Result { - max_abs_offset_check_overflow_impl(mem::size_of::(), dim, strides) - } - - pub fn size_of_shape_checked(dim: &Shape) -> Result { - let size_nonzero = dim - .as_slice() - .iter() - .filter(|&&d| d != 0) - .try_fold(1usize, |acc, &d| acc.checked_mul(d)) - .ok_or(ShapeError::Overflow)?; - if size_nonzero > ::std::isize::MAX as usize { - Err(ShapeError::Overflow) - } else { - Ok(dim.size()) - } - } - - /// 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) - } - - fn can_index_slice_impl( - max_offset: usize, - data_len: usize, - dim: &Shape, - strides: &Stride, - ) -> Result<(), ShapeError> { - // Check condition 3. - let is_empty = dim.as_slice().iter().any(|&d| d == 0); - if is_empty && max_offset > data_len { - return Err(ShapeError::OutOfBounds); - } - if !is_empty && max_offset >= data_len { - return Err(ShapeError::OutOfBounds); - } - - // Check condition 4. - if !is_empty && dim_stride_overlap(dim, strides) { - return Err(ShapeError::Unsupported); - } - - Ok(()) - } - - fn max_abs_offset_check_overflow_impl( - elem_size: usize, - dim: &Shape, - strides: &Stride, - ) -> Result { - // Condition 1. - if dim.rank() != strides.rank() { - return Err(ShapeError::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.as_slice(), strides.as_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(|| ShapeError::Overflow)?; - // Condition 2a. - if max_offset > isize::MAX as usize { - return Err(ShapeError::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(ShapeError::Overflow)?; - // Condition 2b. - if max_offset_bytes > isize::MAX as usize { - return Err(ShapeError::Overflow); - } - - Ok(max_offset) - } -} diff --git a/tensor/src/shape/error.rs b/tensor/src/shape/error.rs deleted file mode 100644 index de97ba0c..00000000 --- a/tensor/src/shape/error.rs +++ /dev/null @@ -1,54 +0,0 @@ -/* - Appellation: error - Contrib: FL03 -*/ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumProperty, EnumString, VariantNames}; - -pub type ShapeResult = std::result::Result; - -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize), - serde(rename_all = "snake_case", untagged) -)] -#[derive( - Clone, - Copy, - Debug, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumProperty, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - VariantNames, -)] -#[repr(usize)] -#[strum(serialize_all = "snake_case")] -pub enum ShapeError { - #[strum(props(desc = "Dimension mismatch"))] - DimensionMismatch, - #[strum(props(desc = "incompatible shapes"))] - IncompatibleShapes, - IncompatibleLayout, - InvalidAxis, - InvalidShape, - MismatchedElements, - NotSquare, - OutOfBounds, - Overflow, - Unsupported, -} - -unsafe impl Send for ShapeError {} - -unsafe impl Sync for ShapeError {} - -impl std::error::Error for ShapeError {} diff --git a/tensor/src/shape/layout.rs b/tensor/src/shape/layout.rs deleted file mode 100644 index 9aa8058b..00000000 --- a/tensor/src/shape/layout.rs +++ /dev/null @@ -1,230 +0,0 @@ -/* - Appellation: layout - Contrib: FL03 -*/ -use crate::iter::LayoutIter; -use crate::shape::dim::stride_offset; -use crate::shape::{Axis, IntoShape, IntoStride, Rank, Shape, ShapeError, ShapeResult, Stride}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// The layout describes the memory layout of a tensor. -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -pub struct Layout { - pub(crate) offset: usize, - pub(crate) shape: Shape, - pub(crate) strides: Stride, -} - -impl Layout { - pub unsafe fn new(offset: usize, shape: impl IntoShape, strides: impl IntoStride) -> Self { - Self { - offset, - shape: shape.into_shape(), - strides: strides.into_stride(), - } - } - /// Create a new layout with a contiguous stride. - pub fn contiguous(shape: impl IntoShape) -> Self { - let shape = shape.into_shape(); - let stride = shape.stride_contiguous(); - Self { - offset: 0, - shape, - strides: stride, - } - } - /// Create a new layout with a scalar stride. - pub fn scalar() -> Self { - Self::contiguous(()) - } - #[doc(hidden)] - /// Return stride offset for index. - pub fn stride_offset(index: impl AsRef<[usize]>, strides: &Stride) -> isize { - let mut offset = 0; - for (&i, &s) in izip!(index.as_ref(), strides.as_slice()) { - offset += stride_offset(i, s); - } - offset - } - /// Broadcast the layout to a new shape. - /// - /// The new shape must have the same or higher rank than the current shape. - pub fn broadcast_as(&self, shape: impl IntoShape) -> ShapeResult { - let shape = shape.into_shape(); - if shape.rank() < self.shape().rank() { - return Err(ShapeError::IncompatibleShapes); - } - let diff = shape.rank() - self.shape().rank(); - let mut stride = vec![0; *diff]; - for (&dst_dim, (&src_dim, &src_stride)) in shape[*diff..] - .iter() - .zip(self.shape().iter().zip(self.strides().iter())) - { - let s = if dst_dim == src_dim { - src_stride - } else if src_dim != 1 { - return Err(ShapeError::IncompatibleShapes); - } else { - 0 - }; - stride.push(s) - } - let layout = unsafe { Layout::new(0, shape, stride) }; - Ok(layout) - } - /// Determine if the current layout is contiguous or not. - pub fn is_contiguous(&self) -> bool { - self.shape().is_contiguous(&self.strides) - } - /// Checks to see if the layout is empy; i.e. a scalar of Rank(0) - pub fn is_scalar(&self) -> bool { - self.shape().is_scalar() - } - /// A function for determining if the layout is square. - /// An n-dimensional object is square if all of its dimensions are equal. - pub fn is_square(&self) -> bool { - self.shape().is_square() - } - - pub fn iter(&self) -> LayoutIter { - LayoutIter::new(self.clone()) - } - /// Peek the offset of the layout. - pub fn offset(&self) -> usize { - self.offset - } - /// Returns the offset from the lowest-address element to the logically first - /// element. - pub fn offset_from_low_addr_ptr_to_logical_ptr(&self) -> usize { - let offset = - izip!(self.shape().as_slice(), self.strides().as_slice()).fold(0, |acc, (d, s)| { - let d = *d as isize; - let s = *s as isize; - if s < 0 && d > 1 { - acc - s * (d - 1) - } else { - acc - } - }); - debug_assert!(offset >= 0); - offset as usize - } - /// Return the rank (number of dimensions) of the layout. - pub fn rank(&self) -> Rank { - debug_assert_eq!(self.strides.len(), *self.shape.rank()); - self.shape.rank() - } - /// Remove an axis from the current layout, returning the new layout. - pub fn remove_axis(&self, axis: Axis) -> Self { - Self { - offset: self.offset, - shape: self.shape().remove_axis(axis), - strides: self.strides().remove_axis(axis), - } - } - /// Reshape the layout to a new shape. - pub fn reshape(&mut self, shape: impl IntoShape) { - self.shape = shape.into_shape(); - self.strides = self.shape.stride_contiguous(); - } - /// Reverse the order of the axes. - pub fn reverse(&mut self) { - self.shape.reverse(); - self.strides.reverse(); - } - /// Reverse the order of the axes. - pub fn reverse_axes(mut self) -> Layout { - self.reverse(); - self - } - /// Get a reference to the shape of the layout. - pub const fn shape(&self) -> &Shape { - &self.shape - } - /// Get a reference to the number of elements in the layout. - pub fn size(&self) -> usize { - self.shape().size() - } - /// Get a reference to the stride of the layout. - pub const fn strides(&self) -> &Stride { - &self.strides - } - /// Swap the axes of the layout. - pub fn swap_axes(&self, a: Axis, b: Axis) -> Layout { - Layout { - offset: self.offset, - shape: self.shape.swap_axes(a, b), - strides: self.strides.swap_axes(a, b), - } - } - /// Transpose the layout. - pub fn transpose(&self) -> Layout { - self.clone().reverse_axes() - } - - pub fn with_offset(mut self, offset: usize) -> Self { - self.offset = offset; - self - } - - pub fn with_shape_c(mut self, shape: impl IntoShape) -> Self { - self.shape = shape.into_shape(); - self.strides = self.shape.stride_contiguous(); - self - } - - pub unsafe fn with_shape_unchecked(mut self, shape: impl IntoShape) -> Self { - self.shape = shape.into_shape(); - self - } - - pub unsafe fn with_strides_unchecked(mut self, stride: impl IntoStride) -> Self { - self.strides = stride.into_stride(); - self - } -} - -// Internal methods -impl Layout { - pub(crate) fn index(&self, idx: Idx) -> usize - where - Idx: AsRef<[usize]>, - { - let idx = idx.as_ref(); - debug_assert_eq!(idx.len(), *self.rank(), "Dimension mismatch"); - self.index_unchecked(idx) - } - - pub(crate) fn index_unchecked(&self, idx: Idx) -> usize - where - Idx: AsRef<[usize]>, - { - crate::coordinates_to_index::(idx, self.strides()) - } - - pub(crate) fn _matmul(&self, rhs: &Layout) -> Result { - let shape = self.shape().matmul(rhs.shape())?; - let layout = Layout { - offset: self.offset(), - shape, - strides: self.strides().clone(), - }; - Ok(layout) - } -} - -#[cfg(test)] -mod tests { - use super::Layout; - - #[test] - fn test_position() { - let shape = (3, 3); - let layout = Layout::contiguous(shape); - assert_eq!(layout.index_unchecked([0, 0]), 0); - assert_eq!(layout.index([0, 1]), 1); - assert_eq!(layout.index([2, 2]), 8); - } -} diff --git a/tensor/src/shape/mod.rs b/tensor/src/shape/mod.rs deleted file mode 100644 index 713430f8..00000000 --- a/tensor/src/shape/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -/* - Appellation: shapes - Contrib: FL03 -*/ -//! # Shapes -//! -//! This modules provides implements several useful primitives for working with -//! the shape of a [Tensor](crate::tensor::TensorBase). -//! -pub use self::{axis::*, error::*, layout::Layout, rank::*, shape::Shape, stride::*}; - -pub(crate) mod axis; -pub(crate) mod error; -pub(crate) mod layout; -pub(crate) mod rank; -pub(crate) mod shape; -pub(crate) mod stride; - -#[doc(hidden)] -pub mod dim; - -pub trait IntoShape { - fn into_shape(self) -> Shape; -} - -impl IntoShape for S -where - S: Into, -{ - fn into_shape(self) -> Shape { - self.into() - } -} - -impl<'a> IntoShape for &'a Shape { - fn into_shape(self) -> Shape { - self.clone() - } -} - -pub(crate) mod prelude { - pub use super::IntoShape; - - pub use super::axis::{Axis, IntoAxis}; - pub use super::error::{ShapeError, ShapeResult}; - pub use super::layout::Layout; - pub use super::rank::{IntoRank, Rank}; - pub use super::shape::Shape; - pub use super::stride::{IntoStride, Stride}; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_shape() { - let mut shape = Shape::default(); - shape.extend([1, 1, 1]); - assert_eq!(shape, Shape::new(vec![1, 1, 1])); - assert_eq!(shape.size(), 1); - assert_eq!(*shape.rank(), 3); - } -} diff --git a/tensor/src/shape/rank.rs b/tensor/src/shape/rank.rs deleted file mode 100644 index c9670dc1..00000000 --- a/tensor/src/shape/rank.rs +++ /dev/null @@ -1,201 +0,0 @@ -/* - Appellation: rank - Contrib: FL03 -*/ -//! # Rank -//! -//! The rank of a n-dimensional array describes the number of dimensions -use core::borrow::Borrow; -use core::ops::{Deref, DerefMut, Not}; -use num::traits::{Num, One, Zero}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -pub trait IntoRank { - fn into_rank(self) -> Rank; -} - -impl IntoRank for usize { - fn into_rank(self) -> Rank { - Rank::new(self) - } -} - -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize,))] -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Rank(pub(crate) usize); - -impl Rank { - pub fn new(rank: usize) -> Self { - Self(rank) - } - - pub const fn scalar() -> Self { - Self(0) - } - - pub fn into_inner(self) -> usize { - self.0 - } - - pub fn is_scalar(&self) -> bool { - self.0 == 0 - } - - pub fn rank(&self) -> usize { - self.0 - } -} - -impl std::fmt::Display for Rank { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl AsRef for Rank { - fn as_ref(&self) -> &usize { - &self.0 - } -} - -impl AsMut for Rank { - fn as_mut(&mut self) -> &mut usize { - &mut self.0 - } -} - -impl Borrow for Rank { - fn borrow(&self) -> &usize { - &self.0 - } -} - -impl Deref for Rank { - type Target = usize; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Rank { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl From for Rank { - fn from(rank: usize) -> Self { - Self(rank) - } -} - -impl From for usize { - fn from(rank: Rank) -> Self { - rank.0 - } -} - -unsafe impl Send for Rank {} - -unsafe impl Sync for Rank {} - -impl_partial_eq!(Rank -> 0: [usize]); - -macro_rules! impl_std_ops { - ($(($trait:tt, $method:ident, $e:tt)),*) => { - $( - impl_std_ops!($trait, $method, $e); - )* - }; - ($trait:tt, $method:ident, $e:tt) => { - impl core::ops::$trait for Rank { - type Output = Rank; - - fn $method(self, rhs: usize) -> Self::Output { - let rank = self.0 $e rhs; - Rank(rank) - } - } - - impl<'a> core::ops::$trait for &'a Rank { - type Output = Rank; - - fn $method(self, rhs: usize) -> Self::Output { - let rank = self.0 $e rhs; - Rank(rank) - } - } - - impl core::ops::$trait for Rank { - type Output = Rank; - - fn $method(self, rhs: Rank) -> Self::Output { - let rank = self.0 $e rhs.0; - Rank(rank) - } - } - - impl<'a> core::ops::$trait for &'a Rank { - type Output = Rank; - - fn $method(self, rhs: Rank) -> Self::Output { - let rank = self.0 $e rhs.0; - Rank(rank) - } - } - - impl<'a> core::ops::$trait<&'a Rank> for Rank { - type Output = Rank; - - fn $method(self, rhs: &'a Rank) -> Self::Output { - let rank = self.0 $e rhs.0; - Rank(rank) - } - } - - impl<'a> core::ops::$trait<&'a Rank> for &'a Rank { - type Output = Rank; - - fn $method(self, rhs: &'a Rank) -> Self::Output { - let rank = self.0 $e rhs.0; - Rank(rank) - } - } - }; -} - -impl_std_ops!((Add, add, +), (Sub, sub, -), (Mul, mul, *), (Div, div, /), (Rem, rem, %)); - -impl Not for Rank { - type Output = Rank; - - fn not(self) -> Self::Output { - Rank(!self.0) - } -} - -impl Num for Rank { - type FromStrRadixErr = ::FromStrRadixErr; - - fn from_str_radix(str: &str, radix: u32) -> Result { - usize::from_str_radix(str, radix).map(Rank) - } -} - -impl One for Rank { - fn one() -> Self { - Self(1) - } -} - -impl Zero for Rank { - fn zero() -> Self { - Self(0) - } - - fn is_zero(&self) -> bool { - self.0 == 0 - } -} diff --git a/tensor/src/shape/shape.rs b/tensor/src/shape/shape.rs deleted file mode 100644 index dc8ee4d5..00000000 --- a/tensor/src/shape/shape.rs +++ /dev/null @@ -1,557 +0,0 @@ -/* - Appellation: shape - Contrib: FL03 -*/ -use super::{Axis, Rank, ShapeError, Stride}; -use crate::iter::zip; -use crate::prelude::{Ixs, SwapAxes}; -#[cfg(not(feature = "std"))] -use alloc::vec; -use core::ops::{self, Deref}; -#[cfg(feature = "std")] -use std::vec; - -/// A shape is a description of the number of elements in each dimension. -#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize,))] -pub struct Shape(Vec); - -impl Shape { - pub fn new(shape: Vec) -> Self { - Self(shape) - } - /// Creates a new shape of rank 0. - pub fn scalar() -> Self { - Self(Vec::new()) - } - - pub fn stride_offset(index: &[usize], strides: &Stride) -> Ixs { - let mut offset = 0; - for (&i, &s) in index.iter().zip(strides.as_slice()) { - offset += super::dim::stride_offset(i, s); - } - offset - } - - pub fn with_capacity(capacity: usize) -> Self { - Self(Vec::with_capacity(capacity)) - } - /// Creates a new shape of the given rank with all dimensions set to 0. - pub fn zeros(rank: usize) -> Self { - Self(vec![0; rank]) - } - /// Get a reference to the shape as a slice. - pub fn as_slice(&self) -> &[usize] { - &self.0 - } - /// Get a mutable reference to the shape as a slice. - pub fn as_slice_mut(&mut self) -> &mut [usize] { - &mut self.0 - } - - pub fn check_size(&self) -> Result { - let size_nonzero = self - .as_slice() - .iter() - .filter(|&&d| d != 0) - .try_fold(1usize, |acc, &d| acc.checked_mul(d)) - .ok_or(ShapeError::Overflow)?; - if size_nonzero > core::isize::MAX as usize { - Err(ShapeError::Overflow) - } else { - Ok(self.size()) - } - } - /// Decrement the dimensions of the shape by 1, - /// returning a new shape. - pub fn dec(&self) -> Self { - let mut shape = self.clone(); - shape.dec_inplace(); - shape - } - /// Decrement the dimensions of the shape by 1, inplace. - pub fn dec_inplace(&mut self) { - for dim in self.iter_mut() { - *dim -= 1; - } - } - /// Decrement the dimension at the given [Axis] by 1. - pub fn dec_axis(&mut self, axis: Axis) { - self[axis] -= 1; - } - /// Attempts to create a one-dimensional shape that describes the - /// diagonal of the current shape. - pub fn diag(&self) -> Shape { - Self::new(i![self.nrows()]) - } - - pub fn first_index(&self) -> Option> { - if self.is_empty() { - return None; - } - Some(vec![0; *self.rank()]) - } - pub fn get_final_position(&self) -> Vec { - self.dec().to_vec() - } - /// Inserts a new dimension along the given [Axis], inplace. - pub fn insert(&mut self, index: Axis, dim: usize) { - self.0.insert(*index, dim) - } - /// Inserts a new dimension along the given [Axis]. - pub fn insert_axis(&self, index: Axis) -> Self { - let mut shape = self.clone(); - shape.insert(index, 1); - shape - } - /// Returns true if the strides are C contiguous (aka row major). - pub fn is_contiguous(&self, stride: &Stride) -> bool { - if self.0.len() != stride.len() { - return false; - } - let mut acc = 1; - for (&stride, &dim) in stride.iter().zip(self.iter()).rev() { - if stride != acc { - return false; - } - acc *= dim; - } - true - } - /// Returns true if the shape is a scalar. - pub fn is_scalar(&self) -> bool { - self.0.is_empty() - } - /// Checks to see if the shape is square - pub fn is_square(&self) -> bool { - self.iter().all(|&dim| dim == self[0]) - } - /// Creates an immutable iterator over the elements of the shape - pub fn iter(&self) -> core::slice::Iter { - self.0.iter() - } - /// Creates a mutable iterator over the elements of the shape. - pub fn iter_mut(&mut self) -> core::slice::IterMut { - self.0.iter_mut() - } - /// The number of columns in the shape. - pub fn ncols(&self) -> usize { - if self.len() >= 2 { - self[1] - } else if self.len() == 1 { - 1 - } else { - 0 - } - } - #[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] - pub fn next_for(&self, index: D) -> Option> - where - D: AsRef<[usize]>, - { - let mut index = index.as_ref().to_vec(); - let mut done = false; - for (&dim, ix) in zip(self.as_slice(), index.as_mut_slice()).rev() { - *ix += 1; - if *ix == dim { - *ix = 0; - } else { - done = true; - break; - } - } - if done { - Some(index) - } else { - None - } - } - /// The number of rows in the shape. - pub fn nrows(&self) -> usize { - if self.len() >= 1 { - self[0] - } else { - 0 - } - } - /// Removes and returns the last dimension of the shape. - pub fn pop(&mut self) -> Option { - self.0.pop() - } - /// Add a new dimension to the shape. - pub fn push(&mut self, dim: usize) { - self.0.push(dim) - } - /// Get the number of dimensions, or [Rank], of the shape - pub fn rank(&self) -> Rank { - self.0.len().into() - } - /// Remove the dimension at the given [Axis], - pub fn remove(&mut self, index: Axis) -> usize { - self.0.remove(*index) - } - /// Remove the dimension at the given [Axis]. - pub fn remove_axis(&self, index: Axis) -> Shape { - let mut shape = self.clone(); - shape.remove(index); - shape - } - /// Reverse the dimensions of the shape. - pub fn reverse(&mut self) { - self.0.reverse() - } - /// Set the dimension at the given [Axis]. - pub fn set(&mut self, index: Axis, dim: usize) { - self[index] = dim - } - /// The number of elements in the shape. - pub fn size(&self) -> usize { - self.0.iter().product() - } - /// Swap the dimensions of the current [Shape] at the given [Axis]. - pub fn swap(&mut self, a: Axis, b: Axis) { - self.0.swap(a.axis(), b.axis()) - } - /// Swap the dimensions at the given [Axis], creating a new [Shape] - pub fn swap_axes(&self, swap: Axis, with: Axis) -> Self { - let mut shape = self.clone(); - shape.swap(swap, with); - shape - } - /// A utilitarian function for converting the shape to a vector. - pub fn to_vec(&self) -> Vec { - self.0.clone() - } -} - -// Internal methods -#[allow(dead_code)] -#[doc(hidden)] -impl Shape { - pub fn default_strides(&self) -> Stride { - // Compute default array strides - // Shape (a, b, c) => Give strides (b * c, c, 1) - let mut strides = Stride::zeros(self.rank()); - // For empty arrays, use all zero strides. - if self.iter().all(|&d| d != 0) { - let mut it = strides.as_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.iter().rev()) { - cum_prod *= *dim; - *rs = cum_prod; - } - } - strides - } - - pub(crate) fn matmul(&self, other: &Self) -> Result { - if self.rank() == 2 && other.rank() == 2 { - return Ok(Self::from((self[0], other[1]))); - } else if self.rank() == 2 && other.rank() == 1 { - return Ok(Self::from(self[0])); - } else if self.rank() == 1 && other.rank() == 2 { - return Ok(Self::from(other[0])); - } else if self.rank() == 1 && other.rank() == 1 { - return Ok(Self::scalar()); - } - Err(ShapeError::IncompatibleShapes) - } - - pub(crate) fn matmul_shape(&self, other: &Self) -> Result { - if *self.rank() != 2 || *other.rank() != 2 || self[1] != other[0] { - return Err(ShapeError::IncompatibleShapes); - } - Ok(Self::from((self[0], other[1]))) - } - - pub(crate) fn stride_contiguous(&self) -> Stride { - let mut stride: Vec<_> = self - .0 - .iter() - .rev() - .scan(1, |prod, u| { - let prod_pre_mult = *prod; - *prod *= u; - Some(prod_pre_mult) - }) - .collect(); - stride.reverse(); - stride.into() - } - - pub(crate) fn upcast(&self, to: &Shape, stride: &Stride) -> Option { - let mut new_stride = to.as_slice().to_vec(); - // begin at the back (the least significant dimension) - // size of the axis has to either agree or `from` has to be 1 - if to.rank() < self.rank() { - return None; - } - - let mut iter = new_stride.as_mut_slice().iter_mut().rev(); - for ((er, es), dr) in self - .as_slice() - .iter() - .rev() - .zip(stride.as_slice().iter().rev()) - .zip(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 iter { - *dr = 0; - } - - Some(new_stride.into()) - } -} - -impl AsRef<[usize]> for Shape { - fn as_ref(&self) -> &[usize] { - &self.0 - } -} - -impl AsMut<[usize]> for Shape { - fn as_mut(&mut self) -> &mut [usize] { - &mut self.0 - } -} - -impl Deref for Shape { - type Target = [usize]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Extend for Shape { - fn extend>(&mut self, iter: I) { - self.0.extend(iter) - } -} - -impl From for Vec { - fn from(shape: Shape) -> Self { - shape.0 - } -} - -impl_partial_eq!(Shape -> 0: [[usize], Vec]); - -impl SwapAxes for Shape { - fn swap_axes(&self, a: Axis, b: Axis) -> Self { - self.swap_axes(a, b) - } -} - -impl FromIterator for Shape { - fn from_iter>(iter: I) -> Self { - Self(Vec::from_iter(iter)) - } -} - -impl IntoIterator for Shape { - type Item = usize; - type IntoIter = vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a mut Shape { - type Item = &'a mut usize; - type IntoIter = core::slice::IterMut<'a, usize>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl ops::Index for Shape { - type Output = usize; - - fn index(&self, index: usize) -> &Self::Output { - &self.0[index] - } -} - -impl ops::Index for Shape { - type Output = usize; - - fn index(&self, index: Axis) -> &Self::Output { - &self.0[*index] - } -} - -impl ops::IndexMut for Shape { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] - } -} - -impl ops::IndexMut for Shape { - fn index_mut(&mut self, index: Axis) -> &mut Self::Output { - &mut self.0[*index] - } -} - -impl ops::Index> for Shape { - type Output = [usize]; - - fn index(&self, index: ops::Range) -> &Self::Output { - &self.0[index] - } -} - -impl ops::Index> for Shape { - type Output = [usize]; - - fn index(&self, index: ops::RangeTo) -> &Self::Output { - &self.0[index] - } -} - -impl ops::Index> for Shape { - type Output = [usize]; - - fn index(&self, index: ops::RangeFrom) -> &Self::Output { - &self.0[index] - } -} - -impl ops::Index for Shape { - type Output = [usize]; - - fn index(&self, index: ops::RangeFull) -> &Self::Output { - &self.0[index] - } -} - -impl ops::Index> for Shape { - type Output = [usize]; - - fn index(&self, index: ops::RangeInclusive) -> &Self::Output { - &self.0[index] - } -} - -impl ops::Index> for Shape { - type Output = [usize]; - - fn index(&self, index: ops::RangeToInclusive) -> &Self::Output { - &self.0[index] - } -} - -unsafe impl Send for Shape {} - -unsafe impl Sync for Shape {} - -impl From<()> for Shape { - fn from(_: ()) -> Self { - Self::default() - } -} - -impl From for Shape { - fn from(dim: usize) -> Self { - Self(vec![dim]) - } -} - -impl From> for Shape { - fn from(shape: Vec) -> Self { - Self(shape) - } -} - -impl From<&[usize]> for Shape { - fn from(shape: &[usize]) -> Self { - Self(shape.to_vec()) - } -} - -impl From<[usize; N]> for Shape { - fn from(shape: [usize; N]) -> Self { - Self(shape.to_vec()) - } -} - -impl From<(usize,)> for Shape { - fn from(shape: (usize,)) -> Self { - Self(vec![shape.0]) - } -} - -impl From<(usize, usize)> for Shape { - fn from(shape: (usize, usize)) -> Self { - Self(vec![shape.0, shape.1]) - } -} - -impl From<(usize, usize, usize)> for Shape { - fn from(shape: (usize, usize, usize)) -> Self { - Self(vec![shape.0, shape.1, shape.2]) - } -} - -impl From<(usize, usize, usize, usize)> for Shape { - fn from(shape: (usize, usize, usize, usize)) -> Self { - Self(vec![shape.0, shape.1, shape.2, shape.3]) - } -} - -impl From<(usize, usize, usize, usize, usize)> for Shape { - fn from(shape: (usize, usize, usize, usize, usize)) -> Self { - Self(vec![shape.0, shape.1, shape.2, shape.3, shape.4]) - } -} - -impl From<(usize, usize, usize, usize, usize, usize)> for Shape { - fn from(shape: (usize, usize, usize, usize, usize, usize)) -> Self { - Self(vec![shape.0, shape.1, shape.2, shape.3, shape.4, shape.5]) - } -} - -// macro_rules! tuple_vec { -// ($($n:tt),*) => { -// vec![$($n,)*] -// }; - -// } - -// macro_rules! impl_from_tuple { -// ($($n:tt: $name:ident),+) => { -// impl<$($name),+> From<($($name,)+)> for Shape -// where -// $($name: Into,)+ -// { -// fn from(shape: ($($name,)+)) -> Self { -// Self(vec![$($name.into(),)+]) -// } -// } -// }; -// } - -// impl_from_tuple!(A: A); diff --git a/tensor/src/shape/stride.rs b/tensor/src/shape/stride.rs deleted file mode 100644 index 2b91583e..00000000 --- a/tensor/src/shape/stride.rs +++ /dev/null @@ -1,239 +0,0 @@ -/* - Appellation: stride - Contrib: FL03 -*/ -use super::{dim, Axis, Rank}; -use core::borrow::{Borrow, BorrowMut}; -use core::ops::{Deref, DerefMut, Index, IndexMut}; -use core::slice::{Iter as SliceIter, IterMut as SliceIterMut}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -pub trait IntoStride { - fn into_stride(self) -> Stride; -} - -impl IntoStride for S -where - S: Into, -{ - fn into_stride(self) -> Stride { - self.into() - } -} - -pub enum Strides { - Contiguous, - Fortran, - Stride(Stride), -} - -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -pub struct Stride(pub(crate) Vec); - -impl Stride { - pub fn new(stride: Vec) -> Self { - Self(stride) - } - - pub fn with_capacity(capacity: usize) -> Self { - Self(Vec::with_capacity(capacity)) - } - - pub fn zeros(rank: Rank) -> Self { - Self(vec![0; *rank]) - } - /// Returns a reference to the stride. - pub fn as_slice(&self) -> &[usize] { - &self.0 - } - /// Returns a mutable reference to the stride. - pub fn as_slice_mut(&mut self) -> &mut [usize] { - &mut self.0 - } - /// Returns the capacity of the stride. - pub fn capacity(&self) -> usize { - self.0.capacity() - } - /// Clears the stride, removing all elements. - pub fn clear(&mut self) { - self.0.clear() - } - /// Gets the element at the specified axis, returning None if the axis is out of bounds. - pub fn get(&self, axis: Axis) -> Option<&usize> { - self.0.get(*axis) - } - /// Returns an iterator over references to the elements of the stride. - pub fn iter(&self) -> SliceIter { - self.0.iter() - } - /// Returns an iterator over mutable references to the elements of the stride. - pub fn iter_mut(&mut self) -> SliceIterMut { - self.0.iter_mut() - } - /// Returns the rank of the stride; i.e., the number of dimensions. - pub fn rank(&self) -> Rank { - self.0.len().into() - } - /// Removes and returns the stride of the axis. - pub fn remove(&mut self, axis: Axis) -> usize { - self.0.remove(*axis) - } - /// Returns a new stride with the axis removed. - pub fn remove_axis(&self, axis: Axis) -> Self { - let mut stride = self.clone(); - stride.remove(axis); - stride - } - /// Reverses the stride. - pub fn reverse(&mut self) { - self.0.reverse() - } - /// Returns a new stride with the elements reversed. - pub fn reversed(&self) -> Self { - let mut stride = self.clone(); - stride.reverse(); - stride - } - /// Sets the element at the specified axis, returning None if the axis is out of bounds. - pub fn set(&mut self, axis: Axis, value: usize) -> Option { - self.0.get_mut(*axis).map(|v| core::mem::replace(v, value)) - } - /// - pub fn stride_offset(&self, index: &Idx) -> isize - where - Idx: AsRef<[usize]>, - { - index - .as_ref() - .iter() - .copied() - .zip(self.iter().copied()) - .fold(0, |acc, (i, s)| acc + dim::stride_offset(i, s)) - } - /// Swaps two elements in the stride, inplace. - pub fn swap(&mut self, a: usize, b: usize) { - self.0.swap(a, b) - } - /// Returns a new shape with the two axes swapped. - pub fn swap_axes(&self, a: Axis, b: Axis) -> Self { - let mut stride = self.clone(); - stride.swap(a.axis(), b.axis()); - stride - } -} - -// Internal methods -impl Stride { - pub(crate) fn _fastest_varying_stride_order(&self) -> Self { - let mut indices = self.clone(); - for (i, elt) in indices.as_slice_mut().iter_mut().enumerate() { - *elt = i; - } - let strides = self.as_slice(); - indices - .as_slice_mut() - .sort_by_key(|&i| (strides[i] as isize).abs()); - indices - } -} - -impl AsRef<[usize]> for Stride { - fn as_ref(&self) -> &[usize] { - &self.0 - } -} - -impl AsMut<[usize]> for Stride { - fn as_mut(&mut self) -> &mut [usize] { - &mut self.0 - } -} - -impl Borrow<[usize]> for Stride { - fn borrow(&self) -> &[usize] { - &self.0 - } -} - -impl BorrowMut<[usize]> for Stride { - fn borrow_mut(&mut self) -> &mut [usize] { - &mut self.0 - } -} - -impl Deref for Stride { - type Target = [usize]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Stride { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Extend for Stride { - fn extend>(&mut self, iter: I) { - self.0.extend(iter) - } -} - -impl FromIterator for Stride { - fn from_iter>(iter: I) -> Self { - Stride(Vec::from_iter(iter)) - } -} - -impl Index for Stride { - type Output = usize; - - fn index(&self, index: usize) -> &Self::Output { - &self.0[index] - } -} - -impl IndexMut for Stride { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] - } -} - -impl Index for Stride { - type Output = usize; - - fn index(&self, index: Axis) -> &Self::Output { - &self.0[*index] - } -} - -impl IndexMut for Stride { - fn index_mut(&mut self, index: Axis) -> &mut Self::Output { - &mut self.0[*index] - } -} - -impl IntoIterator for Stride { - type Item = usize; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl From> for Stride { - fn from(v: Vec) -> Self { - Stride(v) - } -} - -impl From<&[usize]> for Stride { - fn from(v: &[usize]) -> Self { - Stride(v.to_vec()) - } -} diff --git a/tensor/src/specs/mod.rs b/tensor/src/specs/mod.rs deleted file mode 100644 index bb59cf5c..00000000 --- a/tensor/src/specs/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 -*/ -pub use self::{moves::*, ndtensor::*, scalar::*}; - -pub(crate) mod moves; -pub(crate) mod ndtensor; -pub(crate) mod scalar; - -pub(crate) mod prelude { - pub use super::moves::*; - pub use super::ndtensor::*; - pub use super::scalar::*; -} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/specs/moves.rs b/tensor/src/specs/moves.rs deleted file mode 100644 index efa028d0..00000000 --- a/tensor/src/specs/moves.rs +++ /dev/null @@ -1,23 +0,0 @@ -/* - Appellation: reshape - Contrib: FL03 -*/ -use crate::shape::Axis; - -pub trait Swap { - type Key; - - fn swap(&mut self, swap: Self::Key, with: Self::Key); -} - -impl Swap for [T] { - type Key = usize; - - fn swap(&mut self, swap: Self::Key, with: Self::Key) { - self.swap(swap, with); - } -} - -pub trait SwapAxes { - fn swap_axes(&self, swap: Axis, with: Axis) -> Self; -} diff --git a/tensor/src/specs/ndtensor.rs b/tensor/src/specs/ndtensor.rs deleted file mode 100644 index 55d514ee..00000000 --- a/tensor/src/specs/ndtensor.rs +++ /dev/null @@ -1,54 +0,0 @@ -/* - Appellation: ndtensor - Contrib: FL03 -*/ -use crate::prelude::{Layout, TensorId}; -use crate::shape::{Rank, Shape, Stride}; - -pub trait NdTensor { - type Data: TensorData; - - fn as_mut_ptr(&mut self) -> *mut T; - - fn as_ptr(&self) -> *const T; - - fn id(&self) -> TensorId; - - fn layout(&self) -> &Layout; - - fn rank(&self) -> Rank { - self.layout().shape().rank() - } - - fn shape(&self) -> &Shape { - self.layout().shape() - } - - fn size(&self) -> usize { - self.shape().size() - } - - fn strides(&self) -> &Stride { - self.layout().strides() - } -} - -pub trait TensorData { - type Elem; -} - -impl TensorData for [T] { - type Elem = T; -} - -impl<'a, T> TensorData for &'a [T] { - type Elem = T; -} - -impl TensorData for Vec { - type Elem = T; -} - -pub trait TensorDataMut: TensorData { - fn as_mut_ptr(&mut self) -> *mut Self::Elem; -} diff --git a/tensor/src/specs/scalar.rs b/tensor/src/specs/scalar.rs deleted file mode 100644 index 7024f574..00000000 --- a/tensor/src/specs/scalar.rs +++ /dev/null @@ -1,17 +0,0 @@ -/* - Appellation: scalar - Contrib: FL03 -*/ -use crate::tensor::TensorBase; -use acme::prelude::Scalar; - -pub trait ScalarExt: Scalar { - fn into_tensor(self) -> TensorBase { - TensorBase::from_scalar(self) - } - fn sigmoid(self) -> Self { - (Self::one() + self.neg().exp()).recip() - } -} - -impl ScalarExt for S where S: Scalar {} diff --git a/tensor/src/stats/impl_stats.rs b/tensor/src/stats/impl_stats.rs deleted file mode 100644 index 87956552..00000000 --- a/tensor/src/stats/impl_stats.rs +++ /dev/null @@ -1,85 +0,0 @@ -use super::Statistics; -use crate::prelude::Scalar; -use crate::TensorBase; - -impl TensorBase { - pub fn max(&self) -> &T - where - T: Ord, - { - self.iter().max().unwrap() - } - - pub fn mean(&self) -> T - where - T: Scalar, - { - self.sum() / T::from_usize(self.size()).unwrap() - } - - pub fn min(&self) -> &T - where - T: Ord, - { - self.iter().min().unwrap() - } - - pub fn sort(&mut self) - where - T: Ord, - { - self.data_mut().sort(); - } - - pub fn std(&self) -> T - where - T: Scalar, - { - self.variance().sqrt() - } - - pub fn variance(&self) -> T - where - T: Scalar, - { - let mean = self.mean(); - self.iter().map(|x| (*x - mean).powi(2)).sum::() / T::from_usize(self.size()).unwrap() - } -} - -impl Statistics for TensorBase -where - T: Ord + Scalar, -{ - fn max(&self) -> T { - *self.max() - } - - fn mean(&self) -> T { - self.mean() - } - - fn median(&self) -> T { - self.data().median() - } - - fn min(&self) -> T { - *self.min() - } - - fn mode(&self) -> T { - self.data().mode() - } - - fn sum(&self) -> T { - self.sum() - } - - fn std(&self) -> T { - self.std() - } - - fn variance(&self) -> T { - self.variance() - } -} diff --git a/tensor/src/stats/mod.rs b/tensor/src/stats/mod.rs deleted file mode 100644 index 8e8d57b7..00000000 --- a/tensor/src/stats/mod.rs +++ /dev/null @@ -1,101 +0,0 @@ -/* - Appellation: stats - Contrib: FL03 -*/ - -mod impl_stats; - -use crate::prelude::{Axis, Scalar}; -#[cfg(not(feature = "std"))] -use alloc::collections::BTreeMap; -#[cfg(feature = "std")] -use std::collections::BTreeMap; - -/// A trait describing the behavior of a collection of values that can be used to compute statistics. -pub trait Statistics { - /// Returns the maximum value in the collection. - fn max(&self) -> T; - /// Returns the mean (average) value of the collection. - fn mean(&self) -> T; - /// Returns the median value in the collection. - fn median(&self) -> T; - /// Returns the minimum value in the collection. - fn min(&self) -> T; - /// Get the mode of the collection. - fn mode(&self) -> T; - - fn sum(&self) -> T; - /// Compute the standard deviation - fn std(&self) -> T; - /// Compute the variance - fn variance(&self) -> T; -} - -macro_rules! impl_stats { - ($container:ty, $size:ident) => { - impl Statistics for $container - where - Self: Clone, - T: Ord + Scalar, - { - fn max(&self) -> T { - self.iter().max().unwrap().clone() - } - - fn mean(&self) -> T { - self.sum() / T::from_usize(self.$size()).unwrap() - } - - fn median(&self) -> T { - let mut sorted = self.clone(); - sorted.sort(); - let mid = sorted.$size() / 2; - if sorted.$size() % 2 == 0 { - (sorted[mid - 1] + sorted[mid]) / T::from_usize(2).unwrap() - } else { - sorted[mid] - } - } - - fn min(&self) -> T { - self.iter().min().unwrap().clone() - } - - fn mode(&self) -> T { - let mut freqs = BTreeMap::new(); - for &val in self.iter() { - *freqs.entry(val).or_insert(0) += 1; - } - let max_freq = freqs.values().max().unwrap(); - *freqs.iter().find(|(_, &freq)| freq == *max_freq).unwrap().0 - } - - fn sum(&self) -> T { - self.iter().copied().sum() - } - - fn std(&self) -> T { - self.variance().sqrt() - } - - fn variance(&self) -> T { - let sqr = |x| x * x; - let mean = self.mean(); - self.iter().map(|x| sqr(*x - mean)).sum::() - / T::from_usize(self.$size()).unwrap() - } - } - }; -} -impl_stats!(Vec, len); -pub trait StatisticsExt: Statistics { - /// Compute the mean along the specified axis. - fn mean_axis(&self, axis: Axis) -> T; -} - -pub(crate) mod prelude { - pub use super::{Statistics, StatisticsExt}; -} - -#[cfg(test)] -mod tests {} diff --git a/tensor/src/tensor.rs b/tensor/src/tensor.rs deleted file mode 100644 index da9b0a4f..00000000 --- a/tensor/src/tensor.rs +++ /dev/null @@ -1,535 +0,0 @@ -/* - Appellation: tensor - Contrib: FL03 -*/ -use crate::actions::iter::{Iter, IterMut}; -use crate::error::{TensorError, TensorResult}; -use crate::ops::{BackpropOp, TensorExpr}; -use crate::prelude::{TensorId, TensorKind}; -use crate::shape::{IntoShape, IntoStride, Layout, Rank, Shape, Stride}; - -#[cfg(not(feature = "std"))] -use alloc::vec::{self, Vec}; -use core::iter::Map; -use core::ops::{Index, IndexMut}; -use core::slice::Iter as SliceIter; -#[cfg(feature = "std")] -use std::vec; - -pub(crate) fn create( - kind: impl Into, - op: impl Into>, - shape: impl IntoShape, - data: Vec, -) -> TensorBase { - TensorBase { - id: TensorId::new(), - data, - kind: kind.into(), - layout: Layout::contiguous(shape), - op: op.into(), - } -} -#[allow(dead_code)] -pub(crate) fn from_scalar_with_op( - kind: impl Into, - op: TensorExpr, - data: T, -) -> TensorBase { - create( - kind.into(), - BackpropOp::new(op), - Shape::scalar(), - vec![data], - ) -} - -pub(crate) fn from_vec_with_kind( - kind: impl Into, - shape: impl IntoShape, - data: Vec, -) -> TensorBase { - create(kind, BackpropOp::none(), shape, data) -} - -pub(crate) fn from_vec_with_op( - kind: impl Into, - op: TensorExpr, - shape: impl IntoShape, - data: Vec, -) -> TensorBase { - create(kind.into(), BackpropOp::new(op), shape, data) -} - -#[derive(Clone, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct TensorBase { - pub(crate) id: TensorId, - pub(crate) data: Vec, - pub(crate) kind: TensorKind, - pub(crate) layout: Layout, - pub(crate) op: BackpropOp, -} - -impl TensorBase { - /// Create a new tensor from an iterator. - pub fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - Self::from_vec(Vec::from_iter(iter)) - } - pub unsafe fn from_raw_parts( - ptr: *mut T, - shape: impl IntoShape, - stride: impl IntoStride, - ) -> Self { - let shape = shape.into_shape(); - let stride = stride.into_stride(); - - let data = Vec::from_raw_parts(ptr, shape.size(), shape.size()); - Self { - id: TensorId::new(), - kind: TensorKind::default(), - layout: Layout::new(0, shape, stride), - data, - op: BackpropOp::none(), - } - } - /// Create a new tensor from a scalar value. - pub fn from_scalar(value: T) -> Self { - Self { - id: TensorId::new(), - data: vec![value], - kind: TensorKind::default(), - layout: Layout::contiguous(()), - op: None.into(), - } - } - /// Create a new tensor from an iterator, with a particular shape. - pub fn from_shape_iter(shape: impl IntoShape, iter: I) -> Self - where - I: IntoIterator, - { - Self::from_shape_vec(shape, Vec::from_iter(iter)) - } - pub unsafe fn from_shape_ptr(shape: impl IntoShape, ptr: *mut T) -> Self { - let layout = Layout::contiguous(shape); - let data = Vec::from_raw_parts(ptr, layout.size(), layout.size()); - Self { - id: TensorId::new(), - kind: TensorKind::default(), - layout: layout.clone(), - data, - op: BackpropOp::none(), - } - } - /// Create a new tensor from a [Vec], with a specified [shape](Shape). - pub fn from_shape_vec(shape: impl IntoShape, data: Vec) -> Self { - Self { - id: TensorId::new(), - data, - kind: TensorKind::default(), - layout: Layout::contiguous(shape), - op: BackpropOp::none(), - } - } - /// Create a new, one-dimensional tensor from a [Vec]. - pub fn from_vec(data: Vec) -> Self { - let shape = Shape::from(data.len()); - Self { - id: TensorId::new(), - data, - kind: TensorKind::default(), - layout: Layout::contiguous(shape), - op: BackpropOp::none(), - } - } - /// Return a mutable pointer to the tensor's data. - pub fn as_mut_ptr(&mut self) -> *mut T { - self.data_mut().as_mut_ptr() - } - /// Return a pointer to the tensor's data. - pub fn as_ptr(&self) -> *const T { - self.data().as_ptr() - } - /// Return a reference to the tensor's data. - pub fn as_slice(&self) -> &[T] { - &self.data - } - /// Return a mutable reference to the tensor's data. - pub fn as_mut_slice(&mut self) -> &mut [T] { - &mut self.data - } - /// Assign the values of another tensor to this tensor. - pub fn assign(&mut self, other: &Self) - where - T: Clone, - { - self.data_mut() - .iter_mut() - .zip(other.data()) - .for_each(|(a, b)| *a = b.clone()); - } - - pub fn boxed(self) -> Box { - Box::new(self) - } - /// Detach the computational graph from the tensor - pub fn detach(&self) -> Self - where - T: Clone, - { - if self.op.is_none() && !self.is_variable() { - self.clone() - } else { - Self { - id: self.id, - kind: self.kind, - layout: self.layout.clone(), - op: BackpropOp::none(), - data: self.data.clone(), - } - } - } - /// Returns a reference to the first element of the tensor. - pub fn first(&self) -> Option<&T> { - let pos = vec![0; *self.rank()]; - self.get(pos) - } - /// Returns a mutable reference to the first element of the tensor. - pub fn first_mut(&mut self) -> Option<&mut T> { - let pos = vec![0; *self.rank()]; - self.get_mut(pos) - } - /// Returns the data at the specified index. - pub fn get(&self, index: impl AsRef<[usize]>) -> Option<&T> { - let i = self.layout.index(index); - self.data().get(i) - } - /// Returns a mutable reference to the data at the specified index. - pub fn get_mut(&mut self, index: impl AsRef<[usize]>) -> Option<&mut T> { - let i = self.layout.index(index); - self.data_mut().get_mut(i) - } - /// Returns the unique identifier of the tensor. - pub const fn id(&self) -> TensorId { - self.id - } - - pub unsafe fn into_scalar(self) -> T - where - T: Clone, - { - debug_assert!(self.is_scalar(), "Tensor is not scalar"); - self.data.first().unwrap().clone() - } - /// Returns true if the tensor is contiguous. - pub fn is_contiguous(&self) -> bool { - self.layout().is_contiguous() - } - /// Returns true if the tensor is empty. - pub fn is_empty(&self) -> bool { - self.data().is_empty() - } - /// A function to check if the tensor is a scalar - pub fn is_scalar(&self) -> bool { - *self.rank() == 0 - } - /// Returns true if the tensor is a square matrix. - pub fn is_square(&self) -> bool { - self.shape().is_square() - } - /// A function to check if the tensor is a variable - pub const fn is_variable(&self) -> bool { - self.kind().is_variable() - } - /// Creates an immutable iterator over the elements in the tensor. - pub fn iter(&self) -> Iter<'_, T> { - Iter::new(self.view()) - } - /// Create a mutable iterator over the elements in the tensor. - pub fn iter_mut(&mut self) -> IterMut<'_, T> { - IterMut::new(self) - } - /// Get the kind of the tensor - pub const fn kind(&self) -> TensorKind { - self.kind - } - /// Get a reference to the last element of the tensor - pub fn last(&self) -> Option<&T> { - let pos = self.shape().get_final_position(); - self.get(pos) - } - /// Get a mutable reference to the last element of the tensor - pub fn last_mut(&mut self) -> Option<&mut T> { - let pos = self.shape().get_final_position(); - self.get_mut(pos) - } - /// Get a reference to the [Layout] of the tensor - pub const fn layout(&self) -> &Layout { - &self.layout - } - /// Get the number of columns in the tensor - pub fn ncols(&self) -> usize { - self.shape().ncols() - } - /// Get the number of rows in the tensor - pub fn nrows(&self) -> usize { - self.shape().nrows() - } - /// Get a reference to the operation of the tensor - pub const fn op(&self) -> &BackpropOp { - &self.op - } - /// Get a reference to the operation of the tensor - pub fn op_view(&self) -> BackpropOp<&T> { - self.op().view() - } - /// Get an owned reference to the [Rank] of the tensor - pub fn rank(&self) -> Rank { - self.shape().rank() - } - /// Set the value of the tensor at the specified index - pub fn set(&mut self, index: impl AsRef<[usize]>, value: T) { - let i = self.layout().index(index); - self.data_mut()[i] = value; - } - /// An owned reference of the tensors [Shape] - pub fn shape(&self) -> &Shape { - self.layout().shape() - } - /// Returns the number of elements in the tensor. - pub fn size(&self) -> usize { - self.layout().size() - } - /// Get a reference to the stride of the tensor - pub fn strides(&self) -> &Stride { - self.layout().strides() - } - /// Turn the tensor into a scalar - /// If the tensor has a rank greater than 0, this will return an error - pub fn to_scalar(&self) -> TensorResult<&T> { - if !self.is_scalar() { - return Err(TensorError::NotScalar); - } - Ok(self.first().unwrap()) - } - /// Turn the tensor into a one-dimensional vector - pub fn to_vec(&self) -> Vec - where - T: Clone, - { - self.data().to_vec() - } - /// Changes the kind of tensor to a variable - pub fn variable(mut self) -> Self { - self.kind = TensorKind::Variable; - self - } - /// Set the layout of the tensor - pub fn with_layout(self, layout: Layout) -> Self { - if layout.size() != self.size() { - panic!("Size mismatch"); - } - unsafe { self.with_layout_unchecked(layout) } - } - /// Set the layout of the tensor without checking for compatibility - /// - /// # Safety - /// - /// This function is unsafe because it does not check if the layout is compatible with the tensor. - pub unsafe fn with_layout_unchecked(mut self, layout: Layout) -> Self { - self.layout = layout; - self - } - - pub fn with_op(mut self, op: BackpropOp) -> Self { - self.op = op; - self - } - - pub fn with_shape_c(mut self, shape: impl IntoShape) -> Self { - self.layout = self.layout.with_shape_c(shape); - self - } -} - -impl<'a, T> TensorBase<&'a T> { - // pub fn as_tensor(&self) -> TensorBase where T: Copy { - // let store = self.data.iter().copied().collect(); - // TensorBase { - // id: self.id, - // kind: self.kind, - // layout: self.layout.clone(), - // op: self.op.clone(), - // data: store, - // } - // } -} - -impl TensorBase { - pub fn view_from_scalar(scalar: &T) -> TensorBase<&T> { - TensorBase { - id: TensorId::new(), - kind: TensorKind::default(), - layout: Layout::scalar(), - op: BackpropOp::none(), - data: vec![scalar], - } - } - pub fn to_owned(&self) -> TensorBase - where - T: Clone, - { - self.clone() - } - - pub fn view(&self) -> TensorBase<&T> { - TensorBase { - id: self.id(), - kind: self.kind(), - layout: self.layout().clone(), - op: self.op().view(), - data: self.data().iter().collect(), - } - } - - pub fn view_mut(&mut self) -> TensorBase<&mut T> { - TensorBase { - id: self.id(), - kind: self.kind(), - layout: self.layout().clone(), - op: self.op.view_mut(), - data: self.data.iter_mut().collect(), - } - } -} -// Inernal Methods -#[allow(dead_code)] -impl TensorBase { - pub(crate) fn data(&self) -> &Vec { - &self.data - } - - pub(crate) fn data_mut(&mut self) -> &mut Vec { - &mut self.data - } - - pub(crate) fn get_by_index(&self, index: usize) -> Option<&T> { - self.data.get(index) - } - - pub(crate) fn get_mut_by_index(&mut self, index: usize) -> Option<&mut T> { - self.data.get_mut(index) - } - - pub(crate) fn map<'a, F>(&'a self, f: F) -> Map, F> - where - F: FnMut(&'a T) -> T, - T: 'a + Clone, - { - self.data.iter().map(f) - } - - pub(crate) fn mapv(&self, f: F) -> TensorBase - where - F: Fn(T) -> T, - T: Copy, - { - let store = self.data.iter().copied().map(f).collect(); - TensorBase { - id: TensorId::new(), - kind: self.kind, - layout: self.layout.clone(), - op: self.op.clone(), - data: store, - } - } - - pub(crate) fn map_binary(&self, other: &TensorBase, op: F) -> TensorBase - where - F: acme::prelude::BinOp, - T: Copy, - { - let store = self - .iter() - .zip(other.iter()) - .map(|(a, b)| op.eval(*a, *b)) - .collect(); - TensorBase { - id: TensorId::new(), - kind: self.kind, - layout: self.layout.clone(), - op: self.op.clone(), - data: store, - } - } -} - -impl<'a, T> AsRef> for TensorBase<&'a T> { - fn as_ref(&self) -> &TensorBase { - unsafe { &*(self as *const TensorBase<&'a T> as *const TensorBase) } - } -} - -impl Index for TensorBase -where - Idx: AsRef<[usize]>, -{ - type Output = T; - - fn index(&self, index: Idx) -> &Self::Output { - let i = self.layout().index(index); - &self.data[i] - } -} - -impl IndexMut for TensorBase -where - Idx: AsRef<[usize]>, -{ - fn index_mut(&mut self, index: Idx) -> &mut Self::Output { - let i = self.layout().index(index); - &mut self.data[i] - } -} - -impl Eq for TensorBase where T: Eq {} - -impl Ord for TensorBase -where - T: Ord, -{ - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.data.cmp(&other.data) - } -} - -impl PartialEq for TensorBase -where - T: PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.layout == other.layout && self.data == other.data - } -} - -impl PartialEq for TensorBase -where - S: AsRef<[T]>, - T: PartialEq, -{ - fn eq(&self, other: &S) -> bool { - &self.data == other.as_ref() - } -} - -impl PartialOrd for TensorBase -where - T: PartialOrd, -{ - fn partial_cmp(&self, other: &Self) -> Option { - self.data.partial_cmp(&other.data) - } -} diff --git a/tensor/src/types/id.rs b/tensor/src/types/id.rs deleted file mode 100644 index 4bebd351..00000000 --- a/tensor/src/types/id.rs +++ /dev/null @@ -1,88 +0,0 @@ -/* - Appellation: id - Contrib: FL03 -*/ -//! # Tensor Id -//! -//! -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use std::ops::{Deref, DerefMut}; - -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize,))] -pub struct TensorId(usize); - -impl TensorId { - pub fn new() -> Self { - use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; - static COUNTER: AtomicUsize = AtomicUsize::new(1); - Self(COUNTER.fetch_add(1, Relaxed)) - } - - pub fn next(&self) -> Self { - Self::new() - } - - pub fn set(&mut self, id: usize) { - self.0 = id; - } - - pub fn get(&self) -> usize { - self.0 - } - - pub fn into_inner(self) -> usize { - self.0 - } -} - -impl AsRef for TensorId { - fn as_ref(&self) -> &usize { - &self.0 - } -} - -impl AsMut for TensorId { - fn as_mut(&mut self) -> &mut usize { - &mut self.0 - } -} - -impl Default for TensorId { - fn default() -> Self { - Self::new() - } -} - -impl Deref for TensorId { - type Target = usize; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for TensorId { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl std::fmt::Display for TensorId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for TensorId { - fn from(id: usize) -> Self { - Self(id) - } -} - -impl From for usize { - fn from(id: TensorId) -> Self { - id.0 - } -} diff --git a/tensor/src/types/kinds.rs b/tensor/src/types/kinds.rs deleted file mode 100644 index 26409665..00000000 --- a/tensor/src/types/kinds.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* - Appellation: mode - Contrib: FL03 -*/ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Default, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - VariantNames, -)] -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize,), - serde(rename_all = "lowercase", untagged) -)] -#[repr(u8)] -#[strum(serialize_all = "lowercase")] -pub enum TensorKind { - #[default] - Normal = 0, - Variable = 1, -} - -impl TensorKind { - pub fn normal() -> Self { - Self::Normal - } - - pub fn variable() -> Self { - Self::Variable - } -} - -impl From for usize { - fn from(mode: TensorKind) -> Self { - mode as usize - } -} - -impl From for TensorKind { - fn from(mode: usize) -> Self { - match mode % Self::COUNT { - 0 => Self::Normal, - _ => Self::Variable, - } - } -} - -impl From for TensorKind { - fn from(is_variable: bool) -> Self { - if is_variable { - Self::Variable - } else { - Self::Normal - } - } -} diff --git a/tensor/src/types/mod.rs b/tensor/src/types/mod.rs deleted file mode 100644 index 9c28b150..00000000 --- a/tensor/src/types/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -/* - Appellation: types - Contrib: FL03 -*/ -pub use self::{id::*, kinds::*, order::*, tensors::*}; - -pub(crate) mod id; -pub(crate) mod kinds; -pub(crate) mod order; -pub(crate) mod tensors; - -pub(crate) mod prelude { - pub use super::id::*; - pub use super::kinds::*; - pub use super::order::*; - pub use super::tensors::*; -} diff --git a/tensor/src/types/order.rs b/tensor/src/types/order.rs deleted file mode 100644 index 9180ab95..00000000 --- a/tensor/src/types/order.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - Appellation: order - Contrib: FL03 -*/ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[cfg_attr( - feature = "serde", - derive(Deserialize, Serialize,), - serde(rename_all = "snake_case", untagged) -)] -#[derive( - Clone, - Copy, - Debug, - Default, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - VariantNames, -)] -#[repr(u8)] -#[strum(serialize_all = "snake_case")] -pub enum Order { - Column, - #[default] - Row, -} - -impl Order { - pub fn column() -> Self { - Self::Column - } - - pub fn row() -> Self { - Self::Row - } -} - -impl From for usize { - fn from(order: Order) -> Self { - order as usize - } -} - -impl From for Order { - fn from(order: usize) -> Self { - match order % Self::COUNT { - 0 => Self::Column, - _ => Self::Row, - } - } -} diff --git a/tensor/src/types/tensors.rs b/tensor/src/types/tensors.rs deleted file mode 100644 index 552c6809..00000000 --- a/tensor/src/types/tensors.rs +++ /dev/null @@ -1,79 +0,0 @@ -/* - Appellation: tensors - Contrib: FL03 -*/ -use crate::shape::Rank; -use crate::tensor::TensorBase; -use strum::{Display, EnumCount, EnumDiscriminants, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive(Clone, Debug, EnumCount, EnumDiscriminants, EnumIs, Eq, PartialEq)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(rename_all = "lowercase"), - strum_discriminants(derive(serde::Deserialize, serde::Serialize)) -)] -#[repr(C)] -#[strum(serialize_all = "lowercase")] -#[strum_discriminants( - derive( - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Hash, - Ord, - PartialOrd, - VariantNames - ), - name(TensorType) -)] -pub enum Tensors { - Scalar(T), - Tensor(TensorBase), -} - -impl Tensors { - pub fn scalar(scalar: T) -> Self { - Self::Scalar(scalar) - } - - pub fn tensor(tensor: TensorBase) -> Self { - Self::Tensor(tensor) - } - - pub fn rank(&self) -> Rank { - match self { - Self::Tensor(tensor) => tensor.rank(), - _ => Rank::scalar(), - } - } -} - -impl From> for Tensors -where - T: Clone, -{ - fn from(tensor: TensorBase) -> Self { - if tensor.rank().is_scalar() { - Self::Scalar(unsafe { tensor.into_scalar() }) - } else { - Self::Tensor(tensor) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_tensor_type() { - let shape = (2, 3); - let tensor = TensorBase::::ones(shape); - let item = Tensors::tensor(tensor); - - assert_eq!(item.rank(), Rank::from(2)); - } -} diff --git a/tensor/src/utils.rs b/tensor/src/utils.rs deleted file mode 100644 index 4d0446dc..00000000 --- a/tensor/src/utils.rs +++ /dev/null @@ -1,108 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 -*/ -use crate::prelude::{Scalar, TensorExpr, TensorResult}; -use crate::shape::{ShapeError, Stride}; -use crate::tensor::{from_vec_with_op, TensorBase}; - -pub(crate) fn coordinates_to_index(coords: Idx, strides: &Stride) -> usize -where - Idx: AsRef<[usize]>, -{ - coords - .as_ref() - .iter() - .zip(strides.iter()) - .fold(0, |acc, (&i, &s)| acc + i * s) -} - -pub fn matmul(lhs: &TensorBase, rhs: &TensorBase) -> TensorResult> -where - T: Scalar, -{ - if lhs.shape().rank() != rhs.shape().rank() { - return Err(ShapeError::DimensionMismatch.into()); - } - - let shape = lhs.shape().matmul_shape(rhs.shape()).unwrap(); - let mut result = vec![T::zero(); shape.size()]; - - for i in 0..lhs.shape().nrows() { - for j in 0..rhs.shape().ncols() { - for k in 0..lhs.shape().ncols() { - let pos = i * rhs.shape().ncols() + j; - let left = i * lhs.shape().ncols() + k; - let right = k * rhs.shape().ncols() + j; - result[pos] += lhs.data[left] * rhs.data[right]; - } - } - } - let op = TensorExpr::matmul(lhs.clone(), rhs.clone()); - let tensor = from_vec_with_op(false, op, shape, result); - Ok(tensor) -} - -macro_rules! i { - ($($x:expr),*) => { - vec![$($x),*] - }; - -} - -macro_rules! impl_partial_eq { - ($s:ident -> $cmp:tt: [$($t:ty),*]) => { - $( - impl_partial_eq!($s -> $cmp, $t); - )* - }; - ($s:ident -> $cmp:tt, $t:ty) => { - impl PartialEq<$t> for $s { - fn eq(&self, other: &$t) -> bool { - self.$cmp == *other - } - } - - impl PartialEq<$s> for $t { - fn eq(&self, other: &$s) -> bool { - *self == other.$cmp - } - } - }; -} - -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 )*) - ) - }; -} diff --git a/tensor/tests/arith.rs b/tensor/tests/arith.rs deleted file mode 100644 index 29b93913..00000000 --- a/tensor/tests/arith.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - Appellation: arith - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::prelude::Tensor; - -#[test] -fn test_add() { - let shape = (2, 2); - let a = Tensor::::ones(shape); - let b = Tensor::::ones(shape); - let c = &a + &b; - - assert_eq!(c, Tensor::fill(shape, 2_f64)); - - let a = Tensor::::ones(shape); - let b = a + 1_f64; - assert_eq!(b, Tensor::fill(shape, 2_f64)); -} - -#[test] -fn test_div() { - let shape = (2, 2); - let a = Tensor::::ones(shape); - let b = Tensor::::fill(shape, 2_f64); - let c = a / b; - - assert_eq!(c, Tensor::::fill(shape, 0.5)); -} - -#[test] -fn test_mul() { - let shape = (2, 2); - let a = Tensor::::ones(shape); - let b = Tensor::::ones(shape); - let c = a * b; - - assert_eq!(c, Tensor::::ones(shape)); -} - -#[test] -fn test_sub() { - let shape = (2, 2); - let a = Tensor::::ones(shape); - let b = Tensor::::ones(shape); - let c = a - &b; - - assert_eq!(c, Tensor::::zeros(shape)); -} - -#[test] -fn test_trig() { - let a = Tensor::::ones((2, 2)); - let b = a.sin(); - let c = a.cos(); - - assert_eq!(b[[0, 0]], 1_f64.sin()); - assert_eq!(c[[0, 0]], 1_f64.cos()); -} diff --git a/tensor/tests/backward.rs b/tensor/tests/backward.rs deleted file mode 100644 index b9e400b7..00000000 --- a/tensor/tests/backward.rs +++ /dev/null @@ -1,155 +0,0 @@ -/* - Appellation: backward - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::prelude::{IntoShape, Tensor, TensorKind}; -use acme_core::prelude::Scalar; -use core::ops::Neg; - -fn _shapespace(shape: impl IntoShape) -> Tensor -where - T: PartialOrd + Scalar, -{ - let shape = shape.into_shape(); - Tensor::::linspace(T::zero(), T::from(shape.size()).unwrap(), shape.size()) - .reshape(shape) - .unwrap() -} - -#[test] -fn test_backward() { - let shape = (2, 2); - let a = Tensor::::ones(shape).variable(); - let grad = a.grad().unwrap(); - - assert_eq!(grad[&a.id()], Tensor::ones(shape),); -} - -#[test] -fn test_addition() { - let shape = (2, 2); - let a = Tensor::::ones(shape).variable(); - let b = Tensor::::ones(shape).variable(); - let c = &a + &b; - let grad = c.grad().unwrap(); - - assert_eq!(grad[&a.id()], Tensor::ones(shape)); - assert_eq!(grad[&b.id()], Tensor::ones(shape)); -} - -#[test] -fn test_addition_2() { - let shape = (2, 2); - let a = Tensor::::ones(shape).variable(); - let b = Tensor::::ones(shape).variable(); - let c = Tensor::::ones(shape).variable(); - let d = &a + &b + &c; - - assert_eq!(&d, &Tensor::fill(shape, 3_f64)); - - let grad = d.grad().unwrap(); - - for i in [a.id(), b.id(), c.id()].iter() { - assert_eq!(grad[i], Tensor::ones(shape)); - } -} - -#[test] -fn test_division() { - let shape = (2, 2); - - let a = Tensor::::ones(shape).variable(); - let b = Tensor::::fill(shape, 2.0).variable(); - let c = &a / &b; - - let grad = c.grad().unwrap(); - - assert_eq!(grad[&a.id()], Tensor::fill(shape, 0.5)); - assert_eq!(grad[&b.id()], Tensor::fill(shape, -0.25)); -} - -#[test] -fn test_multiplication() { - let shape = (2, 2); - - let a = Tensor::::ones(shape).variable(); - let b = Tensor::::fill(shape, 2_f64).variable(); - let c = &a * &b; - - let grad = c.grad().unwrap(); - - assert_eq!(grad[&a.id()], Tensor::fill(shape, 2_f64)); - assert_eq!(grad[&b.id()], Tensor::ones(shape)); -} - -#[test] -fn test_subtraction() { - let shape = (2, 2); - - let a = Tensor::::ones(shape).variable(); - let b = Tensor::::fill(shape, 2_f64).variable(); - let c = &a - &b; - - let grad = c.grad().unwrap(); - - assert_eq!(grad[&a.id()], Tensor::ones(shape)); - assert_eq!(grad[&b.id()], Tensor::ones(shape).neg()); -} - -#[test] -fn test_mixed() { - let shape = (2, 2); - - let a = Tensor::::ones(shape).variable(); - let b = Tensor::::fill(shape, 2f64).variable(); - - let res = &b * (&a + &b); - - let grad = res.grad().unwrap(); - - assert_eq!(grad[&a.id()], Tensor::fill(shape, 2f64)); - assert_eq!(grad[&b.id()], Tensor::fill(shape, 5f64)); -} - -#[test] -fn test_complex_expr() { - let shape = (2, 2); - - let a = Tensor::::ones(shape).variable(); - let b = Tensor::fill(shape, 2f64).variable(); - let c = Tensor::fill(shape, 3f64).variable(); - let res = (&a + &b) * c.sin() + &b; - - let grad = res.grad().unwrap(); - assert_eq!(grad[&a.id()], c.sin()); - assert_eq!(grad[&b.id()], c.sin() + 1f64); - assert_eq!(grad[&c.id()], (&a + &b) * c.cos()); -} - -#[test] -#[ignore = "Fix: test throws an error"] -fn test_sigmoid() { - use acme::prelude::ScalarExt; - let shape = (2, 2).into_shape(); - let data = (0..shape.size()).map(|x| x as f64).collect::>(); - let a = Tensor::::from_shape_vec(shape.clone(), data.clone()).variable(); - - let _res = a.sigmoid(); - let grad = _res.grad().unwrap(); - - assert_eq!(a.kind(), TensorKind::Variable); - - let exp = Tensor::from_shape_iter( - shape, - data.iter().map(|x| x.sigmoid() * (1f64 - x.sigmoid())), - ); - println!("{:?}", &grad); - assert_eq!( - grad[&a.id()], - exp.detach(), - "Gradient of sigmoid is incorrect" - ); -} diff --git a/tensor/tests/composition.rs b/tensor/tests/composition.rs deleted file mode 100644 index 24f4dec5..00000000 --- a/tensor/tests/composition.rs +++ /dev/null @@ -1,58 +0,0 @@ -/* - Appellation: composition - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::prelude::{Shape, Tensor}; - -#[test] -fn test_ones_and_zeros() { - let shape = (2, 2); - let a = Tensor::::ones(shape); - let b = a.zeros_like(); - - assert_ne!(&a, &b); - assert_ne!(a.id(), b.id()); - assert_eq!(a.shape(), b.shape()); - assert_eq!(a.size(), b.size()); - assert_eq!(a.strides(), b.strides()); - assert_eq!(a, Tensor::ones(shape)); - assert_eq!(b, Tensor::zeros(shape)); - - use num::traits::{One, Zero}; - - assert!(Tensor::::one().is_scalar()); - assert!(Tensor::::zero().is_scalar()); -} - -#[test] -fn test_arange() { - let exp = Shape::from(10); - let a = Tensor::arange(0_f64, 10_f64, 1_f64); - assert_eq!(a.shape(), &exp); - - for i in 0..10 { - assert_eq!(a[&[i]], i as f64); - } -} - -#[test] -fn test_linstep() { - let exp = Shape::from(10); - let a = Tensor::linspace(0_f64, 10_f64, 10); - assert_eq!(a.shape(), &exp); - let b = Tensor::arange(0_f64, 10_f64, 1_f64); - for i in 0..10 { - assert_eq!(a[&[i]], b[&[i]]); - } -} - -#[test] -fn test_fill() { - let shape = (2, 2); - let a = Tensor::fill(shape, 1_f64); - let b = Tensor::ones(shape); - assert_eq!(&a, &b); -} diff --git a/tensor/tests/default.rs b/tensor/tests/default.rs deleted file mode 100644 index d1c044d2..00000000 --- a/tensor/tests/default.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - Appellation: default - Contrib: FL03 -*/ -#![cfg(test)] - -fn addition(a: A, b: B) -> C -where - A: std::ops::Add, -{ - a + b -} - -#[test] -fn compiles() { - let result = addition(2, 2); - assert_eq!(result, 4); -} diff --git a/tensor/tests/iter.rs b/tensor/tests/iter.rs deleted file mode 100644 index 7feb5e43..00000000 --- a/tensor/tests/iter.rs +++ /dev/null @@ -1,94 +0,0 @@ -/* - Appellation: iter - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::create::Linspace; -use acme::prelude::{IntoShape, Layout, Shape, Tensor}; -use num::traits::{FromPrimitive, Num}; - -fn linvec(n: usize) -> (Vec, usize) -where - T: Copy + Default + FromPrimitive + Num + PartialOrd, -{ - let space = Vec::linspace(T::zero(), T::from_usize(n).unwrap(), n); - (space, n) -} - -#[test] -fn test_layout_iter() { - let shape = (2, 2).into_shape(); - let layout = Layout::contiguous(shape); - let exp = [vec![0usize, 0], vec![0, 1], vec![1, 0], vec![1, 1]]; - for (pos, exp) in layout.iter().zip(exp.iter()) { - assert_eq!(pos.position(), *exp); - } - for (pos, exp) in layout.iter().rev().zip(exp.iter().rev()) { - assert_eq!(pos.position(), *exp); - } -} - -#[test] -fn test_iter() { - let shape = Shape::from_iter([2, 2, 2, 2]); - let (exp, n) = linvec::(shape.size()); - let tensor = Tensor::linspace(0f64, n as f64, n) - .reshape(shape.clone()) - .unwrap(); - assert_eq!(&tensor, &exp); - - let mut tensor = Tensor::zeros(shape); - for (elem, val) in tensor.iter_mut().zip(exp.iter()) { - *elem = *val; - } - assert_eq!(&tensor, &exp); -} -#[test] -fn test_iter_scalar() { - let exp = 10f64; - let tensor = Tensor::from_scalar(exp); - let mut iter = tensor.iter(); - assert_eq!(iter.next(), Some(&exp)); - assert_eq!(iter.next(), None); -} - -#[test] -fn test_iter_mut_rev() { - let shape = Shape::from_iter([2, 2, 2, 2]); - let n = shape.size(); - let exp = Vec::linspace(0f64, n as f64, n); - let rev = exp.iter().rev().copied().collect::>(); - let mut tensor = Tensor::zeros(shape); - for (elem, val) in tensor.iter_mut().rev().zip(exp.iter()) { - *elem = *val; - } - assert_eq!(&tensor, &rev); -} - -#[test] -fn test_iter_rev() { - let shape = Shape::from_iter([2, 2]); - let n = shape.size(); - let exp = Vec::linspace(0f64, n as f64, n); - let tensor = Tensor::linspace(0f64, n as f64, n).reshape(shape).unwrap(); - - for (i, j) in tensor.iter().rev().zip(exp.iter().rev()) { - assert_eq!(i, j); - } -} - -#[test] -fn test_sum() { - let shape = (2, 2).into_shape(); - let a = Tensor::fill(shape, 1f64); - assert_eq!(a.sum(), 4.0); -} - -#[test] -fn test_product() { - let shape = (2, 2).into_shape(); - let a = Tensor::fill(shape, 2f64); - assert_eq!(a.product(), 16.0); -} diff --git a/tensor/tests/linalg.rs b/tensor/tests/linalg.rs deleted file mode 100644 index 6b16c7ac..00000000 --- a/tensor/tests/linalg.rs +++ /dev/null @@ -1,56 +0,0 @@ -/* - Appellation: linalg - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::prelude::{Matmul, Shape, Tensor}; - -macro_rules! adiff { - ($a:expr, $b:expr) => { - ($a - $b).abs() - }; -} -macro_rules! assert_diff { - ($a:expr, $b:expr, $tol:expr) => { - let diff = adiff!($a, $b); - assert!( - diff < $tol, - "the difference ({}) between {} and {} exceeds the allowed tolerance", - diff, - $a, - $b - ); - }; - ($a:expr, $b:expr) => { - assert_diff!($a, $b, 1e-10); - }; -} - -#[test] -fn test_inverse() { - let shape = Shape::from((2, 2)); - let arr: Vec = vec![1.0, 4.0, 3.0, 2.0]; - let tensor = Tensor::from_shape_vec(shape.clone(), arr); - let inv_arr = vec![-0.2, 0.4, 0.3, -0.1]; - let exp = Tensor::from_shape_vec(shape.clone(), inv_arr); - - let inverse = tensor.inv().unwrap(); - println!("{:?}", &inverse.to_vec()); - - for i in 0..shape.nrows() { - for j in 0..shape.ncols() { - assert_diff!(inverse[[i, j]], exp[[i, j]]); - } - } -} - -#[test] -fn test_matmul() { - let a = Tensor::::fill((3, 2), 2_f64); - let b = Tensor::::ones((2, 3)); - let c = a.matmul(&b); - - assert_eq!(c, Tensor::::fill((3, 3), 4.0)); -} diff --git a/tensor/tests/reshape.rs b/tensor/tests/reshape.rs deleted file mode 100644 index c2f129ee..00000000 --- a/tensor/tests/reshape.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* - Appellation: reshape - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::prelude::{Shape, Tensor}; - -#[test] -#[ignore = "Not implemented"] -fn test_broadcast() { - let shape = (4, 1); - let a = Tensor::::ones(shape); - let b = a.clone().broadcast((4, 1, 1)); - - assert_ne!(&a.shape(), &b.shape()); - assert_eq!(&a.size(), &b.size()); -} - -#[test] -fn test_reshape() { - let shape = (2, 2); - let a = Tensor::::ones(shape); - let b = a.clone().reshape((4,)).unwrap(); - - assert_ne!(a.rank(), b.rank()); - assert_ne!(a.shape(), b.shape()); - assert_eq!(a.size(), b.size()); -} - -#[test] -fn test_transpose() { - let shape = (2, 3); - let a = Tensor::::linspace(0f64, 6f64, 6) - .reshape(shape) - .unwrap(); - let at = a.t(); - - let exp = Tensor::from_shape_vec((3, 2), vec![0.0, 3.0, 1.0, 4.0, 2.0, 5.0]); - assert_ne!(&a, &at); - assert_eq!(at.shape(), &Shape::new(vec![3, 2])); - for (i, j) in at.iter().zip(exp.iter()) { - assert_eq!(i, j); - } -} diff --git a/tensor/tests/stats.rs b/tensor/tests/stats.rs deleted file mode 100644 index ee351d29..00000000 --- a/tensor/tests/stats.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Appellation: stats - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::prelude::{Shape, Tensor}; - -macro_rules! adiff { - ($a:expr, $b:expr) => { - ($a - $b).abs() - }; -} -macro_rules! assert_diff { - ($a:expr, $b:expr, $tol:expr) => { - let diff = adiff!($a, $b); - assert!( - diff < $tol, - "the difference ({}) between {} and {} exceeds the allowed tolerance", - diff, - $a, - $b - ); - }; - ($a:expr, $b:expr) => { - assert_diff!($a, $b, 1e-10); - }; -} - -#[test] -fn test_std() { - let shape = Shape::from((2, 2)); - let tensor = Tensor::linspace(0f64, shape.size() as f64, shape.size()) - .reshape(shape) - .unwrap(); - let exp = 1.118033988749895; - assert_diff!(tensor.std(), exp); - assert_diff!(tensor.variance(), exp.powi(2)); -} diff --git a/tensor/tests/tensor.rs b/tensor/tests/tensor.rs deleted file mode 100644 index 1d5bc0f4..00000000 --- a/tensor/tests/tensor.rs +++ /dev/null @@ -1,99 +0,0 @@ -/* - Appellation: tensor - Contrib: FL03 -*/ -#![cfg(test)] -extern crate acme_tensor as acme; - -use acme::prelude::{IntoShape, Tensor}; - -#[test] -fn test_tensor() { - let shape = (2, 2); - let a = Tensor::::ones(shape); - let b = a.zeros_like(); - - assert_ne!(a.id(), b.id()); - assert_eq!(a.shape(), b.shape()); - assert_eq!(a.size(), b.size()); - assert_eq!(a.strides(), b.strides()); -} - -#[test] -#[ignore = "reason"] -fn test_raw_tensor() { - let shape = (2, 2).into_shape(); - let stride = shape.default_strides(); - let mut data = vec![1f64, 2f64, 3f64, 4f64]; - let exp = Tensor::::from_shape_vec(shape.clone(), data.clone()); - - unsafe { - let ptr = data.as_mut_ptr(); - let a = Tensor::::from_raw_parts(ptr, shape, stride); - assert_eq!(a, exp); - } -} - -#[test] -fn test_scalar_tensor() { - use num::{One, Zero}; - let one = Tensor::::one(); - let zero = Tensor::::zero(); - assert!(one.is_scalar()); - assert!(zero.is_scalar()); -} - -#[test] -fn test_first_and_last() { - let shape = (3, 3); - let tensor = Tensor::linspace(0f64, 9f64, 9).reshape(shape).unwrap(); - - assert_eq!(tensor.first(), Some(&0f64)); - assert_eq!(tensor.last(), Some(&8f64)); - - let shape = (3, 3, 1); - let tensor = Tensor::linspace(0f64, 9f64, 9).reshape(shape).unwrap(); - - assert_eq!(tensor.first(), Some(&0f64)); - assert_eq!(tensor.last(), Some(&8f64)); -} - -#[test] -fn test_index() { - let shape = (2, 3).into_shape(); - let n = shape.size(); - let a = Tensor::::linspace(0f64, n as f64, n) - .reshape(shape.clone()) - .unwrap(); - - assert_eq!(a[[0, 0]], 0f64); - assert_eq!(a[&[0, 1]], 1f64); - assert_eq!(a[shape.get_final_position()], 5f64); -} - -#[test] -fn test_higher_dim() { - let shape = (2, 2, 2, 2); - let a = Tensor::::ones(shape); - let b = a.zeros_like(); - - assert_ne!(a.id(), b.id()); - assert_eq!(a.shape(), b.shape()); - assert_eq!(a.size(), b.size()); - assert_eq!(a.strides(), b.strides()); - assert_eq!(a.strides().len(), 4); -} - -#[test] -fn test_sum() { - let shape = (2, 2).into_shape(); - let a = Tensor::fill(shape, 1f64); - assert_eq!(a.sum(), 4.0); -} - -#[test] -fn test_product() { - let shape = (2, 2).into_shape(); - let a = Tensor::fill(shape, 2f64); - assert_eq!(a.product(), 16.0); -}