use std::time::Instant;
use topos::{Backend, Network, Shape, Tensor, Tensorial, init};
fn main() {
let mut accelerated = false;
for backend in Backend::ALL {
let status = backend.status();
accelerated |= status.is_ok();
match status {
Ok(()) => println!("{backend:?}: ready"),
Err(reason) => println!("{backend:?}: {reason}"),
}
}
let scale = if accelerated { 1 } else { 8 };
let batch_len = 1024 / scale;
let feature_len = 1024 / scale;
let hidden_len = 2048 / scale;
let class_len = 512 / scale;
let step_flops = (2 * batch_len * feature_len * hidden_len
+ 2 * batch_len * hidden_len * class_len) as f64
* 3.0;
let network: Network<Tensor<f32>> = Network::new();
let mut initializer = init::kaiming(7);
let hidden_weights = network.parameter(initializer(&Shape::new([feature_len, hidden_len])));
let output_weights = network.parameter(initializer(&Shape::new([hidden_len, class_len])));
let features = network.input(Tensor::filled([batch_len, feature_len], 0.0));
let targets = network.input(Tensor::filled([batch_len, class_len], 0.0));
let hidden = features.matmul(hidden_weights).tanh();
let prediction = hidden.matmul(output_weights);
let error = prediction - targets;
let loss = (error * error).sum();
let features_symbol = features.symbol();
let loss_symbol = loss.symbol();
let learning_rate = Tensor::new([], [0.0001_f32]);
let mut batch = init::normal(11, 1.0);
let batch = batch(&Shape::new([batch_len, feature_len]));
let side = 2048 / scale;
let left = initializer(&Shape::new([side, side]));
let right = initializer(&Shape::new([side, side]));
let product_flops = 2.0 * (side as f64).powi(3);
let rounds = if accelerated { 20 } else { 2 };
let started = Instant::now();
for _ in 0..rounds {
std::hint::black_box(left.matmul(&right));
}
let per_product = started.elapsed().as_secs_f64() / rounds as f64;
println!(
"raw product {side} x {side}: {:.1} GFLOP/s",
product_flops / per_product / 1e9
);
let mut network = network;
let warmup = 2;
let steps = 8;
let mut started = Instant::now();
for step in 0..warmup + steps {
if step == warmup {
started = Instant::now();
}
let loss_value = network.resolve(loss_symbol);
let run = network.forward_with([(features_symbol, batch.clone())]);
let gradients = run.backward(loss_value);
network = network.update(&gradients, |parameter, gradient| {
parameter.clone() - gradient.clone() * learning_rate.broadcast_like(gradient)
});
}
let elapsed = started.elapsed().as_secs_f64();
let per_step = elapsed / steps as f64;
println!(
"training step, batch {batch_len}, {feature_len} -> {hidden_len} -> {class_len}: \
{per_step:.3} s/step, {:.1} GFLOP/s of matrix products",
step_flops / per_step / 1e9
);
}