use ghostflow_core::Tensor;
use crate::deep::layers::Dense;
use crate::deep::activations::{ReLU, Tanh};
pub struct ODEFunc {
layers: Vec<Dense>,
}
impl ODEFunc {
pub fn new(hidden_dim: usize, num_layers: usize) -> Self {
let mut layers = Vec::new();
for _ in 0..num_layers {
layers.push(Dense::new(hidden_dim, hidden_dim));
}
ODEFunc { layers }
}
pub fn forward(&mut self, t: f32, x: &Tensor, training: bool) -> Tensor {
let mut out = x.clone();
for (i, layer) in self.layers.iter_mut().enumerate() {
out = layer.forward(&out, training);
if i < self.layers.len() - 1 {
out = Tanh::new().forward(&out);
}
}
out
}
}
pub struct EulerSolver {
dt: f32,
}
impl EulerSolver {
pub fn new(dt: f32) -> Self {
EulerSolver { dt }
}
pub fn solve(&self, ode_func: &mut ODEFunc, x0: &Tensor, t0: f32, t1: f32, training: bool) -> Tensor {
let num_steps = ((t1 - t0) / self.dt).ceil() as usize;
let mut x = x0.clone();
let mut t = t0;
for _ in 0..num_steps {
let dx = ode_func.forward(t, &x, training);
x = self.add_scaled(&x, &dx, self.dt);
t += self.dt;
}
x
}
fn add_scaled(&self, x: &Tensor, dx: &Tensor, scale: f32) -> Tensor {
let x_data = x.data_f32();
let dx_data = dx.data_f32();
let result: Vec<f32> = x_data.iter()
.zip(dx_data.iter())
.map(|(&x_val, &dx_val)| x_val + scale * dx_val)
.collect();
Tensor::from_slice(&result, x.dims()).unwrap()
}
}
pub struct RK4Solver {
dt: f32,
}
impl RK4Solver {
pub fn new(dt: f32) -> Self {
RK4Solver { dt }
}
pub fn solve(&self, ode_func: &mut ODEFunc, x0: &Tensor, t0: f32, t1: f32, training: bool) -> Tensor {
let num_steps = ((t1 - t0) / self.dt).ceil() as usize;
let mut x = x0.clone();
let mut t = t0;
for _ in 0..num_steps {
let k1 = ode_func.forward(t, &x, training);
let x_k2 = self.add_scaled(&x, &k1, self.dt / 2.0);
let k2 = ode_func.forward(t + self.dt / 2.0, &x_k2, training);
let x_k3 = self.add_scaled(&x, &k2, self.dt / 2.0);
let k3 = ode_func.forward(t + self.dt / 2.0, &x_k3, training);
let x_k4 = self.add_scaled(&x, &k3, self.dt);
let k4 = ode_func.forward(t + self.dt, &x_k4, training);
x = self.rk4_step(&x, &k1, &k2, &k3, &k4);
t += self.dt;
}
x
}
fn add_scaled(&self, x: &Tensor, dx: &Tensor, scale: f32) -> Tensor {
let x_data = x.data_f32();
let dx_data = dx.data_f32();
let result: Vec<f32> = x_data.iter()
.zip(dx_data.iter())
.map(|(&x_val, &dx_val)| x_val + scale * dx_val)
.collect();
Tensor::from_slice(&result, x.dims()).unwrap()
}
fn rk4_step(&self, x: &Tensor, k1: &Tensor, k2: &Tensor, k3: &Tensor, k4: &Tensor) -> Tensor {
let x_data = x.data_f32();
let k1_data = k1.data_f32();
let k2_data = k2.data_f32();
let k3_data = k3.data_f32();
let k4_data = k4.data_f32();
let result: Vec<f32> = (0..x_data.len())
.map(|i| {
x_data[i] + self.dt / 6.0 * (k1_data[i] + 2.0 * k2_data[i] + 2.0 * k3_data[i] + k4_data[i])
})
.collect();
Tensor::from_slice(&result, x.dims()).unwrap()
}
}
pub struct NeuralODEBlock {
ode_func: ODEFunc,
solver: RK4Solver,
t0: f32,
t1: f32,
}
impl NeuralODEBlock {
pub fn new(hidden_dim: usize, num_layers: usize, t0: f32, t1: f32, dt: f32) -> Self {
NeuralODEBlock {
ode_func: ODEFunc::new(hidden_dim, num_layers),
solver: RK4Solver::new(dt),
t0,
t1,
}
}
pub fn forward(&mut self, x: &Tensor, training: bool) -> Tensor {
self.solver.solve(&mut self.ode_func, x, self.t0, self.t1, training)
}
}
pub struct NeuralODE {
input_layer: Dense,
ode_block: NeuralODEBlock,
output_layer: Dense,
}
impl NeuralODE {
pub fn new(input_dim: usize, hidden_dim: usize, output_dim: usize, num_ode_layers: usize) -> Self {
NeuralODE {
input_layer: Dense::new(input_dim, hidden_dim),
ode_block: NeuralODEBlock::new(hidden_dim, num_ode_layers, 0.0, 1.0, 0.1),
output_layer: Dense::new(hidden_dim, output_dim),
}
}
pub fn forward(&mut self, x: &Tensor, training: bool) -> Tensor {
let mut out = self.input_layer.forward(x, training);
out = ReLU::new().forward(&out);
out = self.ode_block.forward(&out, training);
self.output_layer.forward(&out, training)
}
}
pub struct AugmentedNeuralODE {
input_layer: Dense,
augment_layer: Dense,
ode_block: NeuralODEBlock,
output_layer: Dense,
augment_dim: usize,
}
impl AugmentedNeuralODE {
pub fn new(input_dim: usize, hidden_dim: usize, output_dim: usize, augment_dim: usize, num_ode_layers: usize) -> Self {
AugmentedNeuralODE {
input_layer: Dense::new(input_dim, hidden_dim),
augment_layer: Dense::new(hidden_dim, augment_dim),
ode_block: NeuralODEBlock::new(hidden_dim + augment_dim, num_ode_layers, 0.0, 1.0, 0.1),
output_layer: Dense::new(hidden_dim + augment_dim, output_dim),
augment_dim,
}
}
pub fn forward(&mut self, x: &Tensor, training: bool) -> Tensor {
let mut h = self.input_layer.forward(x, training);
h = ReLU::new().forward(&h);
let aug = self.augment_layer.forward(&h, training);
let augmented = self.concatenate(&h, &aug);
let out = self.ode_block.forward(&augmented, training);
self.output_layer.forward(&out, training)
}
fn concatenate(&self, a: &Tensor, b: &Tensor) -> Tensor {
let a_data = a.data_f32();
let b_data = b.data_f32();
let batch_size = a.dims()[0];
let dim_a = a.dims()[1];
let dim_b = b.dims()[1];
let mut result = Vec::new();
for i in 0..batch_size {
for j in 0..dim_a {
result.push(a_data[i * dim_a + j]);
}
for j in 0..dim_b {
result.push(b_data[i * dim_b + j]);
}
}
Tensor::from_slice(&result, &[batch_size, dim_a + dim_b]).unwrap()
}
}
pub struct ContinuousNormalizingFlow {
ode_func: ODEFunc,
solver: RK4Solver,
}
impl ContinuousNormalizingFlow {
pub fn new(dim: usize, num_layers: usize) -> Self {
ContinuousNormalizingFlow {
ode_func: ODEFunc::new(dim, num_layers),
solver: RK4Solver::new(0.1),
}
}
pub fn forward(&mut self, x: &Tensor, training: bool) -> (Tensor, f32) {
let z = self.solver.solve(&mut self.ode_func, x, 0.0, 1.0, training);
let log_det = self.compute_log_det(x, &z);
(z, log_det)
}
pub fn inverse(&mut self, z: &Tensor, training: bool) -> Tensor {
self.solver.solve(&mut self.ode_func, z, 1.0, 0.0, training)
}
fn compute_log_det(&self, x: &Tensor, z: &Tensor) -> f32 {
let x_data = x.data_f32();
let z_data = z.data_f32();
let mut log_det = 0.0f32;
for (x_val, z_val) in x_data.iter().zip(z_data.iter()) {
if *x_val != 0.0 {
log_det += (z_val / x_val).abs().ln();
}
}
log_det
}
}
pub struct SecondOrderNeuralODE {
ode_func: ODEFunc,
velocity_func: ODEFunc,
solver: RK4Solver,
}
impl SecondOrderNeuralODE {
pub fn new(dim: usize, num_layers: usize) -> Self {
SecondOrderNeuralODE {
ode_func: ODEFunc::new(dim * 2, num_layers),
velocity_func: ODEFunc::new(dim, num_layers),
solver: RK4Solver::new(0.1),
}
}
pub fn forward(&mut self, x: &Tensor, v: &Tensor, training: bool) -> (Tensor, Tensor) {
let state = self.concatenate(x, v);
let final_state = self.solver.solve(&mut self.ode_func, &state, 0.0, 1.0, training);
self.split(&final_state)
}
fn concatenate(&self, a: &Tensor, b: &Tensor) -> Tensor {
let a_data = a.data_f32();
let b_data = b.data_f32();
let mut result = Vec::new();
result.extend_from_slice(a_data);
result.extend_from_slice(b_data);
let batch_size = a.dims()[0];
let total_dim = a.dims()[1] + b.dims()[1];
Tensor::from_slice(&result, &[batch_size, total_dim]).unwrap()
}
fn split(&self, state: &Tensor) -> (Tensor, Tensor) {
let data = state.data_f32();
let batch_size = state.dims()[0];
let total_dim = state.dims()[1];
let half_dim = total_dim / 2;
let mut x_data = Vec::new();
let mut v_data = Vec::new();
for b in 0..batch_size {
for d in 0..half_dim {
x_data.push(data[b * total_dim + d]);
}
for d in half_dim..total_dim {
v_data.push(data[b * total_dim + d]);
}
}
let x = Tensor::from_slice(&x_data, &[batch_size, half_dim]).unwrap();
let v = Tensor::from_slice(&v_data, &[batch_size, half_dim]).unwrap();
(x, v)
}
}
pub struct HamiltonianNeuralNetwork {
hamiltonian: Dense,
layers: Vec<Dense>,
}
impl HamiltonianNeuralNetwork {
pub fn new(dim: usize, hidden_dim: usize, num_layers: usize) -> Self {
let mut layers = Vec::new();
layers.push(Dense::new(dim * 2, hidden_dim));
for _ in 1..num_layers {
layers.push(Dense::new(hidden_dim, hidden_dim));
}
layers.push(Dense::new(hidden_dim, 1));
HamiltonianNeuralNetwork {
hamiltonian: Dense::new(dim * 2, 1),
layers,
}
}
pub fn forward(&mut self, q: &Tensor, p: &Tensor, training: bool) -> Tensor {
let state = self.concatenate(q, p);
let mut out = state;
for (i, layer) in self.layers.iter_mut().enumerate() {
out = layer.forward(&out, training);
if i < self.layers.len() - 1 {
out = Tanh::new().forward(&out);
}
}
out
}
fn concatenate(&self, a: &Tensor, b: &Tensor) -> Tensor {
let a_data = a.data_f32();
let b_data = b.data_f32();
let mut result = Vec::new();
result.extend_from_slice(a_data);
result.extend_from_slice(b_data);
let batch_size = a.dims()[0];
let total_dim = a.dims()[1] + b.dims()[1];
Tensor::from_slice(&result, &[batch_size, total_dim]).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_neural_ode() {
let mut node = NeuralODE::new(10, 64, 10, 3);
let input = Tensor::from_slice(&vec![0.5f32; 2 * 10], &[2, 10]).unwrap();
let output = node.forward(&input, false);
assert_eq!(output.dims()[1], 10);
}
#[test]
fn test_augmented_neural_ode() {
let mut anode = AugmentedNeuralODE::new(10, 64, 10, 16, 3);
let input = Tensor::from_slice(&vec![0.5f32; 2 * 10], &[2, 10]).unwrap();
let output = anode.forward(&input, false);
assert_eq!(output.dims()[1], 10);
}
#[test]
fn test_continuous_normalizing_flow() {
let mut cnf = ContinuousNormalizingFlow::new(10, 3);
let input = Tensor::from_slice(&vec![0.5f32; 2 * 10], &[2, 10]).unwrap();
let (z, log_det) = cnf.forward(&input, false);
assert_eq!(z.dims()[1], 10);
}
}