Skip to main content

scirs2_autograd/
lib.rs

1#![allow(clippy::non_canonical_partial_ord_impl)]
2#![allow(clippy::module_inception)]
3#![allow(clippy::manual_div_ceil)]
4#![allow(clippy::let_and_return)]
5#![allow(clippy::needless_range_loop)]
6#![allow(clippy::useless_vec)]
7//! # SciRS2 Autograd - Automatic Differentiation for Rust
8#![recursion_limit = "1024"]
9//!
10//! **scirs2-autograd** provides PyTorch-style automatic differentiation with lazy tensor evaluation,
11//! enabling efficient gradient computation for scientific computing and deep learning.
12//!
13//! ## šŸŽÆ Key Features
14//!
15//! - **Reverse-mode Autodiff**: Efficient backpropagation for neural networks
16//! - **Lazy Evaluation**: Build computation graphs, evaluate only when needed
17//! - **Higher-order Gradients**: Compute derivatives of derivatives
18//! - **Neural Network Ops**: Optimized operations for deep learning
19//! - **Optimizers**: Adam, SGD, RMSprop with state management
20//! - **Model Persistence**: Save and load trained models
21//! - **Variable Management**: Namespace-based variable organization
22//!
23//! ## šŸ“¦ Installation
24//!
25//! ```toml
26//! [dependencies]
27//! scirs2-autograd = { version = "0.1.5", features = ["blas"] }
28//! ```
29//!
30//! ### BLAS Acceleration (Recommended)
31//!
32//! For fast matrix operations, enable BLAS (uses OxiBLAS - pure Rust):
33//!
34//! ```toml
35//! [dependencies]
36//! scirs2-autograd = { version = "0.1.5", features = ["blas"] }
37//! ```
38//!
39//! ## šŸš€ Quick Start
40//!
41//! ### Basic Differentiation
42//!
43//! Compute gradients of a simple function:
44//!
45//! ```rust
46//! use scirs2_autograd as ag;
47//! use ag::tensor_ops as T;
48//!
49//! ag::run(|ctx: &mut ag::Context<f64>| {
50//!     // Define variables
51//!     let x = ctx.placeholder("x", &[]);
52//!     let y = ctx.placeholder("y", &[]);
53//!
54//!     // Build computation graph: z = 2x² + 3y + 1
55//!     let z = 2.0 * x * x + 3.0 * y + 1.0;
56//!
57//!     // Compute dz/dy
58//!     let dz_dy = &T::grad(&[z], &[y])[0];
59//!     println!("dz/dy = {:?}", dz_dy.eval(ctx));  // => 3.0
60//!
61//!     // Compute dz/dx (feed x=2)
62//!     let dz_dx = &T::grad(&[z], &[x])[0];
63//!     let x_val = scirs2_core::ndarray::arr0(2.0);
64//!     let result = ctx.evaluator()
65//!         .push(dz_dx)
66//!         .feed(x, x_val.view().into_dyn())
67//!         .run()[0].clone();
68//!     println!("dz/dx at x=2: {:?}", result);  // => 8.0
69//!
70//!     // Higher-order: d²z/dx²
71//!     let d2z_dx2 = &T::grad(&[dz_dx], &[x])[0];
72//!     println!("d²z/dx² = {:?}", d2z_dx2.eval(ctx));  // => 4.0
73//! });
74//! ```
75//!
76//! ### Neural Network Training
77//!
78//! Train a multi-layer perceptron for MNIST:
79//!
80//! ```rust
81//! use scirs2_autograd as ag;
82//! use ag::optimizers::adam::Adam;
83//! use ag::tensor_ops::*;
84//! use ag::prelude::*;
85//!
86//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
87//! // Create variable environment
88//! let mut env = ag::VariableEnvironment::new();
89//! let mut rng = ag::ndarray_ext::ArrayRng::<f32>::default();
90//!
91//! // Initialize network weights
92//! env.name("w1").set(rng.glorot_uniform(&[784, 256]));
93//! env.name("b1").set(ag::ndarray_ext::zeros(&[1, 256]));
94//! env.name("w2").set(rng.glorot_uniform(&[256, 10]));
95//! env.name("b2").set(ag::ndarray_ext::zeros(&[1, 10]));
96//!
97//! // Create Adam optimizer
98//! let var_ids = env.default_namespace().current_var_ids();
99//! let adam = Adam::default("adam", var_ids, &mut env);
100//!
101//! // Training loop
102//! for epoch in 0..10 {
103//!     env.run(|ctx| {
104//!         // Define computation graph
105//!         let x = ctx.placeholder("x", &[-1, 784]);
106//!         let y = ctx.placeholder("y", &[-1]);
107//!
108//!         let w1 = ctx.variable("w1");
109//!         let b1 = ctx.variable("b1");
110//!         let w2 = ctx.variable("w2");
111//!         let b2 = ctx.variable("b2");
112//!
113//!         // Forward pass: x -> hidden -> output
114//!         let hidden = relu(matmul(x, w1) + b1);
115//!         let logits = matmul(hidden, w2) + b2;
116//!
117//!         // Loss: cross-entropy
118//!         let loss = reduce_mean(
119//!             sparse_softmax_cross_entropy(logits, &y),
120//!             &[0],
121//!             false
122//!         );
123//!
124//!         // Backpropagation
125//!         let params = &[w1, b1, w2, b2];
126//!         let grads = &grad(&[loss], params);
127//!
128//!         // Update weights (requires actual data feeding)
129//!         // let mut feeder = ag::Feeder::new();
130//!         // feeder.push(x, x_batch).push(y, y_batch);
131//!         // adam.update(params, grads, ctx, feeder);
132//!     });
133//! }
134//! # Ok(())
135//! # }
136//! ```
137//!
138//! ### Custom Operations
139//!
140//! Define custom differentiable operations:
141//!
142//! ```rust
143//! use scirs2_autograd as ag;
144//! use ag::tensor_ops::*;
145//!
146//! ag::run::<f64, _, _>(|ctx| {
147//!     let x = ones(&[3, 4], ctx);
148//!
149//!     // Apply custom transformations using tensor.map()
150//!     let y = x.map(|arr| arr.mapv(|v: f64| v * 2.0 + 1.0));
151//!
152//!     // Hooks for debugging
153//!     let z = x.showshape();  // Print shape
154//!     let w = x.raw_hook(|arr| println!("Tensor value: {}", arr));
155//! });
156//! ```
157//!
158//! ## 🧠 Core Concepts
159//!
160//! ### Tensors
161//!
162//! Lazy-evaluated multi-dimensional arrays with automatic gradient tracking:
163//!
164//! ```rust,no_run
165//! use scirs2_autograd as ag;
166//! use ag::tensor_ops::*;
167//! use ag::prelude::*;
168//!
169//! ag::run::<f64, _, _>(|ctx| {
170//!     // Create tensors
171//!     let a = zeros(&[2, 3], ctx);        // All zeros
172//!     let b = ones(&[2, 3], ctx);         // All ones
173//!     let c = ctx.placeholder("c", &[2, 3]);  // Placeholder (fill later)
174//!     let d = ctx.variable("d");          // Trainable variable
175//! });
176//! ```
177//!
178//! ### Computation Graphs
179//!
180//! Build graphs of operations, evaluate lazily:
181//!
182//! ```rust
183//! use scirs2_autograd as ag;
184//! use ag::tensor_ops as T;
185//!
186//! ag::run::<f64, _, _>(|ctx| {
187//!     let x = ctx.placeholder("x", &[2, 2]);
188//!     let y = ctx.placeholder("y", &[2, 2]);
189//!
190//!     // Build graph (no computation yet)
191//!     let z = T::matmul(x, y);
192//!     let w = T::sigmoid(z);
193//!
194//!     // Evaluate when needed
195//!     // let result = w.eval(ctx);
196//! });
197//! ```
198//!
199//! ### Gradient Computation
200//!
201//! Reverse-mode automatic differentiation:
202//!
203//! ```rust
204//! use scirs2_autograd as ag;
205//! use ag::tensor_ops as T;
206//!
207//! ag::run(|ctx| {
208//!     let x = ctx.placeholder("x", &[]);
209//!     let y = x * x * x;  // y = x³
210//!
211//!     // Compute dy/dx = 3x²
212//!     let dy_dx = &T::grad(&[y], &[x])[0];
213//!
214//!     // Evaluate at x=2: 3(2²) = 12
215//!     let x_val = scirs2_core::ndarray::arr0(2.0);
216//!     let grad_val = ctx.evaluator()
217//!         .push(dy_dx)
218//!         .feed(x, x_val.view().into_dyn())
219//!         .run()[0].clone();
220//! });
221//! ```
222//!
223//! ## šŸŽØ Available Operations
224//!
225//! ### Basic Math
226//!
227//! - Arithmetic: `+`, `-`, `*`, `/`, `pow`
228//! - Comparison: `equal`, `not_equal`, `greater`, `less`
229//! - Reduction: `sum`, `mean`, `max`, `min`
230//!
231//! ### Neural Network Ops
232//!
233//! - Activations: `relu`, `sigmoid`, `tanh`, `softmax`, `gelu`
234//! - Pooling: `max_pool2d`, `avg_pool2d`
235//! - Convolution: `conv2d`, `conv2d_transpose`
236//! - Normalization: `batch_norm`, `layer_norm`
237//! - Dropout: `dropout`
238//!
239//! ### Matrix Operations
240//!
241//! - `matmul` - Matrix multiplication
242//! - `transpose` - Matrix transpose
243//! - `reshape` - Change tensor shape
244//! - `concat` - Concatenate tensors
245//! - `split` - Split tensor
246//!
247//! ### Loss Functions
248//!
249//! - `sparse_softmax_cross_entropy` - Classification loss
250//! - `sigmoid_cross_entropy` - Binary classification
251//! - `softmax_cross_entropy` - Multi-class loss
252//!
253//! ## šŸ”§ Optimizers
254//!
255//! Built-in optimization algorithms:
256//!
257//! - **Adam**: Adaptive moment estimation (recommended)
258//! - **SGD**: Stochastic gradient descent with momentum
259//! - **RMSprop**: Root mean square propagation
260//! - **Adagrad**: Adaptive learning rates
261//!
262//! ## šŸ’¾ Model Persistence
263//!
264//! Save and load trained models:
265//!
266//! ```rust,no_run
267//! use scirs2_autograd as ag;
268//!
269//! let mut env = ag::VariableEnvironment::<f64>::new();
270//!
271//! // After training...
272//! env.save("model.safetensors")?;
273//!
274//! // Later, load the model
275//! let env = ag::VariableEnvironment::<f64>::load("model.safetensors")?;
276//! # Ok::<(), Box<dyn std::error::Error>>(())
277//! ```
278//!
279//! ## šŸ“Š Performance
280//!
281//! scirs2-autograd is designed for efficiency:
282//!
283//! - **Lazy Evaluation**: Build graphs without computation overhead
284//! - **Minimal Allocations**: Reuse memory where possible
285//! - **BLAS Integration**: Fast matrix operations via OxiBLAS (pure Rust)
286//! - **Zero-copy**: Efficient data handling with ndarray views
287//!
288//! Typical training speed: **0.11 sec/epoch** for MNIST MLP (2.7GHz Intel Core i5)
289//!
290//! ## šŸ”— Integration
291//!
292//! - **scirs2-neural**: High-level neural network layers
293//! - **scirs2-linalg**: Matrix operations
294//! - **scirs2-optimize**: Optimization algorithms
295//! - **ndarray**: Core array library (re-exported)
296//!
297//! ## šŸ“š Comparison with PyTorch
298//!
299//! | Feature | PyTorch | scirs2-autograd |
300//! |---------|---------|-----------------|
301//! | Autodiff | āœ… | āœ… |
302//! | Dynamic Graphs | āœ… | āœ… |
303//! | GPU Support | āœ… | āš ļø (limited) |
304//! | Type Safety | āŒ | āœ… |
305//! | Memory Safety | āš ļø | āœ… |
306//! | Pure Rust | āŒ | āœ… |
307//!
308//! ## šŸ”’ Version
309//!
310//! Current version: **0.1.5** (Released January 15, 2026)
311
312#[allow(unused_imports)]
313// Re-export from scirs2-core for POLICY compliance
314pub use scirs2_core::ndarray;
315pub use scirs2_core::random as rand;
316
317// BLAS dependencies now handled through scirs2-core
318
319extern crate approx;
320extern crate libc;
321extern crate matrixmultiply;
322extern crate num;
323// extern crate rayon;  // Now use scirs2-core parallel abstractions
324extern crate rustc_hash;
325extern crate serde;
326extern crate serde_json;
327pub(crate) extern crate smallvec;
328extern crate special;
329extern crate uuid;
330
331pub mod error;
332pub mod error_helpers;
333pub mod evaluation;
334mod gradient;
335pub mod gradient_clipping;
336pub mod graph;
337pub mod high_performance;
338pub mod hooks;
339pub mod integration;
340pub mod ndarray_ext;
341pub mod op;
342pub mod optimization;
343pub mod optimizers;
344pub mod parallel;
345pub mod prelude;
346pub mod schedulers;
347pub mod tensor;
348pub mod tensor_ops;
349pub mod test_helper;
350pub mod testing;
351pub mod tracing;
352pub mod validation;
353pub mod variable;
354pub mod visualization;
355
356use rustc_hash::{FxHashMap, FxHashSet};
357use std::any::TypeId;
358use std::fmt;
359
360/// A primitive type in this crate, which is actually a decorated `scirs2_core::numeric::Float`.
361pub trait Float:
362    scirs2_core::numeric::Float
363    + scirs2_core::numeric::NumAssignOps
364    + Copy
365    + Send
366    + Sync
367    + fmt::Display
368    + fmt::Debug
369    + Sized
370    + serde::Serialize
371    + serde::de::DeserializeOwned
372    + 'static
373{
374}
375
376#[doc(hidden)]
377/// Internal trait.
378pub trait Int:
379    num::Integer
380    + scirs2_core::numeric::NumAssignOps
381    + scirs2_core::numeric::ToPrimitive
382    + Copy
383    + Send
384    + fmt::Display
385    + Sized
386    + serde::Serialize
387    + serde::de::DeserializeOwned
388    + 'static
389{
390}
391
392impl<T> Float for T where
393    T: num::Float
394        + scirs2_core::numeric::NumAssignOps
395        + Copy
396        + Send
397        + Sync
398        + fmt::Display
399        + fmt::Debug
400        + Sized
401        + serde::Serialize
402        + serde::de::DeserializeOwned
403        + 'static
404{
405}
406
407impl<T> Int for T where
408    T: num::Integer
409        + scirs2_core::numeric::NumAssignOps
410        + scirs2_core::numeric::ToPrimitive
411        + Copy
412        + Send
413        + Sync
414        + fmt::Display
415        + Sized
416        + serde::Serialize
417        + serde::de::DeserializeOwned
418        + 'static
419{
420}
421
422#[inline(always)]
423/// Return `true` if `A` and `B` are the same type
424pub(crate) fn same_type<A: 'static, B: 'static>() -> bool {
425    TypeId::of::<A>() == TypeId::of::<B>()
426}
427
428pub use crate::ndarray_ext::array_gen;
429
430pub use crate::ndarray_ext::{NdArray, NdArrayView, NdArrayViewMut};
431
432pub use crate::evaluation::{Evaluator, Feeder};
433
434pub use crate::tensor::Tensor;
435
436pub(crate) use graph::Graph;
437
438pub use crate::error::{AutogradError, EvalError, OpError, Result};
439pub use crate::graph::{run, Context};
440pub use crate::high_performance::{
441    memory_efficient_grad_accumulation, parallel_gradient_computation, simd_backward_pass,
442    ultra_backward_pass,
443};
444pub use crate::variable::{
445    AutogradTensor, SafeVariable, SafeVariableEnvironment, VariableEnvironment,
446};
447
448// Re-export functional optimizers and training utilities (Issue #94)
449pub use crate::optimizers::{FunctionalAdam, FunctionalOptimizer, FunctionalSGD};