Tensor

Struct Tensor 

Source
pub struct Tensor { /* private fields */ }
Expand description

High-performance multi-dimensional tensor with automatic differentiation support

The core data structure for machine learning operations, designed for maximum performance with zero-cost abstractions. Supports arbitrary dimensionality, SIMD optimization, gradient tracking, device placement, and natural mathematical expressions through operator overloading.

§Key Features

  • Raw Pointer Storage: Zero-overhead memory access for maximum performance
  • SIMD Optimization: AVX2 alignment and vectorized operations
  • Memory Efficiency: Optimized alignment strategies for different tensor sizes
  • gradtrack Integration: Built-in gradient tracking and computation
  • Device Support: CPU and future CUDA device placement
  • View Tensors: Zero-copy tensor views with shared memory management
  • Thread Safety: Send + Sync implementation for concurrent usage
  • Operator Overloading: Natural mathematical expressions (+, -, *, /, +=, -=, *=, /=)

§Memory Layout

Tensors use row-major memory layout with size-dependent alignment:

  • Small tensors (≤8 elements): 16-byte SSE alignment
  • Medium tensors (8-1024 elements): 32-byte AVX2 alignment
  • Large tensors (>1024 elements): 64-byte cache-line alignment

§Performance Characteristics

  • Memory Overhead: ~64 bytes per tensor (excluding data)
  • SIMD Ready: Properly aligned for vectorized operations
  • Cache Friendly: Optimized memory layout for CPU cache hierarchies
  • Zero-Cost Views: View tensors share memory without copying
  • Thread Safe: Atomic ID generation and lock-free operations
  • Operator Performance: Zero-cost operator overloading for mathematical expressions

§Safety

This struct uses unsafe code for performance. The following invariants must be maintained:

  • data must be valid for shape.size elements
  • data must be properly aligned for f32
  • data must not be aliased while the tensor exists
  • shape.size must match the actual allocated memory
  • allocation_owner must be valid if present

§Examples

§Basic Tensor Operations

use train_station::Tensor;

// Create tensors with different configurations
let tensor = Tensor::new(vec![2, 3]);
let tensor_with_grad = Tensor::ones(vec![10, 10]).with_requires_grad();

// Access tensor properties
assert_eq!(tensor.size(), 6);
assert_eq!(tensor.shape().dims, vec![2, 3]);
assert!(tensor.is_contiguous());

§Operator Overloading

use train_station::Tensor;

// Create tensors for operations
let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();

// Tensor operations with operators
let result = a.clone() + b.clone();                    // Tensor addition
let result = a.clone() * b.clone();                    // Element-wise multiplication
let result = a.clone() - b.clone();                    // Tensor subtraction
let result = a.clone() / b.clone();                    // Element-wise division

// Scalar operations
let result = a.clone() + 5.0;                          // Tensor + scalar
let result = 5.0 + a.clone();                          // Scalar + tensor
let result = a.clone() * 3.0;                          // Tensor * scalar
let result = 3.0 * a.clone();                          // Scalar * tensor

// Compound expressions
let result = (a.clone() + b.clone()) * 2.0 - 1.0;      // Complex mathematical expressions

// Assignment operators
let mut c = a.clone();
c += b.clone();                                        // In-place addition
c *= 2.0;                                              // In-place scalar multiplication

// Negation
let result = -a;                                       // Negate all elements

§Thread Safety

This type is Send + Sync and can be safely shared between threads. All operations are thread-safe through atomic ID generation and thread-local gradtrack storage.

Implementations§

Source§

impl Tensor

Source

pub fn new(shape_dims: Vec<usize>) -> Self

Creates a new tensor with the specified shape and optimized memory layout

Allocates memory with size-dependent alignment for optimal performance:

  • Small tensors (≤8 elements): 16-byte SSE alignment
  • Medium tensors (8-1024 elements): 32-byte AVX2 alignment
  • Large tensors (>1024 elements): 64-byte cache-line alignment
§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
§Returns

A new tensor with uninitialized data. The data must be initialized before use to avoid undefined behavior.

§Performance
  • Memory Allocation: Single allocation with optimized alignment
  • SIMD Ready: Properly aligned for vectorized operations
  • Cache Friendly: Optimized for CPU cache hierarchies
  • Thread Safe: Atomic ID generation for gradtrack tracking
§Safety

The returned tensor contains uninitialized memory. You must initialize the data before performing any operations that read from it.

§Examples
use train_station::Tensor;

// Create tensors of different sizes
let small_tensor = Tensor::new(vec![2, 3]);      // 16-byte alignment
let medium_tensor = Tensor::new(vec![32, 32]);   // 32-byte alignment
let large_tensor = Tensor::new(vec![1000, 1000]); // 64-byte alignment

// Initialize data before use
let mut tensor = Tensor::new(vec![2, 3]);
tensor.fill(0.0); // Initialize with zeros
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 70)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
Source

pub fn shape(&self) -> &Shape

Returns the shape and dimensional information of the tensor

Provides access to the tensor’s dimensions, size, strides, and memory layout information. This is used for shape validation, memory access calculations, and optimization decisions.

§Returns

Reference to the tensor’s shape information containing dimensions, size, strides, and memory layout type.

§Performance
  • Time Complexity: O(1) - direct field access
  • Memory: No allocation - returns reference to existing data
§Examples
use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3, 4]);
let shape = tensor.shape();
assert_eq!(shape.dims, vec![2, 3, 4]);
assert_eq!(shape.size, 24);
assert_eq!(shape.rank(), 3);
Examples found in repository?
examples/neural_networks/basic_linear_layer.rs (line 156)
147fn demonstrate_layer_creation() {
148    println!("--- Layer Creation ---");
149
150    let layer = LinearLayer::new(3, 2, Some(42));
151
152    println!("Created linear layer:");
153    println!("  Input size: {}", layer.input_size);
154    println!("  Output size: {}", layer.output_size);
155    println!("  Parameter count: {}", layer.parameter_count());
156    println!("  Weight shape: {:?}", layer.weight.shape().dims);
157    println!("  Bias shape: {:?}", layer.bias.shape().dims);
158    println!("  Weight requires grad: {}", layer.weight.requires_grad());
159    println!("  Bias requires grad: {}", layer.bias.requires_grad());
160}
161
162/// Demonstrate forward pass with gradient tracking
163fn demonstrate_forward_pass() {
164    println!("\n--- Forward Pass (with gradients) ---");
165
166    let layer = LinearLayer::new(3, 2, Some(43));
167
168    // Single input
169    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
170    let output = layer.forward(&input);
171
172    println!("Single input:");
173    println!("  Input: {:?}", input.data());
174    println!("  Output: {:?}", output.data());
175    println!("  Output requires grad: {}", output.requires_grad());
176
177    // Batch input
178    let batch_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
179    let batch_output = layer.forward(&batch_input);
180
181    println!("Batch input:");
182    println!("  Input shape: {:?}", batch_input.shape().dims);
183    println!("  Output shape: {:?}", batch_output.shape().dims);
184    println!("  Output requires grad: {}", batch_output.requires_grad());
185}
186
187/// Demonstrate forward pass without gradient tracking
188fn demonstrate_forward_pass_no_grad() {
189    println!("\n--- Forward Pass (no gradients) ---");
190
191    let layer = LinearLayer::new(3, 2, Some(44));
192
193    // Single input
194    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
195    let output = layer.forward_no_grad(&input);
196
197    println!("Single input (no grad):");
198    println!("  Input: {:?}", input.data());
199    println!("  Output: {:?}", output.data());
200    println!("  Output requires grad: {}", output.requires_grad());
201
202    // Compare with grad version
203    let output_with_grad = layer.forward(&input);
204    println!("Comparison:");
205    println!(
206        "  Same values: {}",
207        output.data() == output_with_grad.data()
208    );
209    println!("  No grad requires grad: {}", output.requires_grad());
210    println!(
211        "  With grad requires grad: {}",
212        output_with_grad.requires_grad()
213    );
214}
215
216/// Demonstrate complete training loop
217fn demonstrate_training_loop() -> Result<(), Box<dyn std::error::Error>> {
218    println!("\n--- Training Loop ---");
219
220    // Create layer and training data
221    let mut layer = LinearLayer::new(2, 1, Some(45));
222
223    // Simple regression task: y = 2*x1 + 3*x2 + 1
224    let x_data = Tensor::from_slice(
225        &[
226            1.0, 1.0, // x1=1, x2=1 -> y=6
227            2.0, 1.0, // x1=2, x2=1 -> y=8
228            1.0, 2.0, // x1=1, x2=2 -> y=9
229            2.0, 2.0, // x1=2, x2=2 -> y=11
230        ],
231        vec![4, 2],
232    )
233    .unwrap();
234
235    let y_true = Tensor::from_slice(&[6.0, 8.0, 9.0, 11.0], vec![4, 1]).unwrap();
236
237    println!("Training data:");
238    println!("  X shape: {:?}", x_data.shape().dims);
239    println!("  Y shape: {:?}", y_true.shape().dims);
240    println!("  Target function: y = 2*x1 + 3*x2 + 1");
241
242    // Create optimizer
243    let config = AdamConfig {
244        learning_rate: 0.01,
245        beta1: 0.9,
246        beta2: 0.999,
247        eps: 1e-8,
248        weight_decay: 0.0,
249        amsgrad: false,
250    };
251
252    let mut optimizer = Adam::with_config(config);
253    let params = layer.parameters();
254    for param in &params {
255        optimizer.add_parameter(param);
256    }
257
258    println!("Optimizer setup complete. Starting training...");
259
260    // Training loop
261    let num_epochs = 100;
262    let mut losses = Vec::new();
263
264    for epoch in 0..num_epochs {
265        // Forward pass
266        let y_pred = layer.forward(&x_data);
267
268        // Compute loss: MSE
269        let diff = y_pred.sub_tensor(&y_true);
270        let mut loss = diff.pow_scalar(2.0).mean();
271
272        // Backward pass
273        loss.backward(None);
274
275        // Optimizer step
276        let mut params = layer.parameters();
277        optimizer.step(&mut params);
278        optimizer.zero_grad(&mut params);
279
280        losses.push(loss.value());
281
282        // Print progress
283        if epoch % 20 == 0 || epoch == num_epochs - 1 {
284            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
285        }
286    }
287
288    // Evaluate final model
289    let final_predictions = layer.forward_no_grad(&x_data);
290
291    println!("\nFinal model evaluation:");
292    println!("  Learned weights: {:?}", layer.weight.data());
293    println!("  Learned bias: {:?}", layer.bias.data());
294    println!("  Target weights: [2.0, 3.0]");
295    println!("  Target bias: [1.0]");
296
297    println!("  Predictions vs True:");
298    for i in 0..4 {
299        let pred = final_predictions.data()[i];
300        let true_val = y_true.data()[i];
301        println!(
302            "    Sample {}: pred={:.3}, true={:.1}, error={:.3}",
303            i + 1,
304            pred,
305            true_val,
306            (pred - true_val).abs()
307        );
308    }
309
310    // Training analysis
311    let initial_loss = losses[0];
312    let final_loss = losses[losses.len() - 1];
313    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
314
315    println!("\nTraining Analysis:");
316    println!("  Initial loss: {:.6}", initial_loss);
317    println!("  Final loss: {:.6}", final_loss);
318    println!("  Loss reduction: {:.1}%", loss_reduction);
319
320    Ok(())
321}
322
323/// Demonstrate single vs batch inference
324fn demonstrate_single_vs_batch_inference() {
325    println!("\n--- Single vs Batch Inference ---");
326
327    let layer = LinearLayer::new(4, 3, Some(46));
328
329    // Single inference
330    println!("Single inference:");
331    let single_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![1, 4]).unwrap();
332    let single_output = layer.forward_no_grad(&single_input);
333    println!("  Input shape: {:?}", single_input.shape().dims);
334    println!("  Output shape: {:?}", single_output.shape().dims);
335    println!("  Output: {:?}", single_output.data());
336
337    // Batch inference
338    println!("Batch inference:");
339    let batch_input = Tensor::from_slice(
340        &[
341            1.0, 2.0, 3.0, 4.0, // Sample 1
342            5.0, 6.0, 7.0, 8.0, // Sample 2
343            9.0, 10.0, 11.0, 12.0, // Sample 3
344        ],
345        vec![3, 4],
346    )
347    .unwrap();
348    let batch_output = layer.forward_no_grad(&batch_input);
349    println!("  Input shape: {:?}", batch_input.shape().dims);
350    println!("  Output shape: {:?}", batch_output.shape().dims);
351
352    // Verify batch consistency - first sample should match single inference
353    let _first_batch_sample = batch_output.view(vec![3, 3]); // Reshape to access first sample
354    let first_sample_data = &batch_output.data()[0..3]; // First 3 elements
355    let single_sample_data = single_output.data();
356
357    println!("Consistency check:");
358    println!("  Single output: {:?}", single_sample_data);
359    println!("  First batch sample: {:?}", first_sample_data);
360    println!(
361        "  Match: {}",
362        single_sample_data
363            .iter()
364            .zip(first_sample_data.iter())
365            .all(|(a, b)| (a - b).abs() < 1e-6)
366    );
367}
More examples
Hide additional examples
examples/iterators/element_iteration.rs (line 91)
77fn demonstrate_basic_iteration() -> Result<(), Box<dyn std::error::Error>> {
78    println!("\n--- Basic Element Iteration ---");
79
80    // Create a simple tensor for demonstration
81    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
82    println!("Original tensor: {:?}", tensor.data());
83
84    // Basic iteration with for loop
85    println!("\nBasic iteration with for loop:");
86    for (i, element) in tensor.iter().enumerate() {
87        println!(
88            "  Element {}: value = {:.1}, shape = {:?}",
89            i,
90            element.value(),
91            element.shape().dims
92        );
93    }
94
95    // Element-wise transformation
96    println!("\nElement-wise transformation (2x + 1):");
97    let transformed: Tensor = tensor
98        .iter()
99        .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
100        .collect();
101    println!("  Result: {:?}", transformed.data());
102
103    // Filtering elements
104    println!("\nFiltering elements (values > 3.0):");
105    let filtered: Tensor = tensor.iter().filter(|elem| elem.value() > 3.0).collect();
106    println!("  Filtered: {:?}", filtered.data());
107
108    Ok(())
109}
examples/getting_started/tensor_operators.rs (line 165)
158fn demonstrate_broadcasting() {
159    println!("\n--- Broadcasting ---");
160
161    // 2D tensor
162    let tensor_2d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
163    println!(
164        "2D tensor: shape {:?}, data: {:?}",
165        tensor_2d.shape().dims,
166        tensor_2d.data()
167    );
168
169    // 1D tensor (will be broadcasted)
170    let tensor_1d = Tensor::from_slice(&[10.0, 20.0], vec![2]).unwrap();
171    println!(
172        "1D tensor: shape {:?}, data: {:?}",
173        tensor_1d.shape().dims,
174        tensor_1d.data()
175    );
176
177    // Broadcasting addition
178    let broadcast_sum = &tensor_2d + &tensor_1d;
179    println!(
180        "Broadcast sum: shape {:?}, data: {:?}",
181        broadcast_sum.shape().dims,
182        broadcast_sum.data()
183    );
184
185    // Broadcasting multiplication
186    let broadcast_mul = &tensor_2d * &tensor_1d;
187    println!(
188        "Broadcast multiplication: shape {:?}, data: {:?}",
189        broadcast_mul.shape().dims,
190        broadcast_mul.data()
191    );
192
193    // Broadcasting with scalar
194    let broadcast_scalar = &tensor_2d + 100.0;
195    println!(
196        "Broadcast scalar: shape {:?}, data: {:?}",
197        broadcast_scalar.shape().dims,
198        broadcast_scalar.data()
199    );
200}
examples/getting_started/tensor_basics.rs (line 49)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
87
88/// Demonstrate basic arithmetic operations
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
118
119/// Demonstrate shape manipulation operations
120fn demonstrate_shape_operations() {
121    println!("\n--- Shape Operations ---");
122
123    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
124    println!(
125        "Original: shape {:?}, data: {:?}",
126        tensor.shape().dims,
127        tensor.data()
128    );
129
130    // Reshape (view)
131    let reshaped = tensor.view(vec![3, 2]);
132    println!(
133        "Reshaped to [3, 2]: shape {:?}, data: {:?}",
134        reshaped.shape().dims,
135        reshaped.data()
136    );
137
138    // Create a different shaped tensor for demonstration
139    let tensor_2d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
140    println!(
141        "2D tensor: shape {:?}, data: {:?}",
142        tensor_2d.shape().dims,
143        tensor_2d.data()
144    );
145
146    // Create a 1D tensor
147    let tensor_1d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
148    println!(
149        "1D tensor: shape {:?}, data: {:?}",
150        tensor_1d.shape().dims,
151        tensor_1d.data()
152    );
153}
154
155/// Demonstrate data access patterns
156fn demonstrate_data_access() {
157    println!("\n--- Data Access ---");
158
159    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
160
161    // Access individual elements
162    println!("Element [0, 0]: {}", tensor.get(&[0, 0]));
163    println!("Element [0, 1]: {}", tensor.get(&[0, 1]));
164    println!("Element [1, 0]: {}", tensor.get(&[1, 0]));
165    println!("Element [1, 1]: {}", tensor.get(&[1, 1]));
166
167    // Access data as slice
168    let data = tensor.data();
169    println!("Data as slice: {:?}", data);
170
171    // Iterate over elements
172    println!("Elements:");
173    for (i, &value) in data.iter().enumerate() {
174        println!("  [{}]: {}", i, value);
175    }
176}
177
178/// Demonstrate utility functions
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
examples/getting_started/optimizer_basics.rs (line 57)
47fn demonstrate_basic_optimizer_setup() {
48    println!("--- Basic Optimizer Setup ---");
49
50    // Create parameters that require gradients
51    let weight = Tensor::randn(vec![3, 2], Some(42)).with_requires_grad();
52    let bias = Tensor::zeros(vec![2]).with_requires_grad();
53
54    println!("Created parameters:");
55    println!(
56        "  Weight: shape {:?}, requires_grad: {}",
57        weight.shape().dims,
58        weight.requires_grad()
59    );
60    println!(
61        "  Bias: shape {:?}, requires_grad: {}",
62        bias.shape().dims,
63        bias.requires_grad()
64    );
65
66    // Create Adam optimizer with default configuration
67    let mut optimizer = Adam::new();
68    println!(
69        "Created Adam optimizer with learning rate: {}",
70        optimizer.learning_rate()
71    );
72
73    // Add parameters to optimizer
74    optimizer.add_parameter(&weight);
75    optimizer.add_parameter(&bias);
76    println!(
77        "Added {} parameters to optimizer",
78        optimizer.parameter_count()
79    );
80
81    // Create optimizer with custom configuration
82    let config = AdamConfig {
83        learning_rate: 0.01,
84        beta1: 0.9,
85        beta2: 0.999,
86        eps: 1e-8,
87        weight_decay: 0.0,
88        amsgrad: false,
89    };
90
91    let mut custom_optimizer = Adam::with_config(config);
92    custom_optimizer.add_parameter(&weight);
93    custom_optimizer.add_parameter(&bias);
94
95    println!(
96        "Created custom optimizer with learning rate: {}",
97        custom_optimizer.learning_rate()
98    );
99
100    // Demonstrate parameter linking
101    println!("Parameter linking completed successfully");
102}
examples/neural_networks/feedforward_network.rs (line 355)
339fn demonstrate_forward_pass() {
340    println!("\n--- Forward Pass ---");
341
342    let config = FeedForwardConfig {
343        input_size: 3,
344        hidden_sizes: vec![5, 3],
345        output_size: 2,
346        use_bias: true,
347    };
348    let network = FeedForwardNetwork::new(config, Some(43));
349
350    // Single input
351    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
352    let output = network.forward(&input);
353
354    println!("Single input forward pass:");
355    println!("  Input shape: {:?}", input.shape().dims);
356    println!("  Output shape: {:?}", output.shape().dims);
357    println!("  Output: {:?}", output.data());
358    println!("  Output requires grad: {}", output.requires_grad());
359
360    // Batch input
361    let batch_input = Tensor::from_slice(
362        &[
363            1.0, 2.0, 3.0, // Sample 1
364            4.0, 5.0, 6.0, // Sample 2
365            7.0, 8.0, 9.0, // Sample 3
366        ],
367        vec![3, 3],
368    )
369    .unwrap();
370    let batch_output = network.forward(&batch_input);
371
372    println!("Batch input forward pass:");
373    println!("  Input shape: {:?}", batch_input.shape().dims);
374    println!("  Output shape: {:?}", batch_output.shape().dims);
375    println!("  Output requires grad: {}", batch_output.requires_grad());
376
377    // Compare with no-grad version
378    let output_no_grad = network.forward_no_grad(&input);
379    println!("No-grad comparison:");
380    println!("  Same values: {}", output.data() == output_no_grad.data());
381    println!("  With grad requires grad: {}", output.requires_grad());
382    println!(
383        "  No grad requires grad: {}",
384        output_no_grad.requires_grad()
385    );
386}
387
388/// Demonstrate different configurable architectures
389fn demonstrate_configurable_architectures() {
390    println!("\n--- Configurable Architectures ---");
391
392    let architectures = vec![
393        ("Shallow", vec![8]),
394        ("Medium", vec![16, 8]),
395        ("Deep", vec![32, 16, 8, 4]),
396        ("Wide", vec![64, 32]),
397        ("Bottleneck", vec![16, 4, 16]),
398    ];
399
400    for (name, hidden_sizes) in architectures {
401        let config = FeedForwardConfig {
402            input_size: 10,
403            hidden_sizes,
404            output_size: 3,
405            use_bias: true,
406        };
407
408        let network = FeedForwardNetwork::new(config.clone(), Some(44));
409
410        // Test forward pass
411        let test_input = Tensor::randn(vec![5, 10], Some(45)); // Batch of 5
412        let output = network.forward_no_grad(&test_input);
413
414        println!("{} network:", name);
415        println!("  Architecture: 10 -> {:?} -> 3", config.hidden_sizes);
416        println!("  Parameters: {}", network.parameter_count());
417        println!("  Test output shape: {:?}", output.shape().dims);
418        println!(
419            "  Output range: [{:.3}, {:.3}]",
420            output.data().iter().fold(f32::INFINITY, |a, &b| a.min(b)),
421            output
422                .data()
423                .iter()
424                .fold(f32::NEG_INFINITY, |a, &b| a.max(b))
425        );
426    }
427}
428
429/// Demonstrate basic training workflow
430fn demonstrate_training_workflow() -> Result<(), Box<dyn std::error::Error>> {
431    println!("\n--- Training Workflow ---");
432
433    // Create a simple classification network
434    let config = FeedForwardConfig {
435        input_size: 2,
436        hidden_sizes: vec![4, 3],
437        output_size: 1,
438        use_bias: true,
439    };
440    let mut network = FeedForwardNetwork::new(config, Some(46));
441
442    println!("Training network: 2 -> [4, 3] -> 1");
443
444    // Create simple binary classification data: XOR problem
445    let x_data = Tensor::from_slice(
446        &[
447            0.0, 0.0, // -> 0
448            0.0, 1.0, // -> 1
449            1.0, 0.0, // -> 1
450            1.0, 1.0, // -> 0
451        ],
452        vec![4, 2],
453    )
454    .unwrap();
455
456    let y_true = Tensor::from_slice(&[0.0, 1.0, 1.0, 0.0], vec![4, 1]).unwrap();
457
458    println!("Training on XOR problem:");
459    println!("  Input shape: {:?}", x_data.shape().dims);
460    println!("  Target shape: {:?}", y_true.shape().dims);
461
462    // Create optimizer
463    let mut optimizer = Adam::with_learning_rate(0.1);
464    let params = network.parameters();
465    for param in &params {
466        optimizer.add_parameter(param);
467    }
468
469    // Training loop
470    let num_epochs = 50;
471    let mut losses = Vec::new();
472
473    for epoch in 0..num_epochs {
474        // Forward pass
475        let y_pred = network.forward(&x_data);
476
477        // Compute loss: MSE
478        let diff = y_pred.sub_tensor(&y_true);
479        let mut loss = diff.pow_scalar(2.0).mean();
480
481        // Backward pass
482        loss.backward(None);
483
484        // Optimizer step and zero grad
485        let mut params = network.parameters();
486        optimizer.step(&mut params);
487        optimizer.zero_grad(&mut params);
488
489        losses.push(loss.value());
490
491        // Print progress
492        if epoch % 10 == 0 || epoch == num_epochs - 1 {
493            println!("Epoch {:2}: Loss = {:.6}", epoch, loss.value());
494        }
495    }
496
497    // Test final model
498    let final_predictions = network.forward_no_grad(&x_data);
499    println!("\nFinal predictions vs targets:");
500    for i in 0..4 {
501        let pred = final_predictions.data()[i];
502        let target = y_true.data()[i];
503        let input_x = x_data.data()[i * 2];
504        let input_y = x_data.data()[i * 2 + 1];
505        println!(
506            "  [{:.0}, {:.0}] -> pred: {:.3}, target: {:.0}, error: {:.3}",
507            input_x,
508            input_y,
509            pred,
510            target,
511            (pred - target).abs()
512        );
513    }
514
515    Ok(())
516}
517
518/// Demonstrate comprehensive training with 100+ steps
519fn demonstrate_comprehensive_training() -> Result<(), Box<dyn std::error::Error>> {
520    println!("\n--- Comprehensive Training (100+ Steps) ---");
521
522    // Create a regression network
523    let config = FeedForwardConfig {
524        input_size: 3,
525        hidden_sizes: vec![8, 6, 4],
526        output_size: 2,
527        use_bias: true,
528    };
529    let mut network = FeedForwardNetwork::new(config, Some(47));
530
531    println!("Network architecture: 3 -> [8, 6, 4] -> 2");
532    println!("Total parameters: {}", network.parameter_count());
533
534    // Create synthetic regression data
535    // Target function: [y1, y2] = [x1 + 2*x2 - x3, x1*x2 + x3]
536    let num_samples = 32;
537    let mut x_vec = Vec::new();
538    let mut y_vec = Vec::new();
539
540    for i in 0..num_samples {
541        let x1 = (i as f32 / num_samples as f32) * 2.0 - 1.0; // [-1, 1]
542        let x2 = ((i * 2) as f32 / num_samples as f32) * 2.0 - 1.0;
543        let x3 = ((i * 3) as f32 / num_samples as f32) * 2.0 - 1.0;
544
545        let y1 = x1 + 2.0 * x2 - x3;
546        let y2 = x1 * x2 + x3;
547
548        x_vec.extend_from_slice(&[x1, x2, x3]);
549        y_vec.extend_from_slice(&[y1, y2]);
550    }
551
552    let x_data = Tensor::from_slice(&x_vec, vec![num_samples, 3]).unwrap();
553    let y_true = Tensor::from_slice(&y_vec, vec![num_samples, 2]).unwrap();
554
555    println!("Training data:");
556    println!("  {} samples", num_samples);
557    println!("  Input shape: {:?}", x_data.shape().dims);
558    println!("  Target shape: {:?}", y_true.shape().dims);
559
560    // Create optimizer with learning rate scheduling
561    let mut optimizer = Adam::with_learning_rate(0.01);
562    let params = network.parameters();
563    for param in &params {
564        optimizer.add_parameter(param);
565    }
566
567    // Comprehensive training loop (150 epochs)
568    let num_epochs = 150;
569    let mut losses = Vec::new();
570    let mut best_loss = f32::INFINITY;
571    let mut patience_counter = 0;
572    let patience = 20;
573
574    println!("Starting comprehensive training...");
575
576    for epoch in 0..num_epochs {
577        // Forward pass
578        let y_pred = network.forward(&x_data);
579
580        // Compute loss: MSE
581        let diff = y_pred.sub_tensor(&y_true);
582        let mut loss = diff.pow_scalar(2.0).mean();
583
584        // Backward pass
585        loss.backward(None);
586
587        // Optimizer step and zero grad
588        let mut params = network.parameters();
589        optimizer.step(&mut params);
590        optimizer.zero_grad(&mut params);
591
592        let current_loss = loss.value();
593        losses.push(current_loss);
594
595        // Learning rate scheduling
596        if epoch > 0 && epoch % 30 == 0 {
597            let new_lr = optimizer.learning_rate() * 0.8;
598            optimizer.set_learning_rate(new_lr);
599            println!("  Reduced learning rate to {:.4}", new_lr);
600        }
601
602        // Early stopping logic
603        if current_loss < best_loss {
604            best_loss = current_loss;
605            patience_counter = 0;
606        } else {
607            patience_counter += 1;
608        }
609
610        // Print progress
611        if epoch % 25 == 0 || epoch == num_epochs - 1 {
612            println!(
613                "Epoch {:3}: Loss = {:.6}, LR = {:.4}, Best = {:.6}",
614                epoch,
615                current_loss,
616                optimizer.learning_rate(),
617                best_loss
618            );
619        }
620
621        // Early stopping
622        if patience_counter >= patience && epoch > 50 {
623            println!("Early stopping at epoch {} (patience exceeded)", epoch);
624            break;
625        }
626    }
627
628    // Final evaluation
629    let final_predictions = network.forward_no_grad(&x_data);
630
631    // Compute final metrics
632    let final_loss = losses[losses.len() - 1];
633    let initial_loss = losses[0];
634    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
635
636    println!("\nTraining completed!");
637    println!("  Initial loss: {:.6}", initial_loss);
638    println!("  Final loss: {:.6}", final_loss);
639    println!("  Best loss: {:.6}", best_loss);
640    println!("  Loss reduction: {:.1}%", loss_reduction);
641    println!("  Final learning rate: {:.4}", optimizer.learning_rate());
642
643    // Sample predictions analysis
644    println!("\nSample predictions (first 5):");
645    for i in 0..5.min(num_samples) {
646        let pred1 = final_predictions.data()[i * 2];
647        let pred2 = final_predictions.data()[i * 2 + 1];
648        let true1 = y_true.data()[i * 2];
649        let true2 = y_true.data()[i * 2 + 1];
650
651        println!(
652            "  Sample {}: pred=[{:.3}, {:.3}], true=[{:.3}, {:.3}], error=[{:.3}, {:.3}]",
653            i + 1,
654            pred1,
655            pred2,
656            true1,
657            true2,
658            (pred1 - true1).abs(),
659            (pred2 - true2).abs()
660        );
661    }
662
663    Ok(())
664}
Source

pub fn size(&self) -> usize

Returns the total number of elements in the tensor

Provides the total count of elements across all dimensions. This is used for memory allocation, iteration bounds, and performance optimization.

§Returns

Total number of elements as usize

§Performance
  • Time Complexity: O(1) - direct field access
  • Memory: No allocation - returns stored value
§Examples
use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3, 4]);
assert_eq!(tensor.size(), 24); // 2 * 3 * 4

let scalar = Tensor::new(vec![1]);
assert_eq!(scalar.size(), 1);

let empty = Tensor::new(vec![0]);
assert_eq!(empty.size(), 0);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 186)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
More examples
Hide additional examples
examples/iterators/performance_optimization.rs (line 187)
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
292
293/// Demonstrate advanced optimization techniques
294///
295/// Shows sophisticated optimization strategies and techniques
296/// for maximizing performance in tensor iterator operations.
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
examples/iterators/advanced_patterns.rs (line 115)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
156
157/// Demonstrate conditional processing patterns
158///
159/// Shows how to implement dynamic filtering and transformation
160/// based on data characteristics and conditions.
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source

pub fn device(&self) -> Device

Returns the device where this tensor is located

Provides the physical location of the tensor data (CPU/GPU). This determines which operations can be performed on the tensor and where computations will be executed.

§Returns

Device enum indicating the tensor’s physical location

§Performance
  • Time Complexity: O(1) - direct field access
  • Memory: No allocation - returns stored value
§Examples
use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3]);
assert!(tensor.device().is_cpu());
assert!(!tensor.device().is_cuda());
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 188)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
Source

pub fn new_on_device(shape_dims: Vec<usize>, device: Device) -> Self

Creates a new tensor with the specified shape on a specific device

Allocates memory on the specified device with the same optimized alignment strategy as new(). Currently supports CPU device with future CUDA support.

§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
  • device - The device where the tensor should be allocated
§Returns

A new tensor with uninitialized data on the specified device

§Performance
  • Memory Allocation: Device-specific allocation with optimized alignment
  • SIMD Ready: Properly aligned for vectorized operations on target device
  • Thread Safe: Atomic ID generation for gradtrack tracking
§Panics

Panics if the specified device is not supported (e.g., CUDA without feature flag)

§Examples
use train_station::Tensor;

let tensor = Tensor::new_on_device(vec![2, 3], train_station::Device::cpu());
assert!(tensor.device().is_cpu());
assert_eq!(tensor.size(), 6);
§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
  • device - Device where the tensor should be allocated
§Returns

A new tensor with uninitialized data on the specified device

§Panics

Panics if the device is not supported (currently only CPU is supported)

§Performance
  • Memory Allocation: Single allocation with optimized alignment
  • SIMD Ready: Properly aligned for vectorized operations
  • Cache Friendly: Optimized for CPU cache hierarchies
  • Thread Safe: Atomic ID generation for gradtrack tracking
§Examples
use train_station::{Tensor, Device};

// Create tensor on CPU device
let tensor = Tensor::new_on_device(vec![2, 3], Device::cpu());
assert_eq!(tensor.device(), Device::cpu());
assert_eq!(tensor.size(), 6);
Source

pub fn with_requires_grad(self) -> Self

Enable gradient computation for this tensor

Builder method that enables automatic gradient tracking for this tensor. When enabled, all operations involving this tensor will be recorded in the computation graph for gradient computation during backward pass.

§Returns

self with gradient tracking enabled

§Performance
  • Time Complexity: O(1) - simple field assignment
  • Memory: No additional allocation
  • Overhead: Minimal gradtrack tracking overhead when gradients computed
§Examples
use train_station::Tensor;

let tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
assert!(tensor.requires_grad());
Examples found in repository?
examples/neural_networks/feedforward_network.rs (line 73)
68    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
69        let scale = (1.0 / input_size as f32).sqrt();
70
71        let weight = Tensor::randn(vec![input_size, output_size], seed)
72            .mul_scalar(scale)
73            .with_requires_grad();
74        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
75
76        Self {
77            weight,
78            bias,
79            input_size,
80            output_size,
81        }
82    }
83
84    pub fn forward(&self, input: &Tensor) -> Tensor {
85        let output = input.matmul(&self.weight);
86        output.add_tensor(&self.bias)
87    }
88
89    pub fn forward_no_grad(&self, input: &Tensor) -> Tensor {
90        let _guard = NoGradTrack::new();
91        self.forward(input)
92    }
93
94    pub fn parameters(&mut self) -> Vec<&mut Tensor> {
95        vec![&mut self.weight, &mut self.bias]
96    }
97}
98
99/// Configuration for feed-forward network
100#[derive(Debug, Clone)]
101pub struct FeedForwardConfig {
102    pub input_size: usize,
103    pub hidden_sizes: Vec<usize>,
104    pub output_size: usize,
105    pub use_bias: bool,
106}
107
108impl Default for FeedForwardConfig {
109    fn default() -> Self {
110        Self {
111            input_size: 4,
112            hidden_sizes: vec![8, 4],
113            output_size: 2,
114            use_bias: true,
115        }
116    }
117}
118
119/// A configurable feed-forward neural network
120pub struct FeedForwardNetwork {
121    layers: Vec<LinearLayer>,
122    config: FeedForwardConfig,
123}
124
125impl FeedForwardNetwork {
126    /// Create a new feed-forward network with the given configuration
127    pub fn new(config: FeedForwardConfig, seed: Option<u64>) -> Self {
128        let mut layers = Vec::new();
129        let mut current_size = config.input_size;
130        let mut current_seed = seed;
131
132        // Create hidden layers
133        for &hidden_size in &config.hidden_sizes {
134            layers.push(LinearLayer::new(current_size, hidden_size, current_seed));
135            current_size = hidden_size;
136            current_seed = current_seed.map(|s| s + 1);
137        }
138
139        // Create output layer
140        layers.push(LinearLayer::new(
141            current_size,
142            config.output_size,
143            current_seed,
144        ));
145
146        Self { layers, config }
147    }
148
149    /// Forward pass through the entire network
150    pub fn forward(&self, input: &Tensor) -> Tensor {
151        let mut x = input.clone();
152
153        // Pass through all layers except the last one with ReLU activation
154        for layer in &self.layers[..self.layers.len() - 1] {
155            x = layer.forward(&x);
156            x = ReLU::forward(&x);
157        }
158
159        // Final layer without activation (raw logits)
160        if let Some(final_layer) = self.layers.last() {
161            x = final_layer.forward(&x);
162        }
163
164        x
165    }
166
167    /// Forward pass without gradients (for inference)
168    pub fn forward_no_grad(&self, input: &Tensor) -> Tensor {
169        let _guard = NoGradTrack::new();
170        self.forward(input)
171    }
172
173    /// Get all parameters for optimization
174    pub fn parameters(&mut self) -> Vec<&mut Tensor> {
175        let mut params = Vec::new();
176        for layer in &mut self.layers {
177            params.extend(layer.parameters());
178        }
179        params
180    }
181
182    /// Get the number of layers
183    pub fn num_layers(&self) -> usize {
184        self.layers.len()
185    }
186
187    /// Get the total number of parameters
188    pub fn parameter_count(&self) -> usize {
189        let mut count = 0;
190        let mut current_size = self.config.input_size;
191
192        for &hidden_size in &self.config.hidden_sizes {
193            count += current_size * hidden_size + hidden_size; // weights + bias
194            current_size = hidden_size;
195        }
196
197        // Output layer
198        count += current_size * self.config.output_size + self.config.output_size;
199
200        count
201    }
202
203    /// Save network parameters to JSON
204    pub fn save_json(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
205        if let Some(parent) = std::path::Path::new(path).parent() {
206            fs::create_dir_all(parent)?;
207        }
208
209        for (i, layer) in self.layers.iter().enumerate() {
210            let layer_path = format!("{}_layer_{}", path, i);
211            let weight_path = format!("{}_weight.json", layer_path);
212            let bias_path = format!("{}_bias.json", layer_path);
213
214            layer.weight.save_json(&weight_path)?;
215            layer.bias.save_json(&bias_path)?;
216        }
217
218        println!(
219            "Saved feed-forward network to {} ({} layers)",
220            path,
221            self.layers.len()
222        );
223        Ok(())
224    }
225
226    /// Load network parameters from JSON
227    pub fn load_json(
228        path: &str,
229        config: FeedForwardConfig,
230    ) -> Result<Self, Box<dyn std::error::Error>> {
231        let mut layers = Vec::new();
232        let mut current_size = config.input_size;
233        let mut layer_idx = 0;
234
235        // Load hidden layers
236        for &hidden_size in &config.hidden_sizes {
237            let layer_path = format!("{}_layer_{}", path, layer_idx);
238            let weight_path = format!("{}_weight.json", layer_path);
239            let bias_path = format!("{}_bias.json", layer_path);
240
241            let weight = Tensor::load_json(&weight_path)?.with_requires_grad();
242            let bias = Tensor::load_json(&bias_path)?.with_requires_grad();
243
244            layers.push(LinearLayer {
245                weight,
246                bias,
247                input_size: current_size,
248                output_size: hidden_size,
249            });
250
251            current_size = hidden_size;
252            layer_idx += 1;
253        }
254
255        // Load output layer
256        let layer_path = format!("{}_layer_{}", path, layer_idx);
257        let weight_path = format!("{}_weight.json", layer_path);
258        let bias_path = format!("{}_bias.json", layer_path);
259
260        let weight = Tensor::load_json(&weight_path)?.with_requires_grad();
261        let bias = Tensor::load_json(&bias_path)?.with_requires_grad();
262
263        layers.push(LinearLayer {
264            weight,
265            bias,
266            input_size: current_size,
267            output_size: config.output_size,
268        });
269
270        Ok(Self { layers, config })
271    }
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 58)
52    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
53        // Xavier/Glorot initialization: scale by sqrt(1/input_size)
54        let scale = (1.0 / input_size as f32).sqrt();
55
56        let weight = Tensor::randn(vec![input_size, output_size], seed)
57            .mul_scalar(scale)
58            .with_requires_grad();
59        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
60
61        Self {
62            weight,
63            bias,
64            input_size,
65            output_size,
66        }
67    }
68
69    /// Forward pass: output = input @ weight + bias
70    pub fn forward(&self, input: &Tensor) -> Tensor {
71        // Matrix multiplication: [batch_size, input_size] @ [input_size, output_size] = [batch_size, output_size]
72        let output = input.matmul(&self.weight);
73        // Add bias: [batch_size, output_size] + [output_size] = [batch_size, output_size]
74        output.add_tensor(&self.bias)
75    }
76
77    /// Forward pass without gradients (for inference)
78    pub fn forward_no_grad(&self, input: &Tensor) -> Tensor {
79        let _guard = NoGradTrack::new();
80        self.forward(input)
81    }
82
83    /// Get all parameters for optimization
84    pub fn parameters(&mut self) -> Vec<&mut Tensor> {
85        vec![&mut self.weight, &mut self.bias]
86    }
87
88    /// Save layer parameters to JSON
89    pub fn save_json(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
90        // Create directory if it doesn't exist
91        if let Some(parent) = std::path::Path::new(path).parent() {
92            fs::create_dir_all(parent)?;
93        }
94
95        let weight_path = format!("{}_weight.json", path);
96        let bias_path = format!("{}_bias.json", path);
97
98        self.weight.save_json(&weight_path)?;
99        self.bias.save_json(&bias_path)?;
100
101        println!("Saved linear layer to {} (weight and bias)", path);
102        Ok(())
103    }
104
105    /// Load layer parameters from JSON
106    pub fn load_json(
107        path: &str,
108        input_size: usize,
109        output_size: usize,
110    ) -> Result<Self, Box<dyn std::error::Error>> {
111        let weight_path = format!("{}_weight.json", path);
112        let bias_path = format!("{}_bias.json", path);
113
114        let weight = Tensor::load_json(&weight_path)?.with_requires_grad();
115        let bias = Tensor::load_json(&bias_path)?.with_requires_grad();
116
117        Ok(Self {
118            weight,
119            bias,
120            input_size,
121            output_size,
122        })
123    }
examples/iterators/element_iteration.rs (line 163)
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
examples/getting_started/optimizer_basics.rs (line 51)
47fn demonstrate_basic_optimizer_setup() {
48    println!("--- Basic Optimizer Setup ---");
49
50    // Create parameters that require gradients
51    let weight = Tensor::randn(vec![3, 2], Some(42)).with_requires_grad();
52    let bias = Tensor::zeros(vec![2]).with_requires_grad();
53
54    println!("Created parameters:");
55    println!(
56        "  Weight: shape {:?}, requires_grad: {}",
57        weight.shape().dims,
58        weight.requires_grad()
59    );
60    println!(
61        "  Bias: shape {:?}, requires_grad: {}",
62        bias.shape().dims,
63        bias.requires_grad()
64    );
65
66    // Create Adam optimizer with default configuration
67    let mut optimizer = Adam::new();
68    println!(
69        "Created Adam optimizer with learning rate: {}",
70        optimizer.learning_rate()
71    );
72
73    // Add parameters to optimizer
74    optimizer.add_parameter(&weight);
75    optimizer.add_parameter(&bias);
76    println!(
77        "Added {} parameters to optimizer",
78        optimizer.parameter_count()
79    );
80
81    // Create optimizer with custom configuration
82    let config = AdamConfig {
83        learning_rate: 0.01,
84        beta1: 0.9,
85        beta2: 0.999,
86        eps: 1e-8,
87        weight_decay: 0.0,
88        amsgrad: false,
89    };
90
91    let mut custom_optimizer = Adam::with_config(config);
92    custom_optimizer.add_parameter(&weight);
93    custom_optimizer.add_parameter(&bias);
94
95    println!(
96        "Created custom optimizer with learning rate: {}",
97        custom_optimizer.learning_rate()
98    );
99
100    // Demonstrate parameter linking
101    println!("Parameter linking completed successfully");
102}
103
104/// Demonstrate simple linear regression training
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
examples/getting_started/serialization_basics.rs (line 113)
109fn demonstrate_optimizer_serialization() -> Result<(), Box<dyn std::error::Error>> {
110    println!("\n--- Optimizer Serialization ---");
111
112    // Create an optimizer with some parameters
113    let mut weight = Tensor::randn(vec![2, 2], Some(42)).with_requires_grad();
114    let mut bias = Tensor::randn(vec![2], Some(43)).with_requires_grad();
115
116    let config = AdamConfig {
117        learning_rate: 0.001,
118        beta1: 0.9,
119        beta2: 0.999,
120        eps: 1e-8,
121        weight_decay: 0.0,
122        amsgrad: false,
123    };
124
125    let mut optimizer = Adam::with_config(config);
126    optimizer.add_parameter(&weight);
127    optimizer.add_parameter(&bias);
128
129    println!(
130        "Created optimizer with {} parameters",
131        optimizer.parameter_count()
132    );
133    println!("Learning rate: {}", optimizer.learning_rate());
134
135    // Simulate some training steps
136    for _ in 0..3 {
137        let mut loss = weight.sum() + bias.sum();
138        loss.backward(None);
139        optimizer.step(&mut [&mut weight, &mut bias]);
140        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
141    }
142
143    // Save optimizer state
144    let optimizer_path = "temp_optimizer.json";
145    optimizer.save_json(optimizer_path)?;
146    println!("Saved optimizer to: {}", optimizer_path);
147
148    // Load optimizer state
149    let loaded_optimizer = Adam::load_json(optimizer_path)?;
150    println!(
151        "Loaded optimizer with {} parameters",
152        loaded_optimizer.parameter_count()
153    );
154    println!("Learning rate: {}", loaded_optimizer.learning_rate());
155
156    // Verify optimizer state
157    assert_eq!(
158        optimizer.parameter_count(),
159        loaded_optimizer.parameter_count()
160    );
161    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
162    println!("Optimizer serialization verification: PASSED");
163
164    Ok(())
165}
166
167/// Demonstrate format comparison and performance characteristics
168fn demonstrate_format_comparison() -> Result<(), Box<dyn std::error::Error>> {
169    println!("\n--- Format Comparison ---");
170
171    // Create a larger tensor for comparison
172    let tensor = Tensor::randn(vec![10, 10], Some(44));
173
174    // Save in both formats
175    tensor.save_json("temp_comparison.json")?;
176    tensor.save_binary("temp_comparison.bin")?;
177
178    // Compare file sizes
179    let json_size = fs::metadata("temp_comparison.json")?.len();
180    let binary_size = fs::metadata("temp_comparison.bin")?.len();
181
182    println!("JSON file size: {} bytes", json_size);
183    println!("Binary file size: {} bytes", binary_size);
184    println!(
185        "Compression ratio: {:.2}x",
186        json_size as f64 / binary_size as f64
187    );
188
189    // Load and verify both formats
190    let json_tensor = Tensor::load_json("temp_comparison.json")?;
191    let binary_tensor = Tensor::load_binary("temp_comparison.bin")?;
192
193    assert_eq!(tensor.shape().dims, json_tensor.shape().dims);
194    assert_eq!(tensor.shape().dims, binary_tensor.shape().dims);
195    assert_eq!(tensor.data(), json_tensor.data());
196    assert_eq!(tensor.data(), binary_tensor.data());
197
198    println!("Format comparison verification: PASSED");
199
200    Ok(())
201}
202
203/// Demonstrate a basic model checkpointing workflow
204fn demonstrate_model_checkpointing() -> Result<(), Box<dyn std::error::Error>> {
205    println!("\n--- Model Checkpointing ---");
206
207    // Create a simple model (weights and bias)
208    let mut weights = Tensor::randn(vec![2, 1], Some(45)).with_requires_grad();
209    let mut bias = Tensor::randn(vec![1], Some(46)).with_requires_grad();
210
211    // Create optimizer
212    let mut optimizer = Adam::with_learning_rate(0.01);
213    optimizer.add_parameter(&weights);
214    optimizer.add_parameter(&bias);
215
216    println!("Initial weights: {:?}", weights.data());
217    println!("Initial bias: {:?}", bias.data());
218
219    // Simulate training
220    for epoch in 0..5 {
221        let mut loss = weights.sum() + bias.sum();
222        loss.backward(None);
223        optimizer.step(&mut [&mut weights, &mut bias]);
224        optimizer.zero_grad(&mut [&mut weights, &mut bias]);
225
226        if epoch % 2 == 0 {
227            // Save checkpoint
228            let checkpoint_dir = format!("checkpoint_epoch_{}", epoch);
229            fs::create_dir_all(&checkpoint_dir)?;
230
231            weights.save_json(format!("{}/weights.json", checkpoint_dir))?;
232            bias.save_json(format!("{}/bias.json", checkpoint_dir))?;
233            optimizer.save_json(format!("{}/optimizer.json", checkpoint_dir))?;
234
235            println!("Saved checkpoint for epoch {}", epoch);
236        }
237    }
238
239    // Load from checkpoint
240    let loaded_weights = Tensor::load_json("checkpoint_epoch_4/weights.json")?;
241    let loaded_bias = Tensor::load_json("checkpoint_epoch_4/bias.json")?;
242    let loaded_optimizer = Adam::load_json("checkpoint_epoch_4/optimizer.json")?;
243
244    println!("Loaded weights: {:?}", loaded_weights.data());
245    println!("Loaded bias: {:?}", loaded_bias.data());
246    println!(
247        "Loaded optimizer learning rate: {}",
248        loaded_optimizer.learning_rate()
249    );
250
251    // Verify checkpoint integrity
252    assert_eq!(weights.shape().dims, loaded_weights.shape().dims);
253    assert_eq!(bias.shape().dims, loaded_bias.shape().dims);
254    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
255
256    println!("Checkpointing verification: PASSED");
257
258    Ok(())
259}
examples/optimizers/adam_configurations.rs (line 92)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
Source

pub fn set_requires_grad(&mut self, requires_grad: bool)

Set gradient tracking for this tensor

Controls whether the gradtrack system tracks operations on this tensor and computes gradients during backward pass. When disabled, clears any existing gradients and gradient functions.

§Arguments
  • requires_grad - Whether to track gradients for this tensor
§Performance
  • Time Complexity: O(1) - simple field assignment
  • Memory: May free gradient storage when disabled
  • Overhead: Zero overhead when gradients disabled
§Examples
use train_station::Tensor;

let mut tensor = Tensor::ones(vec![2, 3]);
tensor.set_requires_grad(true);
assert!(tensor.requires_grad());

// Disable gradient tracking
tensor.set_requires_grad(false);
assert!(!tensor.requires_grad());
Source

pub fn requires_grad(&self) -> bool

Check if this tensor requires gradients

§Returns

true if gradient tracking is enabled for this tensor

§Examples
use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3]);
assert!(!tensor.requires_grad());

let grad_tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
assert!(grad_tensor.requires_grad());
Examples found in repository?
examples/neural_networks/basic_linear_layer.rs (line 158)
147fn demonstrate_layer_creation() {
148    println!("--- Layer Creation ---");
149
150    let layer = LinearLayer::new(3, 2, Some(42));
151
152    println!("Created linear layer:");
153    println!("  Input size: {}", layer.input_size);
154    println!("  Output size: {}", layer.output_size);
155    println!("  Parameter count: {}", layer.parameter_count());
156    println!("  Weight shape: {:?}", layer.weight.shape().dims);
157    println!("  Bias shape: {:?}", layer.bias.shape().dims);
158    println!("  Weight requires grad: {}", layer.weight.requires_grad());
159    println!("  Bias requires grad: {}", layer.bias.requires_grad());
160}
161
162/// Demonstrate forward pass with gradient tracking
163fn demonstrate_forward_pass() {
164    println!("\n--- Forward Pass (with gradients) ---");
165
166    let layer = LinearLayer::new(3, 2, Some(43));
167
168    // Single input
169    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
170    let output = layer.forward(&input);
171
172    println!("Single input:");
173    println!("  Input: {:?}", input.data());
174    println!("  Output: {:?}", output.data());
175    println!("  Output requires grad: {}", output.requires_grad());
176
177    // Batch input
178    let batch_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
179    let batch_output = layer.forward(&batch_input);
180
181    println!("Batch input:");
182    println!("  Input shape: {:?}", batch_input.shape().dims);
183    println!("  Output shape: {:?}", batch_output.shape().dims);
184    println!("  Output requires grad: {}", batch_output.requires_grad());
185}
186
187/// Demonstrate forward pass without gradient tracking
188fn demonstrate_forward_pass_no_grad() {
189    println!("\n--- Forward Pass (no gradients) ---");
190
191    let layer = LinearLayer::new(3, 2, Some(44));
192
193    // Single input
194    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
195    let output = layer.forward_no_grad(&input);
196
197    println!("Single input (no grad):");
198    println!("  Input: {:?}", input.data());
199    println!("  Output: {:?}", output.data());
200    println!("  Output requires grad: {}", output.requires_grad());
201
202    // Compare with grad version
203    let output_with_grad = layer.forward(&input);
204    println!("Comparison:");
205    println!(
206        "  Same values: {}",
207        output.data() == output_with_grad.data()
208    );
209    println!("  No grad requires grad: {}", output.requires_grad());
210    println!(
211        "  With grad requires grad: {}",
212        output_with_grad.requires_grad()
213    );
214}
More examples
Hide additional examples
examples/iterators/element_iteration.rs (line 176)
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
examples/getting_started/optimizer_basics.rs (line 58)
47fn demonstrate_basic_optimizer_setup() {
48    println!("--- Basic Optimizer Setup ---");
49
50    // Create parameters that require gradients
51    let weight = Tensor::randn(vec![3, 2], Some(42)).with_requires_grad();
52    let bias = Tensor::zeros(vec![2]).with_requires_grad();
53
54    println!("Created parameters:");
55    println!(
56        "  Weight: shape {:?}, requires_grad: {}",
57        weight.shape().dims,
58        weight.requires_grad()
59    );
60    println!(
61        "  Bias: shape {:?}, requires_grad: {}",
62        bias.shape().dims,
63        bias.requires_grad()
64    );
65
66    // Create Adam optimizer with default configuration
67    let mut optimizer = Adam::new();
68    println!(
69        "Created Adam optimizer with learning rate: {}",
70        optimizer.learning_rate()
71    );
72
73    // Add parameters to optimizer
74    optimizer.add_parameter(&weight);
75    optimizer.add_parameter(&bias);
76    println!(
77        "Added {} parameters to optimizer",
78        optimizer.parameter_count()
79    );
80
81    // Create optimizer with custom configuration
82    let config = AdamConfig {
83        learning_rate: 0.01,
84        beta1: 0.9,
85        beta2: 0.999,
86        eps: 1e-8,
87        weight_decay: 0.0,
88        amsgrad: false,
89    };
90
91    let mut custom_optimizer = Adam::with_config(config);
92    custom_optimizer.add_parameter(&weight);
93    custom_optimizer.add_parameter(&bias);
94
95    println!(
96        "Created custom optimizer with learning rate: {}",
97        custom_optimizer.learning_rate()
98    );
99
100    // Demonstrate parameter linking
101    println!("Parameter linking completed successfully");
102}
examples/neural_networks/feedforward_network.rs (line 358)
339fn demonstrate_forward_pass() {
340    println!("\n--- Forward Pass ---");
341
342    let config = FeedForwardConfig {
343        input_size: 3,
344        hidden_sizes: vec![5, 3],
345        output_size: 2,
346        use_bias: true,
347    };
348    let network = FeedForwardNetwork::new(config, Some(43));
349
350    // Single input
351    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
352    let output = network.forward(&input);
353
354    println!("Single input forward pass:");
355    println!("  Input shape: {:?}", input.shape().dims);
356    println!("  Output shape: {:?}", output.shape().dims);
357    println!("  Output: {:?}", output.data());
358    println!("  Output requires grad: {}", output.requires_grad());
359
360    // Batch input
361    let batch_input = Tensor::from_slice(
362        &[
363            1.0, 2.0, 3.0, // Sample 1
364            4.0, 5.0, 6.0, // Sample 2
365            7.0, 8.0, 9.0, // Sample 3
366        ],
367        vec![3, 3],
368    )
369    .unwrap();
370    let batch_output = network.forward(&batch_input);
371
372    println!("Batch input forward pass:");
373    println!("  Input shape: {:?}", batch_input.shape().dims);
374    println!("  Output shape: {:?}", batch_output.shape().dims);
375    println!("  Output requires grad: {}", batch_output.requires_grad());
376
377    // Compare with no-grad version
378    let output_no_grad = network.forward_no_grad(&input);
379    println!("No-grad comparison:");
380    println!("  Same values: {}", output.data() == output_no_grad.data());
381    println!("  With grad requires grad: {}", output.requires_grad());
382    println!(
383        "  No grad requires grad: {}",
384        output_no_grad.requires_grad()
385    );
386}
examples/iterators/performance_optimization.rs (line 394)
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
Source

pub fn grad(&self) -> Option<&Tensor>

Get the accumulated gradients (if any)

Returns a reference to the gradient tensor if gradients have been computed and this tensor has gradient tracking enabled.

§Returns

Optional reference to the gradient tensor, or None if no gradients exist

§Examples
use train_station::Tensor;

let tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
assert!(tensor.grad().is_none()); // No gradients computed yet
Examples found in repository?
examples/iterators/element_iteration.rs (line 183)
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
Source

pub fn grad_by_value(&self) -> Option<Tensor>

Get accumulated gradient by value (helper for testing)

Returns the gradient tensor by value, which is useful for testing and when you need to own the gradient data.

§Returns

Optional gradient tensor, or None if no gradients exist

§Examples
use train_station::Tensor;

let tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
assert!(tensor.grad_by_value().is_none()); // No gradients computed yet
Examples found in repository?
examples/getting_started/optimizer_basics.rs (line 214)
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
Source

pub fn id(&self) -> usize

Get the unique ID of this tensor

Returns the unique identifier assigned to this tensor during creation. This ID is used for gradtrack tracking and tensor identification.

§Returns

Unique tensor ID as usize

§Examples
use train_station::Tensor;

let tensor1 = Tensor::new(vec![2, 3]);
let tensor2 = Tensor::new(vec![2, 3]);
assert_ne!(tensor1.id(), tensor2.id()); // Each tensor has unique ID
Source

pub fn detach(&self) -> Self

Detach this tensor from the computation graph

Returns a new tensor with the same data but no gradient tracking. This is useful when you want to use a tensor in inference without affecting the computation graph.

§Returns

A new tensor with the same data but gradient tracking disabled

§Examples
use train_station::Tensor;

let tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
let detached = tensor.detach();
assert!(!detached.requires_grad());
assert_eq!(tensor.size(), detached.size());
Source

pub fn detach_(&mut self)

Create a new tensor that doesn’t track gradients from this one

Similar to detach() but modifies this tensor in place. This is useful when you want to disable gradient tracking for the current tensor without creating a copy.

§Examples
use train_station::Tensor;

let mut tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
assert!(tensor.requires_grad());
tensor.detach_();
assert!(!tensor.requires_grad());
Source

pub fn backward(&mut self, grad_output: Option<Tensor>)

Entry point for backward pass on this tensor

Computes gradients for all tensors in the computation graph that have requires_grad set to true. This is the main entry point for automatic differentiation.

§Arguments
  • grad_output - Optional gradient tensor for the output. If None, assumes the tensor is a scalar (e.g., loss value) and uses a tensor of ones.
§Examples
use train_station::Tensor;

let mut tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
let mut result = tensor.add_scalar(5.0);
result.backward(None);
// Note: Gradient computation depends on the gradtrack system implementation
Examples found in repository?
examples/iterators/element_iteration.rs (line 180)
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
More examples
Hide additional examples
examples/getting_started/serialization_basics.rs (line 138)
109fn demonstrate_optimizer_serialization() -> Result<(), Box<dyn std::error::Error>> {
110    println!("\n--- Optimizer Serialization ---");
111
112    // Create an optimizer with some parameters
113    let mut weight = Tensor::randn(vec![2, 2], Some(42)).with_requires_grad();
114    let mut bias = Tensor::randn(vec![2], Some(43)).with_requires_grad();
115
116    let config = AdamConfig {
117        learning_rate: 0.001,
118        beta1: 0.9,
119        beta2: 0.999,
120        eps: 1e-8,
121        weight_decay: 0.0,
122        amsgrad: false,
123    };
124
125    let mut optimizer = Adam::with_config(config);
126    optimizer.add_parameter(&weight);
127    optimizer.add_parameter(&bias);
128
129    println!(
130        "Created optimizer with {} parameters",
131        optimizer.parameter_count()
132    );
133    println!("Learning rate: {}", optimizer.learning_rate());
134
135    // Simulate some training steps
136    for _ in 0..3 {
137        let mut loss = weight.sum() + bias.sum();
138        loss.backward(None);
139        optimizer.step(&mut [&mut weight, &mut bias]);
140        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
141    }
142
143    // Save optimizer state
144    let optimizer_path = "temp_optimizer.json";
145    optimizer.save_json(optimizer_path)?;
146    println!("Saved optimizer to: {}", optimizer_path);
147
148    // Load optimizer state
149    let loaded_optimizer = Adam::load_json(optimizer_path)?;
150    println!(
151        "Loaded optimizer with {} parameters",
152        loaded_optimizer.parameter_count()
153    );
154    println!("Learning rate: {}", loaded_optimizer.learning_rate());
155
156    // Verify optimizer state
157    assert_eq!(
158        optimizer.parameter_count(),
159        loaded_optimizer.parameter_count()
160    );
161    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
162    println!("Optimizer serialization verification: PASSED");
163
164    Ok(())
165}
166
167/// Demonstrate format comparison and performance characteristics
168fn demonstrate_format_comparison() -> Result<(), Box<dyn std::error::Error>> {
169    println!("\n--- Format Comparison ---");
170
171    // Create a larger tensor for comparison
172    let tensor = Tensor::randn(vec![10, 10], Some(44));
173
174    // Save in both formats
175    tensor.save_json("temp_comparison.json")?;
176    tensor.save_binary("temp_comparison.bin")?;
177
178    // Compare file sizes
179    let json_size = fs::metadata("temp_comparison.json")?.len();
180    let binary_size = fs::metadata("temp_comparison.bin")?.len();
181
182    println!("JSON file size: {} bytes", json_size);
183    println!("Binary file size: {} bytes", binary_size);
184    println!(
185        "Compression ratio: {:.2}x",
186        json_size as f64 / binary_size as f64
187    );
188
189    // Load and verify both formats
190    let json_tensor = Tensor::load_json("temp_comparison.json")?;
191    let binary_tensor = Tensor::load_binary("temp_comparison.bin")?;
192
193    assert_eq!(tensor.shape().dims, json_tensor.shape().dims);
194    assert_eq!(tensor.shape().dims, binary_tensor.shape().dims);
195    assert_eq!(tensor.data(), json_tensor.data());
196    assert_eq!(tensor.data(), binary_tensor.data());
197
198    println!("Format comparison verification: PASSED");
199
200    Ok(())
201}
202
203/// Demonstrate a basic model checkpointing workflow
204fn demonstrate_model_checkpointing() -> Result<(), Box<dyn std::error::Error>> {
205    println!("\n--- Model Checkpointing ---");
206
207    // Create a simple model (weights and bias)
208    let mut weights = Tensor::randn(vec![2, 1], Some(45)).with_requires_grad();
209    let mut bias = Tensor::randn(vec![1], Some(46)).with_requires_grad();
210
211    // Create optimizer
212    let mut optimizer = Adam::with_learning_rate(0.01);
213    optimizer.add_parameter(&weights);
214    optimizer.add_parameter(&bias);
215
216    println!("Initial weights: {:?}", weights.data());
217    println!("Initial bias: {:?}", bias.data());
218
219    // Simulate training
220    for epoch in 0..5 {
221        let mut loss = weights.sum() + bias.sum();
222        loss.backward(None);
223        optimizer.step(&mut [&mut weights, &mut bias]);
224        optimizer.zero_grad(&mut [&mut weights, &mut bias]);
225
226        if epoch % 2 == 0 {
227            // Save checkpoint
228            let checkpoint_dir = format!("checkpoint_epoch_{}", epoch);
229            fs::create_dir_all(&checkpoint_dir)?;
230
231            weights.save_json(format!("{}/weights.json", checkpoint_dir))?;
232            bias.save_json(format!("{}/bias.json", checkpoint_dir))?;
233            optimizer.save_json(format!("{}/optimizer.json", checkpoint_dir))?;
234
235            println!("Saved checkpoint for epoch {}", epoch);
236        }
237    }
238
239    // Load from checkpoint
240    let loaded_weights = Tensor::load_json("checkpoint_epoch_4/weights.json")?;
241    let loaded_bias = Tensor::load_json("checkpoint_epoch_4/bias.json")?;
242    let loaded_optimizer = Adam::load_json("checkpoint_epoch_4/optimizer.json")?;
243
244    println!("Loaded weights: {:?}", loaded_weights.data());
245    println!("Loaded bias: {:?}", loaded_bias.data());
246    println!(
247        "Loaded optimizer learning rate: {}",
248        loaded_optimizer.learning_rate()
249    );
250
251    // Verify checkpoint integrity
252    assert_eq!(weights.shape().dims, loaded_weights.shape().dims);
253    assert_eq!(bias.shape().dims, loaded_bias.shape().dims);
254    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
255
256    println!("Checkpointing verification: PASSED");
257
258    Ok(())
259}
examples/optimizers/adam_configurations.rs (line 115)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
examples/optimizers/learning_rate_scheduling.rs (line 347)
319fn train_with_scheduler(
320    scheduler: &mut dyn LearningRateScheduler,
321    num_epochs: usize,
322) -> Result<TrainingStats, Box<dyn std::error::Error>> {
323    // Create training data: y = 2*x + 1
324    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
325    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
326
327    // Create model parameters
328    let mut weight = Tensor::randn(vec![1, 1], Some(456)).with_requires_grad();
329    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
330
331    // Create optimizer with initial learning rate
332    let mut optimizer = Adam::with_learning_rate(0.05);
333    optimizer.add_parameter(&weight);
334    optimizer.add_parameter(&bias);
335
336    // Training loop
337    let mut losses = Vec::new();
338    let mut lr_history = Vec::new();
339    let mut convergence_epoch = num_epochs;
340
341    for epoch in 0..num_epochs {
342        // Forward pass
343        let y_pred = x_data.matmul(&weight) + &bias;
344        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
345
346        // Backward pass
347        loss.backward(None);
348
349        // Update learning rate using scheduler
350        let current_lr = optimizer.learning_rate();
351        let new_lr = scheduler.step(current_lr, epoch, loss.value());
352
353        if (new_lr - current_lr).abs() > 1e-8 {
354            optimizer.set_learning_rate(new_lr);
355        }
356
357        // Optimizer step
358        optimizer.step(&mut [&mut weight, &mut bias]);
359        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
360
361        let loss_value = loss.value();
362        losses.push(loss_value);
363        lr_history.push(new_lr);
364
365        // Check for convergence
366        if loss_value < 0.01 && convergence_epoch == num_epochs {
367            convergence_epoch = epoch;
368        }
369    }
370
371    Ok(TrainingStats {
372        scheduler_name: scheduler.name().to_string(),
373        final_loss: losses[losses.len() - 1],
374        lr_history,
375        loss_history: losses,
376        convergence_epoch,
377    })
378}
examples/getting_started/optimizer_basics.rs (line 138)
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
examples/neural_networks/feedforward_network.rs (line 482)
430fn demonstrate_training_workflow() -> Result<(), Box<dyn std::error::Error>> {
431    println!("\n--- Training Workflow ---");
432
433    // Create a simple classification network
434    let config = FeedForwardConfig {
435        input_size: 2,
436        hidden_sizes: vec![4, 3],
437        output_size: 1,
438        use_bias: true,
439    };
440    let mut network = FeedForwardNetwork::new(config, Some(46));
441
442    println!("Training network: 2 -> [4, 3] -> 1");
443
444    // Create simple binary classification data: XOR problem
445    let x_data = Tensor::from_slice(
446        &[
447            0.0, 0.0, // -> 0
448            0.0, 1.0, // -> 1
449            1.0, 0.0, // -> 1
450            1.0, 1.0, // -> 0
451        ],
452        vec![4, 2],
453    )
454    .unwrap();
455
456    let y_true = Tensor::from_slice(&[0.0, 1.0, 1.0, 0.0], vec![4, 1]).unwrap();
457
458    println!("Training on XOR problem:");
459    println!("  Input shape: {:?}", x_data.shape().dims);
460    println!("  Target shape: {:?}", y_true.shape().dims);
461
462    // Create optimizer
463    let mut optimizer = Adam::with_learning_rate(0.1);
464    let params = network.parameters();
465    for param in &params {
466        optimizer.add_parameter(param);
467    }
468
469    // Training loop
470    let num_epochs = 50;
471    let mut losses = Vec::new();
472
473    for epoch in 0..num_epochs {
474        // Forward pass
475        let y_pred = network.forward(&x_data);
476
477        // Compute loss: MSE
478        let diff = y_pred.sub_tensor(&y_true);
479        let mut loss = diff.pow_scalar(2.0).mean();
480
481        // Backward pass
482        loss.backward(None);
483
484        // Optimizer step and zero grad
485        let mut params = network.parameters();
486        optimizer.step(&mut params);
487        optimizer.zero_grad(&mut params);
488
489        losses.push(loss.value());
490
491        // Print progress
492        if epoch % 10 == 0 || epoch == num_epochs - 1 {
493            println!("Epoch {:2}: Loss = {:.6}", epoch, loss.value());
494        }
495    }
496
497    // Test final model
498    let final_predictions = network.forward_no_grad(&x_data);
499    println!("\nFinal predictions vs targets:");
500    for i in 0..4 {
501        let pred = final_predictions.data()[i];
502        let target = y_true.data()[i];
503        let input_x = x_data.data()[i * 2];
504        let input_y = x_data.data()[i * 2 + 1];
505        println!(
506            "  [{:.0}, {:.0}] -> pred: {:.3}, target: {:.0}, error: {:.3}",
507            input_x,
508            input_y,
509            pred,
510            target,
511            (pred - target).abs()
512        );
513    }
514
515    Ok(())
516}
517
518/// Demonstrate comprehensive training with 100+ steps
519fn demonstrate_comprehensive_training() -> Result<(), Box<dyn std::error::Error>> {
520    println!("\n--- Comprehensive Training (100+ Steps) ---");
521
522    // Create a regression network
523    let config = FeedForwardConfig {
524        input_size: 3,
525        hidden_sizes: vec![8, 6, 4],
526        output_size: 2,
527        use_bias: true,
528    };
529    let mut network = FeedForwardNetwork::new(config, Some(47));
530
531    println!("Network architecture: 3 -> [8, 6, 4] -> 2");
532    println!("Total parameters: {}", network.parameter_count());
533
534    // Create synthetic regression data
535    // Target function: [y1, y2] = [x1 + 2*x2 - x3, x1*x2 + x3]
536    let num_samples = 32;
537    let mut x_vec = Vec::new();
538    let mut y_vec = Vec::new();
539
540    for i in 0..num_samples {
541        let x1 = (i as f32 / num_samples as f32) * 2.0 - 1.0; // [-1, 1]
542        let x2 = ((i * 2) as f32 / num_samples as f32) * 2.0 - 1.0;
543        let x3 = ((i * 3) as f32 / num_samples as f32) * 2.0 - 1.0;
544
545        let y1 = x1 + 2.0 * x2 - x3;
546        let y2 = x1 * x2 + x3;
547
548        x_vec.extend_from_slice(&[x1, x2, x3]);
549        y_vec.extend_from_slice(&[y1, y2]);
550    }
551
552    let x_data = Tensor::from_slice(&x_vec, vec![num_samples, 3]).unwrap();
553    let y_true = Tensor::from_slice(&y_vec, vec![num_samples, 2]).unwrap();
554
555    println!("Training data:");
556    println!("  {} samples", num_samples);
557    println!("  Input shape: {:?}", x_data.shape().dims);
558    println!("  Target shape: {:?}", y_true.shape().dims);
559
560    // Create optimizer with learning rate scheduling
561    let mut optimizer = Adam::with_learning_rate(0.01);
562    let params = network.parameters();
563    for param in &params {
564        optimizer.add_parameter(param);
565    }
566
567    // Comprehensive training loop (150 epochs)
568    let num_epochs = 150;
569    let mut losses = Vec::new();
570    let mut best_loss = f32::INFINITY;
571    let mut patience_counter = 0;
572    let patience = 20;
573
574    println!("Starting comprehensive training...");
575
576    for epoch in 0..num_epochs {
577        // Forward pass
578        let y_pred = network.forward(&x_data);
579
580        // Compute loss: MSE
581        let diff = y_pred.sub_tensor(&y_true);
582        let mut loss = diff.pow_scalar(2.0).mean();
583
584        // Backward pass
585        loss.backward(None);
586
587        // Optimizer step and zero grad
588        let mut params = network.parameters();
589        optimizer.step(&mut params);
590        optimizer.zero_grad(&mut params);
591
592        let current_loss = loss.value();
593        losses.push(current_loss);
594
595        // Learning rate scheduling
596        if epoch > 0 && epoch % 30 == 0 {
597            let new_lr = optimizer.learning_rate() * 0.8;
598            optimizer.set_learning_rate(new_lr);
599            println!("  Reduced learning rate to {:.4}", new_lr);
600        }
601
602        // Early stopping logic
603        if current_loss < best_loss {
604            best_loss = current_loss;
605            patience_counter = 0;
606        } else {
607            patience_counter += 1;
608        }
609
610        // Print progress
611        if epoch % 25 == 0 || epoch == num_epochs - 1 {
612            println!(
613                "Epoch {:3}: Loss = {:.6}, LR = {:.4}, Best = {:.6}",
614                epoch,
615                current_loss,
616                optimizer.learning_rate(),
617                best_loss
618            );
619        }
620
621        // Early stopping
622        if patience_counter >= patience && epoch > 50 {
623            println!("Early stopping at epoch {} (patience exceeded)", epoch);
624            break;
625        }
626    }
627
628    // Final evaluation
629    let final_predictions = network.forward_no_grad(&x_data);
630
631    // Compute final metrics
632    let final_loss = losses[losses.len() - 1];
633    let initial_loss = losses[0];
634    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
635
636    println!("\nTraining completed!");
637    println!("  Initial loss: {:.6}", initial_loss);
638    println!("  Final loss: {:.6}", final_loss);
639    println!("  Best loss: {:.6}", best_loss);
640    println!("  Loss reduction: {:.1}%", loss_reduction);
641    println!("  Final learning rate: {:.4}", optimizer.learning_rate());
642
643    // Sample predictions analysis
644    println!("\nSample predictions (first 5):");
645    for i in 0..5.min(num_samples) {
646        let pred1 = final_predictions.data()[i * 2];
647        let pred2 = final_predictions.data()[i * 2 + 1];
648        let true1 = y_true.data()[i * 2];
649        let true2 = y_true.data()[i * 2 + 1];
650
651        println!(
652            "  Sample {}: pred=[{:.3}, {:.3}], true=[{:.3}, {:.3}], error=[{:.3}, {:.3}]",
653            i + 1,
654            pred1,
655            pred2,
656            true1,
657            true2,
658            (pred1 - true1).abs(),
659            (pred2 - true2).abs()
660        );
661    }
662
663    Ok(())
664}
665
666/// Demonstrate network serialization
667fn demonstrate_network_serialization() -> Result<(), Box<dyn std::error::Error>> {
668    println!("\n--- Network Serialization ---");
669
670    // Create and train a network
671    let config = FeedForwardConfig {
672        input_size: 2,
673        hidden_sizes: vec![4, 2],
674        output_size: 1,
675        use_bias: true,
676    };
677    let mut original_network = FeedForwardNetwork::new(config.clone(), Some(48));
678
679    // Quick training
680    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
681    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
682
683    let mut optimizer = Adam::with_learning_rate(0.01);
684    let params = original_network.parameters();
685    for param in &params {
686        optimizer.add_parameter(param);
687    }
688
689    for _ in 0..20 {
690        let y_pred = original_network.forward(&x_data);
691        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
692        loss.backward(None);
693
694        let mut params = original_network.parameters();
695        optimizer.step(&mut params);
696        optimizer.zero_grad(&mut params);
697    }
698
699    // Test original network
700    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
701    let original_output = original_network.forward_no_grad(&test_input);
702
703    println!("Original network output: {:?}", original_output.data());
704
705    // Save network
706    original_network.save_json("temp_feedforward_network")?;
707
708    // Load network
709    let loaded_network = FeedForwardNetwork::load_json("temp_feedforward_network", config)?;
710    let loaded_output = loaded_network.forward_no_grad(&test_input);
711
712    println!("Loaded network output: {:?}", loaded_output.data());
713
714    // Verify consistency
715    let match_check = original_output
716        .data()
717        .iter()
718        .zip(loaded_output.data().iter())
719        .all(|(a, b)| (a - b).abs() < 1e-6);
720
721    println!(
722        "Serialization verification: {}",
723        if match_check { "PASSED" } else { "FAILED" }
724    );
725
726    Ok(())
727}
Source

pub unsafe fn as_ptr(&self) -> *const f32

Returns a raw pointer to the tensor data for unsafe operations

§Safety

This is unsafe because it provides direct access to the underlying memory. The caller must ensure:

  • The tensor is not dropped while the pointer is used
  • No concurrent mutable access occurs
  • Bounds are respected
Source

pub unsafe fn as_mut_ptr(&mut self) -> *mut f32

Returns a mutable raw pointer to the tensor data for unsafe operations

§Safety

This is unsafe because it provides direct mutable access to the underlying memory. The caller must ensure:

  • The tensor is not dropped while the pointer is used
  • No concurrent access occurs
  • Bounds are respected
Source

pub fn grad_fn(&self) -> &GradFn

Get a reference to the gradient function (for gradtrack)

Returns a reference to the gradient function associated with this tensor. This is used internally by the gradtrack system to compute gradients.

§Returns

Reference to the gradient function

§Implementation Details

This method is used by the gradtrack engine to access the gradient computation function during backward pass.

Source

pub fn set_grad(&mut self, grad: Tensor)

Set gradient from external source

Sets the gradient tensor for this tensor. This is used internally by the gradtrack system to set gradients during backward pass.

§Arguments
  • grad - The gradient tensor to set
§Implementation Details

This method is used internally by the gradtrack engine to set gradients during backward pass. It only sets the gradient if gradient tracking is enabled for this tensor.

Source

pub fn zero_grad(&mut self)

Clear accumulated gradients for this tensor

This method is used by optimizers to zero gradients before each backward pass. It clears any accumulated gradients, allowing for fresh gradient computation.

§Examples
use train_station::Tensor;

let mut tensor = Tensor::ones(vec![2, 3]).with_requires_grad();
tensor.set_grad(Tensor::ones(vec![2, 3]));
assert!(tensor.grad().is_some());
tensor.zero_grad();
assert!(tensor.grad().is_none());
Source

pub fn is_contiguous(&self) -> bool

Checks if the tensor data is stored contiguously in memory

§Returns

true if the tensor data is contiguous, enabling optimized SIMD operations

§Examples
use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3, 4]);
assert!(tensor.is_contiguous());
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 187)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
Source

pub fn is_view(&self) -> bool

Checks if this tensor is a view of another tensor

§Returns

true if this tensor is a view (non-contiguous reference)

Source

pub fn strides(&self) -> &[usize]

Gets the memory strides for all dimensions

§Returns

Reference to the stride vector for efficient memory access calculations

§Examples
use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3, 4]);
assert_eq!(tensor.strides(), &[12, 4, 1]);
Source

pub fn stride(&self, dim: usize) -> usize

Gets the memory stride for a specific dimension

§Arguments
  • dim - The dimension index
§Returns

The memory stride for the given dimension

§Panics

Panics if dim is out of bounds

Source

pub fn layout(&self) -> &MemoryLayout

Gets the memory layout type for optimization decisions

§Returns

Reference to the memory layout information

Source

pub fn memory_offset(&self, indices: &[usize]) -> usize

Calculates the linear memory offset for given multi-dimensional indices

§Arguments
  • indices - Vector of indices for each dimension
§Returns

Linear memory offset for direct memory access

§Examples
use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3, 4]);
let offset = tensor.memory_offset(&[1, 2, 3]);
// offset = 1*12 + 2*4 + 3*1 = 23
Source

pub fn broadcast_with( &self, other: &Tensor, ) -> Result<(Tensor, Tensor, Shape), BroadcastError>

Broadcast this tensor with another tensor for element-wise operations

Returns a tuple containing:

  • Broadcasted view of self
  • Broadcasted view of other
  • Result shape for the operation
§Arguments
  • other - The tensor to broadcast with
§Returns

A tuple (broadcasted_self, broadcasted_other, result_shape)

§Examples
use train_station::Tensor;

let a = Tensor::ones(vec![2, 1, 4]);
let b = Tensor::ones(vec![3, 1]);
let result = a.broadcast_with(&b);
assert!(result.is_ok());
Source

pub fn is_simd_aligned(&self) -> bool

Checks if the tensor data is properly aligned for SIMD operations

§Returns

true if the tensor data is aligned to 32-byte boundaries for AVX2

Source

pub fn memory_alignment(&self) -> usize

Gets the memory alignment of the tensor data

§Returns

The memory alignment in bytes (typically 32 for SIMD optimization)

Source

pub fn is_broadcastable_with(&self, other: &Tensor) -> bool

Checks if this tensor is broadcastable with another tensor

§Arguments
  • other - The other tensor to check broadcasting compatibility
§Returns

true if the tensors are broadcastable according to NumPy broadcasting rules

§Examples
use train_station::Tensor;

let a = Tensor::new(vec![2, 3, 4]);
let b = Tensor::new(vec![1, 3, 4]);
assert!(a.is_broadcastable_with(&b));
Source

pub fn memory_footprint(&self) -> usize

Gets the total number of bytes allocated for this tensor

§Returns

Total memory footprint in bytes

Source

pub fn get(&self, indices: &[usize]) -> f32

Get a single element from the tensor at the specified indices

§Arguments
  • indices - Multi-dimensional indices to access the element
§Returns

The value at the specified position

§Panics

Panics if indices are out of bounds or indices length doesn’t match tensor rank

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let value = tensor.get(&[0, 1]);
assert_eq!(value, 2.0);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 162)
156fn demonstrate_data_access() {
157    println!("\n--- Data Access ---");
158
159    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
160
161    // Access individual elements
162    println!("Element [0, 0]: {}", tensor.get(&[0, 0]));
163    println!("Element [0, 1]: {}", tensor.get(&[0, 1]));
164    println!("Element [1, 0]: {}", tensor.get(&[1, 0]));
165    println!("Element [1, 1]: {}", tensor.get(&[1, 1]));
166
167    // Access data as slice
168    let data = tensor.data();
169    println!("Data as slice: {:?}", data);
170
171    // Iterate over elements
172    println!("Elements:");
173    for (i, &value) in data.iter().enumerate() {
174        println!("  [{}]: {}", i, value);
175    }
176}
Source

pub fn set(&mut self, indices: &[usize], value: f32)

Set a single element in the tensor at the specified indices

§Arguments
  • indices - Multi-dimensional indices to set the element
  • value - The value to set
§Panics

Panics if indices are out of bounds or indices length doesn’t match tensor rank

§Examples
use train_station::Tensor;

let mut tensor = Tensor::new(vec![2, 2]);
tensor.set(&[0, 1], 42.0);
assert_eq!(tensor.get(&[0, 1]), 42.0);
Source

pub fn data(&self) -> &[f32]

Returns a safe slice of the tensor’s underlying data

Provides safe access to the tensor’s data without requiring unsafe pointer operations. This is the preferred way to access tensor data for reading values, comparisons, and other operations that don’t require direct pointer manipulation.

§Returns

A slice containing all tensor elements in row-major order

§Performance
  • Zero-Cost: Direct slice creation with no copying
  • Cache-Friendly: Sequential memory access pattern
  • Safe: No unsafe code required for basic data access
§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let data = tensor.data();

// Safe indexing and comparisons
assert_eq!(data[0], 1.0);
assert_eq!(data.len(), tensor.size());
Examples found in repository?
examples/getting_started/tensor_operators.rs (line 52)
46fn demonstrate_basic_operators() {
47    println!("--- Basic Tensor-Tensor Operators ---");
48
49    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
50    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
51
52    println!("Tensor A: {:?}", a.data());
53    println!("Tensor B: {:?}", b.data());
54
55    // Addition
56    let c = &a + &b;
57    println!("A + B: {:?}", c.data());
58
59    // Subtraction
60    let d = &a - &b;
61    println!("A - B: {:?}", d.data());
62
63    // Multiplication
64    let e = &a * &b;
65    println!("A * B: {:?}", e.data());
66
67    // Division
68    let f = &a / &b;
69    println!("A / B: {:?}", f.data());
70}
71
72/// Demonstrate tensor-scalar operators
73fn demonstrate_scalar_operators() {
74    println!("\n--- Tensor-Scalar Operators ---");
75
76    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
77    println!("Original tensor: {:?}", tensor.data());
78
79    // Tensor + scalar
80    let result1 = &tensor + 5.0;
81    println!("Tensor + 5.0: {:?}", result1.data());
82
83    // Scalar + tensor
84    let result2 = 5.0 + &tensor;
85    println!("5.0 + Tensor: {:?}", result2.data());
86
87    // Tensor - scalar
88    let result3 = &tensor - 2.0;
89    println!("Tensor - 2.0: {:?}", result3.data());
90
91    // Tensor * scalar
92    let result4 = &tensor * 3.0;
93    println!("Tensor * 3.0: {:?}", result4.data());
94
95    // Scalar * tensor
96    let result5 = 3.0 * &tensor;
97    println!("3.0 * Tensor: {:?}", result5.data());
98
99    // Tensor / scalar
100    let result6 = &tensor / 2.0;
101    println!("Tensor / 2.0: {:?}", result6.data());
102}
103
104/// Demonstrate assignment operators
105fn demonstrate_operator_assignment() {
106    println!("\n--- Assignment Operators ---");
107
108    let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
109    println!("Original tensor: {:?}", tensor.data());
110
111    // In-place addition
112    tensor += 5.0;
113    println!("After += 5.0: {:?}", tensor.data());
114
115    // In-place subtraction
116    tensor -= 2.0;
117    println!("After -= 2.0: {:?}", tensor.data());
118
119    // In-place multiplication
120    tensor *= 3.0;
121    println!("After *= 3.0: {:?}", tensor.data());
122
123    // In-place division
124    tensor /= 2.0;
125    println!("After /= 2.0: {:?}", tensor.data());
126}
127
128/// Demonstrate operator chaining and complex expressions
129fn demonstrate_operator_chaining() {
130    println!("\n--- Operator Chaining ---");
131
132    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
133    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
134    let c = Tensor::from_slice(&[9.0, 10.0, 11.0, 12.0], vec![2, 2]).unwrap();
135
136    println!("Tensor A: {:?}", a.data());
137    println!("Tensor B: {:?}", b.data());
138    println!("Tensor C: {:?}", c.data());
139
140    // Complex expression: (A + B) * C - 5
141    let result = (&a + &b) * &c - 5.0;
142    println!("(A + B) * C - 5: {:?}", result.data());
143
144    // Another complex expression: A * 2 + B / 2
145    let result2 = &a * 2.0 + &b / 2.0;
146    println!("A * 2 + B / 2: {:?}", result2.data());
147
148    // Negation and addition: -A + B * C
149    let result3 = -&a + &b * &c;
150    println!("-A + B * C: {:?}", result3.data());
151
152    // Division with parentheses: (A + B) / (C - 1)
153    let result4 = (&a + &b) / (&c - 1.0);
154    println!("(A + B) / (C - 1): {:?}", result4.data());
155}
156
157/// Demonstrate broadcasting behavior
158fn demonstrate_broadcasting() {
159    println!("\n--- Broadcasting ---");
160
161    // 2D tensor
162    let tensor_2d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
163    println!(
164        "2D tensor: shape {:?}, data: {:?}",
165        tensor_2d.shape().dims,
166        tensor_2d.data()
167    );
168
169    // 1D tensor (will be broadcasted)
170    let tensor_1d = Tensor::from_slice(&[10.0, 20.0], vec![2]).unwrap();
171    println!(
172        "1D tensor: shape {:?}, data: {:?}",
173        tensor_1d.shape().dims,
174        tensor_1d.data()
175    );
176
177    // Broadcasting addition
178    let broadcast_sum = &tensor_2d + &tensor_1d;
179    println!(
180        "Broadcast sum: shape {:?}, data: {:?}",
181        broadcast_sum.shape().dims,
182        broadcast_sum.data()
183    );
184
185    // Broadcasting multiplication
186    let broadcast_mul = &tensor_2d * &tensor_1d;
187    println!(
188        "Broadcast multiplication: shape {:?}, data: {:?}",
189        broadcast_mul.shape().dims,
190        broadcast_mul.data()
191    );
192
193    // Broadcasting with scalar
194    let broadcast_scalar = &tensor_2d + 100.0;
195    println!(
196        "Broadcast scalar: shape {:?}, data: {:?}",
197        broadcast_scalar.shape().dims,
198        broadcast_scalar.data()
199    );
200}
201
202/// Demonstrate equivalence between operators and method calls
203fn demonstrate_method_equivalence() {
204    println!("\n--- Operator vs Method Call Equivalence ---");
205
206    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
207    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
208
209    // Addition: operator vs method
210    let operator_result = &a + &b;
211    let method_result = a.add_tensor(&b);
212
213    println!("A + B (operator): {:?}", operator_result.data());
214    println!("A.add_tensor(B): {:?}", method_result.data());
215    println!(
216        "Results are equal: {}",
217        operator_result.data() == method_result.data()
218    );
219
220    // Multiplication: operator vs method
221    let operator_result = &a * &b;
222    let method_result = a.mul_tensor(&b);
223
224    println!("A * B (operator): {:?}", operator_result.data());
225    println!("A.mul_tensor(B): {:?}", method_result.data());
226    println!(
227        "Results are equal: {}",
228        operator_result.data() == method_result.data()
229    );
230
231    // Scalar addition: operator vs method
232    let operator_result = &a + 5.0;
233    let method_result = a.add_scalar(5.0);
234
235    println!("A + 5.0 (operator): {:?}", operator_result.data());
236    println!("A.add_scalar(5.0): {:?}", method_result.data());
237    println!(
238        "Results are equal: {}",
239        operator_result.data() == method_result.data()
240    );
241}
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 173)
163fn demonstrate_forward_pass() {
164    println!("\n--- Forward Pass (with gradients) ---");
165
166    let layer = LinearLayer::new(3, 2, Some(43));
167
168    // Single input
169    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
170    let output = layer.forward(&input);
171
172    println!("Single input:");
173    println!("  Input: {:?}", input.data());
174    println!("  Output: {:?}", output.data());
175    println!("  Output requires grad: {}", output.requires_grad());
176
177    // Batch input
178    let batch_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
179    let batch_output = layer.forward(&batch_input);
180
181    println!("Batch input:");
182    println!("  Input shape: {:?}", batch_input.shape().dims);
183    println!("  Output shape: {:?}", batch_output.shape().dims);
184    println!("  Output requires grad: {}", batch_output.requires_grad());
185}
186
187/// Demonstrate forward pass without gradient tracking
188fn demonstrate_forward_pass_no_grad() {
189    println!("\n--- Forward Pass (no gradients) ---");
190
191    let layer = LinearLayer::new(3, 2, Some(44));
192
193    // Single input
194    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
195    let output = layer.forward_no_grad(&input);
196
197    println!("Single input (no grad):");
198    println!("  Input: {:?}", input.data());
199    println!("  Output: {:?}", output.data());
200    println!("  Output requires grad: {}", output.requires_grad());
201
202    // Compare with grad version
203    let output_with_grad = layer.forward(&input);
204    println!("Comparison:");
205    println!(
206        "  Same values: {}",
207        output.data() == output_with_grad.data()
208    );
209    println!("  No grad requires grad: {}", output.requires_grad());
210    println!(
211        "  With grad requires grad: {}",
212        output_with_grad.requires_grad()
213    );
214}
215
216/// Demonstrate complete training loop
217fn demonstrate_training_loop() -> Result<(), Box<dyn std::error::Error>> {
218    println!("\n--- Training Loop ---");
219
220    // Create layer and training data
221    let mut layer = LinearLayer::new(2, 1, Some(45));
222
223    // Simple regression task: y = 2*x1 + 3*x2 + 1
224    let x_data = Tensor::from_slice(
225        &[
226            1.0, 1.0, // x1=1, x2=1 -> y=6
227            2.0, 1.0, // x1=2, x2=1 -> y=8
228            1.0, 2.0, // x1=1, x2=2 -> y=9
229            2.0, 2.0, // x1=2, x2=2 -> y=11
230        ],
231        vec![4, 2],
232    )
233    .unwrap();
234
235    let y_true = Tensor::from_slice(&[6.0, 8.0, 9.0, 11.0], vec![4, 1]).unwrap();
236
237    println!("Training data:");
238    println!("  X shape: {:?}", x_data.shape().dims);
239    println!("  Y shape: {:?}", y_true.shape().dims);
240    println!("  Target function: y = 2*x1 + 3*x2 + 1");
241
242    // Create optimizer
243    let config = AdamConfig {
244        learning_rate: 0.01,
245        beta1: 0.9,
246        beta2: 0.999,
247        eps: 1e-8,
248        weight_decay: 0.0,
249        amsgrad: false,
250    };
251
252    let mut optimizer = Adam::with_config(config);
253    let params = layer.parameters();
254    for param in &params {
255        optimizer.add_parameter(param);
256    }
257
258    println!("Optimizer setup complete. Starting training...");
259
260    // Training loop
261    let num_epochs = 100;
262    let mut losses = Vec::new();
263
264    for epoch in 0..num_epochs {
265        // Forward pass
266        let y_pred = layer.forward(&x_data);
267
268        // Compute loss: MSE
269        let diff = y_pred.sub_tensor(&y_true);
270        let mut loss = diff.pow_scalar(2.0).mean();
271
272        // Backward pass
273        loss.backward(None);
274
275        // Optimizer step
276        let mut params = layer.parameters();
277        optimizer.step(&mut params);
278        optimizer.zero_grad(&mut params);
279
280        losses.push(loss.value());
281
282        // Print progress
283        if epoch % 20 == 0 || epoch == num_epochs - 1 {
284            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
285        }
286    }
287
288    // Evaluate final model
289    let final_predictions = layer.forward_no_grad(&x_data);
290
291    println!("\nFinal model evaluation:");
292    println!("  Learned weights: {:?}", layer.weight.data());
293    println!("  Learned bias: {:?}", layer.bias.data());
294    println!("  Target weights: [2.0, 3.0]");
295    println!("  Target bias: [1.0]");
296
297    println!("  Predictions vs True:");
298    for i in 0..4 {
299        let pred = final_predictions.data()[i];
300        let true_val = y_true.data()[i];
301        println!(
302            "    Sample {}: pred={:.3}, true={:.1}, error={:.3}",
303            i + 1,
304            pred,
305            true_val,
306            (pred - true_val).abs()
307        );
308    }
309
310    // Training analysis
311    let initial_loss = losses[0];
312    let final_loss = losses[losses.len() - 1];
313    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
314
315    println!("\nTraining Analysis:");
316    println!("  Initial loss: {:.6}", initial_loss);
317    println!("  Final loss: {:.6}", final_loss);
318    println!("  Loss reduction: {:.1}%", loss_reduction);
319
320    Ok(())
321}
322
323/// Demonstrate single vs batch inference
324fn demonstrate_single_vs_batch_inference() {
325    println!("\n--- Single vs Batch Inference ---");
326
327    let layer = LinearLayer::new(4, 3, Some(46));
328
329    // Single inference
330    println!("Single inference:");
331    let single_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![1, 4]).unwrap();
332    let single_output = layer.forward_no_grad(&single_input);
333    println!("  Input shape: {:?}", single_input.shape().dims);
334    println!("  Output shape: {:?}", single_output.shape().dims);
335    println!("  Output: {:?}", single_output.data());
336
337    // Batch inference
338    println!("Batch inference:");
339    let batch_input = Tensor::from_slice(
340        &[
341            1.0, 2.0, 3.0, 4.0, // Sample 1
342            5.0, 6.0, 7.0, 8.0, // Sample 2
343            9.0, 10.0, 11.0, 12.0, // Sample 3
344        ],
345        vec![3, 4],
346    )
347    .unwrap();
348    let batch_output = layer.forward_no_grad(&batch_input);
349    println!("  Input shape: {:?}", batch_input.shape().dims);
350    println!("  Output shape: {:?}", batch_output.shape().dims);
351
352    // Verify batch consistency - first sample should match single inference
353    let _first_batch_sample = batch_output.view(vec![3, 3]); // Reshape to access first sample
354    let first_sample_data = &batch_output.data()[0..3]; // First 3 elements
355    let single_sample_data = single_output.data();
356
357    println!("Consistency check:");
358    println!("  Single output: {:?}", single_sample_data);
359    println!("  First batch sample: {:?}", first_sample_data);
360    println!(
361        "  Match: {}",
362        single_sample_data
363            .iter()
364            .zip(first_sample_data.iter())
365            .all(|(a, b)| (a - b).abs() < 1e-6)
366    );
367}
368
369/// Demonstrate serialization and loading
370fn demonstrate_serialization() -> Result<(), Box<dyn std::error::Error>> {
371    println!("\n--- Serialization ---");
372
373    // Create and train a simple layer
374    let mut original_layer = LinearLayer::new(2, 1, Some(47));
375
376    // Simple training data
377    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
378    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
379
380    let mut optimizer = Adam::with_learning_rate(0.01);
381    let params = original_layer.parameters();
382    for param in &params {
383        optimizer.add_parameter(param);
384    }
385
386    // Train for a few epochs
387    for _ in 0..10 {
388        let y_pred = original_layer.forward(&x_data);
389        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
390        loss.backward(None);
391
392        let mut params = original_layer.parameters();
393        optimizer.step(&mut params);
394        optimizer.zero_grad(&mut params);
395    }
396
397    println!("Original layer trained");
398    println!("  Weight: {:?}", original_layer.weight.data());
399    println!("  Bias: {:?}", original_layer.bias.data());
400
401    // Save layer
402    original_layer.save_json("temp_linear_layer")?;
403
404    // Load layer
405    let loaded_layer = LinearLayer::load_json("temp_linear_layer", 2, 1)?;
406
407    println!("Loaded layer");
408    println!("  Weight: {:?}", loaded_layer.weight.data());
409    println!("  Bias: {:?}", loaded_layer.bias.data());
410
411    // Verify consistency
412    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
413    let original_output = original_layer.forward_no_grad(&test_input);
414    let loaded_output = loaded_layer.forward_no_grad(&test_input);
415
416    println!("Consistency check:");
417    println!("  Original output: {:?}", original_output.data());
418    println!("  Loaded output: {:?}", loaded_output.data());
419    println!(
420        "  Match: {}",
421        original_output
422            .data()
423            .iter()
424            .zip(loaded_output.data().iter())
425            .all(|(a, b)| (a - b).abs() < 1e-6)
426    );
427
428    println!("Serialization verification: PASSED");
429
430    Ok(())
431}
examples/iterators/element_iteration.rs (line 82)
77fn demonstrate_basic_iteration() -> Result<(), Box<dyn std::error::Error>> {
78    println!("\n--- Basic Element Iteration ---");
79
80    // Create a simple tensor for demonstration
81    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
82    println!("Original tensor: {:?}", tensor.data());
83
84    // Basic iteration with for loop
85    println!("\nBasic iteration with for loop:");
86    for (i, element) in tensor.iter().enumerate() {
87        println!(
88            "  Element {}: value = {:.1}, shape = {:?}",
89            i,
90            element.value(),
91            element.shape().dims
92        );
93    }
94
95    // Element-wise transformation
96    println!("\nElement-wise transformation (2x + 1):");
97    let transformed: Tensor = tensor
98        .iter()
99        .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
100        .collect();
101    println!("  Result: {:?}", transformed.data());
102
103    // Filtering elements
104    println!("\nFiltering elements (values > 3.0):");
105    let filtered: Tensor = tensor.iter().filter(|elem| elem.value() > 3.0).collect();
106    println!("  Filtered: {:?}", filtered.data());
107
108    Ok(())
109}
110
111/// Demonstrate standard iterator trait methods
112///
113/// Shows compatibility with Rust's standard library iterator methods
114/// and demonstrates various functional programming patterns.
115fn demonstrate_standard_methods() -> Result<(), Box<dyn std::error::Error>> {
116    println!("\n--- Standard Iterator Methods ---");
117
118    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
119
120    // Using map for transformations
121    println!("\nMap transformation (square each element):");
122    let squared: Tensor = tensor.iter().map(|elem| elem.pow_scalar(2.0)).collect();
123    println!("  Squared: {:?}", squared.data());
124
125    // Using enumerate for indexed operations
126    println!("\nEnumerate with indexed operations:");
127    let indexed: Tensor = tensor
128        .iter()
129        .enumerate()
130        .map(|(i, elem)| elem.add_scalar(i as f32))
131        .collect();
132    println!("  Indexed: {:?}", indexed.data());
133
134    // Using fold for reduction
135    println!("\nFold for sum calculation:");
136    let sum: f32 = tensor.iter().fold(0.0, |acc, elem| acc + elem.value());
137    println!("  Sum: {:.1}", sum);
138
139    // Using find for element search
140    println!("\nFind specific element:");
141    if let Some(found) = tensor.iter().find(|elem| elem.value() == 3.0) {
142        println!("  Found element with value 3.0: {:.1}", found.value());
143    }
144
145    // Using any/all for condition checking
146    println!("\nCondition checking:");
147    let all_positive = tensor.iter().all(|elem| elem.value() > 0.0);
148    let any_large = tensor.iter().any(|elem| elem.value() > 4.0);
149    println!("  All positive: {}", all_positive);
150    println!("  Any > 4.0: {}", any_large);
151
152    Ok(())
153}
154
155/// Demonstrate gradient tracking through element operations
156///
157/// Shows how gradient tracking works seamlessly through iterator
158/// operations, maintaining the computational graph for backpropagation.
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
187
188/// Demonstrate advanced iterator patterns
189///
190/// Shows complex iterator chains and advanced functional programming
191/// patterns for sophisticated data processing workflows.
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
examples/getting_started/tensor_basics.rs (line 50)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
87
88/// Demonstrate basic arithmetic operations
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
118
119/// Demonstrate shape manipulation operations
120fn demonstrate_shape_operations() {
121    println!("\n--- Shape Operations ---");
122
123    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
124    println!(
125        "Original: shape {:?}, data: {:?}",
126        tensor.shape().dims,
127        tensor.data()
128    );
129
130    // Reshape (view)
131    let reshaped = tensor.view(vec![3, 2]);
132    println!(
133        "Reshaped to [3, 2]: shape {:?}, data: {:?}",
134        reshaped.shape().dims,
135        reshaped.data()
136    );
137
138    // Create a different shaped tensor for demonstration
139    let tensor_2d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
140    println!(
141        "2D tensor: shape {:?}, data: {:?}",
142        tensor_2d.shape().dims,
143        tensor_2d.data()
144    );
145
146    // Create a 1D tensor
147    let tensor_1d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
148    println!(
149        "1D tensor: shape {:?}, data: {:?}",
150        tensor_1d.shape().dims,
151        tensor_1d.data()
152    );
153}
154
155/// Demonstrate data access patterns
156fn demonstrate_data_access() {
157    println!("\n--- Data Access ---");
158
159    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
160
161    // Access individual elements
162    println!("Element [0, 0]: {}", tensor.get(&[0, 0]));
163    println!("Element [0, 1]: {}", tensor.get(&[0, 1]));
164    println!("Element [1, 0]: {}", tensor.get(&[1, 0]));
165    println!("Element [1, 1]: {}", tensor.get(&[1, 1]));
166
167    // Access data as slice
168    let data = tensor.data();
169    println!("Data as slice: {:?}", data);
170
171    // Iterate over elements
172    println!("Elements:");
173    for (i, &value) in data.iter().enumerate() {
174        println!("  [{}]: {}", i, value);
175    }
176}
examples/neural_networks/feedforward_network.rs (line 357)
339fn demonstrate_forward_pass() {
340    println!("\n--- Forward Pass ---");
341
342    let config = FeedForwardConfig {
343        input_size: 3,
344        hidden_sizes: vec![5, 3],
345        output_size: 2,
346        use_bias: true,
347    };
348    let network = FeedForwardNetwork::new(config, Some(43));
349
350    // Single input
351    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
352    let output = network.forward(&input);
353
354    println!("Single input forward pass:");
355    println!("  Input shape: {:?}", input.shape().dims);
356    println!("  Output shape: {:?}", output.shape().dims);
357    println!("  Output: {:?}", output.data());
358    println!("  Output requires grad: {}", output.requires_grad());
359
360    // Batch input
361    let batch_input = Tensor::from_slice(
362        &[
363            1.0, 2.0, 3.0, // Sample 1
364            4.0, 5.0, 6.0, // Sample 2
365            7.0, 8.0, 9.0, // Sample 3
366        ],
367        vec![3, 3],
368    )
369    .unwrap();
370    let batch_output = network.forward(&batch_input);
371
372    println!("Batch input forward pass:");
373    println!("  Input shape: {:?}", batch_input.shape().dims);
374    println!("  Output shape: {:?}", batch_output.shape().dims);
375    println!("  Output requires grad: {}", batch_output.requires_grad());
376
377    // Compare with no-grad version
378    let output_no_grad = network.forward_no_grad(&input);
379    println!("No-grad comparison:");
380    println!("  Same values: {}", output.data() == output_no_grad.data());
381    println!("  With grad requires grad: {}", output.requires_grad());
382    println!(
383        "  No grad requires grad: {}",
384        output_no_grad.requires_grad()
385    );
386}
387
388/// Demonstrate different configurable architectures
389fn demonstrate_configurable_architectures() {
390    println!("\n--- Configurable Architectures ---");
391
392    let architectures = vec![
393        ("Shallow", vec![8]),
394        ("Medium", vec![16, 8]),
395        ("Deep", vec![32, 16, 8, 4]),
396        ("Wide", vec![64, 32]),
397        ("Bottleneck", vec![16, 4, 16]),
398    ];
399
400    for (name, hidden_sizes) in architectures {
401        let config = FeedForwardConfig {
402            input_size: 10,
403            hidden_sizes,
404            output_size: 3,
405            use_bias: true,
406        };
407
408        let network = FeedForwardNetwork::new(config.clone(), Some(44));
409
410        // Test forward pass
411        let test_input = Tensor::randn(vec![5, 10], Some(45)); // Batch of 5
412        let output = network.forward_no_grad(&test_input);
413
414        println!("{} network:", name);
415        println!("  Architecture: 10 -> {:?} -> 3", config.hidden_sizes);
416        println!("  Parameters: {}", network.parameter_count());
417        println!("  Test output shape: {:?}", output.shape().dims);
418        println!(
419            "  Output range: [{:.3}, {:.3}]",
420            output.data().iter().fold(f32::INFINITY, |a, &b| a.min(b)),
421            output
422                .data()
423                .iter()
424                .fold(f32::NEG_INFINITY, |a, &b| a.max(b))
425        );
426    }
427}
428
429/// Demonstrate basic training workflow
430fn demonstrate_training_workflow() -> Result<(), Box<dyn std::error::Error>> {
431    println!("\n--- Training Workflow ---");
432
433    // Create a simple classification network
434    let config = FeedForwardConfig {
435        input_size: 2,
436        hidden_sizes: vec![4, 3],
437        output_size: 1,
438        use_bias: true,
439    };
440    let mut network = FeedForwardNetwork::new(config, Some(46));
441
442    println!("Training network: 2 -> [4, 3] -> 1");
443
444    // Create simple binary classification data: XOR problem
445    let x_data = Tensor::from_slice(
446        &[
447            0.0, 0.0, // -> 0
448            0.0, 1.0, // -> 1
449            1.0, 0.0, // -> 1
450            1.0, 1.0, // -> 0
451        ],
452        vec![4, 2],
453    )
454    .unwrap();
455
456    let y_true = Tensor::from_slice(&[0.0, 1.0, 1.0, 0.0], vec![4, 1]).unwrap();
457
458    println!("Training on XOR problem:");
459    println!("  Input shape: {:?}", x_data.shape().dims);
460    println!("  Target shape: {:?}", y_true.shape().dims);
461
462    // Create optimizer
463    let mut optimizer = Adam::with_learning_rate(0.1);
464    let params = network.parameters();
465    for param in &params {
466        optimizer.add_parameter(param);
467    }
468
469    // Training loop
470    let num_epochs = 50;
471    let mut losses = Vec::new();
472
473    for epoch in 0..num_epochs {
474        // Forward pass
475        let y_pred = network.forward(&x_data);
476
477        // Compute loss: MSE
478        let diff = y_pred.sub_tensor(&y_true);
479        let mut loss = diff.pow_scalar(2.0).mean();
480
481        // Backward pass
482        loss.backward(None);
483
484        // Optimizer step and zero grad
485        let mut params = network.parameters();
486        optimizer.step(&mut params);
487        optimizer.zero_grad(&mut params);
488
489        losses.push(loss.value());
490
491        // Print progress
492        if epoch % 10 == 0 || epoch == num_epochs - 1 {
493            println!("Epoch {:2}: Loss = {:.6}", epoch, loss.value());
494        }
495    }
496
497    // Test final model
498    let final_predictions = network.forward_no_grad(&x_data);
499    println!("\nFinal predictions vs targets:");
500    for i in 0..4 {
501        let pred = final_predictions.data()[i];
502        let target = y_true.data()[i];
503        let input_x = x_data.data()[i * 2];
504        let input_y = x_data.data()[i * 2 + 1];
505        println!(
506            "  [{:.0}, {:.0}] -> pred: {:.3}, target: {:.0}, error: {:.3}",
507            input_x,
508            input_y,
509            pred,
510            target,
511            (pred - target).abs()
512        );
513    }
514
515    Ok(())
516}
517
518/// Demonstrate comprehensive training with 100+ steps
519fn demonstrate_comprehensive_training() -> Result<(), Box<dyn std::error::Error>> {
520    println!("\n--- Comprehensive Training (100+ Steps) ---");
521
522    // Create a regression network
523    let config = FeedForwardConfig {
524        input_size: 3,
525        hidden_sizes: vec![8, 6, 4],
526        output_size: 2,
527        use_bias: true,
528    };
529    let mut network = FeedForwardNetwork::new(config, Some(47));
530
531    println!("Network architecture: 3 -> [8, 6, 4] -> 2");
532    println!("Total parameters: {}", network.parameter_count());
533
534    // Create synthetic regression data
535    // Target function: [y1, y2] = [x1 + 2*x2 - x3, x1*x2 + x3]
536    let num_samples = 32;
537    let mut x_vec = Vec::new();
538    let mut y_vec = Vec::new();
539
540    for i in 0..num_samples {
541        let x1 = (i as f32 / num_samples as f32) * 2.0 - 1.0; // [-1, 1]
542        let x2 = ((i * 2) as f32 / num_samples as f32) * 2.0 - 1.0;
543        let x3 = ((i * 3) as f32 / num_samples as f32) * 2.0 - 1.0;
544
545        let y1 = x1 + 2.0 * x2 - x3;
546        let y2 = x1 * x2 + x3;
547
548        x_vec.extend_from_slice(&[x1, x2, x3]);
549        y_vec.extend_from_slice(&[y1, y2]);
550    }
551
552    let x_data = Tensor::from_slice(&x_vec, vec![num_samples, 3]).unwrap();
553    let y_true = Tensor::from_slice(&y_vec, vec![num_samples, 2]).unwrap();
554
555    println!("Training data:");
556    println!("  {} samples", num_samples);
557    println!("  Input shape: {:?}", x_data.shape().dims);
558    println!("  Target shape: {:?}", y_true.shape().dims);
559
560    // Create optimizer with learning rate scheduling
561    let mut optimizer = Adam::with_learning_rate(0.01);
562    let params = network.parameters();
563    for param in &params {
564        optimizer.add_parameter(param);
565    }
566
567    // Comprehensive training loop (150 epochs)
568    let num_epochs = 150;
569    let mut losses = Vec::new();
570    let mut best_loss = f32::INFINITY;
571    let mut patience_counter = 0;
572    let patience = 20;
573
574    println!("Starting comprehensive training...");
575
576    for epoch in 0..num_epochs {
577        // Forward pass
578        let y_pred = network.forward(&x_data);
579
580        // Compute loss: MSE
581        let diff = y_pred.sub_tensor(&y_true);
582        let mut loss = diff.pow_scalar(2.0).mean();
583
584        // Backward pass
585        loss.backward(None);
586
587        // Optimizer step and zero grad
588        let mut params = network.parameters();
589        optimizer.step(&mut params);
590        optimizer.zero_grad(&mut params);
591
592        let current_loss = loss.value();
593        losses.push(current_loss);
594
595        // Learning rate scheduling
596        if epoch > 0 && epoch % 30 == 0 {
597            let new_lr = optimizer.learning_rate() * 0.8;
598            optimizer.set_learning_rate(new_lr);
599            println!("  Reduced learning rate to {:.4}", new_lr);
600        }
601
602        // Early stopping logic
603        if current_loss < best_loss {
604            best_loss = current_loss;
605            patience_counter = 0;
606        } else {
607            patience_counter += 1;
608        }
609
610        // Print progress
611        if epoch % 25 == 0 || epoch == num_epochs - 1 {
612            println!(
613                "Epoch {:3}: Loss = {:.6}, LR = {:.4}, Best = {:.6}",
614                epoch,
615                current_loss,
616                optimizer.learning_rate(),
617                best_loss
618            );
619        }
620
621        // Early stopping
622        if patience_counter >= patience && epoch > 50 {
623            println!("Early stopping at epoch {} (patience exceeded)", epoch);
624            break;
625        }
626    }
627
628    // Final evaluation
629    let final_predictions = network.forward_no_grad(&x_data);
630
631    // Compute final metrics
632    let final_loss = losses[losses.len() - 1];
633    let initial_loss = losses[0];
634    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
635
636    println!("\nTraining completed!");
637    println!("  Initial loss: {:.6}", initial_loss);
638    println!("  Final loss: {:.6}", final_loss);
639    println!("  Best loss: {:.6}", best_loss);
640    println!("  Loss reduction: {:.1}%", loss_reduction);
641    println!("  Final learning rate: {:.4}", optimizer.learning_rate());
642
643    // Sample predictions analysis
644    println!("\nSample predictions (first 5):");
645    for i in 0..5.min(num_samples) {
646        let pred1 = final_predictions.data()[i * 2];
647        let pred2 = final_predictions.data()[i * 2 + 1];
648        let true1 = y_true.data()[i * 2];
649        let true2 = y_true.data()[i * 2 + 1];
650
651        println!(
652            "  Sample {}: pred=[{:.3}, {:.3}], true=[{:.3}, {:.3}], error=[{:.3}, {:.3}]",
653            i + 1,
654            pred1,
655            pred2,
656            true1,
657            true2,
658            (pred1 - true1).abs(),
659            (pred2 - true2).abs()
660        );
661    }
662
663    Ok(())
664}
665
666/// Demonstrate network serialization
667fn demonstrate_network_serialization() -> Result<(), Box<dyn std::error::Error>> {
668    println!("\n--- Network Serialization ---");
669
670    // Create and train a network
671    let config = FeedForwardConfig {
672        input_size: 2,
673        hidden_sizes: vec![4, 2],
674        output_size: 1,
675        use_bias: true,
676    };
677    let mut original_network = FeedForwardNetwork::new(config.clone(), Some(48));
678
679    // Quick training
680    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
681    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
682
683    let mut optimizer = Adam::with_learning_rate(0.01);
684    let params = original_network.parameters();
685    for param in &params {
686        optimizer.add_parameter(param);
687    }
688
689    for _ in 0..20 {
690        let y_pred = original_network.forward(&x_data);
691        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
692        loss.backward(None);
693
694        let mut params = original_network.parameters();
695        optimizer.step(&mut params);
696        optimizer.zero_grad(&mut params);
697    }
698
699    // Test original network
700    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
701    let original_output = original_network.forward_no_grad(&test_input);
702
703    println!("Original network output: {:?}", original_output.data());
704
705    // Save network
706    original_network.save_json("temp_feedforward_network")?;
707
708    // Load network
709    let loaded_network = FeedForwardNetwork::load_json("temp_feedforward_network", config)?;
710    let loaded_output = loaded_network.forward_no_grad(&test_input);
711
712    println!("Loaded network output: {:?}", loaded_output.data());
713
714    // Verify consistency
715    let match_check = original_output
716        .data()
717        .iter()
718        .zip(loaded_output.data().iter())
719        .all(|(a, b)| (a - b).abs() < 1e-6);
720
721    println!(
722        "Serialization verification: {}",
723        if match_check { "PASSED" } else { "FAILED" }
724    );
725
726    Ok(())
727}
examples/getting_started/serialization_basics.rs (line 60)
52fn demonstrate_tensor_serialization() -> Result<(), Box<dyn std::error::Error>> {
53    println!("--- Tensor Serialization ---");
54
55    // Create a tensor with some data
56    let original_tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
57    println!(
58        "Original tensor: shape {:?}, data: {:?}",
59        original_tensor.shape().dims,
60        original_tensor.data()
61    );
62
63    // Save tensor in JSON format
64    let json_path = "temp_tensor.json";
65    original_tensor.save_json(json_path)?;
66    println!("Saved tensor to JSON: {}", json_path);
67
68    // Load tensor from JSON
69    let loaded_tensor_json = Tensor::load_json(json_path)?;
70    println!(
71        "Loaded from JSON: shape {:?}, data: {:?}",
72        loaded_tensor_json.shape().dims,
73        loaded_tensor_json.data()
74    );
75
76    // Verify data integrity
77    assert_eq!(
78        original_tensor.shape().dims,
79        loaded_tensor_json.shape().dims
80    );
81    assert_eq!(original_tensor.data(), loaded_tensor_json.data());
82    println!("JSON serialization verification: PASSED");
83
84    // Save tensor in binary format
85    let binary_path = "temp_tensor.bin";
86    original_tensor.save_binary(binary_path)?;
87    println!("Saved tensor to binary: {}", binary_path);
88
89    // Load tensor from binary
90    let loaded_tensor_binary = Tensor::load_binary(binary_path)?;
91    println!(
92        "Loaded from binary: shape {:?}, data: {:?}",
93        loaded_tensor_binary.shape().dims,
94        loaded_tensor_binary.data()
95    );
96
97    // Verify data integrity
98    assert_eq!(
99        original_tensor.shape().dims,
100        loaded_tensor_binary.shape().dims
101    );
102    assert_eq!(original_tensor.data(), loaded_tensor_binary.data());
103    println!("Binary serialization verification: PASSED");
104
105    Ok(())
106}
107
108/// Demonstrate optimizer serialization and deserialization
109fn demonstrate_optimizer_serialization() -> Result<(), Box<dyn std::error::Error>> {
110    println!("\n--- Optimizer Serialization ---");
111
112    // Create an optimizer with some parameters
113    let mut weight = Tensor::randn(vec![2, 2], Some(42)).with_requires_grad();
114    let mut bias = Tensor::randn(vec![2], Some(43)).with_requires_grad();
115
116    let config = AdamConfig {
117        learning_rate: 0.001,
118        beta1: 0.9,
119        beta2: 0.999,
120        eps: 1e-8,
121        weight_decay: 0.0,
122        amsgrad: false,
123    };
124
125    let mut optimizer = Adam::with_config(config);
126    optimizer.add_parameter(&weight);
127    optimizer.add_parameter(&bias);
128
129    println!(
130        "Created optimizer with {} parameters",
131        optimizer.parameter_count()
132    );
133    println!("Learning rate: {}", optimizer.learning_rate());
134
135    // Simulate some training steps
136    for _ in 0..3 {
137        let mut loss = weight.sum() + bias.sum();
138        loss.backward(None);
139        optimizer.step(&mut [&mut weight, &mut bias]);
140        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
141    }
142
143    // Save optimizer state
144    let optimizer_path = "temp_optimizer.json";
145    optimizer.save_json(optimizer_path)?;
146    println!("Saved optimizer to: {}", optimizer_path);
147
148    // Load optimizer state
149    let loaded_optimizer = Adam::load_json(optimizer_path)?;
150    println!(
151        "Loaded optimizer with {} parameters",
152        loaded_optimizer.parameter_count()
153    );
154    println!("Learning rate: {}", loaded_optimizer.learning_rate());
155
156    // Verify optimizer state
157    assert_eq!(
158        optimizer.parameter_count(),
159        loaded_optimizer.parameter_count()
160    );
161    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
162    println!("Optimizer serialization verification: PASSED");
163
164    Ok(())
165}
166
167/// Demonstrate format comparison and performance characteristics
168fn demonstrate_format_comparison() -> Result<(), Box<dyn std::error::Error>> {
169    println!("\n--- Format Comparison ---");
170
171    // Create a larger tensor for comparison
172    let tensor = Tensor::randn(vec![10, 10], Some(44));
173
174    // Save in both formats
175    tensor.save_json("temp_comparison.json")?;
176    tensor.save_binary("temp_comparison.bin")?;
177
178    // Compare file sizes
179    let json_size = fs::metadata("temp_comparison.json")?.len();
180    let binary_size = fs::metadata("temp_comparison.bin")?.len();
181
182    println!("JSON file size: {} bytes", json_size);
183    println!("Binary file size: {} bytes", binary_size);
184    println!(
185        "Compression ratio: {:.2}x",
186        json_size as f64 / binary_size as f64
187    );
188
189    // Load and verify both formats
190    let json_tensor = Tensor::load_json("temp_comparison.json")?;
191    let binary_tensor = Tensor::load_binary("temp_comparison.bin")?;
192
193    assert_eq!(tensor.shape().dims, json_tensor.shape().dims);
194    assert_eq!(tensor.shape().dims, binary_tensor.shape().dims);
195    assert_eq!(tensor.data(), json_tensor.data());
196    assert_eq!(tensor.data(), binary_tensor.data());
197
198    println!("Format comparison verification: PASSED");
199
200    Ok(())
201}
202
203/// Demonstrate a basic model checkpointing workflow
204fn demonstrate_model_checkpointing() -> Result<(), Box<dyn std::error::Error>> {
205    println!("\n--- Model Checkpointing ---");
206
207    // Create a simple model (weights and bias)
208    let mut weights = Tensor::randn(vec![2, 1], Some(45)).with_requires_grad();
209    let mut bias = Tensor::randn(vec![1], Some(46)).with_requires_grad();
210
211    // Create optimizer
212    let mut optimizer = Adam::with_learning_rate(0.01);
213    optimizer.add_parameter(&weights);
214    optimizer.add_parameter(&bias);
215
216    println!("Initial weights: {:?}", weights.data());
217    println!("Initial bias: {:?}", bias.data());
218
219    // Simulate training
220    for epoch in 0..5 {
221        let mut loss = weights.sum() + bias.sum();
222        loss.backward(None);
223        optimizer.step(&mut [&mut weights, &mut bias]);
224        optimizer.zero_grad(&mut [&mut weights, &mut bias]);
225
226        if epoch % 2 == 0 {
227            // Save checkpoint
228            let checkpoint_dir = format!("checkpoint_epoch_{}", epoch);
229            fs::create_dir_all(&checkpoint_dir)?;
230
231            weights.save_json(format!("{}/weights.json", checkpoint_dir))?;
232            bias.save_json(format!("{}/bias.json", checkpoint_dir))?;
233            optimizer.save_json(format!("{}/optimizer.json", checkpoint_dir))?;
234
235            println!("Saved checkpoint for epoch {}", epoch);
236        }
237    }
238
239    // Load from checkpoint
240    let loaded_weights = Tensor::load_json("checkpoint_epoch_4/weights.json")?;
241    let loaded_bias = Tensor::load_json("checkpoint_epoch_4/bias.json")?;
242    let loaded_optimizer = Adam::load_json("checkpoint_epoch_4/optimizer.json")?;
243
244    println!("Loaded weights: {:?}", loaded_weights.data());
245    println!("Loaded bias: {:?}", loaded_bias.data());
246    println!(
247        "Loaded optimizer learning rate: {}",
248        loaded_optimizer.learning_rate()
249    );
250
251    // Verify checkpoint integrity
252    assert_eq!(weights.shape().dims, loaded_weights.shape().dims);
253    assert_eq!(bias.shape().dims, loaded_bias.shape().dims);
254    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
255
256    println!("Checkpointing verification: PASSED");
257
258    Ok(())
259}
Source

pub fn data_mut(&mut self) -> &mut [f32]

Returns a mutable slice of the tensor’s underlying data

Provides safe mutable access to the tensor’s data without requiring unsafe pointer operations. Use this for in-place modifications of tensor values.

§Returns

A mutable slice containing all tensor elements in row-major order

§Performance
  • Zero-Cost: Direct slice creation with no copying
  • Cache-Friendly: Sequential memory access pattern
  • Safe: No unsafe code required for basic data modification
§Examples
use train_station::Tensor;

let mut tensor = Tensor::new(vec![2, 2]);
let data = tensor.data_mut();

// Safe indexing for modification
data[0] = 1.0;
data[1] = 2.0;

assert_eq!(tensor.get(&[0, 0]), 1.0);
assert_eq!(tensor.get(&[0, 1]), 2.0);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 72)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
Source

pub fn value(&self) -> f32

Extract scalar value from single-element tensor

This method provides a convenient way to extract the scalar value from tensors that contain exactly one element. This is commonly used with element iterator results and scalar tensor operations.

§Returns

The scalar value contained in this tensor

§Panics

Panics if the tensor does not contain exactly one element

§Examples
use train_station::Tensor;

// Single-element tensor
let scalar = Tensor::from_slice(&[42.0], vec![1]).unwrap();
assert_eq!(scalar.value(), 42.0);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 192)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
More examples
Hide additional examples
examples/iterators/element_iteration.rs (line 90)
77fn demonstrate_basic_iteration() -> Result<(), Box<dyn std::error::Error>> {
78    println!("\n--- Basic Element Iteration ---");
79
80    // Create a simple tensor for demonstration
81    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
82    println!("Original tensor: {:?}", tensor.data());
83
84    // Basic iteration with for loop
85    println!("\nBasic iteration with for loop:");
86    for (i, element) in tensor.iter().enumerate() {
87        println!(
88            "  Element {}: value = {:.1}, shape = {:?}",
89            i,
90            element.value(),
91            element.shape().dims
92        );
93    }
94
95    // Element-wise transformation
96    println!("\nElement-wise transformation (2x + 1):");
97    let transformed: Tensor = tensor
98        .iter()
99        .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
100        .collect();
101    println!("  Result: {:?}", transformed.data());
102
103    // Filtering elements
104    println!("\nFiltering elements (values > 3.0):");
105    let filtered: Tensor = tensor.iter().filter(|elem| elem.value() > 3.0).collect();
106    println!("  Filtered: {:?}", filtered.data());
107
108    Ok(())
109}
110
111/// Demonstrate standard iterator trait methods
112///
113/// Shows compatibility with Rust's standard library iterator methods
114/// and demonstrates various functional programming patterns.
115fn demonstrate_standard_methods() -> Result<(), Box<dyn std::error::Error>> {
116    println!("\n--- Standard Iterator Methods ---");
117
118    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
119
120    // Using map for transformations
121    println!("\nMap transformation (square each element):");
122    let squared: Tensor = tensor.iter().map(|elem| elem.pow_scalar(2.0)).collect();
123    println!("  Squared: {:?}", squared.data());
124
125    // Using enumerate for indexed operations
126    println!("\nEnumerate with indexed operations:");
127    let indexed: Tensor = tensor
128        .iter()
129        .enumerate()
130        .map(|(i, elem)| elem.add_scalar(i as f32))
131        .collect();
132    println!("  Indexed: {:?}", indexed.data());
133
134    // Using fold for reduction
135    println!("\nFold for sum calculation:");
136    let sum: f32 = tensor.iter().fold(0.0, |acc, elem| acc + elem.value());
137    println!("  Sum: {:.1}", sum);
138
139    // Using find for element search
140    println!("\nFind specific element:");
141    if let Some(found) = tensor.iter().find(|elem| elem.value() == 3.0) {
142        println!("  Found element with value 3.0: {:.1}", found.value());
143    }
144
145    // Using any/all for condition checking
146    println!("\nCondition checking:");
147    let all_positive = tensor.iter().all(|elem| elem.value() > 0.0);
148    let any_large = tensor.iter().any(|elem| elem.value() > 4.0);
149    println!("  All positive: {}", all_positive);
150    println!("  Any > 4.0: {}", any_large);
151
152    Ok(())
153}
154
155/// Demonstrate gradient tracking through element operations
156///
157/// Shows how gradient tracking works seamlessly through iterator
158/// operations, maintaining the computational graph for backpropagation.
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
187
188/// Demonstrate advanced iterator patterns
189///
190/// Shows complex iterator chains and advanced functional programming
191/// patterns for sophisticated data processing workflows.
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
examples/optimizers/adam_configurations.rs (line 102)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
examples/iterators/performance_optimization.rs (line 106)
75fn demonstrate_performance_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
76    println!("\n--- Performance Benchmarking ---");
77
78    // Create test data of different sizes
79    let sizes = vec![100, 1000, 10000];
80
81    for size in sizes {
82        println!("\nBenchmarking with tensor size: {}", size);
83
84        // Generate test data
85        let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
86        let tensor = Tensor::from_slice(&data, vec![size])?;
87
88        // Benchmark 1: Direct tensor operations
89        let start = Instant::now();
90        let direct_result = tensor.mul_scalar(2.0).add_scalar(1.0);
91        let direct_time = start.elapsed();
92
93        // Benchmark 2: Iterator-based operations
94        let start = Instant::now();
95        let iterator_result: Tensor = tensor
96            .iter()
97            .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
98            .collect();
99        let iterator_time = start.elapsed();
100
101        // Benchmark 3: Chained iterator operations
102        let start = Instant::now();
103        let _chained_result: Tensor = tensor
104            .iter()
105            .map(|elem| elem.mul_scalar(2.0))
106            .filter(|elem| elem.value() > size as f32)
107            .map(|elem| elem.add_scalar(1.0))
108            .collect();
109        let chained_time = start.elapsed();
110
111        // Report results
112        println!("  Direct operations: {:?}", direct_time);
113        println!("  Iterator operations: {:?}", iterator_time);
114        println!("  Chained operations: {:?}", chained_time);
115
116        // Verify correctness
117        assert_eq!(direct_result.data(), iterator_result.data());
118        println!(
119            "  Results match: {}",
120            direct_result.data() == iterator_result.data()
121        );
122
123        // Performance ratio
124        let ratio = iterator_time.as_nanos() as f64 / direct_time.as_nanos() as f64;
125        println!("  Iterator/Direct ratio: {:.2}x", ratio);
126    }
127
128    Ok(())
129}
130
131/// Demonstrate memory optimization patterns
132///
133/// Shows memory-efficient processing patterns and techniques
134/// for minimizing memory usage while maintaining performance.
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
292
293/// Demonstrate advanced optimization techniques
294///
295/// Shows sophisticated optimization strategies and techniques
296/// for maximizing performance in tensor iterator operations.
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
examples/optimizers/learning_rate_scheduling.rs (line 351)
319fn train_with_scheduler(
320    scheduler: &mut dyn LearningRateScheduler,
321    num_epochs: usize,
322) -> Result<TrainingStats, Box<dyn std::error::Error>> {
323    // Create training data: y = 2*x + 1
324    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
325    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
326
327    // Create model parameters
328    let mut weight = Tensor::randn(vec![1, 1], Some(456)).with_requires_grad();
329    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
330
331    // Create optimizer with initial learning rate
332    let mut optimizer = Adam::with_learning_rate(0.05);
333    optimizer.add_parameter(&weight);
334    optimizer.add_parameter(&bias);
335
336    // Training loop
337    let mut losses = Vec::new();
338    let mut lr_history = Vec::new();
339    let mut convergence_epoch = num_epochs;
340
341    for epoch in 0..num_epochs {
342        // Forward pass
343        let y_pred = x_data.matmul(&weight) + &bias;
344        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
345
346        // Backward pass
347        loss.backward(None);
348
349        // Update learning rate using scheduler
350        let current_lr = optimizer.learning_rate();
351        let new_lr = scheduler.step(current_lr, epoch, loss.value());
352
353        if (new_lr - current_lr).abs() > 1e-8 {
354            optimizer.set_learning_rate(new_lr);
355        }
356
357        // Optimizer step
358        optimizer.step(&mut [&mut weight, &mut bias]);
359        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
360
361        let loss_value = loss.value();
362        losses.push(loss_value);
363        lr_history.push(new_lr);
364
365        // Check for convergence
366        if loss_value < 0.01 && convergence_epoch == num_epochs {
367            convergence_epoch = epoch;
368        }
369    }
370
371    Ok(TrainingStats {
372        scheduler_name: scheduler.name().to_string(),
373        final_loss: losses[losses.len() - 1],
374        lr_history,
375        loss_history: losses,
376        convergence_epoch,
377    })
378}
examples/getting_started/optimizer_basics.rs (line 144)
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
Source

pub fn view(&self, new_shape: Vec<i32>) -> Tensor

Create a view with a new shape (requires contiguous memory)

Behaves like PyTorch view: tensor must be contiguous and the total number of elements must remain the same. Supports -1 inference for one dimension.

§Arguments
  • new_shape - New shape for the tensor (can contain -1 for inference)
§Returns

A tensor viewing the same data with a new shape

§Examples
use train_station::Tensor;

let x = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
let y = x.view(vec![2, 2]);
assert_eq!(y.shape().dims, vec![2, 2]);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 131)
120fn demonstrate_shape_operations() {
121    println!("\n--- Shape Operations ---");
122
123    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
124    println!(
125        "Original: shape {:?}, data: {:?}",
126        tensor.shape().dims,
127        tensor.data()
128    );
129
130    // Reshape (view)
131    let reshaped = tensor.view(vec![3, 2]);
132    println!(
133        "Reshaped to [3, 2]: shape {:?}, data: {:?}",
134        reshaped.shape().dims,
135        reshaped.data()
136    );
137
138    // Create a different shaped tensor for demonstration
139    let tensor_2d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
140    println!(
141        "2D tensor: shape {:?}, data: {:?}",
142        tensor_2d.shape().dims,
143        tensor_2d.data()
144    );
145
146    // Create a 1D tensor
147    let tensor_1d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
148    println!(
149        "1D tensor: shape {:?}, data: {:?}",
150        tensor_1d.shape().dims,
151        tensor_1d.data()
152    );
153}
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 353)
324fn demonstrate_single_vs_batch_inference() {
325    println!("\n--- Single vs Batch Inference ---");
326
327    let layer = LinearLayer::new(4, 3, Some(46));
328
329    // Single inference
330    println!("Single inference:");
331    let single_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![1, 4]).unwrap();
332    let single_output = layer.forward_no_grad(&single_input);
333    println!("  Input shape: {:?}", single_input.shape().dims);
334    println!("  Output shape: {:?}", single_output.shape().dims);
335    println!("  Output: {:?}", single_output.data());
336
337    // Batch inference
338    println!("Batch inference:");
339    let batch_input = Tensor::from_slice(
340        &[
341            1.0, 2.0, 3.0, 4.0, // Sample 1
342            5.0, 6.0, 7.0, 8.0, // Sample 2
343            9.0, 10.0, 11.0, 12.0, // Sample 3
344        ],
345        vec![3, 4],
346    )
347    .unwrap();
348    let batch_output = layer.forward_no_grad(&batch_input);
349    println!("  Input shape: {:?}", batch_input.shape().dims);
350    println!("  Output shape: {:?}", batch_output.shape().dims);
351
352    // Verify batch consistency - first sample should match single inference
353    let _first_batch_sample = batch_output.view(vec![3, 3]); // Reshape to access first sample
354    let first_sample_data = &batch_output.data()[0..3]; // First 3 elements
355    let single_sample_data = single_output.data();
356
357    println!("Consistency check:");
358    println!("  Single output: {:?}", single_sample_data);
359    println!("  First batch sample: {:?}", first_sample_data);
360    println!(
361        "  Match: {}",
362        single_sample_data
363            .iter()
364            .zip(first_sample_data.iter())
365            .all(|(a, b)| (a - b).abs() < 1e-6)
366    );
367}
Source

pub fn element_view(&self, index: usize) -> Tensor

Create an element view for the specified index

Returns a scalar tensor (shape [1]) that views a single element of the source tensor. Maintains gradient tracking.

§Arguments
  • index - Linear index of the element to view
§Returns

A scalar tensor viewing the specified element

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let element = tensor.element_view(1);
assert_eq!(element.value(), 2.0);
Examples found in repository?
examples/iterators/advanced_patterns.rs (line 119)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
156
157/// Demonstrate conditional processing patterns
158///
159/// Shows how to implement dynamic filtering and transformation
160/// based on data characteristics and conditions.
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
Source

pub fn slice_view(&self, start: usize, step: usize, length: usize) -> Tensor

Create a slice view of the tensor

Returns a view of a contiguous or strided slice of the source tensor.

§Arguments
  • start - Starting index
  • step - Step size (1 for contiguous)
  • length - Number of elements
§Returns

A tensor viewing the specified slice

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5]).unwrap();
let slice = tensor.slice_view(1, 2, 2); // [2.0, 4.0]
assert_eq!(slice.data(), &[2.0, 4.0]);
Source

pub fn allocation_owner(&self) -> Option<&Arc<Allocation>>

Get the allocation owner for this tensor

Returns the shared allocation owner if this tensor is a view, or None if this tensor owns its memory directly.

§Returns

Optional reference to the allocation owner

§Implementation Details

This method is used internally to manage memory lifecycle for tensor views. It helps determine whether a tensor shares memory with another tensor.

Source

pub fn new_uninitialized(shape_dims: Vec<usize>) -> Self

Create a new tensor with uninitialized memory

This method allocates memory for a tensor without initializing it to any value. This is useful for performance-critical operations where the memory will be immediately overwritten, such as matrix multiplication results.

§Safety

The caller must ensure that all memory is written before reading from the tensor. Reading from uninitialized memory is undefined behavior.

§Arguments
  • shape_dims - The dimensions of the tensor
§Returns

A tensor with uninitialized memory

§Performance
  • Zero Initialization: Skips memory initialization for maximum performance
  • SIMD Ready: Properly aligned for vectorized operations
  • Memory Efficient: Uses optimized alignment strategies
§Example
use train_station::Tensor;

// Create uninitialized tensor for matmul result
let mut result = Tensor::new_uninitialized(vec![100, 100]);
// Initialize the memory before use
for value in result.data_mut() {
    *value = 0.0;
}
Source§

impl Tensor

Source

pub fn gather( &self, dim: usize, indices: &[usize], index_shape: &[usize], ) -> Tensor

Gather values along a dimension using a tensor of indices

This operation extracts elements from the input tensor based on indices provided along a specified dimension. The output tensor has the same shape as the index tensor, with each element taken from the input tensor at the corresponding position with the index value substituted for the specified dimension.

The gather operation is commonly used in machine learning for operations like embedding lookups, attention mechanisms, and advanced indexing patterns.

§Arguments
  • dim - The dimension along which to gather values (must be < tensor rank)
  • indices - Flattened indices buffer containing the positions to gather from
  • index_shape - Shape of the indices tensor and output tensor
§Returns

A new tensor with shape index_shape containing the gathered values

§Examples
§Basic Gather Operation
use train_station::Tensor;

// Create a 2x3 tensor: [[0.0, 0.1, 0.2], [0.3, 0.4, 0.5]]
let tensor = Tensor::from_slice(&[0.0, 0.1, 0.2, 0.3, 0.4, 0.5], vec![2, 3]).unwrap();

// Gather along dimension 1 (columns) with indices [2, 0, 1, 1]
let indices = [2, 0, 1, 1];
let index_shape = [2, 2];
let result = tensor.gather(1, &indices, &index_shape);

// Result shape is [2, 2]
assert_eq!(result.shape().dims, vec![2, 2]);

// Row 0: indices [2, 0] -> [0.2, 0.0]
assert!((result.get(&[0, 0]) - 0.2).abs() < 1e-6);
assert!((result.get(&[0, 1]) - 0.0).abs() < 1e-6);

// Row 1: indices [1, 1] -> [0.4, 0.4]
assert!((result.get(&[1, 0]) - 0.4).abs() < 1e-6);
assert!((result.get(&[1, 1]) - 0.4).abs() < 1e-6);
§Gather with Gradient Tracking
use train_station::Tensor;

let tensor = Tensor::from_slice(&[0.0, 0.1, 0.2, 0.3, 0.4, 0.5], vec![2, 3]).unwrap()
    .with_requires_grad();

let indices = [1, 1, 0, 2];
let index_shape = [2, 2];
let mut result = tensor.gather(1, &indices, &index_shape);

// Compute gradients
result.backward(None);
let grad = tensor.grad_by_value().expect("gradient missing");

// Verify gradient accumulation for repeated indices
assert!((grad.get(&[0, 1]) - 2.0).abs() < 1e-6); // Index 1 used twice in row 0
§Performance Characteristics
  • Time Complexity: O(n) where n is the number of elements in the output
  • Memory Usage: Creates a new tensor with the same size as the index tensor
  • Optimization: Uses precomputed strides for efficient memory access
  • GradTrack Overhead: Minimal overhead when gradient tracking is enabled
§Implementation Details

The gather operation works by:

  1. Validating input dimensions and index bounds
  2. Creating an output tensor with the specified index shape
  3. Iterating through all positions in the output tensor
  4. Computing source offsets using the input tensor’s strides
  5. Copying values from the input tensor to the output tensor
  6. Registering the operation for gradient computation if needed
§Safety

This function performs bounds checking to ensure:

  • The specified dimension is within the tensor’s rank
  • All indices are within bounds for the specified dimension
  • The index shape is compatible with the input tensor shape
  • The indices buffer length matches the product of index shape dimensions
§Panics

This function will panic if:

  • dim is greater than or equal to the tensor’s rank
  • Any index in indices is out of bounds for the specified dimension
  • The index_shape rank doesn’t match the input tensor’s rank
  • The index_shape dimensions don’t match the input tensor (except along dim)
  • The indices length doesn’t equal the product of index_shape dimensions
Source§

impl Tensor

Source

pub fn index_select(&self, dim: usize, indices: &[usize]) -> Tensor

Select elements along a dimension using a list of indices

This operation extracts elements from the input tensor along a specified dimension using the provided indices. The output tensor has the same shape as the input except along the specified dimension, where the size becomes the length of the indices array.

The index_select operation is commonly used for extracting specific rows, columns, or slices from tensors, and is particularly useful in machine learning for operations like embedding lookups and attention mechanisms.

§Arguments
  • dim - The dimension along which to select elements (must be < tensor rank)
  • indices - Array of indices specifying which elements to select along dim
§Returns

A new tensor with the same shape as the input except along dim, where the size is indices.len()

§Examples
§Basic Index Selection
use train_station::Tensor;

// Create a 2x3 tensor: [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]
let tensor = Tensor::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0], vec![2, 3]).unwrap();

// Select columns 2 and 0 from dimension 1
let result = tensor.index_select(1, &[2, 0]);

// Result shape is [2, 2] (same as input except dim 1 is now 2)
assert_eq!(result.shape().dims, vec![2, 2]);

// Row 0: selected columns [2, 0] -> [2.0, 0.0]
assert_eq!(result.get(&[0, 0]), 2.0);
assert_eq!(result.get(&[0, 1]), 0.0);

// Row 1: selected columns [2, 0] -> [5.0, 3.0]
assert_eq!(result.get(&[1, 0]), 5.0);
assert_eq!(result.get(&[1, 1]), 3.0);
§Index Selection with Gradient Tracking
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap()
    .with_requires_grad();

// Select specific elements with gradient tracking enabled
let mut result = tensor.index_select(1, &[1, 2]);
result.backward(None);

// Verify gradients are computed correctly
let grad = tensor.grad_by_value().expect("gradient missing");
assert_eq!(grad.shape().dims, vec![2, 3]);
§Selecting Rows from a Matrix
use train_station::Tensor;

// Create a 3x2 matrix
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![3, 2]).unwrap();

// Select rows 2 and 0 (dimension 0)
let result = tensor.index_select(0, &[2, 0]);

// Result shape is [2, 2]
assert_eq!(result.shape().dims, vec![2, 2]);

// Selected rows: row 2 [5.0, 6.0], row 0 [1.0, 2.0]
assert_eq!(result.get(&[0, 0]), 5.0); // First row of result (was row 2)
assert_eq!(result.get(&[0, 1]), 6.0);
assert_eq!(result.get(&[1, 0]), 1.0); // Second row of result (was row 0)
assert_eq!(result.get(&[1, 1]), 2.0);
§Performance Characteristics
  • Time Complexity: O(n) where n is the number of elements in the output tensor
  • Memory Usage: Creates a new tensor with size equal to the output shape
  • Optimization: Uses precomputed strides for efficient memory access
  • GradTrack Overhead: Minimal overhead when gradient tracking is enabled
  • Memory Layout: Output tensor is always contiguous for optimal performance
§Implementation Details

The index_select operation works by:

  1. Validating the dimension and index bounds
  2. Computing the output shape (same as input except along dim)
  3. Creating a new contiguous output tensor
  4. Iterating through all positions in the output tensor using nested loops:
    • Outer loop: iterate over dimensions before dim
    • Middle loop: iterate over the selected indices
    • Inner loop: iterate over dimensions after dim
  5. Computing source offsets using the input tensor’s strides
  6. Copying values from input to output tensor
  7. Registering the operation for gradient computation if needed
§Safety

This function performs comprehensive bounds checking to ensure:

  • The specified dimension is within the tensor’s rank
  • All indices are within bounds for the specified dimension
  • Memory access is safe through proper offset calculations
§Panics

This function will panic if:

  • dim is greater than or equal to the tensor’s rank
  • Any index in indices is out of bounds for the specified dimension
§Thread Safety

This function is thread-safe and can be called concurrently on different tensors. The operation does not modify the input tensor and creates a new output tensor.

Source§

impl Tensor

Source

pub fn masked_fill(&self, mask: &[bool], value: f32) -> Tensor

Fill masked elements with a specified value

This operation returns a copy of the input tensor where elements are replaced by the specified value wherever the corresponding boolean mask is true. Elements where the mask is false retain their original values from the input tensor.

The masked_fill operation is commonly used in machine learning for operations like masking attention weights, zeroing out specific elements, and implementing dropout-like functionality.

§Arguments
  • mask - Boolean array with the same length as the number of tensor elements
  • value - The value to fill masked positions with
§Returns

A new tensor with the same shape as the input, where masked elements are replaced by value and unmasked elements retain their original values

§Examples
§Basic Masked Fill
use train_station::Tensor;

// Create a 2x3 tensor: [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]
let tensor = Tensor::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0], vec![2, 3]).unwrap();

// Create a mask: [false, true, false, true, false, true]
let mask = [false, true, false, true, false, true];
let result = tensor.masked_fill(&mask, -1.0);

// Result: [[0.0, -1.0, 2.0], [-1.0, 4.0, -1.0]]
assert_eq!(result.shape().dims, vec![2, 3]);
assert_eq!(result.get(&[0, 0]), 0.0);   // Unmasked
assert_eq!(result.get(&[0, 1]), -1.0);  // Masked
assert_eq!(result.get(&[0, 2]), 2.0);   // Unmasked
assert_eq!(result.get(&[1, 0]), -1.0);  // Masked
assert_eq!(result.get(&[1, 1]), 4.0);   // Unmasked
assert_eq!(result.get(&[1, 2]), -1.0);  // Masked
§Masked Fill with Gradient Tracking
use train_station::Tensor;

let tensor = Tensor::from_slice(&[0.0, 0.1, 0.2, 0.3, 0.4, 0.5], vec![2, 3]).unwrap()
    .with_requires_grad();

// Create a mask with some true values
let mask = [false, true, false, true, false, false];
let mut result = tensor.masked_fill(&mask, 5.0);

// Compute gradients
result.backward(None);
let grad = tensor.grad_by_value().expect("gradient missing");

// Gradients should be zero where mask is true, 1 elsewhere
assert_eq!(grad.shape().dims, vec![2, 3]);
assert!((grad.get(&[0, 0]) - 1.0).abs() < 1e-6);   // Unmasked: gradient flows
assert!((grad.get(&[0, 1]) - 0.0).abs() < 1e-6);   // Masked: no gradient
assert!((grad.get(&[0, 2]) - 1.0).abs() < 1e-6);   // Unmasked: gradient flows
§Zeroing Out Specific Elements
use train_station::Tensor;

// Create a tensor with some values
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();

// Create a mask to zero out every other element
let mask = [true, false, true, false, true, false];
let result = tensor.masked_fill(&mask, 0.0);

// Result: [[0.0, 2.0, 0.0], [4.0, 0.0, 6.0]]
assert_eq!(result.get(&[0, 0]), 0.0);  // Zeroed
assert_eq!(result.get(&[0, 1]), 2.0);  // Kept
assert_eq!(result.get(&[0, 2]), 0.0);  // Zeroed
assert_eq!(result.get(&[1, 0]), 4.0);  // Kept
assert_eq!(result.get(&[1, 1]), 0.0);  // Zeroed
assert_eq!(result.get(&[1, 2]), 6.0);  // Kept
§Performance Characteristics
  • Time Complexity: O(n) where n is the number of elements in the tensor
  • Memory Usage: Creates a new tensor with the same size as the input
  • Optimization: Uses efficient stride-based iteration for non-contiguous tensors
  • GradTrack Overhead: Minimal overhead when gradient tracking is enabled
  • Memory Layout: Output tensor is always contiguous for optimal performance
§Implementation Details

The masked_fill operation works by:

  1. Validating that the mask length equals the number of tensor elements
  2. Creating a new contiguous output tensor with the same shape
  3. Iterating through all elements in logical order
  4. For each element, checking the corresponding mask value:
    • If mask is true: use the fill value
    • If mask is false: copy the original value from input tensor
  5. Computing source offsets using the input tensor’s shape for non-contiguous tensors
  6. Registering the operation for gradient computation if needed
§Safety

This function performs bounds checking to ensure:

  • The mask length equals the number of tensor elements
  • Memory access is safe through proper offset calculations
  • The operation handles both contiguous and non-contiguous tensors correctly
§Panics

This function will panic if:

  • The mask length does not equal the number of tensor elements
§Thread Safety

This function is thread-safe and can be called concurrently on different tensors. The operation does not modify the input tensor and creates a new output tensor.

§GradTrack Behavior

When gradient tracking is enabled:

  • Gradients do not flow through masked positions (they are zeroed)
  • Gradients flow normally through unmasked positions
  • This behavior is useful for implementing operations like dropout
Source§

impl Tensor

Source

pub fn select(&self, dim: usize, index: usize) -> Tensor

Select a slice along a given dimension at a specific index

This operation extracts a slice from the input tensor by fixing a specific dimension at a given index. The result is a tensor with one fewer dimension than the input, containing the selected slice.

The select operation returns a view (zero-copy) when the base offset is zero, otherwise it creates a contiguous copy to ensure correctness. This operation is commonly used for extracting specific rows, columns, or slices from tensors.

§Arguments
  • dim - The dimension along which to select (must be < tensor rank)
  • index - The index within the specified dimension to select (must be < dim size)
§Returns

A tensor with the selected slice. The result has the same shape as the input except with the specified dimension removed.

§Examples
§Basic Row Selection
use train_station::Tensor;

// Create a 2x3 tensor: [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]
let tensor = Tensor::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0], vec![2, 3]).unwrap();

// Select row 1 (dimension 0, index 1)
let result = tensor.select(0, 1);

// Result shape is [3] (dimension 0 removed)
assert_eq!(result.shape().dims, vec![3]);
assert_eq!(result.get(&[0]), 3.0);  // First element of row 1
assert_eq!(result.get(&[1]), 4.0);  // Second element of row 1
assert_eq!(result.get(&[2]), 5.0);  // Third element of row 1
§Column Selection
use train_station::Tensor;

// Create a 2x3 tensor: [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]
let tensor = Tensor::from_slice(&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0], vec![2, 3]).unwrap();

// Select column 1 (dimension 1, index 1)
let result = tensor.select(1, 1);

// Result shape is [2] (dimension 1 removed)
assert_eq!(result.shape().dims, vec![2]);
assert_eq!(result.get(&[0]), 1.0);  // Column 1, row 0
assert_eq!(result.get(&[1]), 4.0);  // Column 1, row 1
§Select with Gradient Tracking
use train_station::Tensor;

let tensor = Tensor::from_slice(&[0.0, 1.0, 2.0, 3.0], vec![2, 2]).unwrap()
    .with_requires_grad();

// Select row 1 with gradient tracking enabled
let mut result = tensor.select(0, 1);
result.backward(None);

// Verify gradients are computed correctly
let grad = tensor.grad_by_value().expect("gradient missing");
assert_eq!(grad.shape().dims, vec![2, 2]);
// Only row 1 receives gradients
assert_eq!(grad.get(&[0, 0]), 0.0);  // Row 0: no gradient
assert_eq!(grad.get(&[0, 1]), 0.0);  // Row 0: no gradient
assert_eq!(grad.get(&[1, 0]), 1.0);  // Row 1: gradient flows
assert_eq!(grad.get(&[1, 1]), 1.0);  // Row 1: gradient flows
§Performance Characteristics
  • Time Complexity: O(n) where n is the number of elements in the selected slice
  • Memory Usage: Zero-copy view when base offset is zero, otherwise creates a copy
  • Optimization: Uses efficient stride-based access for non-contiguous tensors
  • GradTrack Overhead: Minimal overhead when gradient tracking is enabled
  • Memory Layout: Result is contiguous when a copy is made, view otherwise
§Implementation Details

The select operation works by:

  1. Validating the dimension and index bounds
  2. Computing the new shape by removing the selected dimension
  3. Computing the new strides by removing the selected dimension’s stride
  4. Calculating the base offset for the selected slice
  5. If base offset is zero: creating a view with adjusted shape/strides
  6. If base offset is non-zero: creating a contiguous copy of the slice
  7. Registering the operation for gradient computation if needed
§Safety

This function performs comprehensive bounds checking to ensure:

  • The tensor has non-zero rank
  • The specified dimension is within the tensor’s rank
  • The index is within bounds for the specified dimension
  • Memory access is safe through proper offset calculations
§Panics

This function will panic if:

  • The tensor has zero rank
  • dim is greater than or equal to the tensor’s rank
  • index is greater than or equal to the size of the specified dimension
§Thread Safety

This function is thread-safe and can be called concurrently on different tensors. The operation does not modify the input tensor and creates either a view or a new tensor.

§View vs Copy Behavior
  • View (zero-copy): When the base offset is zero, returns a view that shares the same memory as the input tensor with adjusted shape and strides
  • Copy: When the base offset is non-zero, creates a contiguous copy to ensure correctness across all operations
§GradTrack Behavior

When gradient tracking is enabled:

  • Gradients are scattered back to the selected slice in the input tensor
  • Other positions in the input tensor receive zero gradients
  • This behavior ensures correct gradient flow for the selected elements
Source§

impl Tensor

Source

pub fn zeros(shape_dims: Vec<usize>) -> Self

Creates a new tensor filled with zeros

Convenience constructor that creates a tensor and initializes all elements to zero. Uses optimized SIMD operations for efficient zero initialization.

§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
§Returns

A new tensor with all elements initialized to zero

§Performance
  • Memory Allocation: Single allocation with optimized alignment
  • Initialization: SIMD-optimized zero filling for large tensors
  • Thread Safe: Atomic ID generation for gradtrack tracking
§Examples
use train_station::Tensor;

let tensor = Tensor::zeros(vec![2, 3]);
assert_eq!(tensor.size(), 6);
assert_eq!(tensor.shape().dims, vec![2, 3]);

// Verify all elements are zero
assert_eq!(tensor.get(&[0, 0]), 0.0);
assert_eq!(tensor.get(&[1, 2]), 0.0);
Examples found in repository?
examples/neural_networks/feedforward_network.rs (line 74)
68    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
69        let scale = (1.0 / input_size as f32).sqrt();
70
71        let weight = Tensor::randn(vec![input_size, output_size], seed)
72            .mul_scalar(scale)
73            .with_requires_grad();
74        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
75
76        Self {
77            weight,
78            bias,
79            input_size,
80            output_size,
81        }
82    }
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 59)
52    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
53        // Xavier/Glorot initialization: scale by sqrt(1/input_size)
54        let scale = (1.0 / input_size as f32).sqrt();
55
56        let weight = Tensor::randn(vec![input_size, output_size], seed)
57            .mul_scalar(scale)
58            .with_requires_grad();
59        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
60
61        Self {
62            weight,
63            bias,
64            input_size,
65            output_size,
66        }
67    }
examples/getting_started/tensor_basics.rs (line 46)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
examples/getting_started/optimizer_basics.rs (line 52)
47fn demonstrate_basic_optimizer_setup() {
48    println!("--- Basic Optimizer Setup ---");
49
50    // Create parameters that require gradients
51    let weight = Tensor::randn(vec![3, 2], Some(42)).with_requires_grad();
52    let bias = Tensor::zeros(vec![2]).with_requires_grad();
53
54    println!("Created parameters:");
55    println!(
56        "  Weight: shape {:?}, requires_grad: {}",
57        weight.shape().dims,
58        weight.requires_grad()
59    );
60    println!(
61        "  Bias: shape {:?}, requires_grad: {}",
62        bias.shape().dims,
63        bias.requires_grad()
64    );
65
66    // Create Adam optimizer with default configuration
67    let mut optimizer = Adam::new();
68    println!(
69        "Created Adam optimizer with learning rate: {}",
70        optimizer.learning_rate()
71    );
72
73    // Add parameters to optimizer
74    optimizer.add_parameter(&weight);
75    optimizer.add_parameter(&bias);
76    println!(
77        "Added {} parameters to optimizer",
78        optimizer.parameter_count()
79    );
80
81    // Create optimizer with custom configuration
82    let config = AdamConfig {
83        learning_rate: 0.01,
84        beta1: 0.9,
85        beta2: 0.999,
86        eps: 1e-8,
87        weight_decay: 0.0,
88        amsgrad: false,
89    };
90
91    let mut custom_optimizer = Adam::with_config(config);
92    custom_optimizer.add_parameter(&weight);
93    custom_optimizer.add_parameter(&bias);
94
95    println!(
96        "Created custom optimizer with learning rate: {}",
97        custom_optimizer.learning_rate()
98    );
99
100    // Demonstrate parameter linking
101    println!("Parameter linking completed successfully");
102}
103
104/// Demonstrate simple linear regression training
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
examples/optimizers/adam_configurations.rs (line 93)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
examples/optimizers/learning_rate_scheduling.rs (line 329)
319fn train_with_scheduler(
320    scheduler: &mut dyn LearningRateScheduler,
321    num_epochs: usize,
322) -> Result<TrainingStats, Box<dyn std::error::Error>> {
323    // Create training data: y = 2*x + 1
324    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
325    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
326
327    // Create model parameters
328    let mut weight = Tensor::randn(vec![1, 1], Some(456)).with_requires_grad();
329    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
330
331    // Create optimizer with initial learning rate
332    let mut optimizer = Adam::with_learning_rate(0.05);
333    optimizer.add_parameter(&weight);
334    optimizer.add_parameter(&bias);
335
336    // Training loop
337    let mut losses = Vec::new();
338    let mut lr_history = Vec::new();
339    let mut convergence_epoch = num_epochs;
340
341    for epoch in 0..num_epochs {
342        // Forward pass
343        let y_pred = x_data.matmul(&weight) + &bias;
344        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
345
346        // Backward pass
347        loss.backward(None);
348
349        // Update learning rate using scheduler
350        let current_lr = optimizer.learning_rate();
351        let new_lr = scheduler.step(current_lr, epoch, loss.value());
352
353        if (new_lr - current_lr).abs() > 1e-8 {
354            optimizer.set_learning_rate(new_lr);
355        }
356
357        // Optimizer step
358        optimizer.step(&mut [&mut weight, &mut bias]);
359        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
360
361        let loss_value = loss.value();
362        losses.push(loss_value);
363        lr_history.push(new_lr);
364
365        // Check for convergence
366        if loss_value < 0.01 && convergence_epoch == num_epochs {
367            convergence_epoch = epoch;
368        }
369    }
370
371    Ok(TrainingStats {
372        scheduler_name: scheduler.name().to_string(),
373        final_loss: losses[losses.len() - 1],
374        lr_history,
375        loss_history: losses,
376        convergence_epoch,
377    })
378}
Source

pub fn ones(shape_dims: Vec<usize>) -> Self

Creates a new tensor filled with ones

Convenience constructor that creates a tensor and initializes all elements to one. Uses optimized SIMD operations for efficient initialization.

§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
§Returns

A new tensor with all elements initialized to one

§Performance
  • Memory Allocation: Single allocation with optimized alignment
  • Initialization: SIMD-optimized one filling for large tensors
  • Thread Safe: Atomic ID generation for gradtrack tracking
§Examples
use train_station::Tensor;

let tensor = Tensor::ones(vec![2, 3]);
assert_eq!(tensor.size(), 6);
assert_eq!(tensor.shape().dims, vec![2, 3]);

// Verify all elements are one
assert_eq!(tensor.get(&[0, 0]), 1.0);
assert_eq!(tensor.get(&[1, 2]), 1.0);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 53)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
Source

pub fn zeros_on_device(shape_dims: Vec<usize>, device: Device) -> Self

Creates a new tensor filled with zeros on a specific device

Convenience constructor that creates a tensor on the specified device and initializes all elements to zero. Uses optimized SIMD operations for efficient zero initialization.

§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
  • device - The device where the tensor should be allocated
§Returns

A new tensor with all elements initialized to zero

§Performance
  • Memory Allocation: Device-specific allocation with optimized alignment
  • Initialization: SIMD-optimized zero filling for large tensors
  • Thread Safe: Atomic ID generation for gradtrack tracking
§Examples
use train_station::Tensor;
use train_station::Device;

let tensor = Tensor::zeros_on_device(vec![2, 2], Device::cpu());
assert_eq!(tensor.device(), Device::cpu());
assert_eq!(tensor.size(), 4);

// Verify all elements are zero
assert_eq!(tensor.get(&[0, 0]), 0.0);
assert_eq!(tensor.get(&[1, 1]), 0.0);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 201)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
Source

pub fn ones_on_device(shape_dims: Vec<usize>, device: Device) -> Self

Creates a new tensor filled with ones on a specific device

Convenience constructor that creates a tensor on the specified device and initializes all elements to one. Uses optimized SIMD operations for efficient initialization.

§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
  • device - The device where the tensor should be allocated
§Returns

A new tensor with all elements initialized to one

§Performance
  • Memory Allocation: Device-specific allocation with optimized alignment
  • Initialization: SIMD-optimized one filling for large tensors
  • Thread Safe: Atomic ID generation for gradtrack tracking
§Examples
use train_station::Tensor;
use train_station::Device;

let tensor = Tensor::ones_on_device(vec![2, 2], Device::cpu());
assert_eq!(tensor.device(), Device::cpu());
assert_eq!(tensor.size(), 4);

// Verify all elements are one
assert_eq!(tensor.get(&[0, 0]), 1.0);
assert_eq!(tensor.get(&[1, 1]), 1.0);
Source

pub fn fill(&mut self, value: f32)

Fills the tensor with a constant value using SIMD optimization

Efficiently initializes all elements of the tensor to the specified value. Uses SIMD operations for large tensors to maximize performance.

§Arguments
  • value - The value to fill the tensor with
§Performance
  • SIMD Optimization: Uses AVX2 for large tensors when available
  • Unrolled Loops: 4x unrolling for better instruction throughput
  • Memory Bandwidth: Optimized for maximum memory bandwidth utilization
§Examples
use train_station::Tensor;

let mut tensor = Tensor::new(vec![2, 3]);
tensor.fill(42.0);

// Verify all elements are 42.0
assert_eq!(tensor.get(&[0, 0]), 42.0);
assert_eq!(tensor.get(&[1, 2]), 42.0);
§Zero-Sized Tensor Handling
use train_station::Tensor;

let mut empty_tensor = Tensor::new(vec![0]);
empty_tensor.fill(42.0); // Should not panic
assert_eq!(empty_tensor.size(), 0);
Source§

impl Tensor

Source

pub fn from_slice(data: &[f32], shape_dims: Vec<usize>) -> Result<Self, String>

Creates a tensor from a slice of data

Creates a new tensor with the specified shape and copies data from the provided slice. Validates that the data size matches the tensor shape before performing the copy operation.

This method provides an efficient way to create tensors from existing data sources while ensuring data integrity and proper memory management.

§Arguments
  • data - Slice of f32 values to copy into the tensor
  • shape_dims - Vector of dimension sizes defining the tensor shape
§Returns
  • Ok(Tensor) - Successfully created tensor with copied data
  • Err(String) - Error if data size doesn’t match shape
§Performance
  • Memory Copy: Efficient non-overlapping copy using SIMD when possible
  • Validation: Fast size validation before allocation
  • Alignment: Proper memory alignment for optimal performance
  • Large Data: Optimized handling of large datasets
§Examples
§Basic Usage
use train_station::Tensor;

let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let tensor = Tensor::from_slice(&data, vec![2, 3]).unwrap();
assert_eq!(tensor.size(), 6);
assert_eq!(tensor.get(&[0, 0]), 1.0);
assert_eq!(tensor.get(&[1, 2]), 6.0);
§Multi-Dimensional Data
use train_station::Tensor;

// 1D tensor
let data_1d = [1.0, 2.0, 3.0];
let tensor_1d = Tensor::from_slice(&data_1d, vec![3]).unwrap();
assert_eq!(tensor_1d.shape().dims, vec![3]);
assert_eq!(tensor_1d.get(&[1]), 2.0);

// 3D tensor
let data_3d = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let tensor_3d = Tensor::from_slice(&data_3d, vec![2, 2, 2]).unwrap();
assert_eq!(tensor_3d.shape().dims, vec![2, 2, 2]);
assert_eq!(tensor_3d.get(&[0, 0, 0]), 1.0);
assert_eq!(tensor_3d.get(&[1, 1, 1]), 8.0);
§Error Handling
use train_station::Tensor;

// Size mismatch error
let data = [1.0, 2.0, 3.0];
let result = Tensor::from_slice(&data, vec![2, 2]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("Data size 3 doesn't match shape size 4"));
§Zero-Sized Tensors
use train_station::Tensor;

// Handle empty tensors gracefully
let data: [f32; 0] = [];
let tensor = Tensor::from_slice(&data, vec![0]).unwrap();
assert_eq!(tensor.size(), 0);
assert_eq!(tensor.shape().dims, vec![0]);
§Large Data Sets
use train_station::Tensor;

// Efficient handling of large datasets
let size = 1000;
let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
let tensor = Tensor::from_slice(&data, vec![size]).unwrap();

assert_eq!(tensor.size(), size);
assert_eq!(tensor.get(&[0]), 0.0);
assert_eq!(tensor.get(&[100]), 100.0);
assert_eq!(tensor.get(&[999]), 999.0);
§Implementation Details

This method performs the following steps:

  1. Shape Validation: Creates a Shape object and validates dimensions
  2. Size Check: Ensures data length matches the calculated tensor size
  3. Memory Allocation: Allocates tensor memory with proper alignment
  4. Data Copy: Uses efficient non-overlapping memory copy operation
  5. Return: Returns the created tensor or descriptive error message

The memory copy operation uses std::ptr::copy_nonoverlapping for maximum performance and safety, ensuring no data corruption occurs during the copy process.

Examples found in repository?
examples/getting_started/tensor_operators.rs (line 49)
46fn demonstrate_basic_operators() {
47    println!("--- Basic Tensor-Tensor Operators ---");
48
49    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
50    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
51
52    println!("Tensor A: {:?}", a.data());
53    println!("Tensor B: {:?}", b.data());
54
55    // Addition
56    let c = &a + &b;
57    println!("A + B: {:?}", c.data());
58
59    // Subtraction
60    let d = &a - &b;
61    println!("A - B: {:?}", d.data());
62
63    // Multiplication
64    let e = &a * &b;
65    println!("A * B: {:?}", e.data());
66
67    // Division
68    let f = &a / &b;
69    println!("A / B: {:?}", f.data());
70}
71
72/// Demonstrate tensor-scalar operators
73fn demonstrate_scalar_operators() {
74    println!("\n--- Tensor-Scalar Operators ---");
75
76    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
77    println!("Original tensor: {:?}", tensor.data());
78
79    // Tensor + scalar
80    let result1 = &tensor + 5.0;
81    println!("Tensor + 5.0: {:?}", result1.data());
82
83    // Scalar + tensor
84    let result2 = 5.0 + &tensor;
85    println!("5.0 + Tensor: {:?}", result2.data());
86
87    // Tensor - scalar
88    let result3 = &tensor - 2.0;
89    println!("Tensor - 2.0: {:?}", result3.data());
90
91    // Tensor * scalar
92    let result4 = &tensor * 3.0;
93    println!("Tensor * 3.0: {:?}", result4.data());
94
95    // Scalar * tensor
96    let result5 = 3.0 * &tensor;
97    println!("3.0 * Tensor: {:?}", result5.data());
98
99    // Tensor / scalar
100    let result6 = &tensor / 2.0;
101    println!("Tensor / 2.0: {:?}", result6.data());
102}
103
104/// Demonstrate assignment operators
105fn demonstrate_operator_assignment() {
106    println!("\n--- Assignment Operators ---");
107
108    let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
109    println!("Original tensor: {:?}", tensor.data());
110
111    // In-place addition
112    tensor += 5.0;
113    println!("After += 5.0: {:?}", tensor.data());
114
115    // In-place subtraction
116    tensor -= 2.0;
117    println!("After -= 2.0: {:?}", tensor.data());
118
119    // In-place multiplication
120    tensor *= 3.0;
121    println!("After *= 3.0: {:?}", tensor.data());
122
123    // In-place division
124    tensor /= 2.0;
125    println!("After /= 2.0: {:?}", tensor.data());
126}
127
128/// Demonstrate operator chaining and complex expressions
129fn demonstrate_operator_chaining() {
130    println!("\n--- Operator Chaining ---");
131
132    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
133    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
134    let c = Tensor::from_slice(&[9.0, 10.0, 11.0, 12.0], vec![2, 2]).unwrap();
135
136    println!("Tensor A: {:?}", a.data());
137    println!("Tensor B: {:?}", b.data());
138    println!("Tensor C: {:?}", c.data());
139
140    // Complex expression: (A + B) * C - 5
141    let result = (&a + &b) * &c - 5.0;
142    println!("(A + B) * C - 5: {:?}", result.data());
143
144    // Another complex expression: A * 2 + B / 2
145    let result2 = &a * 2.0 + &b / 2.0;
146    println!("A * 2 + B / 2: {:?}", result2.data());
147
148    // Negation and addition: -A + B * C
149    let result3 = -&a + &b * &c;
150    println!("-A + B * C: {:?}", result3.data());
151
152    // Division with parentheses: (A + B) / (C - 1)
153    let result4 = (&a + &b) / (&c - 1.0);
154    println!("(A + B) / (C - 1): {:?}", result4.data());
155}
156
157/// Demonstrate broadcasting behavior
158fn demonstrate_broadcasting() {
159    println!("\n--- Broadcasting ---");
160
161    // 2D tensor
162    let tensor_2d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
163    println!(
164        "2D tensor: shape {:?}, data: {:?}",
165        tensor_2d.shape().dims,
166        tensor_2d.data()
167    );
168
169    // 1D tensor (will be broadcasted)
170    let tensor_1d = Tensor::from_slice(&[10.0, 20.0], vec![2]).unwrap();
171    println!(
172        "1D tensor: shape {:?}, data: {:?}",
173        tensor_1d.shape().dims,
174        tensor_1d.data()
175    );
176
177    // Broadcasting addition
178    let broadcast_sum = &tensor_2d + &tensor_1d;
179    println!(
180        "Broadcast sum: shape {:?}, data: {:?}",
181        broadcast_sum.shape().dims,
182        broadcast_sum.data()
183    );
184
185    // Broadcasting multiplication
186    let broadcast_mul = &tensor_2d * &tensor_1d;
187    println!(
188        "Broadcast multiplication: shape {:?}, data: {:?}",
189        broadcast_mul.shape().dims,
190        broadcast_mul.data()
191    );
192
193    // Broadcasting with scalar
194    let broadcast_scalar = &tensor_2d + 100.0;
195    println!(
196        "Broadcast scalar: shape {:?}, data: {:?}",
197        broadcast_scalar.shape().dims,
198        broadcast_scalar.data()
199    );
200}
201
202/// Demonstrate equivalence between operators and method calls
203fn demonstrate_method_equivalence() {
204    println!("\n--- Operator vs Method Call Equivalence ---");
205
206    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
207    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
208
209    // Addition: operator vs method
210    let operator_result = &a + &b;
211    let method_result = a.add_tensor(&b);
212
213    println!("A + B (operator): {:?}", operator_result.data());
214    println!("A.add_tensor(B): {:?}", method_result.data());
215    println!(
216        "Results are equal: {}",
217        operator_result.data() == method_result.data()
218    );
219
220    // Multiplication: operator vs method
221    let operator_result = &a * &b;
222    let method_result = a.mul_tensor(&b);
223
224    println!("A * B (operator): {:?}", operator_result.data());
225    println!("A.mul_tensor(B): {:?}", method_result.data());
226    println!(
227        "Results are equal: {}",
228        operator_result.data() == method_result.data()
229    );
230
231    // Scalar addition: operator vs method
232    let operator_result = &a + 5.0;
233    let method_result = a.add_scalar(5.0);
234
235    println!("A + 5.0 (operator): {:?}", operator_result.data());
236    println!("A.add_scalar(5.0): {:?}", method_result.data());
237    println!(
238        "Results are equal: {}",
239        operator_result.data() == method_result.data()
240    );
241}
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 169)
163fn demonstrate_forward_pass() {
164    println!("\n--- Forward Pass (with gradients) ---");
165
166    let layer = LinearLayer::new(3, 2, Some(43));
167
168    // Single input
169    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
170    let output = layer.forward(&input);
171
172    println!("Single input:");
173    println!("  Input: {:?}", input.data());
174    println!("  Output: {:?}", output.data());
175    println!("  Output requires grad: {}", output.requires_grad());
176
177    // Batch input
178    let batch_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
179    let batch_output = layer.forward(&batch_input);
180
181    println!("Batch input:");
182    println!("  Input shape: {:?}", batch_input.shape().dims);
183    println!("  Output shape: {:?}", batch_output.shape().dims);
184    println!("  Output requires grad: {}", batch_output.requires_grad());
185}
186
187/// Demonstrate forward pass without gradient tracking
188fn demonstrate_forward_pass_no_grad() {
189    println!("\n--- Forward Pass (no gradients) ---");
190
191    let layer = LinearLayer::new(3, 2, Some(44));
192
193    // Single input
194    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
195    let output = layer.forward_no_grad(&input);
196
197    println!("Single input (no grad):");
198    println!("  Input: {:?}", input.data());
199    println!("  Output: {:?}", output.data());
200    println!("  Output requires grad: {}", output.requires_grad());
201
202    // Compare with grad version
203    let output_with_grad = layer.forward(&input);
204    println!("Comparison:");
205    println!(
206        "  Same values: {}",
207        output.data() == output_with_grad.data()
208    );
209    println!("  No grad requires grad: {}", output.requires_grad());
210    println!(
211        "  With grad requires grad: {}",
212        output_with_grad.requires_grad()
213    );
214}
215
216/// Demonstrate complete training loop
217fn demonstrate_training_loop() -> Result<(), Box<dyn std::error::Error>> {
218    println!("\n--- Training Loop ---");
219
220    // Create layer and training data
221    let mut layer = LinearLayer::new(2, 1, Some(45));
222
223    // Simple regression task: y = 2*x1 + 3*x2 + 1
224    let x_data = Tensor::from_slice(
225        &[
226            1.0, 1.0, // x1=1, x2=1 -> y=6
227            2.0, 1.0, // x1=2, x2=1 -> y=8
228            1.0, 2.0, // x1=1, x2=2 -> y=9
229            2.0, 2.0, // x1=2, x2=2 -> y=11
230        ],
231        vec![4, 2],
232    )
233    .unwrap();
234
235    let y_true = Tensor::from_slice(&[6.0, 8.0, 9.0, 11.0], vec![4, 1]).unwrap();
236
237    println!("Training data:");
238    println!("  X shape: {:?}", x_data.shape().dims);
239    println!("  Y shape: {:?}", y_true.shape().dims);
240    println!("  Target function: y = 2*x1 + 3*x2 + 1");
241
242    // Create optimizer
243    let config = AdamConfig {
244        learning_rate: 0.01,
245        beta1: 0.9,
246        beta2: 0.999,
247        eps: 1e-8,
248        weight_decay: 0.0,
249        amsgrad: false,
250    };
251
252    let mut optimizer = Adam::with_config(config);
253    let params = layer.parameters();
254    for param in &params {
255        optimizer.add_parameter(param);
256    }
257
258    println!("Optimizer setup complete. Starting training...");
259
260    // Training loop
261    let num_epochs = 100;
262    let mut losses = Vec::new();
263
264    for epoch in 0..num_epochs {
265        // Forward pass
266        let y_pred = layer.forward(&x_data);
267
268        // Compute loss: MSE
269        let diff = y_pred.sub_tensor(&y_true);
270        let mut loss = diff.pow_scalar(2.0).mean();
271
272        // Backward pass
273        loss.backward(None);
274
275        // Optimizer step
276        let mut params = layer.parameters();
277        optimizer.step(&mut params);
278        optimizer.zero_grad(&mut params);
279
280        losses.push(loss.value());
281
282        // Print progress
283        if epoch % 20 == 0 || epoch == num_epochs - 1 {
284            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
285        }
286    }
287
288    // Evaluate final model
289    let final_predictions = layer.forward_no_grad(&x_data);
290
291    println!("\nFinal model evaluation:");
292    println!("  Learned weights: {:?}", layer.weight.data());
293    println!("  Learned bias: {:?}", layer.bias.data());
294    println!("  Target weights: [2.0, 3.0]");
295    println!("  Target bias: [1.0]");
296
297    println!("  Predictions vs True:");
298    for i in 0..4 {
299        let pred = final_predictions.data()[i];
300        let true_val = y_true.data()[i];
301        println!(
302            "    Sample {}: pred={:.3}, true={:.1}, error={:.3}",
303            i + 1,
304            pred,
305            true_val,
306            (pred - true_val).abs()
307        );
308    }
309
310    // Training analysis
311    let initial_loss = losses[0];
312    let final_loss = losses[losses.len() - 1];
313    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
314
315    println!("\nTraining Analysis:");
316    println!("  Initial loss: {:.6}", initial_loss);
317    println!("  Final loss: {:.6}", final_loss);
318    println!("  Loss reduction: {:.1}%", loss_reduction);
319
320    Ok(())
321}
322
323/// Demonstrate single vs batch inference
324fn demonstrate_single_vs_batch_inference() {
325    println!("\n--- Single vs Batch Inference ---");
326
327    let layer = LinearLayer::new(4, 3, Some(46));
328
329    // Single inference
330    println!("Single inference:");
331    let single_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![1, 4]).unwrap();
332    let single_output = layer.forward_no_grad(&single_input);
333    println!("  Input shape: {:?}", single_input.shape().dims);
334    println!("  Output shape: {:?}", single_output.shape().dims);
335    println!("  Output: {:?}", single_output.data());
336
337    // Batch inference
338    println!("Batch inference:");
339    let batch_input = Tensor::from_slice(
340        &[
341            1.0, 2.0, 3.0, 4.0, // Sample 1
342            5.0, 6.0, 7.0, 8.0, // Sample 2
343            9.0, 10.0, 11.0, 12.0, // Sample 3
344        ],
345        vec![3, 4],
346    )
347    .unwrap();
348    let batch_output = layer.forward_no_grad(&batch_input);
349    println!("  Input shape: {:?}", batch_input.shape().dims);
350    println!("  Output shape: {:?}", batch_output.shape().dims);
351
352    // Verify batch consistency - first sample should match single inference
353    let _first_batch_sample = batch_output.view(vec![3, 3]); // Reshape to access first sample
354    let first_sample_data = &batch_output.data()[0..3]; // First 3 elements
355    let single_sample_data = single_output.data();
356
357    println!("Consistency check:");
358    println!("  Single output: {:?}", single_sample_data);
359    println!("  First batch sample: {:?}", first_sample_data);
360    println!(
361        "  Match: {}",
362        single_sample_data
363            .iter()
364            .zip(first_sample_data.iter())
365            .all(|(a, b)| (a - b).abs() < 1e-6)
366    );
367}
368
369/// Demonstrate serialization and loading
370fn demonstrate_serialization() -> Result<(), Box<dyn std::error::Error>> {
371    println!("\n--- Serialization ---");
372
373    // Create and train a simple layer
374    let mut original_layer = LinearLayer::new(2, 1, Some(47));
375
376    // Simple training data
377    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
378    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
379
380    let mut optimizer = Adam::with_learning_rate(0.01);
381    let params = original_layer.parameters();
382    for param in &params {
383        optimizer.add_parameter(param);
384    }
385
386    // Train for a few epochs
387    for _ in 0..10 {
388        let y_pred = original_layer.forward(&x_data);
389        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
390        loss.backward(None);
391
392        let mut params = original_layer.parameters();
393        optimizer.step(&mut params);
394        optimizer.zero_grad(&mut params);
395    }
396
397    println!("Original layer trained");
398    println!("  Weight: {:?}", original_layer.weight.data());
399    println!("  Bias: {:?}", original_layer.bias.data());
400
401    // Save layer
402    original_layer.save_json("temp_linear_layer")?;
403
404    // Load layer
405    let loaded_layer = LinearLayer::load_json("temp_linear_layer", 2, 1)?;
406
407    println!("Loaded layer");
408    println!("  Weight: {:?}", loaded_layer.weight.data());
409    println!("  Bias: {:?}", loaded_layer.bias.data());
410
411    // Verify consistency
412    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
413    let original_output = original_layer.forward_no_grad(&test_input);
414    let loaded_output = loaded_layer.forward_no_grad(&test_input);
415
416    println!("Consistency check:");
417    println!("  Original output: {:?}", original_output.data());
418    println!("  Loaded output: {:?}", loaded_output.data());
419    println!(
420        "  Match: {}",
421        original_output
422            .data()
423            .iter()
424            .zip(loaded_output.data().iter())
425            .all(|(a, b)| (a - b).abs() < 1e-6)
426    );
427
428    println!("Serialization verification: PASSED");
429
430    Ok(())
431}
examples/iterators/element_iteration.rs (line 81)
77fn demonstrate_basic_iteration() -> Result<(), Box<dyn std::error::Error>> {
78    println!("\n--- Basic Element Iteration ---");
79
80    // Create a simple tensor for demonstration
81    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
82    println!("Original tensor: {:?}", tensor.data());
83
84    // Basic iteration with for loop
85    println!("\nBasic iteration with for loop:");
86    for (i, element) in tensor.iter().enumerate() {
87        println!(
88            "  Element {}: value = {:.1}, shape = {:?}",
89            i,
90            element.value(),
91            element.shape().dims
92        );
93    }
94
95    // Element-wise transformation
96    println!("\nElement-wise transformation (2x + 1):");
97    let transformed: Tensor = tensor
98        .iter()
99        .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
100        .collect();
101    println!("  Result: {:?}", transformed.data());
102
103    // Filtering elements
104    println!("\nFiltering elements (values > 3.0):");
105    let filtered: Tensor = tensor.iter().filter(|elem| elem.value() > 3.0).collect();
106    println!("  Filtered: {:?}", filtered.data());
107
108    Ok(())
109}
110
111/// Demonstrate standard iterator trait methods
112///
113/// Shows compatibility with Rust's standard library iterator methods
114/// and demonstrates various functional programming patterns.
115fn demonstrate_standard_methods() -> Result<(), Box<dyn std::error::Error>> {
116    println!("\n--- Standard Iterator Methods ---");
117
118    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
119
120    // Using map for transformations
121    println!("\nMap transformation (square each element):");
122    let squared: Tensor = tensor.iter().map(|elem| elem.pow_scalar(2.0)).collect();
123    println!("  Squared: {:?}", squared.data());
124
125    // Using enumerate for indexed operations
126    println!("\nEnumerate with indexed operations:");
127    let indexed: Tensor = tensor
128        .iter()
129        .enumerate()
130        .map(|(i, elem)| elem.add_scalar(i as f32))
131        .collect();
132    println!("  Indexed: {:?}", indexed.data());
133
134    // Using fold for reduction
135    println!("\nFold for sum calculation:");
136    let sum: f32 = tensor.iter().fold(0.0, |acc, elem| acc + elem.value());
137    println!("  Sum: {:.1}", sum);
138
139    // Using find for element search
140    println!("\nFind specific element:");
141    if let Some(found) = tensor.iter().find(|elem| elem.value() == 3.0) {
142        println!("  Found element with value 3.0: {:.1}", found.value());
143    }
144
145    // Using any/all for condition checking
146    println!("\nCondition checking:");
147    let all_positive = tensor.iter().all(|elem| elem.value() > 0.0);
148    let any_large = tensor.iter().any(|elem| elem.value() > 4.0);
149    println!("  All positive: {}", all_positive);
150    println!("  Any > 4.0: {}", any_large);
151
152    Ok(())
153}
154
155/// Demonstrate gradient tracking through element operations
156///
157/// Shows how gradient tracking works seamlessly through iterator
158/// operations, maintaining the computational graph for backpropagation.
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
187
188/// Demonstrate advanced iterator patterns
189///
190/// Shows complex iterator chains and advanced functional programming
191/// patterns for sophisticated data processing workflows.
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
examples/getting_started/tensor_basics.rs (line 62)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
87
88/// Demonstrate basic arithmetic operations
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
118
119/// Demonstrate shape manipulation operations
120fn demonstrate_shape_operations() {
121    println!("\n--- Shape Operations ---");
122
123    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
124    println!(
125        "Original: shape {:?}, data: {:?}",
126        tensor.shape().dims,
127        tensor.data()
128    );
129
130    // Reshape (view)
131    let reshaped = tensor.view(vec![3, 2]);
132    println!(
133        "Reshaped to [3, 2]: shape {:?}, data: {:?}",
134        reshaped.shape().dims,
135        reshaped.data()
136    );
137
138    // Create a different shaped tensor for demonstration
139    let tensor_2d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
140    println!(
141        "2D tensor: shape {:?}, data: {:?}",
142        tensor_2d.shape().dims,
143        tensor_2d.data()
144    );
145
146    // Create a 1D tensor
147    let tensor_1d = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
148    println!(
149        "1D tensor: shape {:?}, data: {:?}",
150        tensor_1d.shape().dims,
151        tensor_1d.data()
152    );
153}
154
155/// Demonstrate data access patterns
156fn demonstrate_data_access() {
157    println!("\n--- Data Access ---");
158
159    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
160
161    // Access individual elements
162    println!("Element [0, 0]: {}", tensor.get(&[0, 0]));
163    println!("Element [0, 1]: {}", tensor.get(&[0, 1]));
164    println!("Element [1, 0]: {}", tensor.get(&[1, 0]));
165    println!("Element [1, 1]: {}", tensor.get(&[1, 1]));
166
167    // Access data as slice
168    let data = tensor.data();
169    println!("Data as slice: {:?}", data);
170
171    // Iterate over elements
172    println!("Elements:");
173    for (i, &value) in data.iter().enumerate() {
174        println!("  [{}]: {}", i, value);
175    }
176}
177
178/// Demonstrate utility functions
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
examples/neural_networks/feedforward_network.rs (line 351)
339fn demonstrate_forward_pass() {
340    println!("\n--- Forward Pass ---");
341
342    let config = FeedForwardConfig {
343        input_size: 3,
344        hidden_sizes: vec![5, 3],
345        output_size: 2,
346        use_bias: true,
347    };
348    let network = FeedForwardNetwork::new(config, Some(43));
349
350    // Single input
351    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
352    let output = network.forward(&input);
353
354    println!("Single input forward pass:");
355    println!("  Input shape: {:?}", input.shape().dims);
356    println!("  Output shape: {:?}", output.shape().dims);
357    println!("  Output: {:?}", output.data());
358    println!("  Output requires grad: {}", output.requires_grad());
359
360    // Batch input
361    let batch_input = Tensor::from_slice(
362        &[
363            1.0, 2.0, 3.0, // Sample 1
364            4.0, 5.0, 6.0, // Sample 2
365            7.0, 8.0, 9.0, // Sample 3
366        ],
367        vec![3, 3],
368    )
369    .unwrap();
370    let batch_output = network.forward(&batch_input);
371
372    println!("Batch input forward pass:");
373    println!("  Input shape: {:?}", batch_input.shape().dims);
374    println!("  Output shape: {:?}", batch_output.shape().dims);
375    println!("  Output requires grad: {}", batch_output.requires_grad());
376
377    // Compare with no-grad version
378    let output_no_grad = network.forward_no_grad(&input);
379    println!("No-grad comparison:");
380    println!("  Same values: {}", output.data() == output_no_grad.data());
381    println!("  With grad requires grad: {}", output.requires_grad());
382    println!(
383        "  No grad requires grad: {}",
384        output_no_grad.requires_grad()
385    );
386}
387
388/// Demonstrate different configurable architectures
389fn demonstrate_configurable_architectures() {
390    println!("\n--- Configurable Architectures ---");
391
392    let architectures = vec![
393        ("Shallow", vec![8]),
394        ("Medium", vec![16, 8]),
395        ("Deep", vec![32, 16, 8, 4]),
396        ("Wide", vec![64, 32]),
397        ("Bottleneck", vec![16, 4, 16]),
398    ];
399
400    for (name, hidden_sizes) in architectures {
401        let config = FeedForwardConfig {
402            input_size: 10,
403            hidden_sizes,
404            output_size: 3,
405            use_bias: true,
406        };
407
408        let network = FeedForwardNetwork::new(config.clone(), Some(44));
409
410        // Test forward pass
411        let test_input = Tensor::randn(vec![5, 10], Some(45)); // Batch of 5
412        let output = network.forward_no_grad(&test_input);
413
414        println!("{} network:", name);
415        println!("  Architecture: 10 -> {:?} -> 3", config.hidden_sizes);
416        println!("  Parameters: {}", network.parameter_count());
417        println!("  Test output shape: {:?}", output.shape().dims);
418        println!(
419            "  Output range: [{:.3}, {:.3}]",
420            output.data().iter().fold(f32::INFINITY, |a, &b| a.min(b)),
421            output
422                .data()
423                .iter()
424                .fold(f32::NEG_INFINITY, |a, &b| a.max(b))
425        );
426    }
427}
428
429/// Demonstrate basic training workflow
430fn demonstrate_training_workflow() -> Result<(), Box<dyn std::error::Error>> {
431    println!("\n--- Training Workflow ---");
432
433    // Create a simple classification network
434    let config = FeedForwardConfig {
435        input_size: 2,
436        hidden_sizes: vec![4, 3],
437        output_size: 1,
438        use_bias: true,
439    };
440    let mut network = FeedForwardNetwork::new(config, Some(46));
441
442    println!("Training network: 2 -> [4, 3] -> 1");
443
444    // Create simple binary classification data: XOR problem
445    let x_data = Tensor::from_slice(
446        &[
447            0.0, 0.0, // -> 0
448            0.0, 1.0, // -> 1
449            1.0, 0.0, // -> 1
450            1.0, 1.0, // -> 0
451        ],
452        vec![4, 2],
453    )
454    .unwrap();
455
456    let y_true = Tensor::from_slice(&[0.0, 1.0, 1.0, 0.0], vec![4, 1]).unwrap();
457
458    println!("Training on XOR problem:");
459    println!("  Input shape: {:?}", x_data.shape().dims);
460    println!("  Target shape: {:?}", y_true.shape().dims);
461
462    // Create optimizer
463    let mut optimizer = Adam::with_learning_rate(0.1);
464    let params = network.parameters();
465    for param in &params {
466        optimizer.add_parameter(param);
467    }
468
469    // Training loop
470    let num_epochs = 50;
471    let mut losses = Vec::new();
472
473    for epoch in 0..num_epochs {
474        // Forward pass
475        let y_pred = network.forward(&x_data);
476
477        // Compute loss: MSE
478        let diff = y_pred.sub_tensor(&y_true);
479        let mut loss = diff.pow_scalar(2.0).mean();
480
481        // Backward pass
482        loss.backward(None);
483
484        // Optimizer step and zero grad
485        let mut params = network.parameters();
486        optimizer.step(&mut params);
487        optimizer.zero_grad(&mut params);
488
489        losses.push(loss.value());
490
491        // Print progress
492        if epoch % 10 == 0 || epoch == num_epochs - 1 {
493            println!("Epoch {:2}: Loss = {:.6}", epoch, loss.value());
494        }
495    }
496
497    // Test final model
498    let final_predictions = network.forward_no_grad(&x_data);
499    println!("\nFinal predictions vs targets:");
500    for i in 0..4 {
501        let pred = final_predictions.data()[i];
502        let target = y_true.data()[i];
503        let input_x = x_data.data()[i * 2];
504        let input_y = x_data.data()[i * 2 + 1];
505        println!(
506            "  [{:.0}, {:.0}] -> pred: {:.3}, target: {:.0}, error: {:.3}",
507            input_x,
508            input_y,
509            pred,
510            target,
511            (pred - target).abs()
512        );
513    }
514
515    Ok(())
516}
517
518/// Demonstrate comprehensive training with 100+ steps
519fn demonstrate_comprehensive_training() -> Result<(), Box<dyn std::error::Error>> {
520    println!("\n--- Comprehensive Training (100+ Steps) ---");
521
522    // Create a regression network
523    let config = FeedForwardConfig {
524        input_size: 3,
525        hidden_sizes: vec![8, 6, 4],
526        output_size: 2,
527        use_bias: true,
528    };
529    let mut network = FeedForwardNetwork::new(config, Some(47));
530
531    println!("Network architecture: 3 -> [8, 6, 4] -> 2");
532    println!("Total parameters: {}", network.parameter_count());
533
534    // Create synthetic regression data
535    // Target function: [y1, y2] = [x1 + 2*x2 - x3, x1*x2 + x3]
536    let num_samples = 32;
537    let mut x_vec = Vec::new();
538    let mut y_vec = Vec::new();
539
540    for i in 0..num_samples {
541        let x1 = (i as f32 / num_samples as f32) * 2.0 - 1.0; // [-1, 1]
542        let x2 = ((i * 2) as f32 / num_samples as f32) * 2.0 - 1.0;
543        let x3 = ((i * 3) as f32 / num_samples as f32) * 2.0 - 1.0;
544
545        let y1 = x1 + 2.0 * x2 - x3;
546        let y2 = x1 * x2 + x3;
547
548        x_vec.extend_from_slice(&[x1, x2, x3]);
549        y_vec.extend_from_slice(&[y1, y2]);
550    }
551
552    let x_data = Tensor::from_slice(&x_vec, vec![num_samples, 3]).unwrap();
553    let y_true = Tensor::from_slice(&y_vec, vec![num_samples, 2]).unwrap();
554
555    println!("Training data:");
556    println!("  {} samples", num_samples);
557    println!("  Input shape: {:?}", x_data.shape().dims);
558    println!("  Target shape: {:?}", y_true.shape().dims);
559
560    // Create optimizer with learning rate scheduling
561    let mut optimizer = Adam::with_learning_rate(0.01);
562    let params = network.parameters();
563    for param in &params {
564        optimizer.add_parameter(param);
565    }
566
567    // Comprehensive training loop (150 epochs)
568    let num_epochs = 150;
569    let mut losses = Vec::new();
570    let mut best_loss = f32::INFINITY;
571    let mut patience_counter = 0;
572    let patience = 20;
573
574    println!("Starting comprehensive training...");
575
576    for epoch in 0..num_epochs {
577        // Forward pass
578        let y_pred = network.forward(&x_data);
579
580        // Compute loss: MSE
581        let diff = y_pred.sub_tensor(&y_true);
582        let mut loss = diff.pow_scalar(2.0).mean();
583
584        // Backward pass
585        loss.backward(None);
586
587        // Optimizer step and zero grad
588        let mut params = network.parameters();
589        optimizer.step(&mut params);
590        optimizer.zero_grad(&mut params);
591
592        let current_loss = loss.value();
593        losses.push(current_loss);
594
595        // Learning rate scheduling
596        if epoch > 0 && epoch % 30 == 0 {
597            let new_lr = optimizer.learning_rate() * 0.8;
598            optimizer.set_learning_rate(new_lr);
599            println!("  Reduced learning rate to {:.4}", new_lr);
600        }
601
602        // Early stopping logic
603        if current_loss < best_loss {
604            best_loss = current_loss;
605            patience_counter = 0;
606        } else {
607            patience_counter += 1;
608        }
609
610        // Print progress
611        if epoch % 25 == 0 || epoch == num_epochs - 1 {
612            println!(
613                "Epoch {:3}: Loss = {:.6}, LR = {:.4}, Best = {:.6}",
614                epoch,
615                current_loss,
616                optimizer.learning_rate(),
617                best_loss
618            );
619        }
620
621        // Early stopping
622        if patience_counter >= patience && epoch > 50 {
623            println!("Early stopping at epoch {} (patience exceeded)", epoch);
624            break;
625        }
626    }
627
628    // Final evaluation
629    let final_predictions = network.forward_no_grad(&x_data);
630
631    // Compute final metrics
632    let final_loss = losses[losses.len() - 1];
633    let initial_loss = losses[0];
634    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
635
636    println!("\nTraining completed!");
637    println!("  Initial loss: {:.6}", initial_loss);
638    println!("  Final loss: {:.6}", final_loss);
639    println!("  Best loss: {:.6}", best_loss);
640    println!("  Loss reduction: {:.1}%", loss_reduction);
641    println!("  Final learning rate: {:.4}", optimizer.learning_rate());
642
643    // Sample predictions analysis
644    println!("\nSample predictions (first 5):");
645    for i in 0..5.min(num_samples) {
646        let pred1 = final_predictions.data()[i * 2];
647        let pred2 = final_predictions.data()[i * 2 + 1];
648        let true1 = y_true.data()[i * 2];
649        let true2 = y_true.data()[i * 2 + 1];
650
651        println!(
652            "  Sample {}: pred=[{:.3}, {:.3}], true=[{:.3}, {:.3}], error=[{:.3}, {:.3}]",
653            i + 1,
654            pred1,
655            pred2,
656            true1,
657            true2,
658            (pred1 - true1).abs(),
659            (pred2 - true2).abs()
660        );
661    }
662
663    Ok(())
664}
665
666/// Demonstrate network serialization
667fn demonstrate_network_serialization() -> Result<(), Box<dyn std::error::Error>> {
668    println!("\n--- Network Serialization ---");
669
670    // Create and train a network
671    let config = FeedForwardConfig {
672        input_size: 2,
673        hidden_sizes: vec![4, 2],
674        output_size: 1,
675        use_bias: true,
676    };
677    let mut original_network = FeedForwardNetwork::new(config.clone(), Some(48));
678
679    // Quick training
680    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
681    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
682
683    let mut optimizer = Adam::with_learning_rate(0.01);
684    let params = original_network.parameters();
685    for param in &params {
686        optimizer.add_parameter(param);
687    }
688
689    for _ in 0..20 {
690        let y_pred = original_network.forward(&x_data);
691        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
692        loss.backward(None);
693
694        let mut params = original_network.parameters();
695        optimizer.step(&mut params);
696        optimizer.zero_grad(&mut params);
697    }
698
699    // Test original network
700    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
701    let original_output = original_network.forward_no_grad(&test_input);
702
703    println!("Original network output: {:?}", original_output.data());
704
705    // Save network
706    original_network.save_json("temp_feedforward_network")?;
707
708    // Load network
709    let loaded_network = FeedForwardNetwork::load_json("temp_feedforward_network", config)?;
710    let loaded_output = loaded_network.forward_no_grad(&test_input);
711
712    println!("Loaded network output: {:?}", loaded_output.data());
713
714    // Verify consistency
715    let match_check = original_output
716        .data()
717        .iter()
718        .zip(loaded_output.data().iter())
719        .all(|(a, b)| (a - b).abs() < 1e-6);
720
721    println!(
722        "Serialization verification: {}",
723        if match_check { "PASSED" } else { "FAILED" }
724    );
725
726    Ok(())
727}
examples/getting_started/serialization_basics.rs (line 56)
52fn demonstrate_tensor_serialization() -> Result<(), Box<dyn std::error::Error>> {
53    println!("--- Tensor Serialization ---");
54
55    // Create a tensor with some data
56    let original_tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
57    println!(
58        "Original tensor: shape {:?}, data: {:?}",
59        original_tensor.shape().dims,
60        original_tensor.data()
61    );
62
63    // Save tensor in JSON format
64    let json_path = "temp_tensor.json";
65    original_tensor.save_json(json_path)?;
66    println!("Saved tensor to JSON: {}", json_path);
67
68    // Load tensor from JSON
69    let loaded_tensor_json = Tensor::load_json(json_path)?;
70    println!(
71        "Loaded from JSON: shape {:?}, data: {:?}",
72        loaded_tensor_json.shape().dims,
73        loaded_tensor_json.data()
74    );
75
76    // Verify data integrity
77    assert_eq!(
78        original_tensor.shape().dims,
79        loaded_tensor_json.shape().dims
80    );
81    assert_eq!(original_tensor.data(), loaded_tensor_json.data());
82    println!("JSON serialization verification: PASSED");
83
84    // Save tensor in binary format
85    let binary_path = "temp_tensor.bin";
86    original_tensor.save_binary(binary_path)?;
87    println!("Saved tensor to binary: {}", binary_path);
88
89    // Load tensor from binary
90    let loaded_tensor_binary = Tensor::load_binary(binary_path)?;
91    println!(
92        "Loaded from binary: shape {:?}, data: {:?}",
93        loaded_tensor_binary.shape().dims,
94        loaded_tensor_binary.data()
95    );
96
97    // Verify data integrity
98    assert_eq!(
99        original_tensor.shape().dims,
100        loaded_tensor_binary.shape().dims
101    );
102    assert_eq!(original_tensor.data(), loaded_tensor_binary.data());
103    println!("Binary serialization verification: PASSED");
104
105    Ok(())
106}
Source§

impl Tensor

Source

pub fn randn(shape_dims: Vec<usize>, seed: Option<u64>) -> Self

Creates a tensor with normally distributed random values (mean=0, std=1)

Similar to PyTorch’s torch.randn(), creates a tensor filled with random values drawn from a standard normal distribution (mean=0, standard deviation=1). Uses Box-Muller transform for efficient normal distribution generation.

This method provides high-quality random number generation with optional reproducibility through seed-based generation. The generated values follow a standard normal distribution suitable for machine learning applications.

§Arguments
  • shape_dims - Vector of dimension sizes defining the tensor shape
  • seed - Optional seed for reproducible random generation
§Returns

A new tensor with normally distributed random values

§Performance
  • Box-Muller Transform: Efficient normal distribution generation
  • SIMD Optimization: Vectorized operations for large tensors
  • Memory Efficient: Single-pass generation with optimized allocation
  • Thread Safe: Uses thread-local random state
§Examples
§Basic Usage
use train_station::Tensor;

// Create a 2x3 tensor with random normal values
let tensor = Tensor::randn(vec![2, 3], None);
assert_eq!(tensor.size(), 6);
assert_eq!(tensor.shape().dims, vec![2, 3]);

// Verify random values are generated
let first_value = tensor.get(&[0, 0]);
assert!(first_value != 0.0); // Should be random
§Reproducible Generation
use train_station::Tensor;

// Create with fixed seed for reproducible results
let tensor1 = Tensor::randn(vec![100], Some(42));
let tensor2 = Tensor::randn(vec![100], Some(42));

// tensor1 and tensor2 will have identical values
for i in 0..tensor1.size() {
    assert!((tensor1.get(&[i]) - tensor2.get(&[i])).abs() < 1e-6);
}
§Statistical Properties
use train_station::Tensor;

// Generate large tensor for statistical analysis
let tensor = Tensor::randn(vec![1000], Some(42));
assert_eq!(tensor.size(), 1000);

// Check that values are reasonable (within 4 standard deviations)
let mut min_val = f32::INFINITY;
let mut max_val = f32::NEG_INFINITY;
let mut sum = 0.0;

for i in 0..tensor.size() {
    let val = tensor.get(&[i]);
    min_val = min_val.min(val);
    max_val = max_val.max(val);
    sum += val;
}

let mean = sum / tensor.size() as f32;

// Mean should be close to 0, values should be within reasonable bounds
assert!(mean.abs() < 0.1, "Mean should be close to 0, got {}", mean);
assert!(min_val > -4.0, "Values should not be too negative, min: {}", min_val);
assert!(max_val < 4.0, "Values should not be too positive, max: {}", max_val);
§Zero-Sized Tensors
use train_station::Tensor;

// Handle empty tensors gracefully
let tensor = Tensor::randn(vec![0], Some(42));
assert_eq!(tensor.size(), 0);
assert_eq!(tensor.shape().dims, vec![0]);
§Implementation Details

This method uses the Box-Muller transform to generate normally distributed random variables from uniform random variables. The process involves:

  1. Random Number Generation: Uses Xorshift algorithm for uniform random numbers
  2. Box-Muller Transform: Converts uniform random variables to normal distribution
  3. SIMD Optimization: Vectorized operations for large tensors when available
  4. Numerical Stability: Robust handling of edge cases and potential NaN values

The Box-Muller transform ensures that the generated values follow a true normal distribution with mean=0 and standard deviation=1, making it suitable for machine learning applications requiring normally distributed random values.

Examples found in repository?
examples/neural_networks/feedforward_network.rs (line 71)
68    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
69        let scale = (1.0 / input_size as f32).sqrt();
70
71        let weight = Tensor::randn(vec![input_size, output_size], seed)
72            .mul_scalar(scale)
73            .with_requires_grad();
74        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
75
76        Self {
77            weight,
78            bias,
79            input_size,
80            output_size,
81        }
82    }
83
84    pub fn forward(&self, input: &Tensor) -> Tensor {
85        let output = input.matmul(&self.weight);
86        output.add_tensor(&self.bias)
87    }
88
89    pub fn forward_no_grad(&self, input: &Tensor) -> Tensor {
90        let _guard = NoGradTrack::new();
91        self.forward(input)
92    }
93
94    pub fn parameters(&mut self) -> Vec<&mut Tensor> {
95        vec![&mut self.weight, &mut self.bias]
96    }
97}
98
99/// Configuration for feed-forward network
100#[derive(Debug, Clone)]
101pub struct FeedForwardConfig {
102    pub input_size: usize,
103    pub hidden_sizes: Vec<usize>,
104    pub output_size: usize,
105    pub use_bias: bool,
106}
107
108impl Default for FeedForwardConfig {
109    fn default() -> Self {
110        Self {
111            input_size: 4,
112            hidden_sizes: vec![8, 4],
113            output_size: 2,
114            use_bias: true,
115        }
116    }
117}
118
119/// A configurable feed-forward neural network
120pub struct FeedForwardNetwork {
121    layers: Vec<LinearLayer>,
122    config: FeedForwardConfig,
123}
124
125impl FeedForwardNetwork {
126    /// Create a new feed-forward network with the given configuration
127    pub fn new(config: FeedForwardConfig, seed: Option<u64>) -> Self {
128        let mut layers = Vec::new();
129        let mut current_size = config.input_size;
130        let mut current_seed = seed;
131
132        // Create hidden layers
133        for &hidden_size in &config.hidden_sizes {
134            layers.push(LinearLayer::new(current_size, hidden_size, current_seed));
135            current_size = hidden_size;
136            current_seed = current_seed.map(|s| s + 1);
137        }
138
139        // Create output layer
140        layers.push(LinearLayer::new(
141            current_size,
142            config.output_size,
143            current_seed,
144        ));
145
146        Self { layers, config }
147    }
148
149    /// Forward pass through the entire network
150    pub fn forward(&self, input: &Tensor) -> Tensor {
151        let mut x = input.clone();
152
153        // Pass through all layers except the last one with ReLU activation
154        for layer in &self.layers[..self.layers.len() - 1] {
155            x = layer.forward(&x);
156            x = ReLU::forward(&x);
157        }
158
159        // Final layer without activation (raw logits)
160        if let Some(final_layer) = self.layers.last() {
161            x = final_layer.forward(&x);
162        }
163
164        x
165    }
166
167    /// Forward pass without gradients (for inference)
168    pub fn forward_no_grad(&self, input: &Tensor) -> Tensor {
169        let _guard = NoGradTrack::new();
170        self.forward(input)
171    }
172
173    /// Get all parameters for optimization
174    pub fn parameters(&mut self) -> Vec<&mut Tensor> {
175        let mut params = Vec::new();
176        for layer in &mut self.layers {
177            params.extend(layer.parameters());
178        }
179        params
180    }
181
182    /// Get the number of layers
183    pub fn num_layers(&self) -> usize {
184        self.layers.len()
185    }
186
187    /// Get the total number of parameters
188    pub fn parameter_count(&self) -> usize {
189        let mut count = 0;
190        let mut current_size = self.config.input_size;
191
192        for &hidden_size in &self.config.hidden_sizes {
193            count += current_size * hidden_size + hidden_size; // weights + bias
194            current_size = hidden_size;
195        }
196
197        // Output layer
198        count += current_size * self.config.output_size + self.config.output_size;
199
200        count
201    }
202
203    /// Save network parameters to JSON
204    pub fn save_json(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
205        if let Some(parent) = std::path::Path::new(path).parent() {
206            fs::create_dir_all(parent)?;
207        }
208
209        for (i, layer) in self.layers.iter().enumerate() {
210            let layer_path = format!("{}_layer_{}", path, i);
211            let weight_path = format!("{}_weight.json", layer_path);
212            let bias_path = format!("{}_bias.json", layer_path);
213
214            layer.weight.save_json(&weight_path)?;
215            layer.bias.save_json(&bias_path)?;
216        }
217
218        println!(
219            "Saved feed-forward network to {} ({} layers)",
220            path,
221            self.layers.len()
222        );
223        Ok(())
224    }
225
226    /// Load network parameters from JSON
227    pub fn load_json(
228        path: &str,
229        config: FeedForwardConfig,
230    ) -> Result<Self, Box<dyn std::error::Error>> {
231        let mut layers = Vec::new();
232        let mut current_size = config.input_size;
233        let mut layer_idx = 0;
234
235        // Load hidden layers
236        for &hidden_size in &config.hidden_sizes {
237            let layer_path = format!("{}_layer_{}", path, layer_idx);
238            let weight_path = format!("{}_weight.json", layer_path);
239            let bias_path = format!("{}_bias.json", layer_path);
240
241            let weight = Tensor::load_json(&weight_path)?.with_requires_grad();
242            let bias = Tensor::load_json(&bias_path)?.with_requires_grad();
243
244            layers.push(LinearLayer {
245                weight,
246                bias,
247                input_size: current_size,
248                output_size: hidden_size,
249            });
250
251            current_size = hidden_size;
252            layer_idx += 1;
253        }
254
255        // Load output layer
256        let layer_path = format!("{}_layer_{}", path, layer_idx);
257        let weight_path = format!("{}_weight.json", layer_path);
258        let bias_path = format!("{}_bias.json", layer_path);
259
260        let weight = Tensor::load_json(&weight_path)?.with_requires_grad();
261        let bias = Tensor::load_json(&bias_path)?.with_requires_grad();
262
263        layers.push(LinearLayer {
264            weight,
265            bias,
266            input_size: current_size,
267            output_size: config.output_size,
268        });
269
270        Ok(Self { layers, config })
271    }
272}
273
274fn main() -> Result<(), Box<dyn std::error::Error>> {
275    println!("=== Feed-Forward Network Example ===\n");
276
277    demonstrate_network_creation();
278    demonstrate_forward_pass();
279    demonstrate_configurable_architectures();
280    demonstrate_training_workflow()?;
281    demonstrate_comprehensive_training()?;
282    demonstrate_network_serialization()?;
283    cleanup_temp_files()?;
284
285    println!("\n=== Example completed successfully! ===");
286    Ok(())
287}
288
289/// Demonstrate creating different network configurations
290fn demonstrate_network_creation() {
291    println!("--- Network Creation ---");
292
293    // Default configuration
294    let config = FeedForwardConfig::default();
295    let network = FeedForwardNetwork::new(config.clone(), Some(42));
296
297    println!("Default network configuration:");
298    println!("  Input size: {}", config.input_size);
299    println!("  Hidden sizes: {:?}", config.hidden_sizes);
300    println!("  Output size: {}", config.output_size);
301    println!("  Number of layers: {}", network.num_layers());
302    println!("  Total parameters: {}", network.parameter_count());
303
304    // Custom configurations
305    let configs = [
306        FeedForwardConfig {
307            input_size: 2,
308            hidden_sizes: vec![4],
309            output_size: 1,
310            use_bias: true,
311        },
312        FeedForwardConfig {
313            input_size: 8,
314            hidden_sizes: vec![16, 8, 4],
315            output_size: 3,
316            use_bias: true,
317        },
318        FeedForwardConfig {
319            input_size: 10,
320            hidden_sizes: vec![20, 15, 10, 5],
321            output_size: 2,
322            use_bias: true,
323        },
324    ];
325
326    for (i, config) in configs.iter().enumerate() {
327        let network = FeedForwardNetwork::new(config.clone(), Some(42 + i as u64));
328        println!("\nCustom network {}:", i + 1);
329        println!(
330            "  Architecture: {} -> {:?} -> {}",
331            config.input_size, config.hidden_sizes, config.output_size
332        );
333        println!("  Layers: {}", network.num_layers());
334        println!("  Parameters: {}", network.parameter_count());
335    }
336}
337
338/// Demonstrate forward pass through the network
339fn demonstrate_forward_pass() {
340    println!("\n--- Forward Pass ---");
341
342    let config = FeedForwardConfig {
343        input_size: 3,
344        hidden_sizes: vec![5, 3],
345        output_size: 2,
346        use_bias: true,
347    };
348    let network = FeedForwardNetwork::new(config, Some(43));
349
350    // Single input
351    let input = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
352    let output = network.forward(&input);
353
354    println!("Single input forward pass:");
355    println!("  Input shape: {:?}", input.shape().dims);
356    println!("  Output shape: {:?}", output.shape().dims);
357    println!("  Output: {:?}", output.data());
358    println!("  Output requires grad: {}", output.requires_grad());
359
360    // Batch input
361    let batch_input = Tensor::from_slice(
362        &[
363            1.0, 2.0, 3.0, // Sample 1
364            4.0, 5.0, 6.0, // Sample 2
365            7.0, 8.0, 9.0, // Sample 3
366        ],
367        vec![3, 3],
368    )
369    .unwrap();
370    let batch_output = network.forward(&batch_input);
371
372    println!("Batch input forward pass:");
373    println!("  Input shape: {:?}", batch_input.shape().dims);
374    println!("  Output shape: {:?}", batch_output.shape().dims);
375    println!("  Output requires grad: {}", batch_output.requires_grad());
376
377    // Compare with no-grad version
378    let output_no_grad = network.forward_no_grad(&input);
379    println!("No-grad comparison:");
380    println!("  Same values: {}", output.data() == output_no_grad.data());
381    println!("  With grad requires grad: {}", output.requires_grad());
382    println!(
383        "  No grad requires grad: {}",
384        output_no_grad.requires_grad()
385    );
386}
387
388/// Demonstrate different configurable architectures
389fn demonstrate_configurable_architectures() {
390    println!("\n--- Configurable Architectures ---");
391
392    let architectures = vec![
393        ("Shallow", vec![8]),
394        ("Medium", vec![16, 8]),
395        ("Deep", vec![32, 16, 8, 4]),
396        ("Wide", vec![64, 32]),
397        ("Bottleneck", vec![16, 4, 16]),
398    ];
399
400    for (name, hidden_sizes) in architectures {
401        let config = FeedForwardConfig {
402            input_size: 10,
403            hidden_sizes,
404            output_size: 3,
405            use_bias: true,
406        };
407
408        let network = FeedForwardNetwork::new(config.clone(), Some(44));
409
410        // Test forward pass
411        let test_input = Tensor::randn(vec![5, 10], Some(45)); // Batch of 5
412        let output = network.forward_no_grad(&test_input);
413
414        println!("{} network:", name);
415        println!("  Architecture: 10 -> {:?} -> 3", config.hidden_sizes);
416        println!("  Parameters: {}", network.parameter_count());
417        println!("  Test output shape: {:?}", output.shape().dims);
418        println!(
419            "  Output range: [{:.3}, {:.3}]",
420            output.data().iter().fold(f32::INFINITY, |a, &b| a.min(b)),
421            output
422                .data()
423                .iter()
424                .fold(f32::NEG_INFINITY, |a, &b| a.max(b))
425        );
426    }
427}
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 56)
52    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
53        // Xavier/Glorot initialization: scale by sqrt(1/input_size)
54        let scale = (1.0 / input_size as f32).sqrt();
55
56        let weight = Tensor::randn(vec![input_size, output_size], seed)
57            .mul_scalar(scale)
58            .with_requires_grad();
59        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
60
61        Self {
62            weight,
63            bias,
64            input_size,
65            output_size,
66        }
67    }
examples/getting_started/tensor_basics.rs (line 80)
42fn demonstrate_tensor_creation() {
43    println!("--- Tensor Creation ---");
44
45    // Create tensors with different initializations
46    let zeros = Tensor::zeros(vec![2, 3]);
47    println!(
48        "Zeros tensor: shape {:?}, data: {:?}",
49        zeros.shape().dims,
50        zeros.data()
51    );
52
53    let ones = Tensor::ones(vec![3, 2]);
54    println!(
55        "Ones tensor: shape {:?}, data: {:?}",
56        ones.shape().dims,
57        ones.data()
58    );
59
60    // Create tensor from slice
61    let data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
62    let from_slice = Tensor::from_slice(&data, vec![2, 3]).unwrap();
63    println!(
64        "From slice: shape {:?}, data: {:?}",
65        from_slice.shape().dims,
66        from_slice.data()
67    );
68
69    // Create tensor with specific value
70    let mut filled = Tensor::new(vec![2, 2]);
71    {
72        let data = filled.data_mut();
73        for value in data.iter_mut() {
74            *value = 42.0;
75        }
76    }
77    println!("Filled with 42: {:?}", filled.data());
78
79    // Create tensor with random data
80    let random = Tensor::randn(vec![2, 2], Some(42));
81    println!(
82        "Random tensor: shape {:?}, data: {:?}",
83        random.shape().dims,
84        random.data()
85    );
86}
examples/getting_started/optimizer_basics.rs (line 51)
47fn demonstrate_basic_optimizer_setup() {
48    println!("--- Basic Optimizer Setup ---");
49
50    // Create parameters that require gradients
51    let weight = Tensor::randn(vec![3, 2], Some(42)).with_requires_grad();
52    let bias = Tensor::zeros(vec![2]).with_requires_grad();
53
54    println!("Created parameters:");
55    println!(
56        "  Weight: shape {:?}, requires_grad: {}",
57        weight.shape().dims,
58        weight.requires_grad()
59    );
60    println!(
61        "  Bias: shape {:?}, requires_grad: {}",
62        bias.shape().dims,
63        bias.requires_grad()
64    );
65
66    // Create Adam optimizer with default configuration
67    let mut optimizer = Adam::new();
68    println!(
69        "Created Adam optimizer with learning rate: {}",
70        optimizer.learning_rate()
71    );
72
73    // Add parameters to optimizer
74    optimizer.add_parameter(&weight);
75    optimizer.add_parameter(&bias);
76    println!(
77        "Added {} parameters to optimizer",
78        optimizer.parameter_count()
79    );
80
81    // Create optimizer with custom configuration
82    let config = AdamConfig {
83        learning_rate: 0.01,
84        beta1: 0.9,
85        beta2: 0.999,
86        eps: 1e-8,
87        weight_decay: 0.0,
88        amsgrad: false,
89    };
90
91    let mut custom_optimizer = Adam::with_config(config);
92    custom_optimizer.add_parameter(&weight);
93    custom_optimizer.add_parameter(&bias);
94
95    println!(
96        "Created custom optimizer with learning rate: {}",
97        custom_optimizer.learning_rate()
98    );
99
100    // Demonstrate parameter linking
101    println!("Parameter linking completed successfully");
102}
103
104/// Demonstrate simple linear regression training
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
examples/getting_started/serialization_basics.rs (line 113)
109fn demonstrate_optimizer_serialization() -> Result<(), Box<dyn std::error::Error>> {
110    println!("\n--- Optimizer Serialization ---");
111
112    // Create an optimizer with some parameters
113    let mut weight = Tensor::randn(vec![2, 2], Some(42)).with_requires_grad();
114    let mut bias = Tensor::randn(vec![2], Some(43)).with_requires_grad();
115
116    let config = AdamConfig {
117        learning_rate: 0.001,
118        beta1: 0.9,
119        beta2: 0.999,
120        eps: 1e-8,
121        weight_decay: 0.0,
122        amsgrad: false,
123    };
124
125    let mut optimizer = Adam::with_config(config);
126    optimizer.add_parameter(&weight);
127    optimizer.add_parameter(&bias);
128
129    println!(
130        "Created optimizer with {} parameters",
131        optimizer.parameter_count()
132    );
133    println!("Learning rate: {}", optimizer.learning_rate());
134
135    // Simulate some training steps
136    for _ in 0..3 {
137        let mut loss = weight.sum() + bias.sum();
138        loss.backward(None);
139        optimizer.step(&mut [&mut weight, &mut bias]);
140        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
141    }
142
143    // Save optimizer state
144    let optimizer_path = "temp_optimizer.json";
145    optimizer.save_json(optimizer_path)?;
146    println!("Saved optimizer to: {}", optimizer_path);
147
148    // Load optimizer state
149    let loaded_optimizer = Adam::load_json(optimizer_path)?;
150    println!(
151        "Loaded optimizer with {} parameters",
152        loaded_optimizer.parameter_count()
153    );
154    println!("Learning rate: {}", loaded_optimizer.learning_rate());
155
156    // Verify optimizer state
157    assert_eq!(
158        optimizer.parameter_count(),
159        loaded_optimizer.parameter_count()
160    );
161    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
162    println!("Optimizer serialization verification: PASSED");
163
164    Ok(())
165}
166
167/// Demonstrate format comparison and performance characteristics
168fn demonstrate_format_comparison() -> Result<(), Box<dyn std::error::Error>> {
169    println!("\n--- Format Comparison ---");
170
171    // Create a larger tensor for comparison
172    let tensor = Tensor::randn(vec![10, 10], Some(44));
173
174    // Save in both formats
175    tensor.save_json("temp_comparison.json")?;
176    tensor.save_binary("temp_comparison.bin")?;
177
178    // Compare file sizes
179    let json_size = fs::metadata("temp_comparison.json")?.len();
180    let binary_size = fs::metadata("temp_comparison.bin")?.len();
181
182    println!("JSON file size: {} bytes", json_size);
183    println!("Binary file size: {} bytes", binary_size);
184    println!(
185        "Compression ratio: {:.2}x",
186        json_size as f64 / binary_size as f64
187    );
188
189    // Load and verify both formats
190    let json_tensor = Tensor::load_json("temp_comparison.json")?;
191    let binary_tensor = Tensor::load_binary("temp_comparison.bin")?;
192
193    assert_eq!(tensor.shape().dims, json_tensor.shape().dims);
194    assert_eq!(tensor.shape().dims, binary_tensor.shape().dims);
195    assert_eq!(tensor.data(), json_tensor.data());
196    assert_eq!(tensor.data(), binary_tensor.data());
197
198    println!("Format comparison verification: PASSED");
199
200    Ok(())
201}
202
203/// Demonstrate a basic model checkpointing workflow
204fn demonstrate_model_checkpointing() -> Result<(), Box<dyn std::error::Error>> {
205    println!("\n--- Model Checkpointing ---");
206
207    // Create a simple model (weights and bias)
208    let mut weights = Tensor::randn(vec![2, 1], Some(45)).with_requires_grad();
209    let mut bias = Tensor::randn(vec![1], Some(46)).with_requires_grad();
210
211    // Create optimizer
212    let mut optimizer = Adam::with_learning_rate(0.01);
213    optimizer.add_parameter(&weights);
214    optimizer.add_parameter(&bias);
215
216    println!("Initial weights: {:?}", weights.data());
217    println!("Initial bias: {:?}", bias.data());
218
219    // Simulate training
220    for epoch in 0..5 {
221        let mut loss = weights.sum() + bias.sum();
222        loss.backward(None);
223        optimizer.step(&mut [&mut weights, &mut bias]);
224        optimizer.zero_grad(&mut [&mut weights, &mut bias]);
225
226        if epoch % 2 == 0 {
227            // Save checkpoint
228            let checkpoint_dir = format!("checkpoint_epoch_{}", epoch);
229            fs::create_dir_all(&checkpoint_dir)?;
230
231            weights.save_json(format!("{}/weights.json", checkpoint_dir))?;
232            bias.save_json(format!("{}/bias.json", checkpoint_dir))?;
233            optimizer.save_json(format!("{}/optimizer.json", checkpoint_dir))?;
234
235            println!("Saved checkpoint for epoch {}", epoch);
236        }
237    }
238
239    // Load from checkpoint
240    let loaded_weights = Tensor::load_json("checkpoint_epoch_4/weights.json")?;
241    let loaded_bias = Tensor::load_json("checkpoint_epoch_4/bias.json")?;
242    let loaded_optimizer = Adam::load_json("checkpoint_epoch_4/optimizer.json")?;
243
244    println!("Loaded weights: {:?}", loaded_weights.data());
245    println!("Loaded bias: {:?}", loaded_bias.data());
246    println!(
247        "Loaded optimizer learning rate: {}",
248        loaded_optimizer.learning_rate()
249    );
250
251    // Verify checkpoint integrity
252    assert_eq!(weights.shape().dims, loaded_weights.shape().dims);
253    assert_eq!(bias.shape().dims, loaded_bias.shape().dims);
254    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
255
256    println!("Checkpointing verification: PASSED");
257
258    Ok(())
259}
260
261/// Demonstrate error handling for serialization operations
262fn demonstrate_error_handling() -> Result<(), Box<dyn std::error::Error>> {
263    println!("\n--- Error Handling ---");
264
265    // Test loading non-existent file
266    match Tensor::load_json("nonexistent_file.json") {
267        Ok(_) => println!("Unexpected: Successfully loaded non-existent file"),
268        Err(e) => println!("Expected error loading non-existent file: {}", e),
269    }
270
271    // Test loading with wrong format
272    let tensor = Tensor::randn(vec![2, 2], Some(47));
273    tensor.save_binary("temp_binary.bin")?;
274
275    match Tensor::load_json("temp_binary.bin") {
276        Ok(_) => println!("Unexpected: Successfully loaded binary as JSON"),
277        Err(e) => println!("Expected error loading binary as JSON: {}", e),
278    }
279
280    // Test loading corrupted file
281    fs::write("temp_invalid.json", "invalid json content")?;
282    match Tensor::load_json("temp_invalid.json") {
283        Ok(_) => println!("Unexpected: Successfully loaded invalid JSON"),
284        Err(e) => println!("Expected error loading invalid JSON: {}", e),
285    }
286
287    println!("Error handling verification: PASSED");
288
289    Ok(())
290}
examples/optimizers/adam_configurations.rs (line 92)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
Source

pub fn fill_randn(&mut self, seed: Option<u64>)

Fills the tensor with normally distributed random values

Internal method that fills an existing tensor with random values from a standard normal distribution. Uses Box-Muller transform for efficiency and provides SIMD optimization for large tensors.

This method is used internally by randn() and provides the core random number generation functionality with optimized performance characteristics.

§Arguments
  • seed - Optional seed for reproducible random generation
§Performance
  • Box-Muller Transform: Generates pairs of normal random variables
  • SIMD Optimization: Vectorized operations when possible
  • Memory Efficient: Single-pass generation
  • Unrolled Loops: 4x unrolling for better instruction throughput
§Implementation Details

The method performs the following steps:

  1. Zero-sized Check: Returns early for empty tensors
  2. RNG Initialization: Creates Xorshift RNG with seed or system time
  3. SIMD Detection: Checks for AVX2 availability for optimized path
  4. Generation: Uses SIMD or scalar path based on hardware support
  5. Completion: Fills all tensor elements with normal random values

The method automatically handles hardware capabilities and falls back to scalar operations when SIMD is not available, ensuring compatibility across different CPU architectures.

Source§

impl Tensor

Source

pub fn iter(&self) -> TensorElementIterator<'_>

Create an iterator over tensor elements as view tensors

Each element becomes a Tensor of shape [1] that supports all tensor operations and gradient tracking. This is the main entry point for element-wise iteration with full tensor operation support.

The iterator provides zero-copy access to tensor elements through view tensors, enabling efficient element-wise operations while maintaining full compatibility with Rust’s standard library iterator methods.

§Returns

An iterator that yields view tensors for each element

§Performance
  • Zero-Copy Views: Each element is a view sharing memory with source
  • O(1) Element Access: Constant-time view creation for each element
  • Memory Efficient: ~64 bytes overhead per element view
  • SIMD Compatible: All tensor operations use existing optimizations
  • Gradient Tracking: Full gradtrack support through element operations
§Examples
§Basic Element Operations
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();

// Use any std iterator method
let result: Tensor = tensor.iter()
    .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0)) // 2x + 1
    .filter(|elem| elem.value() > 3.0)                // Keep values > 3
    .collect();

assert_eq!(result.data(), &[5.0, 7.0]);
§Advanced Iterator Chains
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5]).unwrap();

// Chain with enumerate, zip, etc.
let indexed: Tensor = tensor.iter()
    .enumerate()
    .map(|(i, elem)| elem.add_scalar(i as f32))
    .collect();

assert_eq!(indexed.data(), &[1.0, 3.0, 5.0, 7.0, 9.0]);
§Double-Ended Iteration
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();

// Use double-ended iterator
let reversed: Tensor = tensor.iter()
    .rev()
    .collect();

assert_eq!(reversed.data(), &[4.0, 3.0, 2.0, 1.0]);
§Gradient Tracking
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0], vec![2])
    .unwrap()
    .with_requires_grad();

let result: Tensor = tensor.iter()
    .map(|elem| elem.mul_scalar(2.0))
    .collect();

assert!(result.requires_grad());
assert_eq!(result.data(), &[2.0, 4.0]);
Examples found in repository?
examples/iterators/element_iteration.rs (line 86)
77fn demonstrate_basic_iteration() -> Result<(), Box<dyn std::error::Error>> {
78    println!("\n--- Basic Element Iteration ---");
79
80    // Create a simple tensor for demonstration
81    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
82    println!("Original tensor: {:?}", tensor.data());
83
84    // Basic iteration with for loop
85    println!("\nBasic iteration with for loop:");
86    for (i, element) in tensor.iter().enumerate() {
87        println!(
88            "  Element {}: value = {:.1}, shape = {:?}",
89            i,
90            element.value(),
91            element.shape().dims
92        );
93    }
94
95    // Element-wise transformation
96    println!("\nElement-wise transformation (2x + 1):");
97    let transformed: Tensor = tensor
98        .iter()
99        .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
100        .collect();
101    println!("  Result: {:?}", transformed.data());
102
103    // Filtering elements
104    println!("\nFiltering elements (values > 3.0):");
105    let filtered: Tensor = tensor.iter().filter(|elem| elem.value() > 3.0).collect();
106    println!("  Filtered: {:?}", filtered.data());
107
108    Ok(())
109}
110
111/// Demonstrate standard iterator trait methods
112///
113/// Shows compatibility with Rust's standard library iterator methods
114/// and demonstrates various functional programming patterns.
115fn demonstrate_standard_methods() -> Result<(), Box<dyn std::error::Error>> {
116    println!("\n--- Standard Iterator Methods ---");
117
118    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
119
120    // Using map for transformations
121    println!("\nMap transformation (square each element):");
122    let squared: Tensor = tensor.iter().map(|elem| elem.pow_scalar(2.0)).collect();
123    println!("  Squared: {:?}", squared.data());
124
125    // Using enumerate for indexed operations
126    println!("\nEnumerate with indexed operations:");
127    let indexed: Tensor = tensor
128        .iter()
129        .enumerate()
130        .map(|(i, elem)| elem.add_scalar(i as f32))
131        .collect();
132    println!("  Indexed: {:?}", indexed.data());
133
134    // Using fold for reduction
135    println!("\nFold for sum calculation:");
136    let sum: f32 = tensor.iter().fold(0.0, |acc, elem| acc + elem.value());
137    println!("  Sum: {:.1}", sum);
138
139    // Using find for element search
140    println!("\nFind specific element:");
141    if let Some(found) = tensor.iter().find(|elem| elem.value() == 3.0) {
142        println!("  Found element with value 3.0: {:.1}", found.value());
143    }
144
145    // Using any/all for condition checking
146    println!("\nCondition checking:");
147    let all_positive = tensor.iter().all(|elem| elem.value() > 0.0);
148    let any_large = tensor.iter().any(|elem| elem.value() > 4.0);
149    println!("  All positive: {}", all_positive);
150    println!("  Any > 4.0: {}", any_large);
151
152    Ok(())
153}
154
155/// Demonstrate gradient tracking through element operations
156///
157/// Shows how gradient tracking works seamlessly through iterator
158/// operations, maintaining the computational graph for backpropagation.
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
187
188/// Demonstrate advanced iterator patterns
189///
190/// Shows complex iterator chains and advanced functional programming
191/// patterns for sophisticated data processing workflows.
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
More examples
Hide additional examples
examples/iterators/performance_optimization.rs (line 96)
75fn demonstrate_performance_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
76    println!("\n--- Performance Benchmarking ---");
77
78    // Create test data of different sizes
79    let sizes = vec![100, 1000, 10000];
80
81    for size in sizes {
82        println!("\nBenchmarking with tensor size: {}", size);
83
84        // Generate test data
85        let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
86        let tensor = Tensor::from_slice(&data, vec![size])?;
87
88        // Benchmark 1: Direct tensor operations
89        let start = Instant::now();
90        let direct_result = tensor.mul_scalar(2.0).add_scalar(1.0);
91        let direct_time = start.elapsed();
92
93        // Benchmark 2: Iterator-based operations
94        let start = Instant::now();
95        let iterator_result: Tensor = tensor
96            .iter()
97            .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
98            .collect();
99        let iterator_time = start.elapsed();
100
101        // Benchmark 3: Chained iterator operations
102        let start = Instant::now();
103        let _chained_result: Tensor = tensor
104            .iter()
105            .map(|elem| elem.mul_scalar(2.0))
106            .filter(|elem| elem.value() > size as f32)
107            .map(|elem| elem.add_scalar(1.0))
108            .collect();
109        let chained_time = start.elapsed();
110
111        // Report results
112        println!("  Direct operations: {:?}", direct_time);
113        println!("  Iterator operations: {:?}", iterator_time);
114        println!("  Chained operations: {:?}", chained_time);
115
116        // Verify correctness
117        assert_eq!(direct_result.data(), iterator_result.data());
118        println!(
119            "  Results match: {}",
120            direct_result.data() == iterator_result.data()
121        );
122
123        // Performance ratio
124        let ratio = iterator_time.as_nanos() as f64 / direct_time.as_nanos() as f64;
125        println!("  Iterator/Direct ratio: {:.2}x", ratio);
126    }
127
128    Ok(())
129}
130
131/// Demonstrate memory optimization patterns
132///
133/// Shows memory-efficient processing patterns and techniques
134/// for minimizing memory usage while maintaining performance.
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
292
293/// Demonstrate advanced optimization techniques
294///
295/// Shows sophisticated optimization strategies and techniques
296/// for maximizing performance in tensor iterator operations.
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
examples/iterators/advanced_patterns.rs (line 100)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
156
157/// Demonstrate conditional processing patterns
158///
159/// Shows how to implement dynamic filtering and transformation
160/// based on data characteristics and conditions.
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source

pub fn iter_range(&self, start: usize, end: usize) -> TensorElementIterator<'_>

Create an iterator over a range of elements

Creates an iterator that yields view tensors for elements in the specified range. The range is automatically clamped to valid tensor bounds for safety.

§Arguments
  • start - Starting index (inclusive)
  • end - Ending index (exclusive)
§Returns

An iterator that yields view tensors for elements in the specified range

§Safety

The range is automatically clamped to valid tensor bounds:

  • start is clamped to [0, tensor.size()]
  • end is clamped to [start, tensor.size()]
  • Empty ranges (start >= end) are handled gracefully
§Performance
  • O(1) Creation: Constant-time iterator initialization
  • Bounds Checking: Automatic range validation and clamping
  • Zero-Copy Views: Each element is a view sharing memory with source
  • Memory Efficient: Minimal overhead for range iteration
§Examples
§Basic Range Iteration
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5]).unwrap();
let middle: Tensor = tensor.iter_range(1, 4)
    .map(|elem| elem.mul_scalar(2.0))
    .collect();

assert_eq!(middle.data(), &[4.0, 6.0, 8.0]);
§Range with Operations
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5]).unwrap();

// Apply complex operations to range
let result: Tensor = tensor.iter_range(0, 3)
    .enumerate()
    .map(|(i, elem)| elem.add_scalar(i as f32))
    .collect();

assert_eq!(result.data(), &[1.0, 3.0, 5.0]);
§Out of Bounds Handling
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();

// Out of bounds range is clamped
let empty: Tensor = tensor.iter_range(5, 10).collect();
assert_eq!(empty.size(), 0);

// Partial out of bounds
let partial: Tensor = tensor.iter_range(1, 10).collect();
assert_eq!(partial.data(), &[2.0, 3.0]);
Examples found in repository?
examples/iterators/performance_optimization.rs (line 154)
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
More examples
Hide additional examples
examples/iterators/advanced_patterns.rs (line 350)
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source§

impl Tensor

Source

pub fn add_tensor(&self, other: &Tensor) -> Tensor

Element-wise addition with another tensor with broadcasting support.

Performs element-wise addition with automatic broadcasting: output[i] = self[i] + other[i]

Broadcasting enables addition between tensors of different but compatible shapes. Compatible shapes follow NumPy broadcasting rules:

  • Dimensions are aligned from the rightmost dimension
  • Dimensions are compatible if they are equal, or one of them is 1
  • Missing dimensions are treated as 1
§Arguments
  • other - Tensor to add. Shapes must be broadcast-compatible.
§Returns

A new tensor containing the element-wise sum with broadcast result shape

§Examples
§Same Shape Addition
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = Tensor::from_slice(&[4.0, 5.0, 6.0], vec![3]).unwrap();
let c = a.add_tensor(&b);
assert_eq!(c.shape().dims, vec![3]);
assert_eq!(c.get(&[0]), 5.0);
assert_eq!(c.get(&[1]), 7.0);
assert_eq!(c.get(&[2]), 9.0);
§Broadcasting Addition
use train_station::Tensor;

// Broadcasting: [2, 1] + [1, 3] -> [2, 3]
let a = Tensor::from_slice(&[1.0, 2.0], vec![2, 1]).unwrap();
let b = Tensor::from_slice(&[10.0, 20.0, 30.0], vec![1, 3]).unwrap();
let c = a.add_tensor(&b);
assert_eq!(c.shape().dims, vec![2, 3]);
assert_eq!(c.get(&[0, 0]), 11.0);
assert_eq!(c.get(&[0, 1]), 21.0);
assert_eq!(c.get(&[1, 0]), 12.0);
assert_eq!(c.get(&[1, 1]), 22.0);
§Scalar Broadcasting
use train_station::Tensor;

// Scalar broadcasting: [2, 3] + scalar -> [2, 3]
let a = Tensor::ones(vec![2, 3]);
let b = Tensor::from_slice(&[5.0], vec![1]).unwrap();
let c = a.add_tensor(&b);
assert_eq!(c.shape().dims, vec![2, 3]);
assert_eq!(c.get(&[0, 0]), 6.0);
assert_eq!(c.get(&[1, 2]), 6.0);
§Panics

Panics if tensor shapes are not broadcast-compatible

Examples found in repository?
examples/neural_networks/feedforward_network.rs (line 86)
84    pub fn forward(&self, input: &Tensor) -> Tensor {
85        let output = input.matmul(&self.weight);
86        output.add_tensor(&self.bias)
87    }
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 74)
70    pub fn forward(&self, input: &Tensor) -> Tensor {
71        // Matrix multiplication: [batch_size, input_size] @ [input_size, output_size] = [batch_size, output_size]
72        let output = input.matmul(&self.weight);
73        // Add bias: [batch_size, output_size] + [output_size] = [batch_size, output_size]
74        output.add_tensor(&self.bias)
75    }
examples/getting_started/tensor_basics.rs (line 96)
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
examples/getting_started/tensor_operators.rs (line 211)
203fn demonstrate_method_equivalence() {
204    println!("\n--- Operator vs Method Call Equivalence ---");
205
206    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
207    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
208
209    // Addition: operator vs method
210    let operator_result = &a + &b;
211    let method_result = a.add_tensor(&b);
212
213    println!("A + B (operator): {:?}", operator_result.data());
214    println!("A.add_tensor(B): {:?}", method_result.data());
215    println!(
216        "Results are equal: {}",
217        operator_result.data() == method_result.data()
218    );
219
220    // Multiplication: operator vs method
221    let operator_result = &a * &b;
222    let method_result = a.mul_tensor(&b);
223
224    println!("A * B (operator): {:?}", operator_result.data());
225    println!("A.mul_tensor(B): {:?}", method_result.data());
226    println!(
227        "Results are equal: {}",
228        operator_result.data() == method_result.data()
229    );
230
231    // Scalar addition: operator vs method
232    let operator_result = &a + 5.0;
233    let method_result = a.add_scalar(5.0);
234
235    println!("A + 5.0 (operator): {:?}", operator_result.data());
236    println!("A.add_scalar(5.0): {:?}", method_result.data());
237    println!(
238        "Results are equal: {}",
239        operator_result.data() == method_result.data()
240    );
241}
examples/iterators/advanced_patterns.rs (line 121)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
Source

pub fn add_scalar(&self, scalar: f32) -> Tensor

Broadcast addition with a scalar value.

Adds the scalar to every element: output[i] = self[i] + scalar

§Arguments
  • scalar - Value to add to each element
§Returns

A new tensor with the scalar added to each element

§Examples
§Basic Scalar Addition
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = a.add_scalar(10.0);
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 11.0);
assert_eq!(b.get(&[1]), 12.0);
assert_eq!(b.get(&[2]), 13.0);
§Multi-dimensional Scalar Addition
use train_station::Tensor;

let a = Tensor::ones(vec![2, 3]);
let b = a.add_scalar(5.0);
assert_eq!(b.shape().dims, vec![2, 3]);
assert_eq!(b.get(&[0, 0]), 6.0);
assert_eq!(b.get(&[1, 2]), 6.0);
Examples found in repository?
examples/getting_started/tensor_basics.rs (line 112)
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
More examples
Hide additional examples
examples/iterators/element_iteration.rs (line 99)
77fn demonstrate_basic_iteration() -> Result<(), Box<dyn std::error::Error>> {
78    println!("\n--- Basic Element Iteration ---");
79
80    // Create a simple tensor for demonstration
81    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
82    println!("Original tensor: {:?}", tensor.data());
83
84    // Basic iteration with for loop
85    println!("\nBasic iteration with for loop:");
86    for (i, element) in tensor.iter().enumerate() {
87        println!(
88            "  Element {}: value = {:.1}, shape = {:?}",
89            i,
90            element.value(),
91            element.shape().dims
92        );
93    }
94
95    // Element-wise transformation
96    println!("\nElement-wise transformation (2x + 1):");
97    let transformed: Tensor = tensor
98        .iter()
99        .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
100        .collect();
101    println!("  Result: {:?}", transformed.data());
102
103    // Filtering elements
104    println!("\nFiltering elements (values > 3.0):");
105    let filtered: Tensor = tensor.iter().filter(|elem| elem.value() > 3.0).collect();
106    println!("  Filtered: {:?}", filtered.data());
107
108    Ok(())
109}
110
111/// Demonstrate standard iterator trait methods
112///
113/// Shows compatibility with Rust's standard library iterator methods
114/// and demonstrates various functional programming patterns.
115fn demonstrate_standard_methods() -> Result<(), Box<dyn std::error::Error>> {
116    println!("\n--- Standard Iterator Methods ---");
117
118    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
119
120    // Using map for transformations
121    println!("\nMap transformation (square each element):");
122    let squared: Tensor = tensor.iter().map(|elem| elem.pow_scalar(2.0)).collect();
123    println!("  Squared: {:?}", squared.data());
124
125    // Using enumerate for indexed operations
126    println!("\nEnumerate with indexed operations:");
127    let indexed: Tensor = tensor
128        .iter()
129        .enumerate()
130        .map(|(i, elem)| elem.add_scalar(i as f32))
131        .collect();
132    println!("  Indexed: {:?}", indexed.data());
133
134    // Using fold for reduction
135    println!("\nFold for sum calculation:");
136    let sum: f32 = tensor.iter().fold(0.0, |acc, elem| acc + elem.value());
137    println!("  Sum: {:.1}", sum);
138
139    // Using find for element search
140    println!("\nFind specific element:");
141    if let Some(found) = tensor.iter().find(|elem| elem.value() == 3.0) {
142        println!("  Found element with value 3.0: {:.1}", found.value());
143    }
144
145    // Using any/all for condition checking
146    println!("\nCondition checking:");
147    let all_positive = tensor.iter().all(|elem| elem.value() > 0.0);
148    let any_large = tensor.iter().any(|elem| elem.value() > 4.0);
149    println!("  All positive: {}", all_positive);
150    println!("  Any > 4.0: {}", any_large);
151
152    Ok(())
153}
154
155/// Demonstrate gradient tracking through element operations
156///
157/// Shows how gradient tracking works seamlessly through iterator
158/// operations, maintaining the computational graph for backpropagation.
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
187
188/// Demonstrate advanced iterator patterns
189///
190/// Shows complex iterator chains and advanced functional programming
191/// patterns for sophisticated data processing workflows.
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
examples/getting_started/tensor_operators.rs (line 233)
203fn demonstrate_method_equivalence() {
204    println!("\n--- Operator vs Method Call Equivalence ---");
205
206    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
207    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
208
209    // Addition: operator vs method
210    let operator_result = &a + &b;
211    let method_result = a.add_tensor(&b);
212
213    println!("A + B (operator): {:?}", operator_result.data());
214    println!("A.add_tensor(B): {:?}", method_result.data());
215    println!(
216        "Results are equal: {}",
217        operator_result.data() == method_result.data()
218    );
219
220    // Multiplication: operator vs method
221    let operator_result = &a * &b;
222    let method_result = a.mul_tensor(&b);
223
224    println!("A * B (operator): {:?}", operator_result.data());
225    println!("A.mul_tensor(B): {:?}", method_result.data());
226    println!(
227        "Results are equal: {}",
228        operator_result.data() == method_result.data()
229    );
230
231    // Scalar addition: operator vs method
232    let operator_result = &a + 5.0;
233    let method_result = a.add_scalar(5.0);
234
235    println!("A + 5.0 (operator): {:?}", operator_result.data());
236    println!("A.add_scalar(5.0): {:?}", method_result.data());
237    println!(
238        "Results are equal: {}",
239        operator_result.data() == method_result.data()
240    );
241}
examples/iterators/performance_optimization.rs (line 90)
75fn demonstrate_performance_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
76    println!("\n--- Performance Benchmarking ---");
77
78    // Create test data of different sizes
79    let sizes = vec![100, 1000, 10000];
80
81    for size in sizes {
82        println!("\nBenchmarking with tensor size: {}", size);
83
84        // Generate test data
85        let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
86        let tensor = Tensor::from_slice(&data, vec![size])?;
87
88        // Benchmark 1: Direct tensor operations
89        let start = Instant::now();
90        let direct_result = tensor.mul_scalar(2.0).add_scalar(1.0);
91        let direct_time = start.elapsed();
92
93        // Benchmark 2: Iterator-based operations
94        let start = Instant::now();
95        let iterator_result: Tensor = tensor
96            .iter()
97            .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
98            .collect();
99        let iterator_time = start.elapsed();
100
101        // Benchmark 3: Chained iterator operations
102        let start = Instant::now();
103        let _chained_result: Tensor = tensor
104            .iter()
105            .map(|elem| elem.mul_scalar(2.0))
106            .filter(|elem| elem.value() > size as f32)
107            .map(|elem| elem.add_scalar(1.0))
108            .collect();
109        let chained_time = start.elapsed();
110
111        // Report results
112        println!("  Direct operations: {:?}", direct_time);
113        println!("  Iterator operations: {:?}", iterator_time);
114        println!("  Chained operations: {:?}", chained_time);
115
116        // Verify correctness
117        assert_eq!(direct_result.data(), iterator_result.data());
118        println!(
119            "  Results match: {}",
120            direct_result.data() == iterator_result.data()
121        );
122
123        // Performance ratio
124        let ratio = iterator_time.as_nanos() as f64 / direct_time.as_nanos() as f64;
125        println!("  Iterator/Direct ratio: {:.2}x", ratio);
126    }
127
128    Ok(())
129}
130
131/// Demonstrate memory optimization patterns
132///
133/// Shows memory-efficient processing patterns and techniques
134/// for minimizing memory usage while maintaining performance.
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
292
293/// Demonstrate advanced optimization techniques
294///
295/// Shows sophisticated optimization strategies and techniques
296/// for maximizing performance in tensor iterator operations.
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
examples/iterators/advanced_patterns.rs (line 223)
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source§

impl Tensor

Source

pub fn div_tensor(&self, other: &Tensor) -> Tensor

Element-wise division with another tensor with broadcasting support.

Performs element-wise division with automatic broadcasting: output[i] = self[i] / other[i]

Broadcasting enables division between tensors of different but compatible shapes. Compatible shapes follow NumPy broadcasting rules:

  • Dimensions are aligned from the rightmost dimension
  • Dimensions are compatible if they are equal, or one of them is 1
  • Missing dimensions are treated as 1
§Arguments
  • other - Tensor to divide by. Shapes must be broadcast-compatible.
§Returns

A new tensor containing the element-wise quotient with broadcast result shape

§Examples
§Same Shape Division
use train_station::Tensor;

let a = Tensor::from_slice(&[10.0, 20.0, 30.0], vec![3]).unwrap();
let b = Tensor::from_slice(&[2.0, 4.0, 5.0], vec![3]).unwrap();
let c = a.div_tensor(&b);
assert_eq!(c.shape().dims, vec![3]);
assert_eq!(c.get(&[0]), 5.0);
assert_eq!(c.get(&[1]), 5.0);
assert_eq!(c.get(&[2]), 6.0);
§Broadcasting Division
use train_station::Tensor;

// Broadcasting: [2, 1] / [1, 3] -> [2, 3]
let a = Tensor::from_slice(&[10.0, 20.0], vec![2, 1]).unwrap();
let b = Tensor::from_slice(&[1.0, 2.0, 5.0], vec![1, 3]).unwrap();
let c = a.div_tensor(&b);
assert_eq!(c.shape().dims, vec![2, 3]);
assert_eq!(c.get(&[0, 0]), 10.0);
assert_eq!(c.get(&[0, 1]), 5.0);
assert_eq!(c.get(&[1, 0]), 20.0);
assert_eq!(c.get(&[1, 1]), 10.0);
§Scalar Division
use train_station::Tensor;

// Scalar division: [2, 3] / scalar -> [2, 3]
let a = Tensor::ones(vec![2, 3]);
let b = Tensor::from_slice(&[2.0], vec![1]).unwrap();
let c = a.div_tensor(&b);
assert_eq!(c.shape().dims, vec![2, 3]);
assert_eq!(c.get(&[0, 0]), 0.5);
assert_eq!(c.get(&[1, 2]), 0.5);
§Panics

Panics if tensor shapes are not broadcast-compatible or division by zero

Examples found in repository?
examples/getting_started/tensor_basics.rs (line 108)
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
Source

pub fn div_scalar(&self, scalar: f32) -> Tensor

Broadcast division with a scalar value.

Divides every element by the scalar: output[i] = self[i] / scalar

§Arguments
  • scalar - Value to divide each element by (must not be zero)
§Returns

A new tensor with each element divided by the scalar

§Examples
§Basic Scalar Division
use train_station::Tensor;

let a = Tensor::from_slice(&[10.0, 20.0, 30.0], vec![3]).unwrap();
let b = a.div_scalar(10.0);
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 1.0);
assert_eq!(b.get(&[1]), 2.0);
assert_eq!(b.get(&[2]), 3.0);
§Multi-dimensional Scalar Division
use train_station::Tensor;

let a = Tensor::ones(vec![2, 3]);
let b = a.div_scalar(2.0);
assert_eq!(b.shape().dims, vec![2, 3]);
assert_eq!(b.get(&[0, 0]), 0.5);
assert_eq!(b.get(&[1, 2]), 0.5);
§Panics

Panics if scalar is zero

Examples found in repository?
examples/iterators/advanced_patterns.rs (line 101)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
156
157/// Demonstrate conditional processing patterns
158///
159/// Shows how to implement dynamic filtering and transformation
160/// based on data characteristics and conditions.
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source§

impl Tensor

Source

pub fn exp(&self) -> Tensor

Element-wise exponential function.

Computes e^x for each element: output[i] = e^(self[i])

§Returns

A new tensor with the exponential of each element

§Examples
§Basic Exponential
use train_station::Tensor;

let a = Tensor::from_slice(&[0.0, 1.0, 2.0], vec![3]).unwrap();
let b = a.exp();
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 1.0); // e^0 = 1
assert!((b.get(&[1]) - 2.71828).abs() < 1e-5); // e^1 ≈ 2.71828
assert!((b.get(&[2]) - 7.38906).abs() < 1e-5); // e^2 ≈ 7.38906
§Negative Values
use train_station::Tensor;

let a = Tensor::from_slice(&[-1.0, 0.0, 1.0], vec![3]).unwrap();
let b = a.exp();
assert_eq!(b.shape().dims, vec![3]);
assert!((b.get(&[0]) - 0.36788).abs() < 1e-5); // e^(-1) ≈ 0.36788
assert_eq!(b.get(&[1]), 1.0); // e^0 = 1
assert!((b.get(&[2]) - 2.71828).abs() < 1e-5); // e^1 ≈ 2.71828
Examples found in repository?
examples/iterators/element_iteration.rs (line 224)
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
Source§

impl Tensor

Source

pub fn leaky_relu(&self, negative_slope: f32) -> Tensor

Element-wise Leaky ReLU activation.

Applies Leaky ReLU to each element: output[i] = max(0, x) + negative_slope * min(0, x)

Unlike standard ReLU, allows a small gradient when the unit is not active.

§Arguments
  • negative_slope - Slope for negative values (typically small, e.g., 0.01 or 0.1)
§Returns

A new tensor with Leaky ReLU applied to each element

§Examples
§Basic Leaky ReLU
use train_station::Tensor;

let a = Tensor::from_slice(&[-2.0, -1.0, 0.0, 1.0], vec![4]).unwrap();
let b = a.leaky_relu(0.1);
assert_eq!(b.shape().dims, vec![4]);
assert!((b.get(&[0]) - (-0.2)).abs() < 1e-6); // -2.0 * 0.1 = -0.2
assert!((b.get(&[1]) - (-0.1)).abs() < 1e-6); // -1.0 * 0.1 = -0.1
assert_eq!(b.get(&[2]), 0.0); // max(0, 0) = 0
assert_eq!(b.get(&[3]), 1.0); // max(0, 1) = 1
§Different Negative Slopes
use train_station::Tensor;

let a = Tensor::from_slice(&[-1.0, 0.0, 1.0], vec![3]).unwrap();
let b = a.leaky_relu(0.01); // Smaller negative slope
assert_eq!(b.shape().dims, vec![3]);
assert!((b.get(&[0]) - (-0.01)).abs() < 1e-6); // -1.0 * 0.01 = -0.01
assert_eq!(b.get(&[1]), 0.0); // max(0, 0) = 0
assert_eq!(b.get(&[2]), 1.0); // max(0, 1) = 1
Source§

impl Tensor

Source

pub fn log(&self) -> Tensor

Element-wise natural logarithm.

Computes the natural logarithm for each element: output[i] = ln(self[i])

§Returns

A new tensor with the natural logarithm of each element

§Examples
§Basic Natural Logarithm
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.71828, 7.38906], vec![3]).unwrap();
let b = a.log();
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 0.0); // ln(1) = 0
assert!((b.get(&[1]) - 1.0).abs() < 1e-5); // ln(e) ≈ 1
assert!((b.get(&[2]) - 2.0).abs() < 1e-5); // ln(e^2) ≈ 2
§Mathematical Properties
use train_station::Tensor;

let a = Tensor::from_slice(&[4.0, 8.0, 16.0], vec![3]).unwrap();
let b = a.log();
assert_eq!(b.shape().dims, vec![3]);
assert!((b.get(&[0]) - 1.38629).abs() < 1e-5); // ln(4) ≈ 1.38629
assert!((b.get(&[1]) - 2.07944).abs() < 1e-5); // ln(8) ≈ 2.07944
assert!((b.get(&[2]) - 2.77259).abs() < 1e-5); // ln(16) ≈ 2.77259
§Panics

Panics if any element is non-positive (x <= 0)

Examples found in repository?
examples/iterators/element_iteration.rs (line 226)
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
Source§

impl Tensor

Source

pub fn matmul(&self, other: &Tensor) -> Tensor

Matrix multiplication operation following NumPy semantics

Performs matrix multiplication between this tensor and another tensor with intelligent kernel selection based on matrix dimensions and hardware capabilities. The operation follows broadcasting rules and supports all common matrix multiplication patterns found in machine learning workloads.

§Supported Operations
  • 1D @ 1D: Dot product returning scalar tensor
  • 1D @ 2D: Vector-matrix multiplication (v^T * M) returning 1D tensor
  • 2D @ 1D: Matrix-vector multiplication (M * v) returning 1D tensor
  • 2D @ 2D: Standard matrix multiplication with cache-optimized blocking
  • ND @ ND: Batched matrix multiplication on last two dimensions with broadcasting
§Performance Characteristics

The implementation automatically selects optimal kernels based on matrix dimensions:

  • Small matrices (<64 elements): Direct computation with minimal overhead
  • Medium matrices (64-256 elements): Cache-optimized blocking for L1/L2 cache
  • Large matrices (256+ elements): Memory bandwidth optimized with hierarchical blocking
  • AVX2 acceleration: 8x SIMD operations for compatible hardware
  • Scalar fallbacks: Optimized scalar implementations for non-SIMD platforms
§Automatic Differentiation

This operation supports automatic differentiation when either operand requires gradients. Gradient computation follows PyTorch semantics with proper accumulation and chain rule application through the gradtrack engine. Gradients are computed for both operands when requires_grad is set.

§Arguments
  • other - The tensor to multiply with (must have compatible dimensions)
§Returns

A new tensor containing the matrix multiplication result with appropriate shape determined by broadcasting rules and matrix multiplication semantics

§Panics

Panics if the inner dimensions don’t match for matrix multiplication:

  • For 2D @ 2D: self.shape()[1] != other.shape()[0]
  • For 1D @ 2D: self.shape()[0] != other.shape()[0]
  • For 2D @ 1D: self.shape()[1] != other.shape()[0]
  • For ND @ ND: Last two dimensions must be compatible for matrix multiplication
§Examples
use train_station::Tensor;

// 2D matrix multiplication
let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
let result = a.matmul(&b); // [2, 2] @ [2, 2] -> [2, 2]

assert_eq!(result.shape().dims, vec![2, 2]);
assert_eq!(result.data(), &[19.0, 22.0, 43.0, 50.0]);
§Vector-Matrix Multiplication
use train_station::Tensor;

let v = Tensor::from_slice(&[1.0, 2.0], vec![2]).unwrap();
let m = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let result = v.matmul(&m); // [2] @ [2, 2] -> [2]

assert_eq!(result.shape().dims, vec![2]);
assert_eq!(result.data(), &[7.0, 10.0]); // 1*1+2*3, 1*2+2*4
§Gradient Tracking
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2])
    .unwrap()
    .with_requires_grad();
let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2])
    .unwrap()
    .with_requires_grad();

let result = a.matmul(&b);
assert!(result.requires_grad());
assert_eq!(result.shape().dims, vec![2, 2]);
§Thread Safety

This operation is thread-safe and can be used concurrently across multiple threads. The implementation uses immutable tensor references and thread-local gradtrack state.

§Memory Safety

The implementation uses Tensor::new_uninitialized for performance-critical allocations and handles memory initialization safely through the kernel system. All unsafe operations are validated through comprehensive FFI testing against LibTorch reference implementation.

Examples found in repository?
examples/neural_networks/feedforward_network.rs (line 85)
84    pub fn forward(&self, input: &Tensor) -> Tensor {
85        let output = input.matmul(&self.weight);
86        output.add_tensor(&self.bias)
87    }
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 72)
70    pub fn forward(&self, input: &Tensor) -> Tensor {
71        // Matrix multiplication: [batch_size, input_size] @ [input_size, output_size] = [batch_size, output_size]
72        let output = input.matmul(&self.weight);
73        // Add bias: [batch_size, output_size] + [output_size] = [batch_size, output_size]
74        output.add_tensor(&self.bias)
75    }
examples/optimizers/adam_configurations.rs (line 111)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
examples/optimizers/learning_rate_scheduling.rs (line 343)
319fn train_with_scheduler(
320    scheduler: &mut dyn LearningRateScheduler,
321    num_epochs: usize,
322) -> Result<TrainingStats, Box<dyn std::error::Error>> {
323    // Create training data: y = 2*x + 1
324    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
325    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
326
327    // Create model parameters
328    let mut weight = Tensor::randn(vec![1, 1], Some(456)).with_requires_grad();
329    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
330
331    // Create optimizer with initial learning rate
332    let mut optimizer = Adam::with_learning_rate(0.05);
333    optimizer.add_parameter(&weight);
334    optimizer.add_parameter(&bias);
335
336    // Training loop
337    let mut losses = Vec::new();
338    let mut lr_history = Vec::new();
339    let mut convergence_epoch = num_epochs;
340
341    for epoch in 0..num_epochs {
342        // Forward pass
343        let y_pred = x_data.matmul(&weight) + &bias;
344        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
345
346        // Backward pass
347        loss.backward(None);
348
349        // Update learning rate using scheduler
350        let current_lr = optimizer.learning_rate();
351        let new_lr = scheduler.step(current_lr, epoch, loss.value());
352
353        if (new_lr - current_lr).abs() > 1e-8 {
354            optimizer.set_learning_rate(new_lr);
355        }
356
357        // Optimizer step
358        optimizer.step(&mut [&mut weight, &mut bias]);
359        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
360
361        let loss_value = loss.value();
362        losses.push(loss_value);
363        lr_history.push(new_lr);
364
365        // Check for convergence
366        if loss_value < 0.01 && convergence_epoch == num_epochs {
367            convergence_epoch = epoch;
368        }
369    }
370
371    Ok(TrainingStats {
372        scheduler_name: scheduler.name().to_string(),
373        final_loss: losses[losses.len() - 1],
374        lr_history,
375        loss_history: losses,
376        convergence_epoch,
377    })
378}
examples/getting_started/optimizer_basics.rs (line 132)
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
Source§

impl Tensor

Source

pub fn mul_tensor(&self, other: &Tensor) -> Tensor

Element-wise multiplication with another tensor with broadcasting support.

Performs element-wise multiplication with automatic broadcasting: output[i] = self[i] * other[i]

Broadcasting enables multiplication between tensors of different but compatible shapes. Compatible shapes follow NumPy broadcasting rules:

  • Dimensions are aligned from the rightmost dimension
  • Dimensions are compatible if they are equal, or one of them is 1
  • Missing dimensions are treated as 1
§Arguments
  • other - Tensor to multiply. Shapes must be broadcast-compatible.
§Returns

A new tensor containing the element-wise product with broadcast result shape

§Examples
§Same Shape Multiplication
use train_station::Tensor;

let a = Tensor::from_slice(&[2.0, 3.0, 4.0], vec![3]).unwrap();
let b = Tensor::from_slice(&[5.0, 6.0, 7.0], vec![3]).unwrap();
let c = a.mul_tensor(&b);
assert_eq!(c.shape().dims, vec![3]);
assert_eq!(c.get(&[0]), 10.0); // 2.0 * 5.0
assert_eq!(c.get(&[1]), 18.0); // 3.0 * 6.0
assert_eq!(c.get(&[2]), 28.0); // 4.0 * 7.0
§Broadcasting Multiplication
use train_station::Tensor;

let a = Tensor::from_slice(&[2.0, 3.0], vec![2, 1]).unwrap();
let b = Tensor::from_slice(&[10.0, 20.0, 30.0], vec![1, 3]).unwrap();
let c = a.mul_tensor(&b);
assert_eq!(c.shape().dims, vec![2, 3]);
// Result: [[20.0, 40.0, 60.0], [30.0, 60.0, 90.0]]
assert_eq!(c.get(&[0, 0]), 20.0); // 2.0 * 10.0
assert_eq!(c.get(&[0, 1]), 40.0); // 2.0 * 20.0
assert_eq!(c.get(&[1, 0]), 30.0); // 3.0 * 10.0
§Panics

Panics if tensor shapes are not broadcast-compatible

Examples found in repository?
examples/getting_started/tensor_basics.rs (line 104)
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
More examples
Hide additional examples
examples/getting_started/tensor_operators.rs (line 222)
203fn demonstrate_method_equivalence() {
204    println!("\n--- Operator vs Method Call Equivalence ---");
205
206    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
207    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
208
209    // Addition: operator vs method
210    let operator_result = &a + &b;
211    let method_result = a.add_tensor(&b);
212
213    println!("A + B (operator): {:?}", operator_result.data());
214    println!("A.add_tensor(B): {:?}", method_result.data());
215    println!(
216        "Results are equal: {}",
217        operator_result.data() == method_result.data()
218    );
219
220    // Multiplication: operator vs method
221    let operator_result = &a * &b;
222    let method_result = a.mul_tensor(&b);
223
224    println!("A * B (operator): {:?}", operator_result.data());
225    println!("A.mul_tensor(B): {:?}", method_result.data());
226    println!(
227        "Results are equal: {}",
228        operator_result.data() == method_result.data()
229    );
230
231    // Scalar addition: operator vs method
232    let operator_result = &a + 5.0;
233    let method_result = a.add_scalar(5.0);
234
235    println!("A + 5.0 (operator): {:?}", operator_result.data());
236    println!("A.add_scalar(5.0): {:?}", method_result.data());
237    println!(
238        "Results are equal: {}",
239        operator_result.data() == method_result.data()
240    );
241}
examples/iterators/element_iteration.rs (line 236)
192fn demonstrate_advanced_patterns() -> Result<(), Box<dyn std::error::Error>> {
193    println!("\n--- Advanced Iterator Patterns ---");
194
195    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6])?;
196    println!("Input tensor: {:?}", tensor.data());
197
198    // Complex chain: enumerate -> filter -> map -> collect
199    println!("\nComplex chain (even indices only, add index to value):");
200    let result: Tensor = tensor
201        .iter()
202        .enumerate()
203        .filter(|(i, _)| i % 2 == 0) // Take even indices
204        .map(|(i, elem)| elem.add_scalar(i as f32)) // Add index to value
205        .collect();
206    println!("  Result: {:?}", result.data());
207
208    // Using take and skip for windowing
209    println!("\nWindowing with take and skip:");
210    let window1: Tensor = tensor.iter().take(3).collect();
211    let window2: Tensor = tensor.iter().skip(2).take(3).collect();
212    println!("  Window 1 (first 3): {:?}", window1.data());
213    println!("  Window 2 (middle 3): {:?}", window2.data());
214
215    // Using rev() for reverse iteration
216    println!("\nReverse iteration:");
217    let reversed: Tensor = tensor.iter().rev().collect();
218    println!("  Reversed: {:?}", reversed.data());
219
220    // Chaining with mathematical operations
221    println!("\nMathematical operation chain:");
222    let math_result: Tensor = tensor
223        .iter()
224        .map(|elem| elem.exp()) // e^x
225        .filter(|elem| elem.value() < 50.0) // Filter large values
226        .map(|elem| elem.log()) // ln(x)
227        .collect();
228    println!("  Math chain result: {:?}", math_result.data());
229
230    // Using zip for element-wise combinations
231    println!("\nElement-wise combination with zip:");
232    let tensor2 = Tensor::from_slice(&[10.0, 20.0, 30.0, 40.0, 50.0, 60.0], vec![6])?;
233    let combined: Tensor = tensor
234        .iter()
235        .zip(tensor2.iter())
236        .map(|(a, b)| a.mul_tensor(&b)) // Element-wise multiplication
237        .collect();
238    println!("  Combined: {:?}", combined.data());
239
240    Ok(())
241}
Source

pub fn mul_scalar(&self, scalar: f32) -> Tensor

Broadcast multiplication with a scalar value.

Multiplies every element by the scalar: output[i] = self[i] * scalar

§Arguments
  • scalar - Value to multiply with each element
§Returns

A new tensor with each element multiplied by the scalar

§Examples
§Basic Scalar Multiplication
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = a.mul_scalar(10.0);
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 10.0); // 1.0 * 10.0
assert_eq!(b.get(&[1]), 20.0); // 2.0 * 10.0
assert_eq!(b.get(&[2]), 30.0); // 3.0 * 10.0
§Negative Scalar Multiplication
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = a.mul_scalar(-2.0);
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), -2.0); // 1.0 * -2.0
assert_eq!(b.get(&[1]), -4.0); // 2.0 * -2.0
assert_eq!(b.get(&[2]), -6.0); // 3.0 * -2.0
Examples found in repository?
examples/neural_networks/feedforward_network.rs (line 72)
68    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
69        let scale = (1.0 / input_size as f32).sqrt();
70
71        let weight = Tensor::randn(vec![input_size, output_size], seed)
72            .mul_scalar(scale)
73            .with_requires_grad();
74        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
75
76        Self {
77            weight,
78            bias,
79            input_size,
80            output_size,
81        }
82    }
More examples
Hide additional examples
examples/neural_networks/basic_linear_layer.rs (line 57)
52    pub fn new(input_size: usize, output_size: usize, seed: Option<u64>) -> Self {
53        // Xavier/Glorot initialization: scale by sqrt(1/input_size)
54        let scale = (1.0 / input_size as f32).sqrt();
55
56        let weight = Tensor::randn(vec![input_size, output_size], seed)
57            .mul_scalar(scale)
58            .with_requires_grad();
59        let bias = Tensor::zeros(vec![output_size]).with_requires_grad();
60
61        Self {
62            weight,
63            bias,
64            input_size,
65            output_size,
66        }
67    }
examples/getting_started/tensor_basics.rs (line 115)
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
examples/iterators/element_iteration.rs (line 99)
77fn demonstrate_basic_iteration() -> Result<(), Box<dyn std::error::Error>> {
78    println!("\n--- Basic Element Iteration ---");
79
80    // Create a simple tensor for demonstration
81    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
82    println!("Original tensor: {:?}", tensor.data());
83
84    // Basic iteration with for loop
85    println!("\nBasic iteration with for loop:");
86    for (i, element) in tensor.iter().enumerate() {
87        println!(
88            "  Element {}: value = {:.1}, shape = {:?}",
89            i,
90            element.value(),
91            element.shape().dims
92        );
93    }
94
95    // Element-wise transformation
96    println!("\nElement-wise transformation (2x + 1):");
97    let transformed: Tensor = tensor
98        .iter()
99        .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
100        .collect();
101    println!("  Result: {:?}", transformed.data());
102
103    // Filtering elements
104    println!("\nFiltering elements (values > 3.0):");
105    let filtered: Tensor = tensor.iter().filter(|elem| elem.value() > 3.0).collect();
106    println!("  Filtered: {:?}", filtered.data());
107
108    Ok(())
109}
110
111/// Demonstrate standard iterator trait methods
112///
113/// Shows compatibility with Rust's standard library iterator methods
114/// and demonstrates various functional programming patterns.
115fn demonstrate_standard_methods() -> Result<(), Box<dyn std::error::Error>> {
116    println!("\n--- Standard Iterator Methods ---");
117
118    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
119
120    // Using map for transformations
121    println!("\nMap transformation (square each element):");
122    let squared: Tensor = tensor.iter().map(|elem| elem.pow_scalar(2.0)).collect();
123    println!("  Squared: {:?}", squared.data());
124
125    // Using enumerate for indexed operations
126    println!("\nEnumerate with indexed operations:");
127    let indexed: Tensor = tensor
128        .iter()
129        .enumerate()
130        .map(|(i, elem)| elem.add_scalar(i as f32))
131        .collect();
132    println!("  Indexed: {:?}", indexed.data());
133
134    // Using fold for reduction
135    println!("\nFold for sum calculation:");
136    let sum: f32 = tensor.iter().fold(0.0, |acc, elem| acc + elem.value());
137    println!("  Sum: {:.1}", sum);
138
139    // Using find for element search
140    println!("\nFind specific element:");
141    if let Some(found) = tensor.iter().find(|elem| elem.value() == 3.0) {
142        println!("  Found element with value 3.0: {:.1}", found.value());
143    }
144
145    // Using any/all for condition checking
146    println!("\nCondition checking:");
147    let all_positive = tensor.iter().all(|elem| elem.value() > 0.0);
148    let any_large = tensor.iter().any(|elem| elem.value() > 4.0);
149    println!("  All positive: {}", all_positive);
150    println!("  Any > 4.0: {}", any_large);
151
152    Ok(())
153}
154
155/// Demonstrate gradient tracking through element operations
156///
157/// Shows how gradient tracking works seamlessly through iterator
158/// operations, maintaining the computational graph for backpropagation.
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
examples/iterators/performance_optimization.rs (line 90)
75fn demonstrate_performance_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
76    println!("\n--- Performance Benchmarking ---");
77
78    // Create test data of different sizes
79    let sizes = vec![100, 1000, 10000];
80
81    for size in sizes {
82        println!("\nBenchmarking with tensor size: {}", size);
83
84        // Generate test data
85        let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
86        let tensor = Tensor::from_slice(&data, vec![size])?;
87
88        // Benchmark 1: Direct tensor operations
89        let start = Instant::now();
90        let direct_result = tensor.mul_scalar(2.0).add_scalar(1.0);
91        let direct_time = start.elapsed();
92
93        // Benchmark 2: Iterator-based operations
94        let start = Instant::now();
95        let iterator_result: Tensor = tensor
96            .iter()
97            .map(|elem| elem.mul_scalar(2.0).add_scalar(1.0))
98            .collect();
99        let iterator_time = start.elapsed();
100
101        // Benchmark 3: Chained iterator operations
102        let start = Instant::now();
103        let _chained_result: Tensor = tensor
104            .iter()
105            .map(|elem| elem.mul_scalar(2.0))
106            .filter(|elem| elem.value() > size as f32)
107            .map(|elem| elem.add_scalar(1.0))
108            .collect();
109        let chained_time = start.elapsed();
110
111        // Report results
112        println!("  Direct operations: {:?}", direct_time);
113        println!("  Iterator operations: {:?}", iterator_time);
114        println!("  Chained operations: {:?}", chained_time);
115
116        // Verify correctness
117        assert_eq!(direct_result.data(), iterator_result.data());
118        println!(
119            "  Results match: {}",
120            direct_result.data() == iterator_result.data()
121        );
122
123        // Performance ratio
124        let ratio = iterator_time.as_nanos() as f64 / direct_time.as_nanos() as f64;
125        println!("  Iterator/Direct ratio: {:.2}x", ratio);
126    }
127
128    Ok(())
129}
130
131/// Demonstrate memory optimization patterns
132///
133/// Shows memory-efficient processing patterns and techniques
134/// for minimizing memory usage while maintaining performance.
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
292
293/// Demonstrate advanced optimization techniques
294///
295/// Shows sophisticated optimization strategies and techniques
296/// for maximizing performance in tensor iterator operations.
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
examples/iterators/advanced_patterns.rs (line 178)
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source§

impl Tensor

Source

pub fn pow_scalar(&self, exponent: f32) -> Tensor

Raises each element to a scalar power.

Computes element-wise power: output[i] = self[i]^exponent

§Arguments
  • exponent - The scalar exponent to raise each element to
§Returns

A new tensor with each element raised to the given power

§Examples
§Basic Scalar Power
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = a.pow_scalar(2.0);
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 1.0); // 1.0^2 = 1.0
assert_eq!(b.get(&[1]), 4.0); // 2.0^2 = 4.0
assert_eq!(b.get(&[2]), 9.0); // 3.0^2 = 9.0
§Square Root (Power 0.5)
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 4.0, 9.0], vec![3]).unwrap();
let b = a.pow_scalar(0.5);
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 1.0); // sqrt(1.0) = 1.0
assert_eq!(b.get(&[1]), 2.0); // sqrt(4.0) = 2.0
assert_eq!(b.get(&[2]), 3.0); // sqrt(9.0) = 3.0
Examples found in repository?
examples/iterators/element_iteration.rs (line 122)
115fn demonstrate_standard_methods() -> Result<(), Box<dyn std::error::Error>> {
116    println!("\n--- Standard Iterator Methods ---");
117
118    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
119
120    // Using map for transformations
121    println!("\nMap transformation (square each element):");
122    let squared: Tensor = tensor.iter().map(|elem| elem.pow_scalar(2.0)).collect();
123    println!("  Squared: {:?}", squared.data());
124
125    // Using enumerate for indexed operations
126    println!("\nEnumerate with indexed operations:");
127    let indexed: Tensor = tensor
128        .iter()
129        .enumerate()
130        .map(|(i, elem)| elem.add_scalar(i as f32))
131        .collect();
132    println!("  Indexed: {:?}", indexed.data());
133
134    // Using fold for reduction
135    println!("\nFold for sum calculation:");
136    let sum: f32 = tensor.iter().fold(0.0, |acc, elem| acc + elem.value());
137    println!("  Sum: {:.1}", sum);
138
139    // Using find for element search
140    println!("\nFind specific element:");
141    if let Some(found) = tensor.iter().find(|elem| elem.value() == 3.0) {
142        println!("  Found element with value 3.0: {:.1}", found.value());
143    }
144
145    // Using any/all for condition checking
146    println!("\nCondition checking:");
147    let all_positive = tensor.iter().all(|elem| elem.value() > 0.0);
148    let any_large = tensor.iter().any(|elem| elem.value() > 4.0);
149    println!("  All positive: {}", all_positive);
150    println!("  Any > 4.0: {}", any_large);
151
152    Ok(())
153}
154
155/// Demonstrate gradient tracking through element operations
156///
157/// Shows how gradient tracking works seamlessly through iterator
158/// operations, maintaining the computational graph for backpropagation.
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
More examples
Hide additional examples
examples/optimizers/adam_configurations.rs (line 112)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
examples/optimizers/learning_rate_scheduling.rs (line 344)
319fn train_with_scheduler(
320    scheduler: &mut dyn LearningRateScheduler,
321    num_epochs: usize,
322) -> Result<TrainingStats, Box<dyn std::error::Error>> {
323    // Create training data: y = 2*x + 1
324    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
325    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
326
327    // Create model parameters
328    let mut weight = Tensor::randn(vec![1, 1], Some(456)).with_requires_grad();
329    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
330
331    // Create optimizer with initial learning rate
332    let mut optimizer = Adam::with_learning_rate(0.05);
333    optimizer.add_parameter(&weight);
334    optimizer.add_parameter(&bias);
335
336    // Training loop
337    let mut losses = Vec::new();
338    let mut lr_history = Vec::new();
339    let mut convergence_epoch = num_epochs;
340
341    for epoch in 0..num_epochs {
342        // Forward pass
343        let y_pred = x_data.matmul(&weight) + &bias;
344        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
345
346        // Backward pass
347        loss.backward(None);
348
349        // Update learning rate using scheduler
350        let current_lr = optimizer.learning_rate();
351        let new_lr = scheduler.step(current_lr, epoch, loss.value());
352
353        if (new_lr - current_lr).abs() > 1e-8 {
354            optimizer.set_learning_rate(new_lr);
355        }
356
357        // Optimizer step
358        optimizer.step(&mut [&mut weight, &mut bias]);
359        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
360
361        let loss_value = loss.value();
362        losses.push(loss_value);
363        lr_history.push(new_lr);
364
365        // Check for convergence
366        if loss_value < 0.01 && convergence_epoch == num_epochs {
367            convergence_epoch = epoch;
368        }
369    }
370
371    Ok(TrainingStats {
372        scheduler_name: scheduler.name().to_string(),
373        final_loss: losses[losses.len() - 1],
374        lr_history,
375        loss_history: losses,
376        convergence_epoch,
377    })
378}
examples/getting_started/optimizer_basics.rs (line 135)
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
examples/neural_networks/feedforward_network.rs (line 479)
430fn demonstrate_training_workflow() -> Result<(), Box<dyn std::error::Error>> {
431    println!("\n--- Training Workflow ---");
432
433    // Create a simple classification network
434    let config = FeedForwardConfig {
435        input_size: 2,
436        hidden_sizes: vec![4, 3],
437        output_size: 1,
438        use_bias: true,
439    };
440    let mut network = FeedForwardNetwork::new(config, Some(46));
441
442    println!("Training network: 2 -> [4, 3] -> 1");
443
444    // Create simple binary classification data: XOR problem
445    let x_data = Tensor::from_slice(
446        &[
447            0.0, 0.0, // -> 0
448            0.0, 1.0, // -> 1
449            1.0, 0.0, // -> 1
450            1.0, 1.0, // -> 0
451        ],
452        vec![4, 2],
453    )
454    .unwrap();
455
456    let y_true = Tensor::from_slice(&[0.0, 1.0, 1.0, 0.0], vec![4, 1]).unwrap();
457
458    println!("Training on XOR problem:");
459    println!("  Input shape: {:?}", x_data.shape().dims);
460    println!("  Target shape: {:?}", y_true.shape().dims);
461
462    // Create optimizer
463    let mut optimizer = Adam::with_learning_rate(0.1);
464    let params = network.parameters();
465    for param in &params {
466        optimizer.add_parameter(param);
467    }
468
469    // Training loop
470    let num_epochs = 50;
471    let mut losses = Vec::new();
472
473    for epoch in 0..num_epochs {
474        // Forward pass
475        let y_pred = network.forward(&x_data);
476
477        // Compute loss: MSE
478        let diff = y_pred.sub_tensor(&y_true);
479        let mut loss = diff.pow_scalar(2.0).mean();
480
481        // Backward pass
482        loss.backward(None);
483
484        // Optimizer step and zero grad
485        let mut params = network.parameters();
486        optimizer.step(&mut params);
487        optimizer.zero_grad(&mut params);
488
489        losses.push(loss.value());
490
491        // Print progress
492        if epoch % 10 == 0 || epoch == num_epochs - 1 {
493            println!("Epoch {:2}: Loss = {:.6}", epoch, loss.value());
494        }
495    }
496
497    // Test final model
498    let final_predictions = network.forward_no_grad(&x_data);
499    println!("\nFinal predictions vs targets:");
500    for i in 0..4 {
501        let pred = final_predictions.data()[i];
502        let target = y_true.data()[i];
503        let input_x = x_data.data()[i * 2];
504        let input_y = x_data.data()[i * 2 + 1];
505        println!(
506            "  [{:.0}, {:.0}] -> pred: {:.3}, target: {:.0}, error: {:.3}",
507            input_x,
508            input_y,
509            pred,
510            target,
511            (pred - target).abs()
512        );
513    }
514
515    Ok(())
516}
517
518/// Demonstrate comprehensive training with 100+ steps
519fn demonstrate_comprehensive_training() -> Result<(), Box<dyn std::error::Error>> {
520    println!("\n--- Comprehensive Training (100+ Steps) ---");
521
522    // Create a regression network
523    let config = FeedForwardConfig {
524        input_size: 3,
525        hidden_sizes: vec![8, 6, 4],
526        output_size: 2,
527        use_bias: true,
528    };
529    let mut network = FeedForwardNetwork::new(config, Some(47));
530
531    println!("Network architecture: 3 -> [8, 6, 4] -> 2");
532    println!("Total parameters: {}", network.parameter_count());
533
534    // Create synthetic regression data
535    // Target function: [y1, y2] = [x1 + 2*x2 - x3, x1*x2 + x3]
536    let num_samples = 32;
537    let mut x_vec = Vec::new();
538    let mut y_vec = Vec::new();
539
540    for i in 0..num_samples {
541        let x1 = (i as f32 / num_samples as f32) * 2.0 - 1.0; // [-1, 1]
542        let x2 = ((i * 2) as f32 / num_samples as f32) * 2.0 - 1.0;
543        let x3 = ((i * 3) as f32 / num_samples as f32) * 2.0 - 1.0;
544
545        let y1 = x1 + 2.0 * x2 - x3;
546        let y2 = x1 * x2 + x3;
547
548        x_vec.extend_from_slice(&[x1, x2, x3]);
549        y_vec.extend_from_slice(&[y1, y2]);
550    }
551
552    let x_data = Tensor::from_slice(&x_vec, vec![num_samples, 3]).unwrap();
553    let y_true = Tensor::from_slice(&y_vec, vec![num_samples, 2]).unwrap();
554
555    println!("Training data:");
556    println!("  {} samples", num_samples);
557    println!("  Input shape: {:?}", x_data.shape().dims);
558    println!("  Target shape: {:?}", y_true.shape().dims);
559
560    // Create optimizer with learning rate scheduling
561    let mut optimizer = Adam::with_learning_rate(0.01);
562    let params = network.parameters();
563    for param in &params {
564        optimizer.add_parameter(param);
565    }
566
567    // Comprehensive training loop (150 epochs)
568    let num_epochs = 150;
569    let mut losses = Vec::new();
570    let mut best_loss = f32::INFINITY;
571    let mut patience_counter = 0;
572    let patience = 20;
573
574    println!("Starting comprehensive training...");
575
576    for epoch in 0..num_epochs {
577        // Forward pass
578        let y_pred = network.forward(&x_data);
579
580        // Compute loss: MSE
581        let diff = y_pred.sub_tensor(&y_true);
582        let mut loss = diff.pow_scalar(2.0).mean();
583
584        // Backward pass
585        loss.backward(None);
586
587        // Optimizer step and zero grad
588        let mut params = network.parameters();
589        optimizer.step(&mut params);
590        optimizer.zero_grad(&mut params);
591
592        let current_loss = loss.value();
593        losses.push(current_loss);
594
595        // Learning rate scheduling
596        if epoch > 0 && epoch % 30 == 0 {
597            let new_lr = optimizer.learning_rate() * 0.8;
598            optimizer.set_learning_rate(new_lr);
599            println!("  Reduced learning rate to {:.4}", new_lr);
600        }
601
602        // Early stopping logic
603        if current_loss < best_loss {
604            best_loss = current_loss;
605            patience_counter = 0;
606        } else {
607            patience_counter += 1;
608        }
609
610        // Print progress
611        if epoch % 25 == 0 || epoch == num_epochs - 1 {
612            println!(
613                "Epoch {:3}: Loss = {:.6}, LR = {:.4}, Best = {:.6}",
614                epoch,
615                current_loss,
616                optimizer.learning_rate(),
617                best_loss
618            );
619        }
620
621        // Early stopping
622        if patience_counter >= patience && epoch > 50 {
623            println!("Early stopping at epoch {} (patience exceeded)", epoch);
624            break;
625        }
626    }
627
628    // Final evaluation
629    let final_predictions = network.forward_no_grad(&x_data);
630
631    // Compute final metrics
632    let final_loss = losses[losses.len() - 1];
633    let initial_loss = losses[0];
634    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
635
636    println!("\nTraining completed!");
637    println!("  Initial loss: {:.6}", initial_loss);
638    println!("  Final loss: {:.6}", final_loss);
639    println!("  Best loss: {:.6}", best_loss);
640    println!("  Loss reduction: {:.1}%", loss_reduction);
641    println!("  Final learning rate: {:.4}", optimizer.learning_rate());
642
643    // Sample predictions analysis
644    println!("\nSample predictions (first 5):");
645    for i in 0..5.min(num_samples) {
646        let pred1 = final_predictions.data()[i * 2];
647        let pred2 = final_predictions.data()[i * 2 + 1];
648        let true1 = y_true.data()[i * 2];
649        let true2 = y_true.data()[i * 2 + 1];
650
651        println!(
652            "  Sample {}: pred=[{:.3}, {:.3}], true=[{:.3}, {:.3}], error=[{:.3}, {:.3}]",
653            i + 1,
654            pred1,
655            pred2,
656            true1,
657            true2,
658            (pred1 - true1).abs(),
659            (pred2 - true2).abs()
660        );
661    }
662
663    Ok(())
664}
665
666/// Demonstrate network serialization
667fn demonstrate_network_serialization() -> Result<(), Box<dyn std::error::Error>> {
668    println!("\n--- Network Serialization ---");
669
670    // Create and train a network
671    let config = FeedForwardConfig {
672        input_size: 2,
673        hidden_sizes: vec![4, 2],
674        output_size: 1,
675        use_bias: true,
676    };
677    let mut original_network = FeedForwardNetwork::new(config.clone(), Some(48));
678
679    // Quick training
680    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
681    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
682
683    let mut optimizer = Adam::with_learning_rate(0.01);
684    let params = original_network.parameters();
685    for param in &params {
686        optimizer.add_parameter(param);
687    }
688
689    for _ in 0..20 {
690        let y_pred = original_network.forward(&x_data);
691        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
692        loss.backward(None);
693
694        let mut params = original_network.parameters();
695        optimizer.step(&mut params);
696        optimizer.zero_grad(&mut params);
697    }
698
699    // Test original network
700    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
701    let original_output = original_network.forward_no_grad(&test_input);
702
703    println!("Original network output: {:?}", original_output.data());
704
705    // Save network
706    original_network.save_json("temp_feedforward_network")?;
707
708    // Load network
709    let loaded_network = FeedForwardNetwork::load_json("temp_feedforward_network", config)?;
710    let loaded_output = loaded_network.forward_no_grad(&test_input);
711
712    println!("Loaded network output: {:?}", loaded_output.data());
713
714    // Verify consistency
715    let match_check = original_output
716        .data()
717        .iter()
718        .zip(loaded_output.data().iter())
719        .all(|(a, b)| (a - b).abs() < 1e-6);
720
721    println!(
722        "Serialization verification: {}",
723        if match_check { "PASSED" } else { "FAILED" }
724    );
725
726    Ok(())
727}
examples/iterators/performance_optimization.rs (line 155)
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
292
293/// Demonstrate advanced optimization techniques
294///
295/// Shows sophisticated optimization strategies and techniques
296/// for maximizing performance in tensor iterator operations.
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
Source

pub fn pow_tensor(&self, exponent: &Tensor) -> Tensor

Element-wise power with tensor exponents.

Computes element-wise power: output[i] = self[i]^exponent[i]

§Arguments
  • exponent - Tensor of exponents, must have the same shape as self
§Returns

A new tensor with each element raised to the corresponding power

§Examples
§Basic Tensor Power
use train_station::Tensor;

let base = Tensor::from_slice(&[2.0, 3.0, 4.0], vec![3]).unwrap();
let exp = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let result = base.pow_tensor(&exp);
assert_eq!(result.shape().dims, vec![3]);
assert_eq!(result.get(&[0]), 2.0); // 2.0^1.0 = 2.0
assert_eq!(result.get(&[1]), 9.0); // 3.0^2.0 = 9.0
assert_eq!(result.get(&[2]), 64.0); // 4.0^3.0 = 64.0
§Mixed Exponents
use train_station::Tensor;

let base = Tensor::from_slice(&[4.0, 9.0, 16.0], vec![3]).unwrap();
let exp = Tensor::from_slice(&[0.5, 1.0, 2.0], vec![3]).unwrap();
let result = base.pow_tensor(&exp);
assert_eq!(result.shape().dims, vec![3]);
assert_eq!(result.get(&[0]), 2.0); // sqrt(4.0) = 2.0
assert_eq!(result.get(&[1]), 9.0); // 9.0^1.0 = 9.0
assert_eq!(result.get(&[2]), 256.0); // 16.0^2.0 = 256.0
§Panics

Panics if tensor shapes don’t match

Source§

impl Tensor

Source

pub fn relu(&self) -> Tensor

Element-wise ReLU (Rectified Linear Unit) activation.

Applies ReLU to each element: output[i] = max(0, self[i])

§Returns

A new tensor with ReLU applied to each element

§Examples
§Basic ReLU Activation
use train_station::Tensor;

let a = Tensor::from_slice(&[-1.0, 0.0, 2.5], vec![3]).unwrap();
let b = a.relu();
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 0.0); // max(0, -1.0) = 0.0
assert_eq!(b.get(&[1]), 0.0); // max(0, 0.0) = 0.0
assert_eq!(b.get(&[2]), 2.5); // max(0, 2.5) = 2.5
§Mixed Positive and Negative Values
use train_station::Tensor;

let a = Tensor::from_slice(&[-5.0, -0.1, 0.0, 0.1, 5.0], vec![5]).unwrap();
let b = a.relu();
assert_eq!(b.shape().dims, vec![5]);
assert_eq!(b.get(&[0]), 0.0); // max(0, -5.0) = 0.0
assert_eq!(b.get(&[1]), 0.0); // max(0, -0.1) = 0.0
assert_eq!(b.get(&[2]), 0.0); // max(0, 0.0) = 0.0
assert_eq!(b.get(&[3]), 0.1); // max(0, 0.1) = 0.1
assert_eq!(b.get(&[4]), 5.0); // max(0, 5.0) = 5.0
Examples found in repository?
examples/neural_networks/feedforward_network.rs (line 48)
47    pub fn forward(input: &Tensor) -> Tensor {
48        input.relu()
49    }
Source§

impl Tensor

Source

pub fn sigmoid(&self) -> Tensor

Element-wise sigmoid activation function

Computes the sigmoid function for each element: output[i] = 1 / (1 + e^(-self[i]))

Uses a numerically stable implementation that avoids overflow for large positive/negative values by using different computation paths for positive and negative inputs.

§Returns

A new tensor with sigmoid applied to each element, values in range (0, 1)

§Performance Characteristics
  • Numerical Stability: Avoids overflow using stable implementation
  • Scalar Implementation: Optimized scalar computation for mathematical accuracy
  • Cache-friendly: Linear memory access patterns
  • Mathematical Accuracy: High-precision exponential and division operations
  • Gradient Tracking: Full gradtrack support with efficient gradient computation
§Implementation Details

Uses a numerically stable implementation:

  • For x ≥ 0: computes 1 / (1 + e^(-x)) to avoid overflow in e^x for large positive x
  • For x < 0: computes e^x / (1 + e^x) to avoid overflow in e^(-x) for large negative x This ensures the result is always in the range (0, 1) without numerical overflow.
§Examples
§Basic Sigmoid Activation
use train_station::Tensor;

let a = Tensor::from_slice(&[-1.0, 0.0, 1.0], vec![3]).unwrap();
let b = a.sigmoid();
assert_eq!(b.shape().dims, vec![3]);
assert!((b.get(&[0]) - 0.26894143).abs() < 1e-6); // sigmoid(-1.0)
assert!((b.get(&[1]) - 0.5).abs() < 1e-6); // sigmoid(0.0)
assert!((b.get(&[2]) - 0.7310586).abs() < 1e-6); // sigmoid(1.0)
§Extreme Values
use train_station::Tensor;

let a = Tensor::from_slice(&[-10.0, 10.0], vec![2]).unwrap();
let b = a.sigmoid();
assert_eq!(b.shape().dims, vec![2]);
assert!(b.get(&[0]) < 1e-4); // sigmoid(-10.0) ≈ 0
assert!(b.get(&[1]) > 0.9999); // sigmoid(10.0) ≈ 1
Source§

impl Tensor

Source

pub fn softmax(&self, dim: usize) -> Tensor

Computes softmax activation along the specified dimension

Applies the softmax function along dimension dim, transforming values into probabilities that sum to 1 along that dimension. Uses numerically stable computation to avoid overflow: softmax(x_i) = exp(x_i - max(x)) / sum(exp(x_j - max(x)))

§Arguments
  • dim - Dimension along which to compute softmax (0-based indexing)
§Returns

A new tensor with softmax applied along the specified dimension. Values are in range (0, 1) and sum to 1 along dim.

§Performance Characteristics
  • Numerical Stability: Avoids overflow using max subtraction technique
  • Scalar Implementation: Optimized scalar computation for mathematical accuracy
  • Cache-friendly: Optimized memory access patterns for dimension operations
  • Mathematical Accuracy: High-precision exponential and division operations
  • GradTrack Support: Full automatic differentiation with efficient gradient computation
§Implementation Details

Uses a numerically stable three-pass algorithm:

  1. Max Computation: Find the maximum value along the specified dimension
  2. Exponential Sum: Compute exp(x - max) and sum the results
  3. Normalization: Divide each exp(x - max) by the sum to get probabilities

This approach prevents overflow by subtracting the maximum value before computing exponentials, ensuring numerical stability for any input range.

§Examples
§Basic Softmax Activation
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = a.softmax(0);
assert_eq!(b.shape().dims, vec![3]);

// Verify probabilities sum to 1
let sum = b.get(&[0]) + b.get(&[1]) + b.get(&[2]);
assert!((sum - 1.0).abs() < 1e-6);

// Verify relative ordering is preserved
assert!(b.get(&[0]) < b.get(&[1]));
assert!(b.get(&[1]) < b.get(&[2]));
§2D Softmax Along Different Dimensions
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let b = a.softmax(0); // Softmax along first dimension
assert_eq!(b.shape().dims, vec![2, 2]);

// Each column should sum to 1
let col1_sum = b.get(&[0, 0]) + b.get(&[1, 0]);
let col2_sum = b.get(&[0, 1]) + b.get(&[1, 1]);
assert!((col1_sum - 1.0).abs() < 1e-6);
assert!((col2_sum - 1.0).abs() < 1e-6);
§Panics
  • Panics if dim is out of bounds for the tensor’s rank
  • Panics if the dimension size is 0
Source§

impl Tensor

Source

pub fn sqrt(&self) -> Tensor

Element-wise square root

Computes the square root for each element: output[i] = sqrt(self[i])

Uses SIMD optimization when available for maximum performance, with automatic fallback to optimized scalar computation for non-SIMD hardware.

§Returns

A new tensor with the square root of each element

§Performance Characteristics
  • SIMD Optimization: AVX2-optimized with 32-element blocks and 4x unrolling
  • Scalar Fallback: 4x unrolled scalar implementation for non-SIMD hardware
  • Cache-friendly: Linear memory access patterns
  • Mathematical Accuracy: High-precision square root computation
  • GradTrack Support: Full automatic differentiation with efficient gradient computation
§Implementation Details

Automatically selects between SIMD and scalar implementations based on hardware capabilities. SIMD implementation uses AVX2 vector square root operations for optimal performance. Scalar implementation uses 4x unrolling for better instruction-level parallelism.

§Examples
§Basic Square Root
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 4.0, 9.0], vec![3]).unwrap();
let b = a.sqrt();
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 1.0); // sqrt(1.0) = 1.0
assert_eq!(b.get(&[1]), 2.0); // sqrt(4.0) = 2.0
assert_eq!(b.get(&[2]), 3.0); // sqrt(9.0) = 3.0
§Zero and Special Values
use train_station::Tensor;

let a = Tensor::from_slice(&[0.0, 1.0, 16.0], vec![3]).unwrap();
let b = a.sqrt();
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 0.0); // sqrt(0.0) = 0.0
assert_eq!(b.get(&[1]), 1.0); // sqrt(1.0) = 1.0
assert_eq!(b.get(&[2]), 4.0); // sqrt(16.0) = 4.0
§Note

Results are undefined for negative values (may produce NaN)

Examples found in repository?
examples/iterators/performance_optimization.rs (line 155)
135fn demonstrate_memory_optimization() -> Result<(), Box<dyn std::error::Error>> {
136    println!("\n--- Memory Optimization ---");
137
138    // Create a large tensor for memory testing
139    let size = 10000;
140    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
141    let tensor = Tensor::from_slice(&data, vec![size])?;
142
143    println!("Processing tensor of size: {}", size);
144
145    // Pattern 1: Streaming processing (process in chunks)
146    println!("\nPattern 1: Streaming Processing");
147    let chunk_size = 1000;
148    let start = Instant::now();
149
150    let mut streamed_result = Vec::new();
151    for chunk_start in (0..size).step_by(chunk_size) {
152        let chunk_end = (chunk_start + chunk_size).min(size);
153        let chunk: Tensor = tensor
154            .iter_range(chunk_start, chunk_end)
155            .map(|elem| elem.pow_scalar(2.0).sqrt())
156            .collect();
157        streamed_result.extend(chunk.data().iter().cloned());
158    }
159    let streamed_time = start.elapsed();
160
161    // Pattern 2: Full processing
162    let start = Instant::now();
163    let _full_result: Tensor = tensor
164        .iter()
165        .map(|elem| elem.pow_scalar(2.0).sqrt())
166        .collect();
167    let full_time = start.elapsed();
168
169    println!("  Streaming time: {:?}", streamed_time);
170    println!("  Full processing time: {:?}", full_time);
171    println!(
172        "  Memory efficiency ratio: {:.2}x",
173        full_time.as_nanos() as f64 / streamed_time.as_nanos() as f64
174    );
175
176    // Pattern 3: Lazy evaluation with take
177    println!("\nPattern 2: Lazy Evaluation");
178    let start = Instant::now();
179    let lazy_result: Tensor = tensor
180        .iter()
181        .take(1000) // Only process first 1000 elements
182        .map(|elem| elem.pow_scalar(2.0).sqrt())
183        .collect();
184    let lazy_time = start.elapsed();
185
186    println!("  Lazy processing (1000 elements): {:?}", lazy_time);
187    println!("  Lazy result size: {}", lazy_result.size());
188
189    // Pattern 4: Memory-efficient filtering
190    println!("\nPattern 3: Memory-Efficient Filtering");
191    let start = Instant::now();
192    let filtered_result: Tensor = tensor
193        .iter()
194        .filter(|elem| elem.value() > size as f32 / 2.0) // Keep only large values
195        .map(|elem| elem.mul_scalar(2.0))
196        .collect();
197    let filtered_time = start.elapsed();
198
199    println!("  Filtered processing: {:?}", filtered_time);
200    println!(
201        "  Filtered result size: {} (reduced from {})",
202        filtered_result.size(),
203        size
204    );
205
206    Ok(())
207}
208
209/// Demonstrate large-scale processing techniques
210///
211/// Shows how to efficiently process very large datasets using
212/// iterator patterns and optimization strategies.
213fn demonstrate_large_scale_processing() -> Result<(), Box<dyn std::error::Error>> {
214    println!("\n--- Large-Scale Processing ---");
215
216    // Simulate large dataset processing
217    let sizes = vec![10000, 50000, 100000];
218
219    for size in sizes {
220        println!("\nProcessing dataset of size: {}", size);
221
222        // Generate large dataset
223        let data: Vec<f32> = (0..size)
224            .map(|i| {
225                let x = i as f32 / size as f32;
226                x * x + 0.1 * (i % 10) as f32 // Quadratic with noise
227            })
228            .collect();
229
230        let tensor = Tensor::from_slice(&data, vec![size])?;
231
232        // Technique 1: Batch processing
233        let batch_size = 1000;
234        let start = Instant::now();
235
236        let mut batch_results = Vec::new();
237        for batch_start in (0..size).step_by(batch_size) {
238            let batch_end = (batch_start + batch_size).min(size);
239            let batch: Tensor = tensor
240                .iter_range(batch_start, batch_end)
241                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
242                .collect();
243            batch_results.push(batch);
244        }
245        let batch_time = start.elapsed();
246
247        // Technique 2: Parallel-like processing with stride
248        let start = Instant::now();
249        let stride = 4;
250        let strided_result: Tensor = tensor
251            .iter()
252            .enumerate()
253            .filter(|(i, _)| i % stride == 0)
254            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
255            .collect();
256        let strided_time = start.elapsed();
257
258        // Technique 3: Hierarchical processing
259        let start = Instant::now();
260        let coarse: Tensor = tensor
261            .iter()
262            .enumerate()
263            .filter(|(i, _)| i % 10 == 0) // Every 10th element
264            .map(|(_, elem)| elem.pow_scalar(2.0).add_scalar(1.0))
265            .collect();
266        let fine: Tensor = tensor
267            .iter()
268            .enumerate()
269            .filter(|(i, _)| i % 10 != 0) // Rest of elements
270            .map(|(_, elem)| elem.pow_scalar(1.5).add_scalar(0.5))
271            .collect();
272        let hierarchical_time = start.elapsed();
273
274        // Report performance
275        println!("  Batch processing: {:?}", batch_time);
276        println!("  Strided processing: {:?}", strided_time);
277        println!("  Hierarchical processing: {:?}", hierarchical_time);
278
279        // Memory usage analysis
280        let total_batches = (size + batch_size - 1) / batch_size;
281        println!("  Batch count: {}", total_batches);
282        println!("  Strided result size: {}", strided_result.size());
283        println!(
284            "  Hierarchical: coarse={}, fine={}",
285            coarse.size(),
286            fine.size()
287        );
288    }
289
290    Ok(())
291}
292
293/// Demonstrate advanced optimization techniques
294///
295/// Shows sophisticated optimization strategies and techniques
296/// for maximizing performance in tensor iterator operations.
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
More examples
Hide additional examples
examples/iterators/advanced_patterns.rs (line 178)
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
Source§

impl Tensor

Source

pub fn sub_tensor(&self, other: &Tensor) -> Tensor

Element-wise subtraction with another tensor with broadcasting support

Performs element-wise subtraction with automatic broadcasting: output[i] = self[i] - other[i]

Broadcasting enables subtraction between tensors of different but compatible shapes. Compatible shapes follow NumPy broadcasting rules:

  • Dimensions are aligned from the rightmost dimension
  • Dimensions are compatible if they are equal, or one of them is 1
  • Missing dimensions are treated as 1
§Arguments
  • other - Tensor to subtract. Shapes must be broadcast-compatible.
§Returns

A new tensor containing the element-wise difference with broadcast result shape

§Performance Characteristics
  • Fast Path: Optimized for identical shapes to avoid broadcasting overhead
  • SIMD Optimization: AVX2-optimized with 32-element blocks and 4x unrolling
  • Broadcasting: Efficient broadcasting for compatible shapes
  • Cache-friendly: Linear memory access patterns
  • GradTrack Support: Full automatic differentiation with efficient gradient computation
§Implementation Details

Uses a fast path for identical shapes to avoid broadcasting overhead. For different shapes, performs broadcasting followed by optimized element-wise subtraction. Automatically selects between SIMD and scalar implementations based on hardware capabilities.

§Examples
§Same Shape Subtraction
use train_station::Tensor;

let a = Tensor::from_slice(&[5.0, 7.0, 9.0], vec![3]).unwrap();
let b = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let c = a.sub_tensor(&b);
assert_eq!(c.shape().dims, vec![3]);
assert_eq!(c.get(&[0]), 4.0); // 5.0 - 1.0
assert_eq!(c.get(&[1]), 5.0); // 7.0 - 2.0
assert_eq!(c.get(&[2]), 6.0); // 9.0 - 3.0
§Broadcasting Subtraction
use train_station::Tensor;

let a = Tensor::from_slice(&[5.0, 10.0], vec![2, 1]).unwrap();
let b = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3]).unwrap();
let c = a.sub_tensor(&b);
assert_eq!(c.shape().dims, vec![2, 3]);
// Result: [[4.0, 3.0, 2.0], [9.0, 8.0, 7.0]]
assert_eq!(c.get(&[0, 0]), 4.0); // 5.0 - 1.0
assert_eq!(c.get(&[0, 1]), 3.0); // 5.0 - 2.0
assert_eq!(c.get(&[1, 0]), 9.0); // 10.0 - 1.0
§Scalar Subtraction
use train_station::Tensor;

let a = Tensor::ones(vec![2, 3]);
let b = Tensor::from_slice(&[0.5], vec![1]).unwrap();
let c = a.sub_tensor(&b);
assert_eq!(c.shape().dims, vec![2, 3]);
assert_eq!(c.get(&[0, 0]), 0.5); // 1.0 - 0.5
§Panics

Panics if tensor shapes are not broadcast-compatible

Examples found in repository?
examples/getting_started/tensor_basics.rs (line 100)
89fn demonstrate_basic_operations() {
90    println!("\n--- Basic Operations ---");
91
92    let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
93    let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
94
95    // Addition
96    let sum = a.add_tensor(&b);
97    println!("A + B: {:?}", sum.data());
98
99    // Subtraction
100    let diff = a.sub_tensor(&b);
101    println!("A - B: {:?}", diff.data());
102
103    // Multiplication
104    let product = a.mul_tensor(&b);
105    println!("A * B: {:?}", product.data());
106
107    // Division
108    let quotient = a.div_tensor(&b);
109    println!("A / B: {:?}", quotient.data());
110
111    // Scalar operations
112    let scalar_add = a.add_scalar(5.0);
113    println!("A + 5.0: {:?}", scalar_add.data());
114
115    let scalar_mul = a.mul_scalar(2.0);
116    println!("A * 2.0: {:?}", scalar_mul.data());
117}
More examples
Hide additional examples
examples/neural_networks/feedforward_network.rs (line 478)
430fn demonstrate_training_workflow() -> Result<(), Box<dyn std::error::Error>> {
431    println!("\n--- Training Workflow ---");
432
433    // Create a simple classification network
434    let config = FeedForwardConfig {
435        input_size: 2,
436        hidden_sizes: vec![4, 3],
437        output_size: 1,
438        use_bias: true,
439    };
440    let mut network = FeedForwardNetwork::new(config, Some(46));
441
442    println!("Training network: 2 -> [4, 3] -> 1");
443
444    // Create simple binary classification data: XOR problem
445    let x_data = Tensor::from_slice(
446        &[
447            0.0, 0.0, // -> 0
448            0.0, 1.0, // -> 1
449            1.0, 0.0, // -> 1
450            1.0, 1.0, // -> 0
451        ],
452        vec![4, 2],
453    )
454    .unwrap();
455
456    let y_true = Tensor::from_slice(&[0.0, 1.0, 1.0, 0.0], vec![4, 1]).unwrap();
457
458    println!("Training on XOR problem:");
459    println!("  Input shape: {:?}", x_data.shape().dims);
460    println!("  Target shape: {:?}", y_true.shape().dims);
461
462    // Create optimizer
463    let mut optimizer = Adam::with_learning_rate(0.1);
464    let params = network.parameters();
465    for param in &params {
466        optimizer.add_parameter(param);
467    }
468
469    // Training loop
470    let num_epochs = 50;
471    let mut losses = Vec::new();
472
473    for epoch in 0..num_epochs {
474        // Forward pass
475        let y_pred = network.forward(&x_data);
476
477        // Compute loss: MSE
478        let diff = y_pred.sub_tensor(&y_true);
479        let mut loss = diff.pow_scalar(2.0).mean();
480
481        // Backward pass
482        loss.backward(None);
483
484        // Optimizer step and zero grad
485        let mut params = network.parameters();
486        optimizer.step(&mut params);
487        optimizer.zero_grad(&mut params);
488
489        losses.push(loss.value());
490
491        // Print progress
492        if epoch % 10 == 0 || epoch == num_epochs - 1 {
493            println!("Epoch {:2}: Loss = {:.6}", epoch, loss.value());
494        }
495    }
496
497    // Test final model
498    let final_predictions = network.forward_no_grad(&x_data);
499    println!("\nFinal predictions vs targets:");
500    for i in 0..4 {
501        let pred = final_predictions.data()[i];
502        let target = y_true.data()[i];
503        let input_x = x_data.data()[i * 2];
504        let input_y = x_data.data()[i * 2 + 1];
505        println!(
506            "  [{:.0}, {:.0}] -> pred: {:.3}, target: {:.0}, error: {:.3}",
507            input_x,
508            input_y,
509            pred,
510            target,
511            (pred - target).abs()
512        );
513    }
514
515    Ok(())
516}
517
518/// Demonstrate comprehensive training with 100+ steps
519fn demonstrate_comprehensive_training() -> Result<(), Box<dyn std::error::Error>> {
520    println!("\n--- Comprehensive Training (100+ Steps) ---");
521
522    // Create a regression network
523    let config = FeedForwardConfig {
524        input_size: 3,
525        hidden_sizes: vec![8, 6, 4],
526        output_size: 2,
527        use_bias: true,
528    };
529    let mut network = FeedForwardNetwork::new(config, Some(47));
530
531    println!("Network architecture: 3 -> [8, 6, 4] -> 2");
532    println!("Total parameters: {}", network.parameter_count());
533
534    // Create synthetic regression data
535    // Target function: [y1, y2] = [x1 + 2*x2 - x3, x1*x2 + x3]
536    let num_samples = 32;
537    let mut x_vec = Vec::new();
538    let mut y_vec = Vec::new();
539
540    for i in 0..num_samples {
541        let x1 = (i as f32 / num_samples as f32) * 2.0 - 1.0; // [-1, 1]
542        let x2 = ((i * 2) as f32 / num_samples as f32) * 2.0 - 1.0;
543        let x3 = ((i * 3) as f32 / num_samples as f32) * 2.0 - 1.0;
544
545        let y1 = x1 + 2.0 * x2 - x3;
546        let y2 = x1 * x2 + x3;
547
548        x_vec.extend_from_slice(&[x1, x2, x3]);
549        y_vec.extend_from_slice(&[y1, y2]);
550    }
551
552    let x_data = Tensor::from_slice(&x_vec, vec![num_samples, 3]).unwrap();
553    let y_true = Tensor::from_slice(&y_vec, vec![num_samples, 2]).unwrap();
554
555    println!("Training data:");
556    println!("  {} samples", num_samples);
557    println!("  Input shape: {:?}", x_data.shape().dims);
558    println!("  Target shape: {:?}", y_true.shape().dims);
559
560    // Create optimizer with learning rate scheduling
561    let mut optimizer = Adam::with_learning_rate(0.01);
562    let params = network.parameters();
563    for param in &params {
564        optimizer.add_parameter(param);
565    }
566
567    // Comprehensive training loop (150 epochs)
568    let num_epochs = 150;
569    let mut losses = Vec::new();
570    let mut best_loss = f32::INFINITY;
571    let mut patience_counter = 0;
572    let patience = 20;
573
574    println!("Starting comprehensive training...");
575
576    for epoch in 0..num_epochs {
577        // Forward pass
578        let y_pred = network.forward(&x_data);
579
580        // Compute loss: MSE
581        let diff = y_pred.sub_tensor(&y_true);
582        let mut loss = diff.pow_scalar(2.0).mean();
583
584        // Backward pass
585        loss.backward(None);
586
587        // Optimizer step and zero grad
588        let mut params = network.parameters();
589        optimizer.step(&mut params);
590        optimizer.zero_grad(&mut params);
591
592        let current_loss = loss.value();
593        losses.push(current_loss);
594
595        // Learning rate scheduling
596        if epoch > 0 && epoch % 30 == 0 {
597            let new_lr = optimizer.learning_rate() * 0.8;
598            optimizer.set_learning_rate(new_lr);
599            println!("  Reduced learning rate to {:.4}", new_lr);
600        }
601
602        // Early stopping logic
603        if current_loss < best_loss {
604            best_loss = current_loss;
605            patience_counter = 0;
606        } else {
607            patience_counter += 1;
608        }
609
610        // Print progress
611        if epoch % 25 == 0 || epoch == num_epochs - 1 {
612            println!(
613                "Epoch {:3}: Loss = {:.6}, LR = {:.4}, Best = {:.6}",
614                epoch,
615                current_loss,
616                optimizer.learning_rate(),
617                best_loss
618            );
619        }
620
621        // Early stopping
622        if patience_counter >= patience && epoch > 50 {
623            println!("Early stopping at epoch {} (patience exceeded)", epoch);
624            break;
625        }
626    }
627
628    // Final evaluation
629    let final_predictions = network.forward_no_grad(&x_data);
630
631    // Compute final metrics
632    let final_loss = losses[losses.len() - 1];
633    let initial_loss = losses[0];
634    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
635
636    println!("\nTraining completed!");
637    println!("  Initial loss: {:.6}", initial_loss);
638    println!("  Final loss: {:.6}", final_loss);
639    println!("  Best loss: {:.6}", best_loss);
640    println!("  Loss reduction: {:.1}%", loss_reduction);
641    println!("  Final learning rate: {:.4}", optimizer.learning_rate());
642
643    // Sample predictions analysis
644    println!("\nSample predictions (first 5):");
645    for i in 0..5.min(num_samples) {
646        let pred1 = final_predictions.data()[i * 2];
647        let pred2 = final_predictions.data()[i * 2 + 1];
648        let true1 = y_true.data()[i * 2];
649        let true2 = y_true.data()[i * 2 + 1];
650
651        println!(
652            "  Sample {}: pred=[{:.3}, {:.3}], true=[{:.3}, {:.3}], error=[{:.3}, {:.3}]",
653            i + 1,
654            pred1,
655            pred2,
656            true1,
657            true2,
658            (pred1 - true1).abs(),
659            (pred2 - true2).abs()
660        );
661    }
662
663    Ok(())
664}
665
666/// Demonstrate network serialization
667fn demonstrate_network_serialization() -> Result<(), Box<dyn std::error::Error>> {
668    println!("\n--- Network Serialization ---");
669
670    // Create and train a network
671    let config = FeedForwardConfig {
672        input_size: 2,
673        hidden_sizes: vec![4, 2],
674        output_size: 1,
675        use_bias: true,
676    };
677    let mut original_network = FeedForwardNetwork::new(config.clone(), Some(48));
678
679    // Quick training
680    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
681    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
682
683    let mut optimizer = Adam::with_learning_rate(0.01);
684    let params = original_network.parameters();
685    for param in &params {
686        optimizer.add_parameter(param);
687    }
688
689    for _ in 0..20 {
690        let y_pred = original_network.forward(&x_data);
691        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
692        loss.backward(None);
693
694        let mut params = original_network.parameters();
695        optimizer.step(&mut params);
696        optimizer.zero_grad(&mut params);
697    }
698
699    // Test original network
700    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
701    let original_output = original_network.forward_no_grad(&test_input);
702
703    println!("Original network output: {:?}", original_output.data());
704
705    // Save network
706    original_network.save_json("temp_feedforward_network")?;
707
708    // Load network
709    let loaded_network = FeedForwardNetwork::load_json("temp_feedforward_network", config)?;
710    let loaded_output = loaded_network.forward_no_grad(&test_input);
711
712    println!("Loaded network output: {:?}", loaded_output.data());
713
714    // Verify consistency
715    let match_check = original_output
716        .data()
717        .iter()
718        .zip(loaded_output.data().iter())
719        .all(|(a, b)| (a - b).abs() < 1e-6);
720
721    println!(
722        "Serialization verification: {}",
723        if match_check { "PASSED" } else { "FAILED" }
724    );
725
726    Ok(())
727}
examples/neural_networks/basic_linear_layer.rs (line 269)
217fn demonstrate_training_loop() -> Result<(), Box<dyn std::error::Error>> {
218    println!("\n--- Training Loop ---");
219
220    // Create layer and training data
221    let mut layer = LinearLayer::new(2, 1, Some(45));
222
223    // Simple regression task: y = 2*x1 + 3*x2 + 1
224    let x_data = Tensor::from_slice(
225        &[
226            1.0, 1.0, // x1=1, x2=1 -> y=6
227            2.0, 1.0, // x1=2, x2=1 -> y=8
228            1.0, 2.0, // x1=1, x2=2 -> y=9
229            2.0, 2.0, // x1=2, x2=2 -> y=11
230        ],
231        vec![4, 2],
232    )
233    .unwrap();
234
235    let y_true = Tensor::from_slice(&[6.0, 8.0, 9.0, 11.0], vec![4, 1]).unwrap();
236
237    println!("Training data:");
238    println!("  X shape: {:?}", x_data.shape().dims);
239    println!("  Y shape: {:?}", y_true.shape().dims);
240    println!("  Target function: y = 2*x1 + 3*x2 + 1");
241
242    // Create optimizer
243    let config = AdamConfig {
244        learning_rate: 0.01,
245        beta1: 0.9,
246        beta2: 0.999,
247        eps: 1e-8,
248        weight_decay: 0.0,
249        amsgrad: false,
250    };
251
252    let mut optimizer = Adam::with_config(config);
253    let params = layer.parameters();
254    for param in &params {
255        optimizer.add_parameter(param);
256    }
257
258    println!("Optimizer setup complete. Starting training...");
259
260    // Training loop
261    let num_epochs = 100;
262    let mut losses = Vec::new();
263
264    for epoch in 0..num_epochs {
265        // Forward pass
266        let y_pred = layer.forward(&x_data);
267
268        // Compute loss: MSE
269        let diff = y_pred.sub_tensor(&y_true);
270        let mut loss = diff.pow_scalar(2.0).mean();
271
272        // Backward pass
273        loss.backward(None);
274
275        // Optimizer step
276        let mut params = layer.parameters();
277        optimizer.step(&mut params);
278        optimizer.zero_grad(&mut params);
279
280        losses.push(loss.value());
281
282        // Print progress
283        if epoch % 20 == 0 || epoch == num_epochs - 1 {
284            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
285        }
286    }
287
288    // Evaluate final model
289    let final_predictions = layer.forward_no_grad(&x_data);
290
291    println!("\nFinal model evaluation:");
292    println!("  Learned weights: {:?}", layer.weight.data());
293    println!("  Learned bias: {:?}", layer.bias.data());
294    println!("  Target weights: [2.0, 3.0]");
295    println!("  Target bias: [1.0]");
296
297    println!("  Predictions vs True:");
298    for i in 0..4 {
299        let pred = final_predictions.data()[i];
300        let true_val = y_true.data()[i];
301        println!(
302            "    Sample {}: pred={:.3}, true={:.1}, error={:.3}",
303            i + 1,
304            pred,
305            true_val,
306            (pred - true_val).abs()
307        );
308    }
309
310    // Training analysis
311    let initial_loss = losses[0];
312    let final_loss = losses[losses.len() - 1];
313    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
314
315    println!("\nTraining Analysis:");
316    println!("  Initial loss: {:.6}", initial_loss);
317    println!("  Final loss: {:.6}", final_loss);
318    println!("  Loss reduction: {:.1}%", loss_reduction);
319
320    Ok(())
321}
322
323/// Demonstrate single vs batch inference
324fn demonstrate_single_vs_batch_inference() {
325    println!("\n--- Single vs Batch Inference ---");
326
327    let layer = LinearLayer::new(4, 3, Some(46));
328
329    // Single inference
330    println!("Single inference:");
331    let single_input = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![1, 4]).unwrap();
332    let single_output = layer.forward_no_grad(&single_input);
333    println!("  Input shape: {:?}", single_input.shape().dims);
334    println!("  Output shape: {:?}", single_output.shape().dims);
335    println!("  Output: {:?}", single_output.data());
336
337    // Batch inference
338    println!("Batch inference:");
339    let batch_input = Tensor::from_slice(
340        &[
341            1.0, 2.0, 3.0, 4.0, // Sample 1
342            5.0, 6.0, 7.0, 8.0, // Sample 2
343            9.0, 10.0, 11.0, 12.0, // Sample 3
344        ],
345        vec![3, 4],
346    )
347    .unwrap();
348    let batch_output = layer.forward_no_grad(&batch_input);
349    println!("  Input shape: {:?}", batch_input.shape().dims);
350    println!("  Output shape: {:?}", batch_output.shape().dims);
351
352    // Verify batch consistency - first sample should match single inference
353    let _first_batch_sample = batch_output.view(vec![3, 3]); // Reshape to access first sample
354    let first_sample_data = &batch_output.data()[0..3]; // First 3 elements
355    let single_sample_data = single_output.data();
356
357    println!("Consistency check:");
358    println!("  Single output: {:?}", single_sample_data);
359    println!("  First batch sample: {:?}", first_sample_data);
360    println!(
361        "  Match: {}",
362        single_sample_data
363            .iter()
364            .zip(first_sample_data.iter())
365            .all(|(a, b)| (a - b).abs() < 1e-6)
366    );
367}
368
369/// Demonstrate serialization and loading
370fn demonstrate_serialization() -> Result<(), Box<dyn std::error::Error>> {
371    println!("\n--- Serialization ---");
372
373    // Create and train a simple layer
374    let mut original_layer = LinearLayer::new(2, 1, Some(47));
375
376    // Simple training data
377    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
378    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
379
380    let mut optimizer = Adam::with_learning_rate(0.01);
381    let params = original_layer.parameters();
382    for param in &params {
383        optimizer.add_parameter(param);
384    }
385
386    // Train for a few epochs
387    for _ in 0..10 {
388        let y_pred = original_layer.forward(&x_data);
389        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
390        loss.backward(None);
391
392        let mut params = original_layer.parameters();
393        optimizer.step(&mut params);
394        optimizer.zero_grad(&mut params);
395    }
396
397    println!("Original layer trained");
398    println!("  Weight: {:?}", original_layer.weight.data());
399    println!("  Bias: {:?}", original_layer.bias.data());
400
401    // Save layer
402    original_layer.save_json("temp_linear_layer")?;
403
404    // Load layer
405    let loaded_layer = LinearLayer::load_json("temp_linear_layer", 2, 1)?;
406
407    println!("Loaded layer");
408    println!("  Weight: {:?}", loaded_layer.weight.data());
409    println!("  Bias: {:?}", loaded_layer.bias.data());
410
411    // Verify consistency
412    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
413    let original_output = original_layer.forward_no_grad(&test_input);
414    let loaded_output = loaded_layer.forward_no_grad(&test_input);
415
416    println!("Consistency check:");
417    println!("  Original output: {:?}", original_output.data());
418    println!("  Loaded output: {:?}", loaded_output.data());
419    println!(
420        "  Match: {}",
421        original_output
422            .data()
423            .iter()
424            .zip(loaded_output.data().iter())
425            .all(|(a, b)| (a - b).abs() < 1e-6)
426    );
427
428    println!("Serialization verification: PASSED");
429
430    Ok(())
431}
Source

pub fn sub_scalar(&self, scalar: f32) -> Tensor

Element-wise subtraction of a scalar from this tensor

Performs element-wise subtraction of a scalar value: output[i] = self[i] - scalar

§Arguments
  • scalar - The scalar value to subtract from each element
§Returns

A new tensor with the scalar subtracted from each element

§Performance Characteristics
  • SIMD Optimization: AVX2-optimized with 32-element blocks and 4x unrolling
  • Scalar Fallback: 4x unrolled scalar implementation for non-SIMD hardware
  • Cache-friendly: Linear memory access patterns
  • Mathematical Accuracy: High-precision subtraction computation
  • GradTrack Support: Full automatic differentiation with efficient gradient computation
§Examples
§Basic Scalar Subtraction
use train_station::Tensor;

let a = Tensor::from_slice(&[5.0, 7.0, 9.0], vec![3]).unwrap();
let b = a.sub_scalar(2.0);
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 3.0); // 5.0 - 2.0
assert_eq!(b.get(&[1]), 5.0); // 7.0 - 2.0
assert_eq!(b.get(&[2]), 7.0); // 9.0 - 2.0
§Negative Scalar Subtraction
use train_station::Tensor;

let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = a.sub_scalar(-2.0); // Subtracting negative = adding
assert_eq!(b.shape().dims, vec![3]);
assert_eq!(b.get(&[0]), 3.0); // 1.0 - (-2.0) = 3.0
assert_eq!(b.get(&[1]), 4.0); // 2.0 - (-2.0) = 4.0
assert_eq!(b.get(&[2]), 5.0); // 3.0 - (-2.0) = 5.0
Examples found in repository?
examples/iterators/advanced_patterns.rs (line 101)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
156
157/// Demonstrate conditional processing patterns
158///
159/// Shows how to implement dynamic filtering and transformation
160/// based on data characteristics and conditions.
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source§

impl Tensor

Source

pub fn tanh(&self) -> Tensor

Element-wise hyperbolic tangent activation

Computes hyperbolic tangent for each element: output[i] = tanh(self[i])

The hyperbolic tangent function maps any real number to the range (-1, 1), making it useful as an activation function in neural networks.

§Returns

A new tensor with tanh applied to each element, values in range (-1, 1)

§Performance Characteristics
  • High Precision: Accurate scalar implementation for mathematical validation
  • 4x Unrolling: Optimized scalar operations with instruction-level parallelism
  • Cache-friendly: Linear memory access patterns
  • Numerical Stability: Robust handling of extreme input values
  • GradTrack Support: Full automatic differentiation with efficient gradient computation
§Mathematical Properties
  • Range: Output values are in the range (-1, 1)
  • Symmetry: tanh(-x) = -tanh(x) (odd function)
  • Zero: tanh(0) = 0
  • Gradient: ∂tanh(x)/∂x = 1 - tanh²(x) = sech²(x)
§Examples
§Basic Hyperbolic Tangent
use train_station::Tensor;

let a = Tensor::from_slice(&[-1.0, 0.0, 1.0], vec![3]).unwrap();
let b = a.tanh();
assert_eq!(b.shape().dims, vec![3]);
assert!((b.get(&[0]) - (-0.7615942)).abs() < 1e-6); // tanh(-1.0)
assert!((b.get(&[1]) - 0.0).abs() < 1e-6); // tanh(0.0)
assert!((b.get(&[2]) - 0.7615942).abs() < 1e-6); // tanh(1.0)
§Extreme Values
use train_station::Tensor;

let a = Tensor::from_slice(&[-10.0, 10.0], vec![2]).unwrap();
let b = a.tanh();
assert_eq!(b.shape().dims, vec![2]);
assert!((b.get(&[0]) - (-1.0)).abs() < 1e-6); // tanh(-10.0) ≈ -1
assert!((b.get(&[1]) - 1.0).abs() < 1e-6); // tanh(10.0) ≈ 1
Source§

impl Tensor

Source

pub fn argmax(&self) -> Tensor

Returns the index of the maximum value across all elements in the tensor

This operation finds the flat index (0-based) of the element with the highest value. If multiple elements have the same maximum value, the index of the first occurrence is returned. The output is a scalar tensor with shape [1] containing the index as a float.

This operation is non-differentiable and the output never requires gradients.

§Returns

A tensor with shape [1] containing the flat index of the maximum value

§Examples
use train_station::Tensor;

// 1D tensor
let tensor = Tensor::from_slice(&[1.0, 5.0, 3.0, 2.0], vec![4]).unwrap();
let max_idx = tensor.argmax();
assert_eq!(max_idx.shape().dims, vec![1]);
assert_eq!(max_idx.get(&[0]), 1.0); // Index 1 has value 5.0
use train_station::Tensor;

// 2D tensor
let tensor = Tensor::from_slice(&[1.0, 3.0, 2.0, 4.0, 0.0, 5.0], vec![2, 3]).unwrap();
let max_idx = tensor.argmax();
assert_eq!(max_idx.get(&[0]), 5.0); // Flat index 5 has value 5.0
use train_station::Tensor;

// Tied values return first occurrence
let tensor = Tensor::from_slice(&[3.0, 5.0, 5.0, 2.0], vec![4]).unwrap();
let max_idx = tensor.argmax();
assert_eq!(max_idx.get(&[0]), 1.0); // First occurrence of 5.0 at index 1
Source

pub fn argmax_dim(&self, dim: usize, keepdim: bool) -> Tensor

Returns the indices of maximum values along a specified dimension

This operation finds the indices of maximum values along the specified dimension. For each slice along the dimension, it returns the index of the maximum value. If multiple elements have the same maximum value, the index of the first occurrence is returned.

The output shape depends on the keepdim parameter:

  • If keepdim is true, the reduced dimension is kept with size 1
  • If keepdim is false, the reduced dimension is removed

This operation is non-differentiable and the output never requires gradients.

§Arguments
  • dim - The dimension along which to find argmax indices (0-based)
  • keepdim - Whether to keep the reduced dimension with size 1
§Returns

A tensor containing the indices of maximum values along the specified dimension

§Panics

Panics if dim is out of bounds for the tensor’s rank or if the dimension size is 0.

§Examples
use train_station::Tensor;

// 2D tensor: [[1.0, 3.0, 2.0],
//             [4.0, 0.0, 5.0]]
let tensor = Tensor::from_slice(&[1.0, 3.0, 2.0, 4.0, 0.0, 5.0], vec![2, 3]).unwrap();

// argmax along columns (dim=1)
let col_max_idx = tensor.argmax_dim(1, false);
assert_eq!(col_max_idx.shape().dims, vec![2]);
assert_eq!(col_max_idx.get(&[0]), 1.0); // Row 0: max at index 1 (value 3.0)
assert_eq!(col_max_idx.get(&[1]), 2.0); // Row 1: max at index 2 (value 5.0)
use train_station::Tensor;

// argmax along rows (dim=0) with keepdim
let tensor = Tensor::from_slice(&[1.0, 3.0, 2.0, 4.0, 0.0, 5.0], vec![2, 3]).unwrap();
let row_max_idx = tensor.argmax_dim(0, true);
assert_eq!(row_max_idx.shape().dims, vec![1, 3]);
assert_eq!(row_max_idx.get(&[0, 0]), 1.0); // Col 0: max at index 1 (value 4.0)
assert_eq!(row_max_idx.get(&[0, 1]), 0.0); // Col 1: max at index 0 (value 3.0)
assert_eq!(row_max_idx.get(&[0, 2]), 1.0); // Col 2: max at index 1 (value 5.0)
use train_station::Tensor;

// 1D tensor edge case
let tensor = Tensor::from_slice(&[5.0, 1.0, 8.0, 3.0], vec![4]).unwrap();
let max_idx = tensor.argmax_dim(0, false);
assert_eq!(max_idx.shape().dims, vec![1]); // Special case: becomes [1] not []
assert_eq!(max_idx.get(&[0]), 2.0); // Index 2 has maximum value 8.0
Source§

impl Tensor

Source

pub fn argmin(&self) -> Tensor

Returns the index of the minimum value in the tensor

This method finds the flat index of the minimum value across all elements in the tensor. The result is a scalar tensor containing the index as a floating-point value. This operation is non-differentiable and the output never requires gradient tracking.

§Returns

A tensor with shape [1] containing the flat index of the minimum value as a f32. If the input tensor is empty, returns 0.0.

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[3.0, -2.0, 5.0, -1.0], vec![4]).unwrap();
let min_index = tensor.argmin();
assert_eq!(min_index.get(&[0]), 1.0); // -2.0 is at index 1
use train_station::Tensor;

// Empty tensor case
let empty_tensor = Tensor::new(vec![0]);
let min_index = empty_tensor.argmin();
assert_eq!(min_index.get(&[0]), 0.0);
Source

pub fn argmin_dim(&self, dim: usize, keepdim: bool) -> Tensor

Returns the indices of minimum values along a specified dimension

This method finds the indices of minimum values along the specified dimension. The result contains the indices where the minimum values occur in that dimension. This operation is non-differentiable and the output never requires gradient tracking.

§Arguments
  • dim - The dimension along which to find minimum indices (0-based)
  • keepdim - Whether to keep the reduced dimension in the output shape
    • If true, the reduced dimension is kept with size 1
    • If false, the reduced dimension is removed from the output shape
§Returns

A tensor containing the indices of minimum values along the specified dimension. The output shape depends on keepdim:

  • If keepdim is true, the reduced dimension has size 1
  • If keepdim is false, the reduced dimension is removed
§Panics
  • If dim is out of bounds for the tensor’s rank
  • If the dimension to reduce has size 0
§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[3.0, -2.0, 5.0, -1.0, 0.0, -3.0], vec![2, 3]).unwrap();

// Find minimum indices along dimension 1 (columns), keeping the dimension
let indices = tensor.argmin_dim(1, true);
assert_eq!(indices.shape().dims, vec![2, 1]);
assert_eq!(indices.get(&[0, 0]), 1.0); // -2.0 is at index 1 in first row
assert_eq!(indices.get(&[1, 0]), 2.0); // -3.0 is at index 2 in second row
use train_station::Tensor;

let tensor = Tensor::from_slice(&[3.0, -2.0, 5.0, -1.0, 0.0, -3.0], vec![2, 3]).unwrap();

// Find minimum indices along dimension 1 (columns), removing the dimension
let indices = tensor.argmin_dim(1, false);
assert_eq!(indices.shape().dims, vec![2]);
assert_eq!(indices.get(&[0]), 1.0); // -2.0 is at index 1 in first row
assert_eq!(indices.get(&[1]), 2.0); // -3.0 is at index 2 in second row
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();

// Find minimum index in a 1D tensor
let index = tensor.argmin_dim(0, false);
assert_eq!(index.shape().dims, vec![1]);
assert_eq!(index.get(&[0]), 0.0); // 1.0 is at index 0
Source§

impl Tensor

Source

pub fn max(&self) -> Tensor

Computes the maximum value over all elements in the tensor

Returns a scalar tensor containing the maximum value. For empty tensors, returns negative infinity. This operation supports gradient tracking through the GradTrack system.

§Returns

A tensor with shape [1] containing the maximum value

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 5.0, 3.0, 2.0], vec![2, 2]).unwrap();
let max_val = tensor.max();
assert_eq!(max_val.get(&[0]), 5.0);
§GradTrack Support

When requires_grad is true, this operation is tracked for automatic differentiation. The gradient computation uses the saved input and output for efficient backward pass.

Source

pub fn max_dims(&self, dims: &[usize], keepdim: bool) -> Tensor

Computes the maximum value over specified dimensions

Reduces the tensor along the specified dimensions by computing the maximum value in each reduction group. The keepdim parameter determines whether reduced dimensions are kept with size 1 or removed entirely.

§Arguments
  • dims - Dimensions to reduce over (must be valid for the tensor’s rank)
  • keepdim - If true, reduced dimensions are kept with size 1; if false, they are removed
§Returns

A tensor with the specified dimensions reduced

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();

// Max over columns (dim 1), keeping dimensions
let max_cols = tensor.max_dims(&[1], true);
assert_eq!(max_cols.shape().dims, vec![2, 1]);
assert_eq!(max_cols.get(&[0, 0]), 3.0);
assert_eq!(max_cols.get(&[1, 0]), 6.0);

// Max over rows (dim 0), removing dimensions
let max_rows = tensor.max_dims(&[0], false);
assert_eq!(max_rows.shape().dims, vec![3]);
assert_eq!(max_rows.get(&[0]), 4.0);
assert_eq!(max_rows.get(&[1]), 5.0);
assert_eq!(max_rows.get(&[2]), 6.0);
§Panics

Panics if:

  • dims is empty
  • Any dimension in dims is out of bounds for the tensor’s rank
§GradTrack Support

When requires_grad is true, this operation is tracked for automatic differentiation. The gradient computation preserves the original input shape and handles broadcasting correctly.

Source§

impl Tensor

Source

pub fn mean(&self) -> Tensor

Computes the arithmetic mean of all elements in the tensor

This method calculates the average value across all tensor elements by summing all values and dividing by the total number of elements. The result is a scalar tensor containing the mean value. This operation supports gradient tracking through the GradTrack system.

§Returns

A tensor with shape [1] containing the arithmetic mean of all elements. For empty tensors, returns 0.0 as a safe default.

§Performance Characteristics
  • Linear Time: O(n) complexity for computing the sum
  • Memory Efficient: Single pass through tensor data with SIMD-optimized accumulation
  • Numerical Stability: Uses direct accumulation for typical tensor sizes
  • Edge Case Handling: Returns 0.0 for empty tensors
§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let mean_val = tensor.mean();
assert_eq!(mean_val.get(&[0]), 2.5); // (1+2+3+4)/4 = 2.5
use train_station::Tensor;

// Empty tensor case
let empty_tensor = Tensor::new(vec![0]);
let mean_val = empty_tensor.mean();
assert_eq!(mean_val.get(&[0]), 0.0);
§GradTrack Support

When requires_grad is true, this operation is tracked for automatic differentiation. The gradient computation distributes the gradient equally across all input elements.

Examples found in repository?
examples/getting_started/tensor_basics.rs (line 194)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
More examples
Hide additional examples
examples/optimizers/adam_configurations.rs (line 112)
84fn demonstrate_default_adam() -> Result<(), Box<dyn std::error::Error>> {
85    println!("--- Default Adam Configuration ---");
86
87    // Create a simple regression problem: y = 2*x + 1
88    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
89    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
90
91    // Create model parameters
92    let mut weight = Tensor::randn(vec![1, 1], Some(42)).with_requires_grad();
93    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
94
95    // Create Adam optimizer with default configuration
96    let mut optimizer = Adam::new();
97    optimizer.add_parameter(&weight);
98    optimizer.add_parameter(&bias);
99
100    println!("Default Adam configuration:");
101    println!("  Learning rate: {}", optimizer.learning_rate());
102    println!("  Initial weight: {:.6}", weight.value());
103    println!("  Initial bias: {:.6}", bias.value());
104
105    // Training loop
106    let num_epochs = 50;
107    let mut losses = Vec::new();
108
109    for epoch in 0..num_epochs {
110        // Forward pass
111        let y_pred = x_data.matmul(&weight) + &bias;
112        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
113
114        // Backward pass
115        loss.backward(None);
116
117        // Optimizer step
118        optimizer.step(&mut [&mut weight, &mut bias]);
119        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
120
121        losses.push(loss.value());
122
123        if epoch % 10 == 0 || epoch == num_epochs - 1 {
124            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
125        }
126    }
127
128    // Evaluate final model
129    let _final_predictions = x_data.matmul(&weight) + &bias;
130    println!("\nFinal model:");
131    println!("  Learned weight: {:.6} (target: 2.0)", weight.value());
132    println!("  Learned bias: {:.6} (target: 1.0)", bias.value());
133    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
134
135    Ok(())
136}
137
138/// Demonstrate learning rate comparison
139fn demonstrate_learning_rate_comparison() -> Result<(), Box<dyn std::error::Error>> {
140    println!("\n--- Learning Rate Comparison ---");
141
142    let learning_rates = [0.001, 0.01, 0.1];
143    let mut results = Vec::new();
144
145    for &lr in &learning_rates {
146        println!("\nTesting learning rate: {}", lr);
147
148        let stats = train_with_config(TrainingConfig {
149            learning_rate: lr,
150            ..Default::default()
151        })?;
152
153        results.push((lr, stats.clone()));
154
155        println!("  Final loss: {:.6}", stats.final_loss);
156        println!("  Convergence epoch: {}", stats.convergence_epoch);
157    }
158
159    // Compare results
160    println!("\nLearning Rate Comparison Summary:");
161    for (lr, stats) in &results {
162        println!(
163            "  LR={:6}: Loss={:.6}, Converged@{}",
164            lr, stats.final_loss, stats.convergence_epoch
165        );
166    }
167
168    Ok(())
169}
170
171/// Demonstrate weight decay comparison
172fn demonstrate_weight_decay_comparison() -> Result<(), Box<dyn std::error::Error>> {
173    println!("\n--- Weight Decay Comparison ---");
174
175    let weight_decays = [0.0, 0.001, 0.01];
176    let mut results = Vec::new();
177
178    for &wd in &weight_decays {
179        println!("\nTesting weight decay: {}", wd);
180
181        let stats = train_with_config(TrainingConfig {
182            weight_decay: wd,
183            ..Default::default()
184        })?;
185
186        results.push((wd, stats.clone()));
187
188        println!("  Final loss: {:.6}", stats.final_loss);
189        println!("  Final weight norm: {:.6}", stats.weight_norm);
190    }
191
192    // Compare results
193    println!("\nWeight Decay Comparison Summary:");
194    for (wd, stats) in &results {
195        println!(
196            "  WD={:6}: Loss={:.6}, Weight Norm={:.6}",
197            wd, stats.final_loss, stats.weight_norm
198        );
199    }
200
201    Ok(())
202}
203
204/// Demonstrate beta parameter tuning
205fn demonstrate_beta_parameter_tuning() -> Result<(), Box<dyn std::error::Error>> {
206    println!("\n--- Beta Parameter Tuning ---");
207
208    let beta_configs = [
209        (0.9, 0.999),  // Default
210        (0.8, 0.999),  // More aggressive momentum
211        (0.95, 0.999), // Less aggressive momentum
212        (0.9, 0.99),   // Faster second moment decay
213    ];
214
215    let mut results = Vec::new();
216
217    for (i, (beta1, beta2)) in beta_configs.iter().enumerate() {
218        println!(
219            "\nTesting beta configuration {}: beta1={}, beta2={}",
220            i + 1,
221            beta1,
222            beta2
223        );
224
225        let config = TrainingConfig {
226            beta1: *beta1,
227            beta2: *beta2,
228            ..Default::default()
229        };
230
231        let stats = train_with_config(config)?;
232        results.push(((*beta1, *beta2), stats.clone()));
233
234        println!("  Final loss: {:.6}", stats.final_loss);
235        println!("  Convergence epoch: {}", stats.convergence_epoch);
236    }
237
238    // Compare results
239    println!("\nBeta Parameter Comparison Summary:");
240    for ((beta1, beta2), stats) in &results {
241        println!(
242            "  B1={:4}, B2={:5}: Loss={:.6}, Converged@{}",
243            beta1, beta2, stats.final_loss, stats.convergence_epoch
244        );
245    }
246
247    Ok(())
248}
249
250/// Demonstrate configuration benchmarking
251fn demonstrate_configuration_benchmarking() -> Result<(), Box<dyn std::error::Error>> {
252    println!("\n--- Configuration Benchmarking ---");
253
254    // Define configurations to benchmark
255    let configs = vec![
256        (
257            "Conservative",
258            TrainingConfig {
259                learning_rate: 0.001,
260                weight_decay: 0.001,
261                beta1: 0.95,
262                ..Default::default()
263            },
264        ),
265        (
266            "Balanced",
267            TrainingConfig {
268                learning_rate: 0.01,
269                weight_decay: 0.0,
270                beta1: 0.9,
271                ..Default::default()
272            },
273        ),
274        (
275            "Aggressive",
276            TrainingConfig {
277                learning_rate: 0.1,
278                weight_decay: 0.0,
279                beta1: 0.8,
280                ..Default::default()
281            },
282        ),
283    ];
284
285    let mut benchmark_results = Vec::new();
286
287    for (name, config) in configs {
288        println!("\nBenchmarking {} configuration:", name);
289
290        let start_time = std::time::Instant::now();
291        let stats = train_with_config(config.clone())?;
292        let elapsed = start_time.elapsed();
293
294        println!("  Training time: {:.2}ms", elapsed.as_millis());
295        println!("  Final loss: {:.6}", stats.final_loss);
296        println!("  Convergence: {} epochs", stats.convergence_epoch);
297
298        benchmark_results.push((name.to_string(), stats, elapsed));
299    }
300
301    // Summary
302    println!("\nBenchmarking Summary:");
303    for (name, stats, elapsed) in &benchmark_results {
304        println!(
305            "  {:12}: Loss={:.6}, Time={:4}ms, Converged@{}",
306            name,
307            stats.final_loss,
308            elapsed.as_millis(),
309            stats.convergence_epoch
310        );
311    }
312
313    Ok(())
314}
315
316/// Helper function to train with specific configuration
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
examples/optimizers/learning_rate_scheduling.rs (line 344)
319fn train_with_scheduler(
320    scheduler: &mut dyn LearningRateScheduler,
321    num_epochs: usize,
322) -> Result<TrainingStats, Box<dyn std::error::Error>> {
323    // Create training data: y = 2*x + 1
324    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
325    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
326
327    // Create model parameters
328    let mut weight = Tensor::randn(vec![1, 1], Some(456)).with_requires_grad();
329    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
330
331    // Create optimizer with initial learning rate
332    let mut optimizer = Adam::with_learning_rate(0.05);
333    optimizer.add_parameter(&weight);
334    optimizer.add_parameter(&bias);
335
336    // Training loop
337    let mut losses = Vec::new();
338    let mut lr_history = Vec::new();
339    let mut convergence_epoch = num_epochs;
340
341    for epoch in 0..num_epochs {
342        // Forward pass
343        let y_pred = x_data.matmul(&weight) + &bias;
344        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
345
346        // Backward pass
347        loss.backward(None);
348
349        // Update learning rate using scheduler
350        let current_lr = optimizer.learning_rate();
351        let new_lr = scheduler.step(current_lr, epoch, loss.value());
352
353        if (new_lr - current_lr).abs() > 1e-8 {
354            optimizer.set_learning_rate(new_lr);
355        }
356
357        // Optimizer step
358        optimizer.step(&mut [&mut weight, &mut bias]);
359        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
360
361        let loss_value = loss.value();
362        losses.push(loss_value);
363        lr_history.push(new_lr);
364
365        // Check for convergence
366        if loss_value < 0.01 && convergence_epoch == num_epochs {
367            convergence_epoch = epoch;
368        }
369    }
370
371    Ok(TrainingStats {
372        scheduler_name: scheduler.name().to_string(),
373        final_loss: losses[losses.len() - 1],
374        lr_history,
375        loss_history: losses,
376        convergence_epoch,
377    })
378}
examples/getting_started/optimizer_basics.rs (line 135)
105fn demonstrate_linear_regression() -> Result<(), Box<dyn std::error::Error>> {
106    println!("\n--- Linear Regression Training ---");
107
108    // Create model parameters
109    let mut weight = Tensor::randn(vec![1, 1], Some(43)).with_requires_grad();
110    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
111
112    // Create optimizer
113    let mut optimizer = Adam::with_learning_rate(0.01);
114    optimizer.add_parameter(&weight);
115    optimizer.add_parameter(&bias);
116
117    // Create simple training data: y = 2*x + 1
118    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
119    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
120
121    println!("Training data:");
122    println!("  X: {:?}", x_data.data());
123    println!("  Y: {:?}", y_true.data());
124    println!("  Target: y = 2*x + 1");
125
126    // Training loop
127    let num_epochs = 100;
128    let mut losses = Vec::new();
129
130    for epoch in 0..num_epochs {
131        // Forward pass: y_pred = x * weight + bias
132        let y_pred = x_data.matmul(&weight) + &bias;
133
134        // Compute loss: MSE
135        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
136
137        // Backward pass
138        loss.backward(None);
139
140        // Optimizer step
141        optimizer.step(&mut [&mut weight, &mut bias]);
142        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
143
144        losses.push(loss.value());
145
146        // Print progress every 20 epochs
147        if epoch % 20 == 0 || epoch == num_epochs - 1 {
148            println!("Epoch {:3}: Loss = {:.6}", epoch, loss.value());
149        }
150    }
151
152    // Evaluate final model
153    let final_predictions = x_data.matmul(&weight) + &bias;
154    println!("\nFinal model evaluation:");
155    println!("  Learned weight: {:.6}", weight.value());
156    println!("  Learned bias: {:.6}", bias.value());
157    println!("  Predictions vs True:");
158
159    for i in 0..5 {
160        let x1 = x_data.data()[i];
161        let pred = final_predictions.data()[i];
162        let true_val = y_true.data()[i];
163        println!(
164            "    x={:.1}: pred={:.3}, true={:.1}, error={:.3}",
165            x1,
166            pred,
167            true_val,
168            (pred - true_val).abs()
169        );
170    }
171
172    Ok(())
173}
174
175/// Demonstrate advanced training patterns
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
examples/neural_networks/feedforward_network.rs (line 479)
430fn demonstrate_training_workflow() -> Result<(), Box<dyn std::error::Error>> {
431    println!("\n--- Training Workflow ---");
432
433    // Create a simple classification network
434    let config = FeedForwardConfig {
435        input_size: 2,
436        hidden_sizes: vec![4, 3],
437        output_size: 1,
438        use_bias: true,
439    };
440    let mut network = FeedForwardNetwork::new(config, Some(46));
441
442    println!("Training network: 2 -> [4, 3] -> 1");
443
444    // Create simple binary classification data: XOR problem
445    let x_data = Tensor::from_slice(
446        &[
447            0.0, 0.0, // -> 0
448            0.0, 1.0, // -> 1
449            1.0, 0.0, // -> 1
450            1.0, 1.0, // -> 0
451        ],
452        vec![4, 2],
453    )
454    .unwrap();
455
456    let y_true = Tensor::from_slice(&[0.0, 1.0, 1.0, 0.0], vec![4, 1]).unwrap();
457
458    println!("Training on XOR problem:");
459    println!("  Input shape: {:?}", x_data.shape().dims);
460    println!("  Target shape: {:?}", y_true.shape().dims);
461
462    // Create optimizer
463    let mut optimizer = Adam::with_learning_rate(0.1);
464    let params = network.parameters();
465    for param in &params {
466        optimizer.add_parameter(param);
467    }
468
469    // Training loop
470    let num_epochs = 50;
471    let mut losses = Vec::new();
472
473    for epoch in 0..num_epochs {
474        // Forward pass
475        let y_pred = network.forward(&x_data);
476
477        // Compute loss: MSE
478        let diff = y_pred.sub_tensor(&y_true);
479        let mut loss = diff.pow_scalar(2.0).mean();
480
481        // Backward pass
482        loss.backward(None);
483
484        // Optimizer step and zero grad
485        let mut params = network.parameters();
486        optimizer.step(&mut params);
487        optimizer.zero_grad(&mut params);
488
489        losses.push(loss.value());
490
491        // Print progress
492        if epoch % 10 == 0 || epoch == num_epochs - 1 {
493            println!("Epoch {:2}: Loss = {:.6}", epoch, loss.value());
494        }
495    }
496
497    // Test final model
498    let final_predictions = network.forward_no_grad(&x_data);
499    println!("\nFinal predictions vs targets:");
500    for i in 0..4 {
501        let pred = final_predictions.data()[i];
502        let target = y_true.data()[i];
503        let input_x = x_data.data()[i * 2];
504        let input_y = x_data.data()[i * 2 + 1];
505        println!(
506            "  [{:.0}, {:.0}] -> pred: {:.3}, target: {:.0}, error: {:.3}",
507            input_x,
508            input_y,
509            pred,
510            target,
511            (pred - target).abs()
512        );
513    }
514
515    Ok(())
516}
517
518/// Demonstrate comprehensive training with 100+ steps
519fn demonstrate_comprehensive_training() -> Result<(), Box<dyn std::error::Error>> {
520    println!("\n--- Comprehensive Training (100+ Steps) ---");
521
522    // Create a regression network
523    let config = FeedForwardConfig {
524        input_size: 3,
525        hidden_sizes: vec![8, 6, 4],
526        output_size: 2,
527        use_bias: true,
528    };
529    let mut network = FeedForwardNetwork::new(config, Some(47));
530
531    println!("Network architecture: 3 -> [8, 6, 4] -> 2");
532    println!("Total parameters: {}", network.parameter_count());
533
534    // Create synthetic regression data
535    // Target function: [y1, y2] = [x1 + 2*x2 - x3, x1*x2 + x3]
536    let num_samples = 32;
537    let mut x_vec = Vec::new();
538    let mut y_vec = Vec::new();
539
540    for i in 0..num_samples {
541        let x1 = (i as f32 / num_samples as f32) * 2.0 - 1.0; // [-1, 1]
542        let x2 = ((i * 2) as f32 / num_samples as f32) * 2.0 - 1.0;
543        let x3 = ((i * 3) as f32 / num_samples as f32) * 2.0 - 1.0;
544
545        let y1 = x1 + 2.0 * x2 - x3;
546        let y2 = x1 * x2 + x3;
547
548        x_vec.extend_from_slice(&[x1, x2, x3]);
549        y_vec.extend_from_slice(&[y1, y2]);
550    }
551
552    let x_data = Tensor::from_slice(&x_vec, vec![num_samples, 3]).unwrap();
553    let y_true = Tensor::from_slice(&y_vec, vec![num_samples, 2]).unwrap();
554
555    println!("Training data:");
556    println!("  {} samples", num_samples);
557    println!("  Input shape: {:?}", x_data.shape().dims);
558    println!("  Target shape: {:?}", y_true.shape().dims);
559
560    // Create optimizer with learning rate scheduling
561    let mut optimizer = Adam::with_learning_rate(0.01);
562    let params = network.parameters();
563    for param in &params {
564        optimizer.add_parameter(param);
565    }
566
567    // Comprehensive training loop (150 epochs)
568    let num_epochs = 150;
569    let mut losses = Vec::new();
570    let mut best_loss = f32::INFINITY;
571    let mut patience_counter = 0;
572    let patience = 20;
573
574    println!("Starting comprehensive training...");
575
576    for epoch in 0..num_epochs {
577        // Forward pass
578        let y_pred = network.forward(&x_data);
579
580        // Compute loss: MSE
581        let diff = y_pred.sub_tensor(&y_true);
582        let mut loss = diff.pow_scalar(2.0).mean();
583
584        // Backward pass
585        loss.backward(None);
586
587        // Optimizer step and zero grad
588        let mut params = network.parameters();
589        optimizer.step(&mut params);
590        optimizer.zero_grad(&mut params);
591
592        let current_loss = loss.value();
593        losses.push(current_loss);
594
595        // Learning rate scheduling
596        if epoch > 0 && epoch % 30 == 0 {
597            let new_lr = optimizer.learning_rate() * 0.8;
598            optimizer.set_learning_rate(new_lr);
599            println!("  Reduced learning rate to {:.4}", new_lr);
600        }
601
602        // Early stopping logic
603        if current_loss < best_loss {
604            best_loss = current_loss;
605            patience_counter = 0;
606        } else {
607            patience_counter += 1;
608        }
609
610        // Print progress
611        if epoch % 25 == 0 || epoch == num_epochs - 1 {
612            println!(
613                "Epoch {:3}: Loss = {:.6}, LR = {:.4}, Best = {:.6}",
614                epoch,
615                current_loss,
616                optimizer.learning_rate(),
617                best_loss
618            );
619        }
620
621        // Early stopping
622        if patience_counter >= patience && epoch > 50 {
623            println!("Early stopping at epoch {} (patience exceeded)", epoch);
624            break;
625        }
626    }
627
628    // Final evaluation
629    let final_predictions = network.forward_no_grad(&x_data);
630
631    // Compute final metrics
632    let final_loss = losses[losses.len() - 1];
633    let initial_loss = losses[0];
634    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
635
636    println!("\nTraining completed!");
637    println!("  Initial loss: {:.6}", initial_loss);
638    println!("  Final loss: {:.6}", final_loss);
639    println!("  Best loss: {:.6}", best_loss);
640    println!("  Loss reduction: {:.1}%", loss_reduction);
641    println!("  Final learning rate: {:.4}", optimizer.learning_rate());
642
643    // Sample predictions analysis
644    println!("\nSample predictions (first 5):");
645    for i in 0..5.min(num_samples) {
646        let pred1 = final_predictions.data()[i * 2];
647        let pred2 = final_predictions.data()[i * 2 + 1];
648        let true1 = y_true.data()[i * 2];
649        let true2 = y_true.data()[i * 2 + 1];
650
651        println!(
652            "  Sample {}: pred=[{:.3}, {:.3}], true=[{:.3}, {:.3}], error=[{:.3}, {:.3}]",
653            i + 1,
654            pred1,
655            pred2,
656            true1,
657            true2,
658            (pred1 - true1).abs(),
659            (pred2 - true2).abs()
660        );
661    }
662
663    Ok(())
664}
665
666/// Demonstrate network serialization
667fn demonstrate_network_serialization() -> Result<(), Box<dyn std::error::Error>> {
668    println!("\n--- Network Serialization ---");
669
670    // Create and train a network
671    let config = FeedForwardConfig {
672        input_size: 2,
673        hidden_sizes: vec![4, 2],
674        output_size: 1,
675        use_bias: true,
676    };
677    let mut original_network = FeedForwardNetwork::new(config.clone(), Some(48));
678
679    // Quick training
680    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
681    let y_true = Tensor::from_slice(&[5.0, 11.0], vec![2, 1]).unwrap();
682
683    let mut optimizer = Adam::with_learning_rate(0.01);
684    let params = original_network.parameters();
685    for param in &params {
686        optimizer.add_parameter(param);
687    }
688
689    for _ in 0..20 {
690        let y_pred = original_network.forward(&x_data);
691        let mut loss = (y_pred.sub_tensor(&y_true)).pow_scalar(2.0).mean();
692        loss.backward(None);
693
694        let mut params = original_network.parameters();
695        optimizer.step(&mut params);
696        optimizer.zero_grad(&mut params);
697    }
698
699    // Test original network
700    let test_input = Tensor::from_slice(&[1.0, 1.0], vec![1, 2]).unwrap();
701    let original_output = original_network.forward_no_grad(&test_input);
702
703    println!("Original network output: {:?}", original_output.data());
704
705    // Save network
706    original_network.save_json("temp_feedforward_network")?;
707
708    // Load network
709    let loaded_network = FeedForwardNetwork::load_json("temp_feedforward_network", config)?;
710    let loaded_output = loaded_network.forward_no_grad(&test_input);
711
712    println!("Loaded network output: {:?}", loaded_output.data());
713
714    // Verify consistency
715    let match_check = original_output
716        .data()
717        .iter()
718        .zip(loaded_output.data().iter())
719        .all(|(a, b)| (a - b).abs() < 1e-6);
720
721    println!(
722        "Serialization verification: {}",
723        if match_check { "PASSED" } else { "FAILED" }
724    );
725
726    Ok(())
727}
examples/iterators/advanced_patterns.rs (line 97)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
156
157/// Demonstrate conditional processing patterns
158///
159/// Shows how to implement dynamic filtering and transformation
160/// based on data characteristics and conditions.
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source

pub fn mean_dims(&self, dims: &[usize], keepdim: bool) -> Tensor

Computes the arithmetic mean over specified dimensions

This method calculates the mean value along the specified dimensions by first computing the sum over those dimensions and then dividing by the product of the reduced dimension sizes. The keepdim parameter determines whether reduced dimensions are kept with size 1 or removed entirely.

§Arguments
  • dims - Dimensions to reduce over (must be valid for the tensor’s rank)
  • keepdim - If true, reduced dimensions are kept with size 1; if false, they are removed
§Returns

A tensor with the specified dimensions reduced by computing the mean. The output shape depends on keepdim:

  • If keepdim is true, reduced dimensions have size 1
  • If keepdim is false, reduced dimensions are removed
§Performance Characteristics
  • Efficient Implementation: Uses sum_dims followed by scalar multiplication
  • Memory Optimized: Leverages existing sum reduction for optimal performance
  • Shape Computation: Fast output shape calculation with dimension preservation
  • Numerical Stability: Maintains precision through direct computation
§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();

// Mean over columns (dim 1), keeping dimensions
let mean_cols = tensor.mean_dims(&[1], true);
assert_eq!(mean_cols.shape().dims, vec![2, 1]);
assert_eq!(mean_cols.get(&[0, 0]), 2.0); // (1+2+3)/3 = 2.0
assert_eq!(mean_cols.get(&[1, 0]), 5.0); // (4+5+6)/3 = 5.0
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();

// Mean over rows (dim 0), removing dimensions
let mean_rows = tensor.mean_dims(&[0], false);
assert_eq!(mean_rows.shape().dims, vec![3]);
assert_eq!(mean_rows.get(&[0]), 2.5); // (1+4)/2 = 2.5
assert_eq!(mean_rows.get(&[1]), 3.5); // (2+5)/2 = 3.5
assert_eq!(mean_rows.get(&[2]), 4.5); // (3+6)/2 = 4.5
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();

// Mean over multiple dimensions
let mean_all = tensor.mean_dims(&[0, 1], false);
assert_eq!(mean_all.shape().dims, vec![1]);
assert_eq!(mean_all.get(&[0]), 2.5); // (1+2+3+4)/4 = 2.5
§Panics

Panics if:

  • dims is empty
  • Any dimension in dims is out of bounds for the tensor’s rank
§GradTrack Support

When requires_grad is true, this operation is tracked for automatic differentiation. The gradient computation preserves the original input shape and handles broadcasting correctly through the ReduceMeanDims gradient function.

Source§

impl Tensor

Source

pub fn min(&self) -> Tensor

Computes the minimum value over all elements in the tensor

Returns a scalar tensor containing the minimum value. For empty tensors, returns positive infinity. This operation supports gradient tracking through the GradTrack system.

§Returns

A tensor with shape [1] containing the minimum value

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 5.0, 3.0, 2.0], vec![2, 2]).unwrap();
let min_val = tensor.min();
assert_eq!(min_val.get(&[0]), 1.0);
use train_station::Tensor;

// Empty tensor case
let empty_tensor = Tensor::new(vec![0]);
let min_val = empty_tensor.min();
assert_eq!(min_val.get(&[0]), f32::INFINITY);
§GradTrack Support

When requires_grad is true, this operation is tracked for automatic differentiation. The gradient computation uses the saved input and output for efficient backward pass.

Source

pub fn min_dims(&self, dims: &[usize], keepdim: bool) -> Tensor

Computes the minimum value over specified dimensions

Reduces the tensor along the specified dimensions by computing the minimum value in each reduction group. The keepdim parameter determines whether reduced dimensions are kept with size 1 or removed entirely.

§Arguments
  • dims - Dimensions to reduce over (must be valid for the tensor’s rank)
  • keepdim - If true, reduced dimensions are kept with size 1; if false, they are removed
§Returns

A tensor with the specified dimensions reduced

§Examples
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();

// Min over columns (dim 1), keeping dimensions
let min_cols = tensor.min_dims(&[1], true);
assert_eq!(min_cols.shape().dims, vec![2, 1]);
assert_eq!(min_cols.get(&[0, 0]), 1.0);
assert_eq!(min_cols.get(&[1, 0]), 4.0);
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();

// Min over rows (dim 0), removing dimensions
let min_rows = tensor.min_dims(&[0], false);
assert_eq!(min_rows.shape().dims, vec![3]);
assert_eq!(min_rows.get(&[0]), 1.0);
assert_eq!(min_rows.get(&[1]), 2.0);
assert_eq!(min_rows.get(&[2]), 3.0);
use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();

// Min over multiple dimensions
let min_all = tensor.min_dims(&[0, 1], false);
assert_eq!(min_all.shape().dims, vec![1]);
assert_eq!(min_all.get(&[0]), 1.0);
§Panics

Panics if:

  • dims is empty
  • Any dimension in dims is out of bounds for the tensor’s rank
§GradTrack Support

When requires_grad is true, this operation is tracked for automatic differentiation. The gradient computation preserves the original input shape and handles broadcasting correctly.

Source§

impl Tensor

Source

pub fn norm(&self) -> Tensor

Computes the L2 norm (Euclidean norm) over all elements

The L2 norm is calculated as sqrt(sum(x²)) where x represents each element in the tensor. This operation reduces the tensor to a scalar value [1].

§Returns

A scalar tensor containing the L2 norm value

§Examples
use train_station::Tensor;

// Basic L2 norm calculation
let tensor = Tensor::from_slice(&[3.0, 4.0], vec![2]).unwrap();
let norm = tensor.norm();
assert!((norm.get(&[0]) - 5.0).abs() < 1e-6); // sqrt(3² + 4²) = 5
use train_station::Tensor;

// L2 norm of a larger tensor
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let tensor = Tensor::from_slice(&data, vec![2, 2, 2]).unwrap();
let norm = tensor.norm();
// sqrt(1² + 2² + 3² + 4² + 5² + 6² + 7² + 8²) = sqrt(204) ≈ 14.283
let expected = 204.0_f32.sqrt();
assert!((norm.get(&[0]) - expected).abs() < 1e-5);
§Performance

Uses optimized contiguous tensor path with 4x loop unrolling for better performance. Non-contiguous tensors use stride-aware iteration.

Examples found in repository?
examples/getting_started/tensor_basics.rs (line 197)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
More examples
Hide additional examples
examples/optimizers/adam_configurations.rs (line 370)
317fn train_with_config(config: TrainingConfig) -> Result<TrainingStats, Box<dyn std::error::Error>> {
318    // Create training data
319    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
320    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0, 11.0], vec![5, 1]).unwrap();
321
322    // Create model parameters
323    let mut weight = Tensor::randn(vec![1, 1], Some(123)).with_requires_grad();
324    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
325
326    // Create optimizer with custom configuration
327    let adam_config = AdamConfig {
328        learning_rate: config.learning_rate,
329        beta1: config.beta1,
330        beta2: config.beta2,
331        eps: 1e-8,
332        weight_decay: config.weight_decay,
333        amsgrad: false,
334    };
335
336    let mut optimizer = Adam::with_config(adam_config);
337    optimizer.add_parameter(&weight);
338    optimizer.add_parameter(&bias);
339
340    // Training loop
341    let mut losses = Vec::new();
342    let mut convergence_epoch = config.epochs;
343
344    for epoch in 0..config.epochs {
345        // Forward pass
346        let y_pred = x_data.matmul(&weight) + &bias;
347        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
348
349        // Backward pass
350        loss.backward(None);
351
352        // Optimizer step
353        optimizer.step(&mut [&mut weight, &mut bias]);
354        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
355
356        let loss_value = loss.value();
357        losses.push(loss_value);
358
359        // Check for convergence (loss < 0.01)
360        if loss_value < 0.01 && convergence_epoch == config.epochs {
361            convergence_epoch = epoch;
362        }
363    }
364
365    Ok(TrainingStats {
366        config,
367        final_loss: losses[losses.len() - 1],
368        loss_history: losses,
369        convergence_epoch,
370        weight_norm: weight.norm().value(),
371    })
372}
examples/getting_started/optimizer_basics.rs (line 214)
176fn demonstrate_advanced_training() -> Result<(), Box<dyn std::error::Error>> {
177    println!("\n--- Advanced Training Patterns ---");
178
179    // Create a more complex model
180    let mut weight = Tensor::randn(vec![1, 2], Some(44)).with_requires_grad();
181    let mut bias = Tensor::zeros(vec![2]).with_requires_grad();
182
183    // Create optimizer with different learning rate
184    let mut optimizer = Adam::with_learning_rate(0.005);
185    optimizer.add_parameter(&weight);
186    optimizer.add_parameter(&bias);
187
188    // Create training data: y = 2*x + [1, 3]
189    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5, 1]).unwrap();
190    let y_true = Tensor::from_slice(
191        &[3.0, 5.0, 7.0, 9.0, 11.0, 6.0, 8.0, 10.0, 12.0, 14.0],
192        vec![5, 2],
193    )
194    .unwrap();
195
196    println!("Advanced training with monitoring:");
197    println!("  Initial learning rate: {}", optimizer.learning_rate());
198
199    // Training loop with monitoring
200    let num_epochs = 50;
201    let mut losses = Vec::new();
202    let mut weight_norms = Vec::new();
203    let mut gradient_norms = Vec::new();
204
205    for epoch in 0..num_epochs {
206        // Forward pass
207        let y_pred = x_data.matmul(&weight) + &bias;
208        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
209
210        // Backward pass
211        loss.backward(None);
212
213        // Compute gradient norm before optimizer step
214        let gradient_norm = weight.grad_by_value().unwrap().norm();
215
216        // Optimizer step
217        optimizer.step(&mut [&mut weight, &mut bias]);
218        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
219
220        // Learning rate scheduling: reduce every 10 epochs
221        if epoch > 0 && epoch % 10 == 0 {
222            let current_lr = optimizer.learning_rate();
223            let new_lr = current_lr * 0.5;
224            optimizer.set_learning_rate(new_lr);
225            println!(
226                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
227                epoch, current_lr, new_lr
228            );
229        }
230
231        // Record metrics
232        losses.push(loss.value());
233        weight_norms.push(weight.norm().value());
234        gradient_norms.push(gradient_norm.value());
235
236        // Print detailed progress
237        if epoch % 10 == 0 || epoch == num_epochs - 1 {
238            println!(
239                "Epoch {:2}: Loss = {:.6}, Weight Norm = {:.6}, Gradient Norm = {:.6}",
240                epoch,
241                loss.value(),
242                weight.norm().value(),
243                gradient_norm.value()
244            );
245        }
246    }
247
248    println!("Final learning rate: {}", optimizer.learning_rate());
249
250    // Analyze training progression
251    let initial_loss = losses[0];
252    let final_loss = losses[losses.len() - 1];
253    let loss_reduction = (initial_loss - final_loss) / initial_loss * 100.0;
254
255    println!("\nTraining Analysis:");
256    println!("  Initial loss: {:.6}", initial_loss);
257    println!("  Final loss: {:.6}", final_loss);
258    println!("  Loss reduction: {:.1}%", loss_reduction);
259    println!("  Final weight norm: {:.6}", weight.norm().value());
260    println!("  Final bias: {:?}", bias.data());
261
262    Ok(())
263}
264
265/// Demonstrate learning rate scheduling
266fn demonstrate_learning_rate_scheduling() -> Result<(), Box<dyn std::error::Error>> {
267    println!("\n--- Learning Rate Scheduling ---");
268
269    // Create simple model
270    let mut weight = Tensor::randn(vec![1, 1], Some(45)).with_requires_grad();
271    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
272
273    // Create optimizer with high initial learning rate
274    let mut optimizer = Adam::with_learning_rate(0.1);
275    optimizer.add_parameter(&weight);
276    optimizer.add_parameter(&bias);
277
278    // Simple data
279    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3, 1]).unwrap();
280    let y_true = Tensor::from_slice(&[2.0, 4.0, 6.0], vec![3, 1]).unwrap();
281
282    println!("Initial learning rate: {}", optimizer.learning_rate());
283
284    // Training loop with learning rate scheduling
285    let num_epochs = 50;
286    let mut losses = Vec::new();
287
288    for epoch in 0..num_epochs {
289        // Forward pass
290        let y_pred = x_data.matmul(&weight) + &bias;
291        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
292
293        // Backward pass
294        loss.backward(None);
295
296        // Optimizer step
297        optimizer.step(&mut [&mut weight, &mut bias]);
298        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
299
300        // Learning rate scheduling: reduce every 10 epochs
301        if epoch > 0 && epoch % 10 == 0 {
302            let current_lr = optimizer.learning_rate();
303            let new_lr = current_lr * 0.5;
304            optimizer.set_learning_rate(new_lr);
305            println!(
306                "Epoch {:2}: Reduced learning rate from {:.3} to {:.3}",
307                epoch, current_lr, new_lr
308            );
309        }
310
311        losses.push(loss.value());
312
313        // Print progress
314        if epoch % 10 == 0 || epoch == num_epochs - 1 {
315            println!(
316                "Epoch {:2}: Loss = {:.6}, LR = {:.3}",
317                epoch,
318                loss.value(),
319                optimizer.learning_rate()
320            );
321        }
322    }
323
324    println!("Final learning rate: {}", optimizer.learning_rate());
325
326    Ok(())
327}
328
329/// Demonstrate training monitoring and analysis
330fn demonstrate_training_monitoring() -> Result<(), Box<dyn std::error::Error>> {
331    println!("\n--- Training Monitoring ---");
332
333    // Create model
334    let mut weight = Tensor::randn(vec![1, 1], Some(46)).with_requires_grad();
335    let mut bias = Tensor::zeros(vec![1]).with_requires_grad();
336
337    // Create optimizer
338    let mut optimizer = Adam::with_learning_rate(0.01);
339    optimizer.add_parameter(&weight);
340    optimizer.add_parameter(&bias);
341
342    // Training data
343    let x_data = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4, 1]).unwrap();
344    let y_true = Tensor::from_slice(&[3.0, 5.0, 7.0, 9.0], vec![4, 1]).unwrap();
345
346    // Training loop with comprehensive monitoring
347    let num_epochs = 30;
348    let mut losses = Vec::new();
349    let mut weight_history = Vec::new();
350    let mut bias_history = Vec::new();
351
352    for epoch in 0..num_epochs {
353        // Forward pass
354        let y_pred = x_data.matmul(&weight) + &bias;
355        let mut loss = (&y_pred - &y_true).pow_scalar(2.0).mean();
356
357        // Backward pass
358        loss.backward(None);
359
360        // Optimizer step
361        optimizer.step(&mut [&mut weight, &mut bias]);
362        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
363
364        // Record history
365        losses.push(loss.value());
366        weight_history.push(weight.value());
367        bias_history.push(bias.value());
368
369        // Print detailed monitoring
370        if epoch % 5 == 0 || epoch == num_epochs - 1 {
371            println!(
372                "Epoch {:2}: Loss = {:.6}, Weight = {:.6}, Bias = {:.6}",
373                epoch,
374                loss.value(),
375                weight.value(),
376                bias.value()
377            );
378        }
379    }
380
381    // Analyze training progression
382    println!("\nTraining Analysis:");
383    println!("  Initial loss: {:.6}", losses[0]);
384    println!("  Final loss: {:.6}", losses[losses.len() - 1]);
385    println!(
386        "  Loss reduction: {:.1}%",
387        (losses[0] - losses[losses.len() - 1]) / losses[0] * 100.0
388    );
389
390    // Compute statistics
391    let loss_mean = compute_mean(&losses);
392    let loss_std = compute_std(&losses);
393    let weight_change = (weight_history[weight_history.len() - 1] - weight_history[0]).abs();
394    let bias_change = (bias_history[bias_history.len() - 1] - bias_history[0]).abs();
395
396    println!("  Average loss: {:.6} ± {:.6}", loss_mean, loss_std);
397    println!("  Weight change: {:.6}", weight_change);
398    println!("  Bias change: {:.6}", bias_change);
399    println!("  Final weight norm: {:.6}", weight.norm().value());
400    println!("  Final bias: {:.6}", bias.value());
401
402    Ok(())
403}
Source

pub fn norm_dims(&self, dims: &[usize], keepdim: bool) -> Tensor

Computes the L2 norm over specified dimensions

Reduces the tensor along the specified dimensions by computing the L2 norm of each slice. The result maintains the original tensor structure with reduced dimensions optionally preserved as size-1 dimensions.

§Arguments
  • dims - Vector of dimension indices to reduce over (must be valid for tensor rank)
  • keepdim - Whether to keep reduced dimensions as size-1 dimensions
§Returns

A tensor with L2 norm computed over the specified dimensions

§Examples
use train_station::Tensor;

// Norm along rows (dimension 1) with keepdim=true
let matrix = Tensor::from_slice(&[3.0, 4.0, 0.0, 5.0], vec![2, 2]).unwrap();
let row_norms = matrix.norm_dims(&[1], true);
assert_eq!(row_norms.shape().dims, vec![2, 1]);
assert!((row_norms.get(&[0, 0]) - 5.0).abs() < 1e-6); // sqrt(3² + 4²)
assert!((row_norms.get(&[1, 0]) - 5.0).abs() < 1e-6); // sqrt(0² + 5²)
use train_station::Tensor;

// Norm along columns (dimension 0) with keepdim=false
let matrix = Tensor::from_slice(&[3.0, 4.0, 0.0, 5.0], vec![2, 2]).unwrap();
let col_norms = matrix.norm_dims(&[0], false);
assert_eq!(col_norms.shape().dims, vec![2]);
assert!((col_norms.get(&[0]) - 3.0).abs() < 1e-6); // sqrt(3² + 0²)
assert!((col_norms.get(&[1]) - 6.403).abs() < 1e-3); // sqrt(4² + 5²)
use train_station::Tensor;

// Norm over multiple dimensions
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let norm_all = tensor.norm_dims(&[0, 1], false);
assert_eq!(norm_all.shape().dims, vec![1]);
// sqrt(1² + 2² + 3² + 4²) = sqrt(30) ≈ 5.477
assert!((norm_all.get(&[0]) - 30.0_f32.sqrt()).abs() < 1e-5);
§Panics
  • If dims is empty
  • If any dimension index is out of bounds for the tensor rank
§Performance

Uses efficient coordinate-based iteration that works correctly with both contiguous and non-contiguous tensor layouts.

Source§

impl Tensor

Source

pub fn std(&self) -> Tensor

Computes the standard deviation over all elements

The standard deviation is calculated as sqrt(variance) where variance is the mean of squared differences from the mean. This operation reduces the tensor to a scalar value [1].

The implementation uses population standard deviation (divides by n rather than n-1) to match PyTorch’s default behavior.

§Returns

A scalar tensor containing the standard deviation value

§Examples
use train_station::Tensor;

// Basic standard deviation calculation
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
let std_dev = tensor.std();
assert!((std_dev.get(&[0]) - 1.118_034).abs() < 1e-5);
use train_station::Tensor;

// Standard deviation of a larger dataset
let data = vec![1.0, 3.0, 5.0, 7.0, 2.0, 4.0, 6.0, 8.0];
let tensor = Tensor::from_slice(&data, vec![2, 2, 2]).unwrap();
let std_dev = tensor.std();
// mean=4.5, var=5.25, std=sqrt(5.25)≈2.291
let expected = 5.25_f32.sqrt();
assert!((std_dev.get(&[0]) - expected).abs() < 1e-5);
use train_station::Tensor;

// Standard deviation of constant values (should be 0)
let tensor = Tensor::from_slice(&[5.0, 5.0, 5.0, 5.0], vec![4]).unwrap();
let std_dev = tensor.std();
assert!((std_dev.get(&[0]) - 0.0).abs() < 1e-6);
§Performance

Uses optimized contiguous tensor path with 4x loop unrolling for better performance. Non-contiguous tensors use stride-aware iteration. The algorithm performs two passes: first to compute the mean, then to compute the variance.

Examples found in repository?
examples/iterators/advanced_patterns.rs (line 98)
74fn demonstrate_data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
75    println!("\n--- Data Processing Pipeline ---");
76
77    // Simulate raw sensor data with noise
78    let raw_data: Vec<f32> = (0..20)
79        .map(|i| {
80            let base = i as f32 * 0.5;
81            let noise = (i % 3) as f32 * 0.1;
82            base + noise
83        })
84        .collect();
85
86    let tensor = Tensor::from_slice(&raw_data, vec![20])?;
87    println!("Raw sensor data: {:?}", tensor.data());
88
89    // Multi-stage processing pipeline
90    println!("\nProcessing pipeline:");
91    println!("1. Normalize data (z-score)");
92    println!("2. Apply smoothing filter");
93    println!("3. Detect outliers");
94    println!("4. Apply feature scaling");
95
96    // Stage 1: Normalization
97    let mean = tensor.mean().value();
98    let std = tensor.std().value();
99    let normalized: Tensor = tensor
100        .iter()
101        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
102        .collect();
103    println!(
104        "  Normalized (mean={:.3}, std={:.3}): {:?}",
105        mean,
106        std,
107        normalized.data()
108    );
109
110    // Stage 2: Smoothing (simple moving average)
111    let smoothed: Tensor = normalized
112        .iter()
113        .enumerate()
114        .map(|(i, elem)| {
115            if i == 0 || i == normalized.size() - 1 {
116                elem.clone()
117            } else {
118                // Simple 3-point average
119                let prev = normalized.element_view(i - 1);
120                let next = normalized.element_view(i + 1);
121                elem.add_tensor(&prev).add_tensor(&next).div_scalar(3.0)
122            }
123        })
124        .collect();
125    println!("  Smoothed: {:?}", smoothed.data());
126
127    // Stage 3: Outlier detection and removal
128    let outlier_threshold = 2.0;
129    let cleaned: Tensor = smoothed
130        .iter()
131        .filter(|elem| elem.value().abs() < outlier_threshold)
132        .collect();
133    println!(
134        "  Outliers removed (threshold={}): {:?}",
135        outlier_threshold,
136        cleaned.data()
137    );
138
139    // Stage 4: Feature scaling to [0, 1] range
140    let min_val = cleaned
141        .iter()
142        .map(|e| e.value())
143        .fold(f32::INFINITY, f32::min);
144    let max_val = cleaned
145        .iter()
146        .map(|e| e.value())
147        .fold(f32::NEG_INFINITY, f32::max);
148    let scaled: Tensor = cleaned
149        .iter()
150        .map(|elem| elem.sub_scalar(min_val).div_scalar(max_val - min_val))
151        .collect();
152    println!("  Scaled to [0,1]: {:?}", scaled.data());
153
154    Ok(())
155}
156
157/// Demonstrate conditional processing patterns
158///
159/// Shows how to implement dynamic filtering and transformation
160/// based on data characteristics and conditions.
161fn demonstrate_conditional_processing() -> Result<(), Box<dyn std::error::Error>> {
162    println!("\n--- Conditional Processing ---");
163
164    // Create data with mixed characteristics
165    let data = vec![1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0, -10.0];
166    let tensor = Tensor::from_slice(&data, vec![10])?;
167    println!("Input data: {:?}", tensor.data());
168
169    // Conditional transformation based on sign
170    println!("\nConditional transformation (positive/negative handling):");
171    let processed: Tensor = tensor
172        .iter()
173        .map(|elem| {
174            let val = elem.value();
175            if val > 0.0 {
176                elem.pow_scalar(2.0) // Square positive values
177            } else {
178                elem.mul_scalar(-1.0).sqrt() // Square root of absolute negative values
179            }
180        })
181        .collect();
182    println!("  Processed: {:?}", processed.data());
183
184    // Adaptive filtering based on local statistics
185    println!("\nAdaptive filtering (remove values > 2 std from local mean):");
186    let window_size = 3;
187    let adaptive_filtered: Tensor = tensor
188        .iter()
189        .enumerate()
190        .filter(|(i, elem)| {
191            let start = i.saturating_sub(window_size / 2);
192            let end = (i + window_size / 2 + 1).min(tensor.size());
193
194            // Calculate local mean and std
195            let local_values: Vec<f32> = (start..end)
196                .map(|j| tensor.element_view(j).value())
197                .collect();
198
199            let local_mean = local_values.iter().sum::<f32>() / local_values.len() as f32;
200            let local_variance = local_values
201                .iter()
202                .map(|v| (v - local_mean).powi(2))
203                .sum::<f32>()
204                / local_values.len() as f32;
205            let local_std = local_variance.sqrt();
206
207            let threshold = local_mean + 2.0 * local_std;
208            elem.value() <= threshold
209        })
210        .map(|(_, elem)| elem)
211        .collect();
212    println!("  Adaptive filtered: {:?}", adaptive_filtered.data());
213
214    // Multi-condition processing
215    println!("\nMulti-condition processing:");
216    let multi_processed: Tensor = tensor
217        .iter()
218        .map(|elem| {
219            let val = elem.value();
220            match () {
221                _ if val > 5.0 => elem.mul_scalar(2.0), // Double large values
222                _ if val < -5.0 => elem.div_scalar(2.0), // Halve small values
223                _ if val.abs() < 2.0 => elem.add_scalar(1.0), // Add 1 to small values
224                _ => elem.clone(),                      // Keep others unchanged
225            }
226        })
227        .collect();
228    println!("  Multi-condition: {:?}", multi_processed.data());
229
230    Ok(())
231}
232
233/// Demonstrate batch processing operations
234///
235/// Shows efficient processing of large datasets using iterator
236/// patterns and batch operations for performance optimization.
237fn demonstrate_batch_operations() -> Result<(), Box<dyn std::error::Error>> {
238    println!("\n--- Batch Operations ---");
239
240    // Create a larger dataset for batch processing
241    let size = 100;
242    let data: Vec<f32> = (0..size)
243        .map(|i| {
244            let x = i as f32 / size as f32;
245            x * x + 0.1 * (i % 7) as f32 // Quadratic with some noise
246        })
247        .collect();
248
249    let tensor = Tensor::from_slice(&data, vec![size])?;
250    println!("Dataset size: {}", tensor.size());
251
252    // Batch processing with windowing
253    println!("\nBatch processing with sliding windows:");
254    let batch_size = 10;
255    let batches: Vec<Tensor> = tensor
256        .iter()
257        .collect::<Vec<_>>()
258        .chunks(batch_size)
259        .map(|chunk| {
260            // Process each batch independently
261            chunk
262                .iter()
263                .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
264                .collect()
265        })
266        .collect();
267
268    println!(
269        "  Processed {} batches of size {}",
270        batches.len(),
271        batch_size
272    );
273    for (i, batch) in batches.iter().enumerate() {
274        println!(
275            "    Batch {}: mean={:.3}, std={:.3}",
276            i,
277            batch.mean().value(),
278            batch.std().value()
279        );
280    }
281
282    // Parallel-like processing with stride
283    println!("\nStrided processing (every nth element):");
284    let stride = 5;
285    let strided: Tensor = tensor
286        .iter()
287        .enumerate()
288        .filter(|(i, _)| i % stride == 0)
289        .map(|(_, elem)| elem)
290        .collect();
291    println!("  Strided (every {}th): {:?}", stride, strided.data());
292
293    // Hierarchical processing
294    println!("\nHierarchical processing (coarse to fine):");
295    let coarse: Tensor = tensor
296        .iter()
297        .enumerate()
298        .filter(|(i, _)| i % 4 == 0) // Take every 4th element
299        .map(|(_, elem)| elem)
300        .collect();
301
302    let fine: Tensor = tensor
303        .iter()
304        .enumerate()
305        .filter(|(i, _)| i % 4 != 0) // Take the rest
306        .map(|(_, elem)| elem)
307        .collect();
308
309    println!("  Coarse (every 4th): {:?}", coarse.data());
310    println!("  Fine (rest): {:?}", fine.data());
311
312    // Combine coarse and fine with different processing
313    let combined: Tensor = coarse
314        .iter()
315        .map(|elem| elem.mul_scalar(2.0)) // Scale coarse
316        .chain(fine.iter().map(|elem| elem.div_scalar(2.0))) // Scale fine
317        .collect();
318    println!("  Combined: {:?}", combined.data());
319
320    Ok(())
321}
322
323/// Demonstrate real-world processing scenarios
324///
325/// Shows practical applications of iterator patterns for
326/// common data processing tasks in machine learning and analytics.
327fn demonstrate_real_world_scenarios() -> Result<(), Box<dyn std::error::Error>> {
328    println!("\n--- Real-world Scenarios ---");
329
330    // Scenario 1: Time series analysis
331    println!("\nScenario 1: Time Series Analysis");
332    let time_series: Vec<f32> = (0..24)
333        .map(|hour| {
334            let base = 20.0 + 10.0 * (hour as f32 * std::f32::consts::PI / 12.0).sin();
335            base + (hour % 3) as f32 * 2.0 // Add some noise
336        })
337        .collect();
338
339    let series = Tensor::from_slice(&time_series, vec![24])?;
340    println!("  Time series (24 hours): {:?}", series.data());
341
342    // Calculate moving average
343    let window_size = 3;
344    let moving_avg: Tensor = series
345        .iter()
346        .enumerate()
347        .map(|(i, _)| {
348            let start = i.saturating_sub(window_size / 2);
349            let end = (i + window_size / 2 + 1).min(series.size());
350            let window = series.iter_range(start, end);
351            window.fold(0.0, |acc, elem| acc + elem.value()) / (end - start) as f32
352        })
353        .map(|val| Tensor::from_slice(&[val], vec![1]).unwrap())
354        .collect();
355    println!(
356        "  Moving average (window={}): {:?}",
357        window_size,
358        moving_avg.data()
359    );
360
361    // Scenario 2: Feature engineering
362    println!("\nScenario 2: Feature Engineering");
363    let features = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![5])?;
364    println!("  Original features: {:?}", features.data());
365
366    // Create polynomial features
367    let poly_features: Tensor = features
368        .iter()
369        .flat_map(|elem| {
370            vec![
371                elem.clone(),         // x^1
372                elem.pow_scalar(2.0), // x^2
373                elem.pow_scalar(3.0), // x^3
374            ]
375        })
376        .collect();
377    println!(
378        "  Polynomial features (x, x^2, x^3): {:?}",
379        poly_features.data()
380    );
381
382    // Scenario 3: Data augmentation
383    println!("\nScenario 3: Data Augmentation");
384    let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?;
385    println!("  Original data: {:?}", original.data());
386
387    // Augment with noise and scaling
388    let augmented: Tensor = original
389        .iter()
390        .flat_map(|elem| {
391            vec![
392                elem.clone(),         // Original
393                elem.add_scalar(0.1), // Add noise
394                elem.sub_scalar(0.1), // Subtract noise
395                elem.mul_scalar(1.1), // Scale up
396                elem.mul_scalar(0.9), // Scale down
397            ]
398        })
399        .collect();
400    println!("  Augmented data: {:?}", augmented.data());
401
402    // Scenario 4: Statistical analysis
403    println!("\nScenario 4: Statistical Analysis");
404    let sample_data = Tensor::from_slice(&[1.1, 2.3, 1.8, 2.1, 1.9, 2.0, 1.7, 2.2], vec![8])?;
405    println!("  Sample data: {:?}", sample_data.data());
406
407    // Calculate various statistics
408    let mean = sample_data.mean().value();
409    let std = sample_data.std().value();
410    let min = sample_data
411        .iter()
412        .map(|e| e.value())
413        .fold(f32::INFINITY, f32::min);
414    let max = sample_data
415        .iter()
416        .map(|e| e.value())
417        .fold(f32::NEG_INFINITY, f32::max);
418
419    // Z-score normalization
420    let z_scores: Tensor = sample_data
421        .iter()
422        .map(|elem| elem.sub_scalar(mean).div_scalar(std))
423        .collect();
424
425    println!(
426        "  Statistics: mean={:.3}, std={:.3}, min={:.3}, max={:.3}",
427        mean, std, min, max
428    );
429    println!("  Z-scores: {:?}", z_scores.data());
430
431    Ok(())
432}
Source

pub fn std_dims(&self, dims: &[usize], keepdim: bool) -> Tensor

Computes the standard deviation over specified dimensions

Reduces the tensor along the specified dimensions by computing the standard deviation of each slice. The result maintains the original tensor structure with reduced dimensions optionally preserved as size-1 dimensions.

Uses population standard deviation (divides by n rather than n-1) to match PyTorch’s default behavior.

§Arguments
  • dims - Vector of dimension indices to reduce over (must be valid for tensor rank)
  • keepdim - Whether to keep reduced dimensions as size-1 dimensions
§Returns

A tensor with standard deviation computed over the specified dimensions

§Examples
use train_station::Tensor;

// Standard deviation along rows (dimension 1) with keepdim=true
let matrix = Tensor::from_slice(&[1.0, 3.0, 2.0, 2.0], vec![2, 2]).unwrap();
let row_stds = matrix.std_dims(&[1], true);
assert_eq!(row_stds.shape().dims, vec![2, 1]);
assert!((row_stds.get(&[0, 0]) - 1.0).abs() < 1e-6); // std([1, 3]) = 1.0
assert!((row_stds.get(&[1, 0]) - 0.0).abs() < 1e-6); // std([2, 2]) = 0.0
use train_station::Tensor;

// Standard deviation along columns (dimension 0) with keepdim=false
let matrix = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let col_stds = matrix.std_dims(&[0], false);
assert_eq!(col_stds.shape().dims, vec![2]);
// std([1, 3]) = 1.0, std([2, 4]) = 1.0
assert!((col_stds.get(&[0]) - 1.0).abs() < 1e-6);
assert!((col_stds.get(&[1]) - 1.0).abs() < 1e-6);
use train_station::Tensor;

// Standard deviation over multiple dimensions
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let std_all = tensor.std_dims(&[0, 1], false);
assert_eq!(std_all.shape().dims, vec![1]);
// std([1, 2, 3, 4]) = sqrt(1.25) ≈ 1.118
assert!((std_all.get(&[0]) - 1.25_f32.sqrt()).abs() < 1e-5);
§Panics
  • If dims is empty
  • If any dimension index is out of bounds for the tensor rank
  • If the reduced size is 0 (invalid for standard deviation calculation)
§Performance

Uses efficient coordinate-based iteration that works correctly with both contiguous and non-contiguous tensor layouts. The algorithm performs two passes: first to compute means, then to compute variances.

Source§

impl Tensor

Source

pub fn sum(&self) -> Tensor

Returns the sum of all elements in the tensor

This operation computes the sum of all elements across all dimensions, reducing the tensor to a scalar value. The output is a tensor with shape [1] containing the sum as a float.

When requires_grad is enabled, this operation supports automatic gradient tracking through the GradTrack system.

§Returns

A tensor with shape [1] containing the sum of all elements

§Examples
use train_station::Tensor;

// Basic sum calculation
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let total = tensor.sum();
assert_eq!(total.shape().dims, vec![1]);
assert_eq!(total.get(&[0]), 10.0); // 1 + 2 + 3 + 4 = 10
use train_station::Tensor;

// Sum with gradient tracking
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])
    .unwrap()
    .with_requires_grad();
let mut total = tensor.sum();
total.backward(None);
let grad = tensor.grad_by_value().expect("gradient should exist");
// Gradient should be [1.0, 1.0, 1.0] for each element
assert_eq!(grad.get(&[0]), 1.0);
assert_eq!(grad.get(&[1]), 1.0);
assert_eq!(grad.get(&[2]), 1.0);
use train_station::Tensor;

// Sum of empty tensor
let tensor = Tensor::new(vec![0]);
let total = tensor.sum();
assert_eq!(total.get(&[0]), 0.0); // Sum of empty tensor is 0
§Performance

Uses optimized contiguous tensor path with 4x loop unrolling for better performance. Non-contiguous tensors use stride-aware iteration.

Examples found in repository?
examples/getting_started/tensor_basics.rs (line 191)
179fn demonstrate_utility_functions() {
180    println!("\n--- Utility Functions ---");
181
182    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
183
184    // Basic properties
185    println!("Shape: {:?}", tensor.shape().dims);
186    println!("Size: {}", tensor.size());
187    println!("Is contiguous: {}", tensor.is_contiguous());
188    println!("Device: {:?}", tensor.device());
189
190    // Mathematical operations
191    let sum = tensor.sum();
192    println!("Sum: {}", sum.value());
193
194    let mean = tensor.mean();
195    println!("Mean: {}", mean.value());
196
197    let norm = tensor.norm();
198    println!("Norm: {}", norm.value());
199
200    // Device placement
201    let cpu_tensor = Tensor::zeros_on_device(vec![3, 3], train_station::Device::cpu());
202    println!(
203        "CPU tensor: shape {:?}, device: {:?}",
204        cpu_tensor.shape().dims,
205        cpu_tensor.device()
206    );
207}
More examples
Hide additional examples
examples/iterators/element_iteration.rs (line 179)
159fn demonstrate_gradient_tracking() -> Result<(), Box<dyn std::error::Error>> {
160    println!("\n--- Gradient Tracking ---");
161
162    // Create a tensor with gradient tracking enabled
163    let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3])?.with_requires_grad();
164    println!("Input tensor (requires_grad): {:?}", tensor.data());
165
166    // Perform element-wise operations through iteration
167    let result: Tensor = tensor
168        .iter()
169        .map(|elem| {
170            // Apply a complex transformation: (x^2 + 1) * 2
171            elem.pow_scalar(2.0).add_scalar(1.0).mul_scalar(2.0)
172        })
173        .collect();
174
175    println!("Result tensor: {:?}", result.data());
176    println!("Result requires_grad: {}", result.requires_grad());
177
178    // Compute gradients
179    let mut loss = result.sum();
180    loss.backward(None);
181
182    println!("Loss: {:.6}", loss.value());
183    println!("Input gradients: {:?}", tensor.grad().map(|g| g.data()));
184
185    Ok(())
186}
examples/getting_started/serialization_basics.rs (line 137)
109fn demonstrate_optimizer_serialization() -> Result<(), Box<dyn std::error::Error>> {
110    println!("\n--- Optimizer Serialization ---");
111
112    // Create an optimizer with some parameters
113    let mut weight = Tensor::randn(vec![2, 2], Some(42)).with_requires_grad();
114    let mut bias = Tensor::randn(vec![2], Some(43)).with_requires_grad();
115
116    let config = AdamConfig {
117        learning_rate: 0.001,
118        beta1: 0.9,
119        beta2: 0.999,
120        eps: 1e-8,
121        weight_decay: 0.0,
122        amsgrad: false,
123    };
124
125    let mut optimizer = Adam::with_config(config);
126    optimizer.add_parameter(&weight);
127    optimizer.add_parameter(&bias);
128
129    println!(
130        "Created optimizer with {} parameters",
131        optimizer.parameter_count()
132    );
133    println!("Learning rate: {}", optimizer.learning_rate());
134
135    // Simulate some training steps
136    for _ in 0..3 {
137        let mut loss = weight.sum() + bias.sum();
138        loss.backward(None);
139        optimizer.step(&mut [&mut weight, &mut bias]);
140        optimizer.zero_grad(&mut [&mut weight, &mut bias]);
141    }
142
143    // Save optimizer state
144    let optimizer_path = "temp_optimizer.json";
145    optimizer.save_json(optimizer_path)?;
146    println!("Saved optimizer to: {}", optimizer_path);
147
148    // Load optimizer state
149    let loaded_optimizer = Adam::load_json(optimizer_path)?;
150    println!(
151        "Loaded optimizer with {} parameters",
152        loaded_optimizer.parameter_count()
153    );
154    println!("Learning rate: {}", loaded_optimizer.learning_rate());
155
156    // Verify optimizer state
157    assert_eq!(
158        optimizer.parameter_count(),
159        loaded_optimizer.parameter_count()
160    );
161    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
162    println!("Optimizer serialization verification: PASSED");
163
164    Ok(())
165}
166
167/// Demonstrate format comparison and performance characteristics
168fn demonstrate_format_comparison() -> Result<(), Box<dyn std::error::Error>> {
169    println!("\n--- Format Comparison ---");
170
171    // Create a larger tensor for comparison
172    let tensor = Tensor::randn(vec![10, 10], Some(44));
173
174    // Save in both formats
175    tensor.save_json("temp_comparison.json")?;
176    tensor.save_binary("temp_comparison.bin")?;
177
178    // Compare file sizes
179    let json_size = fs::metadata("temp_comparison.json")?.len();
180    let binary_size = fs::metadata("temp_comparison.bin")?.len();
181
182    println!("JSON file size: {} bytes", json_size);
183    println!("Binary file size: {} bytes", binary_size);
184    println!(
185        "Compression ratio: {:.2}x",
186        json_size as f64 / binary_size as f64
187    );
188
189    // Load and verify both formats
190    let json_tensor = Tensor::load_json("temp_comparison.json")?;
191    let binary_tensor = Tensor::load_binary("temp_comparison.bin")?;
192
193    assert_eq!(tensor.shape().dims, json_tensor.shape().dims);
194    assert_eq!(tensor.shape().dims, binary_tensor.shape().dims);
195    assert_eq!(tensor.data(), json_tensor.data());
196    assert_eq!(tensor.data(), binary_tensor.data());
197
198    println!("Format comparison verification: PASSED");
199
200    Ok(())
201}
202
203/// Demonstrate a basic model checkpointing workflow
204fn demonstrate_model_checkpointing() -> Result<(), Box<dyn std::error::Error>> {
205    println!("\n--- Model Checkpointing ---");
206
207    // Create a simple model (weights and bias)
208    let mut weights = Tensor::randn(vec![2, 1], Some(45)).with_requires_grad();
209    let mut bias = Tensor::randn(vec![1], Some(46)).with_requires_grad();
210
211    // Create optimizer
212    let mut optimizer = Adam::with_learning_rate(0.01);
213    optimizer.add_parameter(&weights);
214    optimizer.add_parameter(&bias);
215
216    println!("Initial weights: {:?}", weights.data());
217    println!("Initial bias: {:?}", bias.data());
218
219    // Simulate training
220    for epoch in 0..5 {
221        let mut loss = weights.sum() + bias.sum();
222        loss.backward(None);
223        optimizer.step(&mut [&mut weights, &mut bias]);
224        optimizer.zero_grad(&mut [&mut weights, &mut bias]);
225
226        if epoch % 2 == 0 {
227            // Save checkpoint
228            let checkpoint_dir = format!("checkpoint_epoch_{}", epoch);
229            fs::create_dir_all(&checkpoint_dir)?;
230
231            weights.save_json(format!("{}/weights.json", checkpoint_dir))?;
232            bias.save_json(format!("{}/bias.json", checkpoint_dir))?;
233            optimizer.save_json(format!("{}/optimizer.json", checkpoint_dir))?;
234
235            println!("Saved checkpoint for epoch {}", epoch);
236        }
237    }
238
239    // Load from checkpoint
240    let loaded_weights = Tensor::load_json("checkpoint_epoch_4/weights.json")?;
241    let loaded_bias = Tensor::load_json("checkpoint_epoch_4/bias.json")?;
242    let loaded_optimizer = Adam::load_json("checkpoint_epoch_4/optimizer.json")?;
243
244    println!("Loaded weights: {:?}", loaded_weights.data());
245    println!("Loaded bias: {:?}", loaded_bias.data());
246    println!(
247        "Loaded optimizer learning rate: {}",
248        loaded_optimizer.learning_rate()
249    );
250
251    // Verify checkpoint integrity
252    assert_eq!(weights.shape().dims, loaded_weights.shape().dims);
253    assert_eq!(bias.shape().dims, loaded_bias.shape().dims);
254    assert_eq!(optimizer.learning_rate(), loaded_optimizer.learning_rate());
255
256    println!("Checkpointing verification: PASSED");
257
258    Ok(())
259}
examples/iterators/performance_optimization.rs (line 387)
297fn demonstrate_optimization_techniques() -> Result<(), Box<dyn std::error::Error>> {
298    println!("\n--- Optimization Techniques ---");
299
300    let size = 50000;
301    let data: Vec<f32> = (0..size).map(|i| i as f32).collect();
302    let tensor = Tensor::from_slice(&data, vec![size])?;
303
304    println!("Optimizing processing for size: {}", size);
305
306    // Technique 1: Operation fusion
307    println!("\nTechnique 1: Operation Fusion");
308    let start = Instant::now();
309    let fused_result: Tensor = tensor
310        .iter()
311        .map(|elem| {
312            // Fuse multiple operations into single chain
313            elem.mul_scalar(2.0).add_scalar(1.0).pow_scalar(2.0).sqrt()
314        })
315        .collect();
316    let fused_time = start.elapsed();
317
318    // Technique 2: Conditional optimization
319    println!("\nTechnique 2: Conditional Optimization");
320    let start = Instant::now();
321    let conditional_result: Tensor = tensor
322        .iter()
323        .map(|elem| {
324            let val = elem.value();
325            if val < size as f32 / 2.0 {
326                elem.mul_scalar(2.0) // Simple operation for small values
327            } else {
328                elem.pow_scalar(2.0).sqrt() // Complex operation for large values
329            }
330        })
331        .collect();
332    let conditional_time = start.elapsed();
333
334    // Technique 3: Cache-friendly processing
335    println!("\nTechnique 3: Cache-Friendly Processing");
336    let start = Instant::now();
337    let cache_friendly_result: Tensor = tensor
338        .iter()
339        .take(1000) // Process in cache-friendly chunks
340        .map(|elem| elem.mul_scalar(2.0))
341        .collect();
342    let cache_friendly_time = start.elapsed();
343
344    // Technique 4: Memory pooling simulation
345    println!("\nTechnique 4: Memory Pooling Simulation");
346    let start = Instant::now();
347    let pooled_result: Tensor = tensor
348        .iter()
349        .enumerate()
350        .filter(|(i, _)| i % 100 == 0) // Process every 100th element
351        .map(|(_, elem)| elem.pow_scalar(2.0))
352        .collect();
353    let pooled_time = start.elapsed();
354
355    // Report optimization results
356    println!("  Fused operations: {:?}", fused_time);
357    println!("  Conditional optimization: {:?}", conditional_time);
358    println!("  Cache-friendly processing: {:?}", cache_friendly_time);
359    println!("  Memory pooling simulation: {:?}", pooled_time);
360
361    // Performance analysis
362    let fastest = fused_time
363        .min(conditional_time)
364        .min(cache_friendly_time)
365        .min(pooled_time);
366    println!("  Fastest technique: {:?}", fastest);
367
368    // Memory efficiency analysis
369    println!("  Fused result size: {}", fused_result.size());
370    println!("  Conditional result size: {}", conditional_result.size());
371    println!(
372        "  Cache-friendly result size: {}",
373        cache_friendly_result.size()
374    );
375    println!("  Pooled result size: {}", pooled_result.size());
376
377    // Technique 5: Gradient optimization
378    println!("\nTechnique 5: Gradient Optimization");
379    let grad_tensor = tensor.with_requires_grad();
380    let start = Instant::now();
381
382    let grad_result: Tensor = grad_tensor
383        .iter()
384        .map(|elem| elem.pow_scalar(2.0).add_scalar(1.0))
385        .collect();
386
387    let mut loss = grad_result.sum();
388    loss.backward(None);
389    let grad_time = start.elapsed();
390
391    println!("  Gradient computation: {:?}", grad_time);
392    println!(
393        "  Gradient tracking enabled: {}",
394        grad_result.requires_grad()
395    );
396
397    Ok(())
398}
Source

pub fn sum_dims(&self, dims: &[usize], keepdim: bool) -> Tensor

Returns the sum of elements along specified dimensions

This operation computes the sum of elements along the specified dimensions, reducing the tensor while optionally preserving the reduced dimensions as size-1 dimensions.

The output shape depends on the keepdim parameter:

  • If keepdim is true, the reduced dimensions are kept with size 1
  • If keepdim is false, the reduced dimensions are removed

When requires_grad is enabled, this operation supports automatic gradient tracking through the GradTrack system.

§Arguments
  • dims - Vector of dimension indices to sum over (must be valid for tensor rank)
  • keepdim - Whether to keep reduced dimensions as size-1 dimensions
§Returns

A tensor with sum computed over the specified dimensions

§Panics
  • If dims is empty
  • If any dimension index is out of bounds for the tensor rank
§Examples
use train_station::Tensor;

// Sum along rows (dimension 0) with keepdim=false
let matrix = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let row_sums = matrix.sum_dims(&[0], false);
assert_eq!(row_sums.shape().dims, vec![2]);
assert_eq!(row_sums.get(&[0]), 4.0); // 1 + 3 = 4
assert_eq!(row_sums.get(&[1]), 6.0); // 2 + 4 = 6
use train_station::Tensor;

// Sum along columns (dimension 1) with keepdim=true
let matrix = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let col_sums = matrix.sum_dims(&[1], true);
assert_eq!(col_sums.shape().dims, vec![2, 1]);
assert_eq!(col_sums.get(&[0, 0]), 3.0); // 1 + 2 = 3
assert_eq!(col_sums.get(&[1, 0]), 7.0); // 3 + 4 = 7
use train_station::Tensor;

// Sum over multiple dimensions
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let total = tensor.sum_dims(&[0, 1], false);
assert_eq!(total.shape().dims, vec![1]);
assert_eq!(total.get(&[0]), 10.0); // 1 + 2 + 3 + 4 = 10
use train_station::Tensor;

// Sum with gradient tracking
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2])
    .unwrap()
    .with_requires_grad();
let mut row_sums = tensor.sum_dims(&[0], false);
row_sums.backward(None);
let grad = tensor.grad_by_value().expect("gradient should exist");
// Gradient should be [1.0, 1.0, 1.0, 1.0] for each element
assert_eq!(grad.get(&[0, 0]), 1.0);
assert_eq!(grad.get(&[0, 1]), 1.0);
assert_eq!(grad.get(&[1, 0]), 1.0);
assert_eq!(grad.get(&[1, 1]), 1.0);
§Performance

Uses efficient coordinate-based iteration that works correctly with both contiguous and non-contiguous tensor layouts.

Source§

impl Tensor

Source

pub fn var(&self) -> Tensor

Computes the variance over all elements

The variance is calculated as the mean of squared differences from the mean. This operation reduces the tensor to a scalar value [1].

The implementation uses population variance (divides by n rather than n-1) to match PyTorch’s default behavior.

§Returns

A scalar tensor containing the variance value

§Examples
use train_station::Tensor;

// Basic variance calculation
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
let variance = tensor.var();
assert!((variance.get(&[0]) - 1.25).abs() < 1e-5);
use train_station::Tensor;

// Variance of a larger dataset
let data = vec![1.0, 3.0, 5.0, 7.0, 2.0, 4.0, 6.0, 8.0];
let tensor = Tensor::from_slice(&data, vec![2, 2, 2]).unwrap();
let variance = tensor.var();
// mean=4.5, var=mean([3.5², 1.5², 0.5², 2.5², 2.5², 0.5², 1.5², 3.5²]) = 5.25
assert!((variance.get(&[0]) - 5.25).abs() < 1e-5);
use train_station::Tensor;

// Variance of constant values (should be 0)
let tensor = Tensor::from_slice(&[5.0, 5.0, 5.0, 5.0], vec![4]).unwrap();
let variance = tensor.var();
assert!((variance.get(&[0]) - 0.0).abs() < 1e-6);
§Performance

Uses optimized contiguous tensor path with manual loop unrolling for better performance. Non-contiguous tensors use stride-aware iteration. The algorithm performs two passes: first to compute the mean, then to compute the variance.

Source

pub fn var_dims(&self, dims: &[usize], keepdim: bool) -> Tensor

Computes the variance over specified dimensions

Reduces the tensor along the specified dimensions by computing the variance of each slice. The result maintains the original tensor structure with reduced dimensions optionally preserved as size-1 dimensions.

Uses population variance (divides by n rather than n-1) to match PyTorch’s default behavior.

§Arguments
  • dims - Vector of dimension indices to reduce over (must be valid for tensor rank)
  • keepdim - Whether to keep reduced dimensions as size-1 dimensions
§Returns

A tensor with variance computed over the specified dimensions

§Examples
use train_station::Tensor;

// Variance along rows (dimension 1) with keepdim=true
let matrix = Tensor::from_slice(&[1.0, 3.0, 2.0, 2.0], vec![2, 2]).unwrap();
let row_vars = matrix.var_dims(&[1], true);
assert_eq!(row_vars.shape().dims, vec![2, 1]);
assert!((row_vars.get(&[0, 0]) - 1.0).abs() < 1e-6); // var([1, 3]) = 1.0
assert!((row_vars.get(&[1, 0]) - 0.0).abs() < 1e-6); // var([2, 2]) = 0.0
use train_station::Tensor;

// Variance along columns (dimension 0) with keepdim=false
let matrix = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let col_vars = matrix.var_dims(&[0], false);
assert_eq!(col_vars.shape().dims, vec![2]);
// var([1, 3]) = 1.0, var([2, 4]) = 1.0
assert!((col_vars.get(&[0]) - 1.0).abs() < 1e-6);
assert!((col_vars.get(&[1]) - 1.0).abs() < 1e-6);
use train_station::Tensor;

// Variance over multiple dimensions
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let var_all = tensor.var_dims(&[0, 1], false);
assert_eq!(var_all.shape().dims, vec![1]);
// var([1, 2, 3, 4]) = 1.25
assert!((var_all.get(&[0]) - 1.25).abs() < 1e-5);
§Panics
  • If dims is empty
  • If any dimension index is out of bounds for the tensor rank
  • If the reduced size is 0 (invalid for variance calculation)
§Performance

Uses efficient coordinate-based iteration that works correctly with both contiguous and non-contiguous tensor layouts. The algorithm performs two passes: first to compute means, then to compute variances.

Source§

impl Tensor

Source

pub fn cat(tensors: &[Tensor], dim: usize) -> Tensor

Concatenate tensors along a given dimension

Joins multiple tensors along the specified dimension, creating a new tensor with the combined data. All input tensors must have the same rank and matching dimensions except for the concatenation dimension.

§Arguments
  • tensors - Slice of tensors to concatenate (must not be empty)
  • dim - Dimension along which to concatenate (must be < tensor rank)
§Returns

A new tensor containing the concatenated data with shape where the concatenation dimension is the sum of all input tensor sizes along that dimension.

§Panics
  • If tensors is empty
  • If dim is out of bounds for the tensor rank
  • If tensors have different ranks
  • If tensors have mismatched dimensions (except along concatenation dimension)
§Examples
use train_station::Tensor;

// Concatenate 1D tensors
let a = Tensor::from_slice(&[1.0, 2.0], vec![2]).unwrap();
let b = Tensor::from_slice(&[3.0, 4.0], vec![2]).unwrap();
let result = Tensor::cat(&[a, b], 0);
assert_eq!(result.shape().dims, vec![4]);
assert_eq!(result.get(&[0]), 1.0);
assert_eq!(result.get(&[1]), 2.0);
assert_eq!(result.get(&[2]), 3.0);
assert_eq!(result.get(&[3]), 4.0);
use train_station::Tensor;

// Concatenate 2D tensors along dimension 1
let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let b = Tensor::from_slice(&[5.0, 6.0], vec![2, 1]).unwrap();
let result = Tensor::cat(&[a, b], 1);
assert_eq!(result.shape().dims, vec![2, 3]);
assert_eq!(result.get(&[0, 0]), 1.0);
assert_eq!(result.get(&[0, 1]), 2.0);
assert_eq!(result.get(&[0, 2]), 5.0);
use train_station::Tensor;

// Concatenate with gradient tracking
let mut a = Tensor::from_slice(&[1.0, 2.0], vec![2]).unwrap();
let mut b = Tensor::from_slice(&[3.0, 4.0], vec![2]).unwrap();
a.set_requires_grad(true);
b.set_requires_grad(true);

let result = Tensor::cat(&[a, b], 0);
assert!(result.requires_grad());
Source§

impl Tensor

Source

pub fn contiguous(&self) -> Tensor

Creates a contiguous copy of the tensor

This operation ensures that the tensor data is stored in a linear, cache-friendly memory layout. If the tensor is already contiguous, this operation returns a clone. For non-contiguous tensors, it creates a new tensor with the same data but in contiguous memory layout.

The operation uses different optimization strategies based on tensor size:

  • Small tensors (≤64 elements): Simple coordinate-based copy
  • Medium tensors (65-1023 elements): Unrolled copy for better performance
  • Large tensors (≥1024 elements): Blocked copy with cache optimization
§Returns

A new tensor with contiguous memory layout containing the same data

§Examples
use train_station::Tensor;

// Already contiguous tensor
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let contiguous = tensor.contiguous();
assert!(contiguous.is_contiguous());
assert_eq!(contiguous.shape().dims, vec![2, 2]);
use train_station::Tensor;

// Non-contiguous tensor from transpose
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let transposed = tensor.transpose(0, 1);
assert!(!transposed.is_contiguous());

let contiguous = transposed.contiguous();
assert!(contiguous.is_contiguous());
assert_eq!(contiguous.get(&[0, 0]), 1.0);
assert_eq!(contiguous.get(&[0, 1]), 3.0);
use train_station::Tensor;

// Preserves gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
tensor.set_requires_grad(true);

let contiguous = tensor.contiguous();
assert!(contiguous.requires_grad());
§Performance
  • Already contiguous: O(1) time complexity, returns a clone
  • Non-contiguous: O(n) time complexity with size-dependent optimizations
  • Memory usage: Creates a new tensor with the same size as the original
Source§

impl Tensor

Source

pub fn flatten(&self) -> Tensor

Flatten the tensor into a 1D representation

Transforms a multi-dimensional tensor into a 1D tensor by reshaping all dimensions into a single dimension. This is equivalent to reshape(vec![-1]) where -1 automatically calculates the size based on the total number of elements.

The flatten operation preserves the total number of elements while changing the tensor’s shape to have a single dimension. This is commonly used in neural networks to prepare tensor data for linear layers or feature extraction.

§Returns

A 1D tensor containing the same data as the original tensor, with shape [total_elements] where total_elements is the product of all original dimensions.

§Examples
use train_station::Tensor;

// Flatten a 2D tensor
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let flattened = tensor.flatten();
assert_eq!(flattened.shape().dims, vec![4]);
assert_eq!(flattened.get(&[0]), 1.0);
assert_eq!(flattened.get(&[1]), 2.0);
assert_eq!(flattened.get(&[2]), 3.0);
assert_eq!(flattened.get(&[3]), 4.0);
use train_station::Tensor;

// Flatten a 3D tensor
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
let tensor = Tensor::from_slice(&data, vec![2, 2, 3]).unwrap();
let flattened = tensor.flatten();
assert_eq!(flattened.shape().dims, vec![12]);
assert_eq!(flattened.size(), 12);
use train_station::Tensor;

// Flatten with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
tensor.set_requires_grad(true);

let flattened = tensor.flatten();
assert!(flattened.requires_grad());
assert_eq!(flattened.shape().dims, vec![4]);
use train_station::Tensor;

// Flatten an already 1D tensor (no change)
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let flattened = tensor.flatten();
assert_eq!(flattened.shape().dims, vec![3]);
assert_eq!(flattened.size(), 3);
§Performance
  • Time Complexity: O(1) - Returns a view when possible
  • Memory Usage: No additional memory allocation for view operations
  • Gradient Tracking: Preserves gradient requirements and tracking
§Relationship to Other Operations

This operation is equivalent to:

use train_station::Tensor;

let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let flattened = tensor.reshape(vec![-1]);

Where -1 is a special value that automatically calculates the dimension size based on the total number of elements in the tensor.

Source§

impl Tensor

Source

pub fn permute(&self, dims: Vec<usize>) -> Tensor

Permute tensor dimensions according to specified order

Rearranges the dimensions of the tensor according to the provided dimension order. This operation returns a view with reordered strides, avoiding data copying while changing the logical arrangement of the tensor’s dimensions.

The permutation is specified as a vector where each element represents the new position of the corresponding dimension from the original tensor. For example, permute(vec![1, 0]) swaps the first two dimensions.

§Arguments
  • dims - Vector specifying the new order of dimensions (must have length equal to tensor rank)
§Returns

A new tensor view with rearranged dimensions and correspondingly adjusted strides. The total number of elements remains unchanged.

§Panics
  • If dims length does not equal the tensor rank
  • If any dimension index is out of bounds for the tensor rank
  • If dims contains duplicate dimension indices
§Examples
use train_station::Tensor;

// Permute 2D tensor (swap dimensions)
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
let permuted = tensor.permute(vec![1, 0]);
assert_eq!(permuted.shape().dims, vec![3, 2]);
assert_eq!(permuted.get(&[0, 0]), 1.0);
assert_eq!(permuted.get(&[1, 0]), 2.0);
assert_eq!(permuted.get(&[2, 1]), 6.0);
use train_station::Tensor;

// Permute 3D tensor (reorder dimensions)
let data: Vec<f32> = (0..24).map(|i| i as f32).collect();
let tensor = Tensor::from_slice(&data, vec![2, 3, 4]).unwrap();
let permuted = tensor.permute(vec![2, 0, 1]);
assert_eq!(permuted.shape().dims, vec![4, 2, 3]);
assert_eq!(permuted.size(), 24); // Total elements unchanged
use train_station::Tensor;

// Permute with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
tensor.set_requires_grad(true);

let permuted = tensor.permute(vec![1, 0]);
assert!(permuted.requires_grad());
assert_eq!(permuted.shape().dims, vec![2, 2]);
use train_station::Tensor;

// Identity permutation (no change)
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let permuted = tensor.permute(vec![0, 1]);
assert_eq!(permuted.shape().dims, vec![2, 2]);
assert_eq!(permuted.get(&[0, 0]), 1.0);
assert_eq!(permuted.get(&[1, 1]), 4.0);
§Performance
  • Time Complexity: O(1) - Returns a view with reordered strides
  • Memory Usage: No additional memory allocation (view operation)
  • Gradient Tracking: Preserves gradient requirements and tracking
§Relationship to Other Operations

This operation is similar to transpose() but more general:

  • transpose(dim0, dim1) is equivalent to permute() with a swap of two dimensions
  • permute() can handle arbitrary dimension reordering for tensors of any rank
§Memory Layout

The permuted tensor maintains the same underlying data but with reordered strides. This means the tensor becomes non-contiguous unless the permutation is the identity permutation.

Source§

impl Tensor

Source

pub fn reshape(&self, new_shape: Vec<i32>) -> Tensor

Reshape the tensor to the specified dimensions

Changes the shape of the tensor while preserving the total number of elements. This operation returns a view when the tensor is contiguous, avoiding data copying. For non-contiguous tensors, data is copied to ensure the reshape is valid.

The reshape operation supports automatic dimension inference using -1, which allows one dimension to be automatically calculated based on the total number of elements and the other specified dimensions.

§Arguments
  • new_shape - Target shape for the tensor. Use -1 for one dimension to have it automatically inferred from the total size.
§Returns

A new tensor with the specified shape containing the same data as the original tensor.

§Panics
  • If more than one dimension is -1
  • If the total number of elements doesn’t match the original tensor
  • If any dimension size is 0 or less than -1
  • If the inferred dimension size is not a whole number
§Examples
use train_station::Tensor;

// Basic reshape
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
let reshaped = tensor.reshape(vec![3, 2]);
assert_eq!(reshaped.shape().dims, vec![3, 2]);
assert_eq!(reshaped.get(&[0, 0]), 1.0);
assert_eq!(reshaped.get(&[2, 1]), 6.0);
use train_station::Tensor;

// Using -1 for automatic dimension inference
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![4]).unwrap();
let reshaped = tensor.reshape(vec![2, -1]);
assert_eq!(reshaped.shape().dims, vec![2, 2]);
assert_eq!(reshaped.get(&[0, 0]), 1.0);
assert_eq!(reshaped.get(&[1, 1]), 4.0);
use train_station::Tensor;

// Reshape with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
tensor.set_requires_grad(true);

let reshaped = tensor.reshape(vec![4]);
assert!(reshaped.requires_grad());
assert_eq!(reshaped.shape().dims, vec![4]);
use train_station::Tensor;

// Reshape 3D tensor
let data: Vec<f32> = (0..24).map(|i| i as f32).collect();
let tensor = Tensor::from_slice(&data, vec![2, 3, 4]).unwrap();
let reshaped = tensor.reshape(vec![6, 4]);
assert_eq!(reshaped.shape().dims, vec![6, 4]);
assert_eq!(reshaped.size(), 24);
§Performance
  • Contiguous tensors: O(1) time complexity, returns a view
  • Non-contiguous tensors: O(n) time complexity with data copying
  • Memory usage: No additional allocation for view operations
  • Gradient tracking: Preserves gradient requirements and tracking
§Automatic Dimension Inference

When using -1 for a dimension, the size is automatically calculated:

use train_station::Tensor;

// For a tensor with 12 elements
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
let tensor = Tensor::from_slice(&data, vec![3, 4]).unwrap();

let reshaped1 = tensor.reshape(vec![3, -1]);  // Results in shape [3, 4]
let reshaped2 = tensor.reshape(vec![-1, 6]);  // Results in shape [2, 6]
let reshaped3 = tensor.reshape(vec![-1]);     // Results in shape [12]
Source§

impl Tensor

Source

pub fn split(&self, split_size: usize, dim: usize) -> Vec<Tensor>

Split tensor into chunks of equal size along specified dimension

Divides the tensor into multiple smaller tensors along the specified dimension, where each chunk (except possibly the last) has the same size. The last chunk may be smaller if the dimension size is not evenly divisible by the split size.

This operation returns a vector of tensors, where each tensor is a view or copy of a portion of the original tensor. The first chunk is returned as a view when possible (zero-copy), while subsequent chunks may require data copying for non-zero base offsets.

§Arguments
  • split_size - Size of each chunk along the specified dimension (must be > 0)
  • dim - Dimension along which to split the tensor (must be < tensor rank)
§Returns

A vector of tensors, each representing a chunk of the original tensor. The number of chunks depends on the dimension size and split size.

§Panics
  • If tensor rank is 0 (scalar tensors cannot be split)
  • If dim is out of bounds for the tensor rank
  • If split_size is 0
§Examples
use train_station::Tensor;

// Split 2D tensor into equal chunks along dimension 1
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
let parts = tensor.split(1, 1);
assert_eq!(parts.len(), 3);
assert_eq!(parts[0].shape().dims, vec![2, 1]);
assert_eq!(parts[1].shape().dims, vec![2, 1]);
assert_eq!(parts[2].shape().dims, vec![2, 1]);
assert_eq!(parts[0].get(&[0, 0]), 1.0);
assert_eq!(parts[1].get(&[0, 0]), 2.0);
assert_eq!(parts[2].get(&[1, 0]), 6.0);
use train_station::Tensor;

// Split with uneven division (last chunk smaller)
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![1, 5]).unwrap();
let parts = tensor.split(2, 1);
assert_eq!(parts.len(), 3);
assert_eq!(parts[0].shape().dims, vec![1, 2]);
assert_eq!(parts[1].shape().dims, vec![1, 2]);
assert_eq!(parts[2].shape().dims, vec![1, 1]); // Last chunk smaller
use train_station::Tensor;

// Split with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
tensor.set_requires_grad(true);

let parts = tensor.split(1, 1);
assert_eq!(parts.len(), 2);
assert!(parts[0].requires_grad());
assert!(parts[1].requires_grad());
use train_station::Tensor;

// Split 1D tensor
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6]).unwrap();
let parts = tensor.split(2, 0);
assert_eq!(parts.len(), 3);
assert_eq!(parts[0].shape().dims, vec![2]);
assert_eq!(parts[1].shape().dims, vec![2]);
assert_eq!(parts[2].shape().dims, vec![2]);
§Performance
  • First Chunk: O(1) - Returns a view when possible (zero-copy)
  • Subsequent Chunks: O(n) - May require data copying for non-zero offsets
  • Memory Usage: Minimal allocation for view operations, copying for non-zero offsets
  • Gradient Tracking: Each chunk preserves gradient requirements and tracking
§Relationship to Other Operations

This operation is related to other tensor transformations:

  • split_with_sizes() - More general version with explicit chunk sizes
  • cat() - Inverse operation that concatenates tensors back together
  • chunk() - Alternative splitting operation with different semantics
§Memory Layout

The first chunk maintains the same underlying data as a view when the base offset is zero. Subsequent chunks may require data copying to handle non-zero base offsets, ensuring proper memory layout.

Source

pub fn split_with_sizes(&self, split_sizes: &[usize], dim: usize) -> Vec<Tensor>

Split tensor into chunks with explicit sizes along specified dimension

Divides the tensor into multiple smaller tensors along the specified dimension according to the provided size specifications. Each chunk has the exact size specified in the split_sizes array, and the sum of all sizes must equal the size of the specified dimension.

This operation provides precise control over the size of each resulting chunk, unlike split() which creates equal-sized chunks. The first chunk is returned as a view when possible (zero-copy), while subsequent chunks may require data copying for non-zero base offsets.

§Arguments
  • split_sizes - Array specifying the size of each chunk along the dimension
  • dim - Dimension along which to split the tensor (must be < tensor rank)
§Returns

A vector of tensors, each representing a chunk of the original tensor with the specified size. The number of chunks equals the length of split_sizes.

§Panics
  • If tensor rank is 0 (scalar tensors cannot be split)
  • If dim is out of bounds for the tensor rank
  • If sum of split_sizes does not equal the size of the specified dimension
§Examples
use train_station::Tensor;

// Split with explicit sizes
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0], vec![1, 5]).unwrap();
let parts = tensor.split_with_sizes(&[2, 3], 1);
assert_eq!(parts.len(), 2);
assert_eq!(parts[0].shape().dims, vec![1, 2]);
assert_eq!(parts[1].shape().dims, vec![1, 3]);
assert_eq!(parts[0].get(&[0, 0]), 1.0);
assert_eq!(parts[0].get(&[0, 1]), 2.0);
assert_eq!(parts[1].get(&[0, 0]), 3.0);
use train_station::Tensor;

// Split 2D tensor with different chunk sizes
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
let parts = tensor.split_with_sizes(&[1, 2], 1);
assert_eq!(parts.len(), 2);
assert_eq!(parts[0].shape().dims, vec![2, 1]);
assert_eq!(parts[1].shape().dims, vec![2, 2]);
use train_station::Tensor;

// Split with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
tensor.set_requires_grad(true);

let parts = tensor.split_with_sizes(&[1, 1], 1);
assert_eq!(parts.len(), 2);
assert!(parts[0].requires_grad());
assert!(parts[1].requires_grad());
use train_station::Tensor;

// Split 1D tensor with explicit sizes
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![6]).unwrap();
let parts = tensor.split_with_sizes(&[2, 2, 2], 0);
assert_eq!(parts.len(), 3);
assert_eq!(parts[0].shape().dims, vec![2]);
assert_eq!(parts[1].shape().dims, vec![2]);
assert_eq!(parts[2].shape().dims, vec![2]);
§Performance
  • First Chunk: O(1) - Returns a view when possible (zero-copy)
  • Subsequent Chunks: O(n) - May require data copying for non-zero offsets
  • Memory Usage: Minimal allocation for view operations, copying for non-zero offsets
  • Gradient Tracking: Each chunk preserves gradient requirements and tracking
§Relationship to Other Operations

This operation is related to other tensor transformations:

  • split() - Simplified version with equal-sized chunks
  • cat() - Inverse operation that concatenates tensors back together
  • chunk() - Alternative splitting operation with different semantics
§Memory Layout

The first chunk maintains the same underlying data as a view when the base offset is zero. Subsequent chunks may require data copying to handle non-zero base offsets, ensuring proper memory layout. Zero-sized chunks are handled by creating empty tensors with appropriate shapes.

Source§

impl Tensor

Source

pub fn squeeze(&self, dim: Option<usize>) -> Tensor

Remove dimensions of size 1 from the tensor

Removes singleton dimensions (dimensions with size 1) from the tensor, reducing its rank while preserving the total number of elements. This operation is useful for cleaning up tensor shapes and preparing data for operations that expect specific dimensionality.

The squeeze operation can remove either all size-1 dimensions or a specific dimension if it has size 1. When all dimensions are size 1, the result is a scalar tensor with shape [1] rather than an empty tensor to maintain mathematical consistency.

§Arguments
  • dim - Optional specific dimension to squeeze. If None, all size-1 dimensions are removed. If Some(d), only dimension d is removed if it has size 1.
§Returns

A new tensor with size-1 dimensions removed. The total number of elements remains unchanged.

§Panics
  • If dim is specified but out of bounds for the tensor rank
  • If dim is specified but the dimension does not have size 1
§Examples
use train_station::Tensor;

// Squeeze all size-1 dimensions
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3, 1]).unwrap();
let squeezed = tensor.squeeze(None);
assert_eq!(squeezed.shape().dims, vec![3]);
assert_eq!(squeezed.get(&[0]), 1.0);
assert_eq!(squeezed.get(&[1]), 2.0);
assert_eq!(squeezed.get(&[2]), 3.0);
use train_station::Tensor;

// Squeeze specific dimension
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3, 1]).unwrap();
let squeezed = tensor.squeeze(Some(0));
assert_eq!(squeezed.shape().dims, vec![3, 1]);
assert_eq!(squeezed.get(&[0, 0]), 1.0);
assert_eq!(squeezed.get(&[1, 0]), 2.0);
assert_eq!(squeezed.get(&[2, 0]), 3.0);
use train_station::Tensor;

// Squeeze preserves data integrity
let data = vec![1.0, 2.0, 3.0, 4.0];
let tensor = Tensor::from_slice(&data, vec![1, 2, 1, 2]).unwrap();
let squeezed = tensor.squeeze(None);
assert_eq!(squeezed.shape().dims, vec![2, 2]);
assert_eq!(squeezed.size(), 4);
assert_eq!(squeezed.get(&[0, 0]), data[0]);
assert_eq!(squeezed.get(&[0, 1]), data[1]);
assert_eq!(squeezed.get(&[1, 0]), data[2]);
assert_eq!(squeezed.get(&[1, 1]), data[3]);
use train_station::Tensor;

// Handle edge case: all dimensions are size 1
let tensor = Tensor::from_slice(&[5.0], vec![1, 1, 1]).unwrap();
let squeezed = tensor.squeeze(None);
assert_eq!(squeezed.shape().dims, vec![1]); // Not empty!
assert_eq!(squeezed.get(&[0]), 5.0);
use train_station::Tensor;

// Squeeze with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![1, 3, 1]).unwrap();
tensor.set_requires_grad(true);

let squeezed = tensor.squeeze(None);
assert!(squeezed.requires_grad());
assert_eq!(squeezed.shape().dims, vec![3]);
use train_station::Tensor;

// Squeeze and unsqueeze roundtrip
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let unsqueezed = tensor.unsqueeze(0);
assert_eq!(unsqueezed.shape().dims, vec![1, 3]);

let squeezed = unsqueezed.squeeze(Some(0));
assert_eq!(squeezed.shape().dims, vec![3]);
assert_eq!(squeezed.get(&[0]), 1.0);
assert_eq!(squeezed.get(&[2]), 3.0);
§Performance
  • Time Complexity: O(1) - Returns a view through reshape operation
  • Memory Usage: No additional memory allocation (view operation)
  • Gradient Tracking: Preserves gradient requirements and tracking
  • Shape Transformation: Reduces tensor rank by removing singleton dimensions
§Relationship to Other Operations

This operation is related to other tensor transformations:

  • unsqueeze() - Inverse operation that adds size-1 dimensions
  • reshape() - More general shape transformation operation
  • flatten() - Reduces tensor to 1D by combining all dimensions
§Memory Layout

The squeezed tensor maintains the same underlying data as the original tensor through the reshape operation. This ensures zero-copy behavior when the tensor is contiguous, with only the shape metadata being modified to reflect the reduced dimensionality.

§Edge Cases
  • All size-1 dimensions: Returns a tensor with shape [1] rather than an empty tensor to maintain mathematical consistency
  • No size-1 dimensions: Returns a tensor with the same shape as the input
  • Mixed dimensions: Only removes dimensions with size 1, preserving others
Source§

impl Tensor

Source

pub fn stack(tensors: &[Tensor], dim: usize) -> Tensor

Stack a list of tensors along a new dimension

Combines multiple tensors by adding a new dimension at the specified position. All input tensors must have identical shapes, and the output tensor will have a new dimension of size equal to the number of input tensors. This operation is similar to PyTorch’s torch.stack function.

The stacking operation creates a new axis in the output tensor, unlike concatenation which operates along existing dimensions. This makes stacking useful for creating batch dimensions, combining feature maps, and implementing operations that require adding new tensor axes.

§Arguments
  • tensors - Array of tensors to stack. All tensors must have identical shapes.
  • dim - Index of the new axis in the output shape (0 <= dim <= rank)
§Returns

A new tensor with the stacked data. The output shape is the input shape with a new dimension of size tensors.len() inserted at position dim.

§Panics
  • If the tensor array is empty
  • If any tensor has a different shape than the first tensor
  • If dim is out of bounds (dim > rank of input tensors)
§Examples
use train_station::Tensor;

// Stack two 1D tensors along dimension 0
let a = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let b = Tensor::from_slice(&[4.0, 5.0, 6.0], vec![3]).unwrap();
let stacked = Tensor::stack(&[a, b], 0);
assert_eq!(stacked.shape().dims, vec![2, 3]);
assert_eq!(stacked.get(&[0, 0]), 1.0);
assert_eq!(stacked.get(&[1, 2]), 6.0);
use train_station::Tensor;

// Stack multiple 2D tensors along dimension 1
let a = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let b = Tensor::from_slice(&[5.0, 6.0, 7.0, 8.0], vec![2, 2]).unwrap();
let c = Tensor::from_slice(&[9.0, 10.0, 11.0, 12.0], vec![2, 2]).unwrap();
let stacked = Tensor::stack(&[a, b, c], 1);
assert_eq!(stacked.shape().dims, vec![2, 3, 2]);
assert_eq!(stacked.get(&[0, 0, 0]), 1.0);
assert_eq!(stacked.get(&[1, 2, 1]), 12.0);
use train_station::Tensor;

// Stack with gradient tracking
let mut a = Tensor::from_slice(&[1.0, 2.0], vec![2]).unwrap();
let mut b = Tensor::from_slice(&[3.0, 4.0], vec![2]).unwrap();
a.set_requires_grad(true);
b.set_requires_grad(true);

let stacked = Tensor::stack(&[a, b], 0);
assert!(stacked.requires_grad());
assert_eq!(stacked.shape().dims, vec![2, 2]);
use train_station::Tensor;

// Stack 3D tensors along the last dimension
let data1: Vec<f32> = (0..8).map(|i| i as f32).collect();
let data2: Vec<f32> = (8..16).map(|i| i as f32).collect();
let a = Tensor::from_slice(&data1, vec![2, 2, 2]).unwrap();
let b = Tensor::from_slice(&data2, vec![2, 2, 2]).unwrap();
let stacked = Tensor::stack(&[a, b], 3);
assert_eq!(stacked.shape().dims, vec![2, 2, 2, 2]);
assert_eq!(stacked.get(&[0, 0, 0, 0]), 0.0);
assert_eq!(stacked.get(&[1, 1, 1, 1]), 15.0);
§Performance
  • Time Complexity: O(n) where n is the total number of elements
  • Memory Usage: Allocates new contiguous tensor for output
  • SIMD Optimization: Uses AVX2 acceleration for large block copies
  • Block-wise Copying: Optimized copying strategy for better cache performance
  • Gradient Tracking: Preserves gradient requirements and tracking
§Relationship to Other Operations

This operation is related to other tensor transformations:

  • cat() - Concatenates tensors along existing dimensions
  • unsqueeze() - Adds a single dimension of size 1
  • reshape() - Changes tensor shape without adding dimensions
§Memory Layout

The output tensor is always contiguous, with elements arranged so that the stacked dimension is the fastest-changing index. This ensures optimal performance for subsequent operations and maintains compatibility with SIMD optimizations.

§Gradient Computation

During backward passes, gradients are split along the stacked dimension and distributed back to the original input tensors. This is implemented using the same gradient function as concatenation, treating the stack operation as concatenation along a new axis.

Source§

impl Tensor

Source

pub fn transpose(&self, dim0: usize, dim1: usize) -> Tensor

Transpose two dimensions of the tensor

Swaps two specified dimensions of the tensor, modifying the shape and memory access pattern. When possible, this operation returns a zero-copy view using stride manipulation. For complex cases or non-contiguous tensors, data is copied to ensure correct transposition.

The transpose operation is its own inverse - applying transpose twice with the same dimensions returns the original tensor.

§Arguments
  • dim0 - First dimension to swap (must be < tensor rank)
  • dim1 - Second dimension to swap (must be < tensor rank)
§Returns

A new tensor with the specified dimensions transposed. The total number of elements remains unchanged.

§Panics
  • If dim0 is out of bounds for the tensor rank
  • If dim1 is out of bounds for the tensor rank
§Examples
use train_station::Tensor;

// Basic 2D transpose
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], vec![2, 3]).unwrap();
let transposed = tensor.transpose(0, 1);
assert_eq!(transposed.shape().dims, vec![3, 2]);
assert_eq!(transposed.get(&[0, 0]), 1.0);
assert_eq!(transposed.get(&[0, 1]), 4.0);
assert_eq!(transposed.get(&[1, 0]), 2.0);
assert_eq!(transposed.get(&[1, 1]), 5.0);
assert_eq!(transposed.get(&[2, 0]), 3.0);
assert_eq!(transposed.get(&[2, 1]), 6.0);
use train_station::Tensor;

// 3D tensor transpose
let data: Vec<f32> = (0..24).map(|i| i as f32).collect();
let tensor = Tensor::from_slice(&data, vec![2, 3, 4]).unwrap();
let transposed = tensor.transpose(0, 1);
assert_eq!(transposed.shape().dims, vec![3, 2, 4]);
use train_station::Tensor;

// Transpose with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
tensor.set_requires_grad(true);

let transposed = tensor.transpose(0, 1);
assert!(transposed.requires_grad());
assert_eq!(transposed.shape().dims, vec![2, 2]);
use train_station::Tensor;

// Transpose same dimension (no change)
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let result = tensor.transpose(1, 1);
assert_eq!(result.shape().dims, tensor.shape().dims);
assert_eq!(result.get(&[0, 0]), tensor.get(&[0, 0]));
use train_station::Tensor;

// Transpose is its own inverse
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let transposed = tensor.transpose(0, 1);
let double_transposed = transposed.transpose(0, 1);
assert_eq!(double_transposed.shape().dims, tensor.shape().dims);
assert_eq!(double_transposed.get(&[0, 0]), tensor.get(&[0, 0]));
§Performance
  • Contiguous tensors: O(1) time complexity, returns a view
  • Non-contiguous tensors: O(n) time complexity with data copying
  • Memory usage: No additional allocation for view operations
  • Gradient tracking: Preserves gradient requirements and tracking
§Relationship to Other Operations

This operation is related to other tensor transformations:

  • t() - Convenience method for matrix transpose (last two dimensions)
  • permute() - More general dimension reordering operation
  • reshape() - Changes shape without changing dimension order
§Memory Layout

For contiguous tensors, transpose returns a view with modified strides, making the tensor non-contiguous. For non-contiguous tensors or complex cases, data is copied to ensure correct transposition.

Source

pub fn t(&self) -> Tensor

Matrix transpose (transpose last two dimensions)

Convenience method for the common case of matrix transposition. For 2D tensors, this performs a standard matrix transpose. For higher-dimensional tensors, this transposes the last two dimensions, treating the tensor as a batch of matrices.

This method is equivalent to transpose(rank-2, rank-1) where rank is the number of dimensions in the tensor.

§Returns

A new tensor with the last two dimensions transposed

§Panics
  • If the tensor has less than 2 dimensions
§Examples
use train_station::Tensor;

// 2D matrix transpose
let matrix = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let transposed = matrix.t();
assert_eq!(transposed.shape().dims, vec![2, 2]);
assert_eq!(transposed.get(&[0, 0]), 1.0);
assert_eq!(transposed.get(&[0, 1]), 3.0);
assert_eq!(transposed.get(&[1, 0]), 2.0);
assert_eq!(transposed.get(&[1, 1]), 4.0);
use train_station::Tensor;

// 3D tensor (batch of matrices)
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
let tensor = Tensor::from_slice(&data, vec![2, 2, 3]).unwrap();
let transposed = tensor.t();
assert_eq!(transposed.shape().dims, vec![2, 3, 2]);
use train_station::Tensor;

// Matrix transpose with gradient tracking
let mut matrix = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
matrix.set_requires_grad(true);

let transposed = matrix.t();
assert!(transposed.requires_grad());
assert_eq!(transposed.shape().dims, vec![2, 2]);
§Performance
  • Time Complexity: Same as transpose() - O(1) for views, O(n) for copies
  • Memory Usage: Same as transpose() - no allocation for views
  • Gradient Tracking: Preserves gradient requirements and tracking
§Relationship to Other Operations

This operation is equivalent to:

use train_station::Tensor;

let tensor = Tensor::new(vec![2, 3, 4]);
let rank = tensor.shape().rank();
let transposed1 = tensor.t();
let transposed2 = tensor.transpose(rank - 2, rank - 1);
// transposed1 and transposed2 are identical
Source§

impl Tensor

Source

pub fn unsqueeze(&self, dim: usize) -> Tensor

Add a dimension of size 1 at the specified position

Inserts a new dimension of size 1 at the specified position in the tensor’s shape, increasing the rank by 1 while preserving the total number of elements. This operation is useful for preparing tensors for broadcasting, creating batch dimensions, and adapting tensor shapes for specific neural network operations.

The unsqueeze operation is the inverse of squeeze() - unsqueezing a dimension and then squeezing it at the same position returns the original tensor.

§Arguments
  • dim - Position to insert the new dimension (0 <= dim <= rank)
§Returns

A new tensor with an additional dimension of size 1 at the specified position. The total number of elements remains unchanged.

§Panics
  • If dim is out of bounds (dim > rank of the tensor)
§Examples
use train_station::Tensor;

// Add dimension at the beginning
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let unsqueezed = tensor.unsqueeze(0);
assert_eq!(unsqueezed.shape().dims, vec![1, 3]);
assert_eq!(unsqueezed.get(&[0, 0]), 1.0);
assert_eq!(unsqueezed.get(&[0, 1]), 2.0);
assert_eq!(unsqueezed.get(&[0, 2]), 3.0);
use train_station::Tensor;

// Add dimension at the end
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let unsqueezed = tensor.unsqueeze(1);
assert_eq!(unsqueezed.shape().dims, vec![3, 1]);
assert_eq!(unsqueezed.get(&[0, 0]), 1.0);
assert_eq!(unsqueezed.get(&[1, 0]), 2.0);
assert_eq!(unsqueezed.get(&[2, 0]), 3.0);
use train_station::Tensor;

// Add dimension in the middle of 2D tensor
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let unsqueezed = tensor.unsqueeze(1);
assert_eq!(unsqueezed.shape().dims, vec![2, 1, 2]);
assert_eq!(unsqueezed.get(&[0, 0, 0]), 1.0);
assert_eq!(unsqueezed.get(&[0, 0, 1]), 2.0);
assert_eq!(unsqueezed.get(&[1, 0, 0]), 3.0);
assert_eq!(unsqueezed.get(&[1, 0, 1]), 4.0);
use train_station::Tensor;

// Unsqueeze preserves data integrity
let data = vec![1.0, 2.0, 3.0, 4.0];
let tensor = Tensor::from_slice(&data, vec![4]).unwrap();
let unsqueezed = tensor.unsqueeze(0);
assert_eq!(unsqueezed.shape().dims, vec![1, 4]);
assert_eq!(unsqueezed.size(), 4);
for (i, &d) in data.iter().enumerate() {
    assert_eq!(unsqueezed.get(&[0, i]), d);
}
use train_station::Tensor;

// Unsqueeze with gradient tracking
let mut tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
tensor.set_requires_grad(true);

let unsqueezed = tensor.unsqueeze(0);
assert!(unsqueezed.requires_grad());
assert_eq!(unsqueezed.shape().dims, vec![1, 3]);
use train_station::Tensor;

// Unsqueeze and squeeze roundtrip
let tensor = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let unsqueezed = tensor.unsqueeze(0);
assert_eq!(unsqueezed.shape().dims, vec![1, 3]);

let squeezed = unsqueezed.squeeze(Some(0));
assert_eq!(squeezed.shape().dims, vec![3]);
assert_eq!(squeezed.get(&[0]), 1.0);
assert_eq!(squeezed.get(&[2]), 3.0);
use train_station::Tensor;

// Multiple unsqueeze operations
let tensor = Tensor::from_slice(&[42.0], vec![1]).unwrap();
let unsqueezed1 = tensor.unsqueeze(0);
assert_eq!(unsqueezed1.shape().dims, vec![1, 1]);

let unsqueezed2 = unsqueezed1.unsqueeze(0);
assert_eq!(unsqueezed2.shape().dims, vec![1, 1, 1]);
assert_eq!(unsqueezed2.get(&[0, 0, 0]), 42.0);
§Performance
  • Time Complexity: O(1) - Returns a view through reshape operation
  • Memory Usage: No additional memory allocation (view operation)
  • Gradient Tracking: Preserves gradient requirements and tracking
  • Shape Transformation: Increases tensor rank by adding singleton dimensions
§Relationship to Other Operations

This operation is related to other tensor transformations:

  • squeeze() - Inverse operation that removes size-1 dimensions
  • reshape() - More general shape transformation operation
  • expand() - Broadcasts dimensions to larger sizes
§Memory Layout

The unsqueezed tensor maintains the same underlying data as the original tensor through the reshape operation. This ensures zero-copy behavior when the tensor is contiguous, with only the shape metadata being modified to reflect the increased dimensionality.

§Broadcasting Applications

Unsqueeze is commonly used for broadcasting operations:

use train_station::Tensor;

// Prepare for broadcasting: [3] -> [1, 3] for row-wise operations
let vector = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let row_vector = vector.unsqueeze(0); // Shape: [1, 3]

// Prepare for broadcasting: [3] -> [3, 1] for column-wise operations
let column_vector = vector.unsqueeze(1); // Shape: [3, 1]
§Neural Network Applications

Unsqueeze is essential for neural network operations:

use train_station::Tensor;

// Single sample -> batch dimension for neural network input
let sample = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let batch = sample.unsqueeze(0); // Shape: [1, 3] for batch processing

// Add channel dimension for convolutional operations
let feature_map = Tensor::from_slice(&[1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let with_channels = feature_map.unsqueeze(0); // Shape: [1, 2, 2] for conv layers

Trait Implementations§

Source§

impl Add<&Tensor> for Tensor

Source§

fn add(self, other: &Tensor) -> Tensor

Adds a tensor and a tensor reference element-wise

§Returns

A new tensor containing the element-wise sum

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl Add<&Tensor> for f32

Source§

fn add(self, tensor: &Tensor) -> Tensor

Adds a scalar to each element of the tensor (reference version)

§Returns

A new tensor with the scalar added to each element

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl Add<Tensor> for &Tensor

Source§

fn add(self, other: Tensor) -> Tensor

Adds a tensor reference and a tensor element-wise

§Returns

A new tensor containing the element-wise sum

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl Add<Tensor> for f32

Scalar-tensor addition operator implementations

Provides addition operations between scalars and tensors. All implementations delegate to the underlying add_scalar method.

Source§

fn add(self, tensor: Tensor) -> Tensor

Adds a scalar to each element of the tensor

§Returns

A new tensor with the scalar added to each element

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl Add<f32> for &Tensor

Source§

fn add(self, scalar: f32) -> Tensor

Adds a scalar to each element of the tensor (reference version)

§Returns

A new tensor with the scalar added to each element

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl Add<f32> for Tensor

Tensor-scalar addition operator implementations

Provides addition operations between tensors and scalars. All implementations delegate to the underlying add_scalar method.

Source§

fn add(self, scalar: f32) -> Tensor

Adds a scalar to each element of the tensor

§Returns

A new tensor with the scalar added to each element

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl Add for &Tensor

Source§

fn add(self, other: &Tensor) -> Tensor

Adds two tensors element-wise (reference version)

§Returns

A new tensor containing the element-wise sum

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl Add for Tensor

Tensor addition operator implementations

Provides addition operations between tensors with various reference combinations. All implementations delegate to the underlying add_tensor method for optimal performance.

Source§

fn add(self, other: Tensor) -> Tensor

Adds two tensors element-wise

§Returns

A new tensor containing the element-wise sum

Source§

type Output = Tensor

The resulting type after applying the + operator.
Source§

impl AddAssign<&Tensor> for Tensor

Source§

fn add_assign(&mut self, other: &Tensor)

Adds another tensor reference to this tensor in-place

Source§

impl AddAssign<f32> for Tensor

Tensor-scalar addition assignment operator implementations

Provides in-place addition operations between tensors and scalars.

Source§

fn add_assign(&mut self, scalar: f32)

Adds a scalar to each element of this tensor in-place

Source§

impl AddAssign for Tensor

Tensor addition assignment operator implementations

Provides in-place addition operations between tensors. All implementations delegate to the underlying add_tensor method.

Source§

fn add_assign(&mut self, other: Tensor)

Adds another tensor to this tensor in-place

Source§

impl Clone for Tensor

Clone implementation for Tensor

Creates a deep copy of the tensor data but resets gradtrack state (new tensor won’t track gradients unless explicitly set)

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Tensor

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Div<&Tensor> for Tensor

Source§

fn div(self, other: &Tensor) -> Tensor

Divides a tensor by a tensor reference element-wise

§Returns

A new tensor containing the element-wise quotient

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl Div<&Tensor> for f32

Source§

fn div(self, tensor: &Tensor) -> Tensor

Divides a scalar by each element of the tensor (reference version)

§Returns

A new tensor with the scalar divided by each element

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl Div<Tensor> for &Tensor

Source§

fn div(self, other: Tensor) -> Tensor

Divides a tensor reference by a tensor element-wise

§Returns

A new tensor containing the element-wise quotient

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl Div<Tensor> for f32

Scalar-tensor division operator implementations

Provides division operations between scalars and tensors. Computes scalar / tensor by computing the reciprocal of the tensor and multiplying by the scalar.

Source§

fn div(self, tensor: Tensor) -> Tensor

Divides a scalar by each element of the tensor

§Returns

A new tensor with the scalar divided by each element

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl Div<f32> for &Tensor

Source§

fn div(self, scalar: f32) -> Tensor

Divides each element of the tensor by a scalar (reference version)

§Returns

A new tensor with each element divided by the scalar

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl Div<f32> for Tensor

Tensor-scalar division operator implementations

Provides division operations between tensors and scalars. All implementations delegate to the underlying div_scalar method.

Source§

fn div(self, scalar: f32) -> Tensor

Divides each element of the tensor by a scalar

§Returns

A new tensor with each element divided by the scalar

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl Div for &Tensor

Source§

fn div(self, other: &Tensor) -> Tensor

Divides two tensors element-wise (reference version)

§Returns

A new tensor containing the element-wise quotient

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl Div for Tensor

Tensor division operator implementations

Provides element-wise division operations between tensors with various reference combinations. All implementations delegate to the underlying div_tensor method for optimal performance.

Source§

fn div(self, other: Tensor) -> Tensor

Divides two tensors element-wise

§Returns

A new tensor containing the element-wise quotient

Source§

type Output = Tensor

The resulting type after applying the / operator.
Source§

impl DivAssign<&Tensor> for Tensor

Source§

fn div_assign(&mut self, other: &Tensor)

Divides this tensor by another tensor reference in-place

Source§

impl DivAssign<f32> for Tensor

Tensor-scalar division assignment operator implementations

Provides in-place division operations between tensors and scalars.

Source§

fn div_assign(&mut self, scalar: f32)

Divides each element of this tensor by a scalar in-place

Source§

impl DivAssign for Tensor

Tensor division assignment operator implementations

Provides in-place division operations between tensors. All implementations delegate to the underlying div_tensor method.

Source§

fn div_assign(&mut self, other: Tensor)

Divides this tensor by another tensor in-place

Source§

impl Drop for Tensor

Source§

fn drop(&mut self)

Frees the tensor’s memory when it goes out of scope

This ensures proper cleanup and prevents memory leaks.

Source§

impl FromFieldValue for Tensor

Source§

fn from_field_value( value: FieldValue, field_name: &str, ) -> SerializationResult<Self>

Convert FieldValue to Tensor for use as struct field

§Arguments
  • value - FieldValue containing tensor data
  • field_name - Name of the field for error reporting
§Returns

Tensor instance or error if deserialization fails

Source§

impl FromIterator<Tensor> for Tensor

Source§

fn from_iter<I: IntoIterator<Item = Tensor>>(iter: I) -> Self

Collect element view tensors back into a single tensor

This method reconstructs a tensor from an iterator of element view tensors. It includes optimizations for common patterns and maintains gradient tracking when appropriate.

The collection process automatically detects whether all elements are scalar views (shape [1]) and uses optimized collection strategies accordingly. Gradient tracking is preserved when any input element requires gradients.

§Performance
  • Optimized Collection: Specialized paths for scalar and mixed views
  • Memory Efficient: Direct memory copying without intermediate allocations
  • Gradient Preservation: Maintains gradtrack functionality when enabled
  • Shape Detection: Automatic detection of element shapes for optimization
§Implementation Details

The method performs the following steps:

  1. Element Collection: Gathers all element tensors from the iterator
  2. Shape Analysis: Determines if all elements are scalar views
  3. Optimized Path: Uses specialized collection for scalar views
  4. General Path: Handles mixed shapes by flattening into 1D tensor
  5. Gradient Setup: Preserves gradient tracking when appropriate
§Examples
§Basic Collection
use train_station::Tensor;

let original = Tensor::from_slice(&[1.0, 2.0, 3.0], vec![3]).unwrap();
let doubled: Tensor = original.iter()
    .map(|elem| elem.mul_scalar(2.0))
    .collect();

assert_eq!(doubled.data(), &[2.0, 4.0, 6.0]);
§Collection with Gradient Tracking
use train_station::Tensor;

let original = Tensor::from_slice(&[1.0, 2.0], vec![2])
    .unwrap()
    .with_requires_grad();

let result: Tensor = original.iter()
    .map(|elem| elem.mul_scalar(2.0))
    .collect();

assert!(result.requires_grad());
assert_eq!(result.data(), &[2.0, 4.0]);
§Empty Iterator Handling
use train_station::Tensor;

let empty: Tensor = Vec::<Tensor>::new().into_iter().collect();
assert_eq!(empty.size(), 0);
assert_eq!(empty.shape().dims, vec![0]);
Source§

impl<'a> IntoIterator for &'a Tensor

Source§

type Item = Tensor

The type of the elements being iterated over.
Source§

type IntoIter = TensorElementIterator<'a>

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
Source§

impl Mul<&Tensor> for Tensor

Source§

fn mul(self, other: &Tensor) -> Tensor

Multiplies a tensor and a tensor reference element-wise

§Returns

A new tensor containing the element-wise product

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl Mul<&Tensor> for f32

Source§

fn mul(self, tensor: &Tensor) -> Tensor

Multiplies each element of the tensor by a scalar (reference version)

§Returns

A new tensor with each element multiplied by the scalar

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl Mul<Tensor> for &Tensor

Source§

fn mul(self, other: Tensor) -> Tensor

Multiplies a tensor reference and a tensor element-wise

§Returns

A new tensor containing the element-wise product

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl Mul<Tensor> for f32

Scalar-tensor multiplication operator implementations

Provides multiplication operations between scalars and tensors. All implementations delegate to the underlying mul_scalar method.

Source§

fn mul(self, tensor: Tensor) -> Tensor

Multiplies each element of the tensor by a scalar

§Returns

A new tensor with each element multiplied by the scalar

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl Mul<f32> for &Tensor

Source§

fn mul(self, scalar: f32) -> Tensor

Multiplies each element of the tensor by a scalar (reference version)

§Returns

A new tensor with each element multiplied by the scalar

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl Mul<f32> for Tensor

Tensor-scalar multiplication operator implementations

Provides multiplication operations between tensors and scalars. All implementations delegate to the underlying mul_scalar method.

Source§

fn mul(self, scalar: f32) -> Tensor

Multiplies each element of the tensor by a scalar

§Returns

A new tensor with each element multiplied by the scalar

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl Mul for &Tensor

Source§

fn mul(self, other: &Tensor) -> Tensor

Multiplies two tensors element-wise (reference version)

§Returns

A new tensor containing the element-wise product

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl Mul for Tensor

Tensor multiplication operator implementations

Provides element-wise multiplication operations between tensors with various reference combinations. All implementations delegate to the underlying mul_tensor method for optimal performance.

Source§

fn mul(self, other: Tensor) -> Tensor

Multiplies two tensors element-wise

§Returns

A new tensor containing the element-wise product

Source§

type Output = Tensor

The resulting type after applying the * operator.
Source§

impl MulAssign<&Tensor> for Tensor

Source§

fn mul_assign(&mut self, other: &Tensor)

Multiplies this tensor by another tensor reference in-place

Source§

impl MulAssign<f32> for Tensor

Tensor-scalar multiplication assignment operator implementations

Provides in-place multiplication operations between tensors and scalars.

Source§

fn mul_assign(&mut self, scalar: f32)

Multiplies each element of this tensor by a scalar in-place

Source§

impl MulAssign for Tensor

Tensor multiplication assignment operator implementations

Provides in-place multiplication operations between tensors. All implementations delegate to the underlying mul_tensor method.

Source§

fn mul_assign(&mut self, other: Tensor)

Multiplies this tensor by another tensor in-place

Source§

impl Neg for &Tensor

Source§

fn neg(self) -> Tensor

Negates each element of the tensor (reference version)

§Returns

A new tensor with each element negated

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Neg for Tensor

Tensor negation operator implementations

Provides unary negation operations for tensors. All implementations delegate to the underlying mul_scalar method with -1.0.

Source§

fn neg(self) -> Tensor

Negates each element of the tensor

§Returns

A new tensor with each element negated

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Serializable for Tensor

Source§

fn to_json(&self) -> SerializationResult<String>

Serialize the tensor to JSON format

This method converts the tensor into a human-readable JSON string representation that includes all tensor data, shape information, device placement, and gradtrack state. The JSON format is suitable for debugging, configuration files, and cross-language interoperability.

§Returns

JSON string representation of the tensor on success, or SerializationError on failure

§Examples
use train_station::Tensor;
use train_station::serialization::Serializable;

let mut tensor = Tensor::zeros(vec![2, 3]);
tensor.set(&[0, 0], 1.0);
tensor.set(&[1, 2], 5.0);

let json = tensor.to_json().unwrap();
assert!(!json.is_empty());
assert!(json.contains("data"));
assert!(json.contains("shape"));
Source§

fn from_json(json: &str) -> SerializationResult<Self>

Deserialize a tensor from JSON format

This method parses a JSON string and reconstructs a tensor with all its data, shape information, device placement, and gradtrack state. The JSON must contain all necessary fields in the expected format.

§Arguments
  • json - JSON string containing serialized tensor data
§Returns

The deserialized tensor on success, or SerializationError on failure

§Examples
use train_station::Tensor;
use train_station::serialization::Serializable;

let mut original = Tensor::ones(vec![2, 2]);
original.set(&[0, 1], 3.0);
original.set_requires_grad(true);

let json = original.to_json().unwrap();
let restored = Tensor::from_json(&json).unwrap();

assert_eq!(original.shape().dims, restored.shape().dims);
assert_eq!(original.get(&[0, 1]), restored.get(&[0, 1]));
assert_eq!(original.requires_grad(), restored.requires_grad());
Source§

fn to_binary(&self) -> SerializationResult<Vec<u8>>

Serialize the tensor to binary format

This method converts the tensor into a compact binary representation optimized for storage and transmission. The binary format provides maximum performance and minimal file sizes, making it ideal for large tensors and production use.

§Returns

Binary representation of the tensor on success, or SerializationError on failure

§Examples
use train_station::Tensor;
use train_station::serialization::Serializable;

let mut tensor = Tensor::zeros(vec![100, 100]);
for i in 0..10 {
    tensor.set(&[i, i], i as f32);
}

let binary = tensor.to_binary().unwrap();
assert!(!binary.is_empty());
// Binary format is more compact than JSON for large tensors
Source§

fn from_binary(data: &[u8]) -> SerializationResult<Self>

Deserialize a tensor from binary format

This method parses binary data and reconstructs a tensor with all its data, shape information, device placement, and gradtrack state. The binary data must contain complete serialized information in the expected format.

§Arguments
  • data - Binary data containing serialized tensor information
§Returns

The deserialized tensor on success, or SerializationError on failure

§Examples
use train_station::Tensor;
use train_station::serialization::Serializable;

let mut original = Tensor::ones(vec![3, 4]);
original.set(&[2, 3], 7.5);
original.set_requires_grad(true);

let binary = original.to_binary().unwrap();
let restored = Tensor::from_binary(&binary).unwrap();

assert_eq!(original.shape().dims, restored.shape().dims);
assert_eq!(original.get(&[2, 3]), restored.get(&[2, 3]));
assert_eq!(original.requires_grad(), restored.requires_grad());
Source§

fn save<P: AsRef<Path>>( &self, path: P, format: Format, ) -> SerializationResult<()>

Save the object to a file in the specified format Read more
Source§

fn save_to_writer<W: Write>( &self, writer: &mut W, format: Format, ) -> SerializationResult<()>

Save the object to a writer in the specified format Read more
Source§

fn load<P: AsRef<Path>>(path: P, format: Format) -> SerializationResult<Self>

Load an object from a file in the specified format Read more
Source§

fn load_from_reader<R: Read>( reader: &mut R, format: Format, ) -> SerializationResult<Self>

Load an object from a reader in the specified format Read more
Source§

impl StructSerializable for Tensor

Source§

fn to_serializer(&self) -> StructSerializer

Convert Tensor to StructSerializer for serialization

Serializes tensor data, shape, device, and gradtrack state. Runtime state (id, grad, grad_fn, allocation_owner) is not serialized.

§Returns

StructSerializer containing all persistent tensor state

Source§

fn from_deserializer( deserializer: &mut StructDeserializer, ) -> SerializationResult<Self>

Create Tensor from StructDeserializer

Reconstructs tensor from serialized data, shape, device, and gradtrack state. Allocates new memory and generates new tensor ID.

§Arguments
  • deserializer - StructDeserializer containing tensor data
§Returns

Reconstructed Tensor instance or error if deserialization fails

Source§

fn save_json<P: AsRef<Path>>(&self, path: P) -> SerializationResult<()>

Saves the struct to a JSON file Read more
Source§

fn save_binary<P: AsRef<Path>>(&self, path: P) -> SerializationResult<()>

Saves the struct to a binary file Read more
Source§

fn load_json<P: AsRef<Path>>(path: P) -> SerializationResult<Self>

Loads the struct from a JSON file Read more
Source§

fn load_binary<P: AsRef<Path>>(path: P) -> SerializationResult<Self>

Loads the struct from a binary file Read more
Source§

fn to_json(&self) -> SerializationResult<String>

Converts the struct to a JSON string Read more
Source§

fn to_binary(&self) -> SerializationResult<Vec<u8>>

Converts the struct to binary data Read more
Source§

fn from_json(json: &str) -> SerializationResult<Self>

Creates the struct from a JSON string Read more
Source§

fn from_binary(data: &[u8]) -> SerializationResult<Self>

Creates the struct from binary data Read more
Source§

impl Sub<&Tensor> for Tensor

Source§

fn sub(self, other: &Tensor) -> Tensor

Subtracts a tensor reference from a tensor element-wise

§Returns

A new tensor containing the element-wise difference

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Sub<&Tensor> for f32

Source§

fn sub(self, tensor: &Tensor) -> Tensor

Subtracts each element of the tensor from the scalar (reference version)

§Returns

A new tensor with each element subtracted from the scalar

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Sub<Tensor> for &Tensor

Source§

fn sub(self, other: Tensor) -> Tensor

Subtracts a tensor from a tensor reference element-wise

§Returns

A new tensor containing the element-wise difference

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Sub<Tensor> for f32

Scalar-tensor subtraction operator implementations

Provides subtraction operations between scalars and tensors. Computes scalar - tensor by negating the tensor and adding the scalar.

Source§

fn sub(self, tensor: Tensor) -> Tensor

Subtracts each element of the tensor from the scalar

§Returns

A new tensor with each element subtracted from the scalar

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Sub<f32> for &Tensor

Source§

fn sub(self, scalar: f32) -> Tensor

Subtracts a scalar from each element of the tensor (reference version)

§Returns

A new tensor with the scalar subtracted from each element

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Sub<f32> for Tensor

Tensor-scalar subtraction operator implementations

Provides subtraction operations between tensors and scalars. All implementations delegate to the underlying sub_scalar method.

Source§

fn sub(self, scalar: f32) -> Tensor

Subtracts a scalar from each element of the tensor

§Returns

A new tensor with the scalar subtracted from each element

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Sub for &Tensor

Source§

fn sub(self, other: &Tensor) -> Tensor

Subtracts two tensors element-wise (reference version)

§Returns

A new tensor containing the element-wise difference

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl Sub for Tensor

Tensor subtraction operator implementations

Provides subtraction operations between tensors with various reference combinations. All implementations delegate to the underlying sub_tensor method for optimal performance.

Source§

fn sub(self, other: Tensor) -> Tensor

Subtracts two tensors element-wise

§Returns

A new tensor containing the element-wise difference

Source§

type Output = Tensor

The resulting type after applying the - operator.
Source§

impl SubAssign<&Tensor> for Tensor

Source§

fn sub_assign(&mut self, other: &Tensor)

Subtracts another tensor reference from this tensor in-place

Source§

impl SubAssign<f32> for Tensor

Tensor-scalar subtraction assignment operator implementations

Provides in-place subtraction operations between tensors and scalars.

Source§

fn sub_assign(&mut self, scalar: f32)

Subtracts a scalar from each element of this tensor in-place

Source§

impl SubAssign for Tensor

Tensor subtraction assignment operator implementations

Provides in-place subtraction operations between tensors. All implementations delegate to the underlying sub_tensor method.

Source§

fn sub_assign(&mut self, other: Tensor)

Subtracts another tensor from this tensor in-place

Source§

impl Send for Tensor

Source§

impl Sync for Tensor

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToFieldValue for T
where T: Serializable,

Source§

fn to_field_value(&self) -> FieldValue

Converts the value to a FieldValue for serialization Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.