pub mod parameter;
pub mod buffer;
pub mod init;
pub mod linear;
pub mod activation;
pub mod loss;
pub mod optim;
pub mod clip;
pub mod scheduler;
pub mod dropout;
pub mod padding;
pub mod layernorm;
pub mod rmsnorm;
pub mod embedding;
pub mod grucell;
pub mod gru;
pub mod lstmcell;
pub mod lstm;
pub mod conv1d;
pub mod conv2d;
pub mod conv_transpose1d;
pub mod conv_transpose2d;
pub mod conv3d;
pub mod conv_transpose3d;
pub mod groupnorm;
pub mod batchnorm;
pub mod instancenorm;
pub mod pooling;
pub mod bilinear;
pub mod attention;
pub mod rope;
pub mod checkpoint;
pub mod amp;
pub mod cuda_graph;
pub mod functional;
pub use parameter::Parameter;
pub use buffer::Buffer;
pub use linear::Linear;
pub use activation::{
Identity, ReLU, Sigmoid, Tanh, GELU, GeluApprox, SiLU, SwiGLU,
LeakyReLU, ELU, Softplus, Mish,
SELU, Hardswish, Hardsigmoid, PReLU,
Softmax, LogSoftmax, Flatten,
};
pub use loss::{
mse_loss, cross_entropy_loss, bce_loss, bce_with_logits_loss,
l1_loss, smooth_l1_loss, kl_div_loss,
nll_loss, ctc_loss, focal_loss,
triplet_margin_loss, cosine_embedding_loss,
hinge_embedding_loss, margin_ranking_loss, poisson_nll_loss,
};
pub use optim::{Optimizer, Stateful, StateKind, migrate_optim_state_file, SGD, SGDBuilder, Adam, AdamBuilder, AdamW, AdamWBuilder, RMSprop, RMSpropBuilder, Adagrad, AdagradBuilder, RAdam, NAdam};
pub use checkpoint::{
save_checkpoint, load_checkpoint, save_checkpoint_file, load_checkpoint_file,
migrate_checkpoint, migrate_checkpoint_file, checkpoint_version, checkpoint_keys,
LoadReport, MigrateReport,
};
pub use amp::{GradScaler, cast_parameters, AutocastGuard, autocast, is_autocast_enabled, is_autocast_enabled_for};
pub use clip::{clip_grad_norm, clip_grad_value};
pub use scheduler::{Scheduler, StepDecay, CosineScheduler, WarmupScheduler, PlateauScheduler, ExponentialLR, MultiStepLR, OneCycleLR, CyclicLR};
pub use dropout::{Dropout, Dropout2d, AlphaDropout};
pub use padding::{ZeroPad2d, ReflectionPad2d};
pub use layernorm::LayerNorm;
pub use rmsnorm::RMSNorm;
pub use embedding::{Embedding, EmbeddingBag};
pub use grucell::GRUCell;
pub use gru::GRU;
pub use lstmcell::LSTMCell;
pub use lstm::LSTM;
pub use conv1d::{Conv1d, Conv1dBuilder};
pub use conv2d::{Conv2d, Conv2dBuilder};
pub use conv_transpose1d::{ConvTranspose1d, ConvTranspose1dBuilder};
pub use conv_transpose2d::{ConvTranspose2d, ConvTranspose2dBuilder};
pub use conv3d::{Conv3d, Conv3dBuilder};
pub use conv_transpose3d::{ConvTranspose3d, ConvTranspose3dBuilder};
pub use groupnorm::GroupNorm;
pub use batchnorm::{BatchNorm, BatchNorm2d};
pub use instancenorm::InstanceNorm;
pub use pooling::{MaxPool2d, AvgPool2d, MaxPool1d, AvgPool1d, AdaptiveMaxPool2d, AdaptiveAvgPool2d, PixelShuffle, PixelUnshuffle, Upsample, Unfold, Fold};
pub use bilinear::Bilinear;
pub use attention::MultiheadAttention;
pub use rope::RotaryEmbedding;
pub use init::{xavier_uniform, xavier_normal, kaiming_uniform, kaiming_normal, uniform_bias, uniform, normal, orthogonal, trunc_normal};
pub use functional::{gaussian_blur_2d, GaussianBlur};
pub use cuda_graph::{CudaGraph, MemPoolId, CaptureMode, cuda_graph_capture, cuda_graph_pool_handle};
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use crate::autograd::Variable;
use crate::tensor::Result;
pub trait Module {
fn forward(&self, input: &Variable) -> Result<Variable>;
fn parameters(&self) -> Vec<Parameter> {
let subs = self.sub_modules();
if subs.is_empty() {
return vec![];
}
let mut params = Vec::new();
let mut seen = HashSet::new();
let mut visited = HashSet::new();
for child in &subs {
walk_modules_visited(child.as_ref(), &mut visited, &mut |m| {
for p in m.parameters() {
let ptr = p.variable.id();
if seen.insert(ptr) {
params.push(p);
}
}
});
}
params
}
fn buffers(&self) -> Vec<Buffer> {
let subs = self.sub_modules();
if subs.is_empty() {
return vec![];
}
let mut bufs = Vec::new();
let mut seen = HashSet::new();
let mut visited = HashSet::new();
for child in &subs {
walk_modules_visited(child.as_ref(), &mut visited, &mut |m| {
for b in m.buffers() {
let ptr = b.id();
if seen.insert(ptr) {
bufs.push(b);
}
}
});
}
bufs
}
fn name(&self) -> &str { "module" }
fn sub_modules(&self) -> Vec<Rc<dyn Module>> { vec![] }
fn move_to_device(&self, device: crate::tensor::Device) {
for p in self.parameters() {
let data = p.variable.data();
if data.device() != device {
let moved = data
.detach()
.and_then(|d| d.to_device(device))
.unwrap_or_else(|e| {
panic!(
"Module::move_to_device: failed to move parameter '{}' to {device:?}: {e}",
p.name
)
});
p.variable.set_data(moved);
}
}
for b in self.buffers() {
if b.get().device() != device {
b.to_device(device).unwrap_or_else(|e| {
panic!("Module::move_to_device: failed to move buffer to {device:?}: {e}")
});
}
}
}
fn set_training(&self, _training: bool) {}
fn train(&self) { self.set_training(true); }
fn eval(&self) { self.set_training(false); }
fn trace(&self) -> Option<Variable> { None }
fn as_named_input(&self) -> Option<&dyn NamedInputModule> { None }
fn as_loop_body(&self) -> Option<&dyn LoopBody> { None }
fn as_any(&self) -> Option<&dyn std::any::Any> { None }
fn structural_hash(&self) -> Option<String> { None }
fn reset(&self) {}
fn detach_state(&self) {}
fn aggregated_metrics_slot(
&self,
) -> Option<std::sync::Arc<
std::sync::Mutex<Option<crate::metrics::EpochMetrics>>,
>> {
None
}
}
impl Module for Box<dyn Module> {
fn forward(&self, input: &Variable) -> Result<Variable> {
(**self).forward(input)
}
fn parameters(&self) -> Vec<Parameter> {
(**self).parameters()
}
fn buffers(&self) -> Vec<Buffer> {
(**self).buffers()
}
fn name(&self) -> &str {
(**self).name()
}
fn sub_modules(&self) -> Vec<Rc<dyn Module>> {
(**self).sub_modules()
}
fn move_to_device(&self, device: crate::tensor::Device) {
(**self).move_to_device(device);
}
fn set_training(&self, training: bool) {
(**self).set_training(training);
}
fn trace(&self) -> Option<Variable> {
(**self).trace()
}
fn as_named_input(&self) -> Option<&dyn NamedInputModule> {
(**self).as_named_input()
}
fn as_loop_body(&self) -> Option<&dyn LoopBody> {
(**self).as_loop_body()
}
fn as_any(&self) -> Option<&dyn std::any::Any> {
(**self).as_any()
}
fn structural_hash(&self) -> Option<String> {
(**self).structural_hash()
}
fn reset(&self) {
(**self).reset();
}
fn detach_state(&self) {
(**self).detach_state();
}
fn aggregated_metrics_slot(
&self,
) -> Option<std::sync::Arc<
std::sync::Mutex<Option<crate::metrics::EpochMetrics>>,
>> {
(**self).aggregated_metrics_slot()
}
}
pub trait NamedInputModule: Module {
fn forward_named(
&self,
input: &Variable,
refs: &HashMap<String, Variable>,
) -> Result<Variable>;
}
pub struct TraceEmit<'a> {
named: Option<&'a mut HashMap<String, Variable>>,
}
impl<'a> TraceEmit<'a> {
pub fn discard() -> TraceEmit<'a> {
TraceEmit { named: None }
}
pub(crate) fn new(named: &'a mut HashMap<String, Variable>) -> Self {
TraceEmit { named: Some(named) }
}
pub fn publish(&mut self, name: &str, v: Variable) {
if let Some(named) = self.named.as_deref_mut() {
if named.contains_key(name) {
panic!(
"TraceEmit::publish: name {:?} already published this step",
name
);
}
named.insert(name.to_string(), v);
}
}
}
pub trait LoopBody: Module {
fn step(
&self,
input: &Variable,
refs: &HashMap<String, Variable>,
emit: &mut TraceEmit<'_>,
) -> Result<Variable>;
}
pub fn forward_via_step<B: LoopBody + ?Sized>(
body: &B,
input: &Variable,
) -> Result<Variable> {
let refs: HashMap<String, Variable> = HashMap::new();
let mut emit = TraceEmit::discard();
body.step(input, &refs, &mut emit)
}
pub fn walk_modules(module: &dyn Module, f: &mut dyn FnMut(&dyn Module)) {
let mut visited = HashSet::new();
walk_modules_visited(module, &mut visited, f);
}
pub fn walk_modules_visited(
module: &dyn Module,
visited: &mut HashSet<usize>,
f: &mut dyn FnMut(&dyn Module),
) {
let ptr = module as *const dyn Module as *const () as usize;
if !visited.insert(ptr) {
return;
}
f(module);
for child in module.sub_modules() {
walk_modules_visited(child.as_ref(), visited, f);
}
}
pub fn collect_parameters(modules: &[&dyn Module]) -> Vec<Parameter> {
let mut params = Vec::new();
for m in modules {
params.extend(m.parameters());
}
params
}
#[cfg(test)]
#[path = "tests.rs"]
mod tests;