Initial commit

This commit is contained in:
redclawsystems
2026-03-04 00:08:42 +00:00
commit 4d88dc0584
4449 changed files with 1556714 additions and 0 deletions
@@ -0,0 +1,219 @@
//! KAN Networks (Kolmogorov-Arnold Networks) Implementation
//!
//! This module implements KAN networks based on the Kolmogorov-Arnold representation theorem.
//! Unlike traditional MLPs that use fixed activations on nodes, KANs use learnable activation
//! functions on edges, enabling superior function approximation and interpretability.
//!
//! ## Key Features
//!
//! - **Edge-based learnable activations**: Each edge has its own trainable activation function
//! - **Multiple basis functions**: Support for B-splines, Chebyshev polynomials, and Fourier series
//! - **Adaptive grid refinement**: Automatic grid extension based on function complexity
//! - **Network pruning**: Interpretability-guided pruning for simplified networks
//! - **Symbolic regression**: Extract symbolic mathematical expressions from trained networks
//! - **Autograd integration**: Full gradient support for training
//!
//! ## Architecture Overview
//!
//! ```text
//! Input Layer Hidden Layer Output Layer
//! x1 ────────────○─────────────── y1
//! │ φ(1,1) / \ φ(2,1) │
//! └────────────○───○──────────── y2
//! x2 ────────────○───○────────────
//! φ(1,2) │ │ φ(2,2)
//! ... ...
//! ```
//!
//! Where each φ(i,j) represents a learnable univariate activation function on the edge
//! from node i in layer L to node j in layer L+1.
//!
//! ## Usage Example
//!
//! ```rust
//! use rtx_transformers::kan::*;
//! use rtx_tensor::{Tensor, Device};
//!
//! let device = Device::cuda(0).unwrap_or(Device::default());
//!
//! // Create a KAN network with architecture [2, 5, 3, 1]
//! let kan_network = KANNetwork::new(
//! &[2, 5, 3, 1], // architecture
//! 20, // grid_size
//! 3, // spline_order (for B-splines)
//! BasisFunctionType::BSpline,
//! &device
//! )?;
//!
//! // Forward pass
//! let input = Tensor::randn(&[batch_size, 2], &device)?;
//! let output = kan_network.forward(&input)?;
//!
//! // Train the network (with your training loop)
//! // ...
//!
//! // Extract symbolic representation
//! let regressor = SymbolicRegressor::new(
//! 5, // max_depth
//! vec![SymbolicOp::Add, SymbolicOp::Mul, SymbolicOp::Sin],
//! 0.01, // tolerance
//! 1000, // max_iterations
//! &device
//! )?;
//! let symbolic_form = regressor.extract_from_kan(&kan_network, &train_data)?;
//! ```
pub mod kan_network;
pub mod basis_functions;
pub mod grid_adapter;
pub mod pruning;
pub mod symbolic;
#[cfg(all(test, feature = "disabled_tests"))]
pub mod kan_network_tests;
// Re-export main types for convenience
pub use kan_network::{KANNetwork, KANLayer, KANConfig, KANState, KANGates};
pub use basis_functions::{BasisFunction, BasisFunctionType};
pub use grid_adapter::{GridAdapter, AdaptationStrategy};
pub use pruning::{KANPruner, PruningStrategy};
pub use symbolic::{SymbolicRegressor, LawDiscoverer, SymbolicOp};
use crate::error::{TransformerError, Result};
/// KAN-specific error types
#[derive(Debug, thiserror::Error)]
pub enum KANError {
#[error("Invalid grid configuration: {msg}")]
InvalidGrid { msg: String },
#[error("Basis function error: {msg}")]
BasisFunction { msg: String },
#[error("Grid adaptation failed: {msg}")]
GridAdaptation { msg: String },
#[error("Pruning operation failed: {msg}")]
Pruning { msg: String },
#[error("Symbolic regression failed: {msg}")]
SymbolicRegression { msg: String },
#[error("Dimension mismatch: expected {expected}, got {actual}")]
DimensionMismatch { expected: usize, actual: usize },
#[error("Autograd integration error: {msg}")]
AutogradIntegration { msg: String },
}
impl From<KANError> for TransformerError {
fn from(err: KANError) -> Self {
TransformerError::KAN(err.to_string())
}
}
/// Utility functions for KAN networks
pub mod utils {
use super::*;
use rtx_tensor::{Tensor, Device};
/// Generate a grid of points for basis function evaluation
pub fn generate_grid(min: f64, max: f64, size: usize, device: &Device) -> Result<Tensor> {
if size < 2 {
return Err(TransformerError::KAN(
"Grid size must be at least 2".to_string()
));
}
Tensor::linspace(min, max, size, device)
.map_err(|e| TransformerError::TensorError(e))
}
/// Compute complexity measure for grid adaptation
pub fn compute_complexity_measure(values: &Tensor) -> Result<f64> {
// Use second derivative as complexity measure
if values.dim() < 1 {
return Ok(0.0);
}
let n = values.shape().dims()[values.dim() - 1];
if n < 3 {
return Ok(0.0);
}
// Approximate second derivative using finite differences
let second_deriv = values.narrow(values.dim() - 1, 2, n - 2)?
.sub(&values.narrow(values.dim() - 1, 1, n - 2)?.mul_scalar(2.0)?)?
.add(&values.narrow(values.dim() - 1, 0, n - 2)?)?;
let complexity = second_deriv.abs()?.mean(None, false)?;
Ok(complexity.get_item([])?)
}
/// Check if a function is approximately linear
pub fn is_approximately_linear(values: &Tensor, tolerance: f64) -> Result<bool> {
let complexity = compute_complexity_measure(values)?;
Ok(complexity < tolerance)
}
/// Estimate optimal grid size based on function complexity
pub fn estimate_grid_size(complexity: f64, base_size: usize, max_size: usize) -> usize {
let adaptive_factor = (complexity * 10.0).max(1.0).min(5.0);
let estimated_size = (base_size as f64 * adaptive_factor) as usize;
estimated_size.min(max_size).max(base_size)
}
}
#[cfg(all(test, feature = "disabled_tests"))]
mod tests {
use super::*;
#[test]
fn test_kan_config_creation() {
let config = KANConfig::new(vec![3, 10, 5, 1]);
assert_eq!(config.architecture, vec![3, 10, 5, 1]);
assert_eq!(config.grid_size, 20);
assert_eq!(config.basis_function_type, BasisFunctionType::BSpline);
}
#[test]
fn test_kan_config_validation() {
// Valid config
let valid_config = KANConfig::new(vec![2, 5, 1]);
assert!(valid_config.validate().is_ok());
// Invalid architecture (too short)
let invalid_config = KANConfig::new(vec![5]);
assert!(invalid_config.validate().is_err());
// Invalid grid size
let mut invalid_grid = KANConfig::new(vec![2, 5, 1]);
invalid_grid.grid_size = 2;
assert!(invalid_grid.validate().is_err());
}
#[test]
fn test_config_builder_methods() {
let config = KANConfig::new(vec![2, 8, 1])
.with_grid_size(30)
.with_spline_order(4)
.with_basis_function(BasisFunctionType::Chebyshev)
.with_basis_range(-1.0, 1.0)
.with_adaptive_grid(true, 0.02, 80)
.with_pruning(true, 0.6)
.with_symbolic_regression(true, 8);
assert_eq!(config.grid_size, 30);
assert_eq!(config.spline_order, 4);
assert_eq!(config.basis_function_type, BasisFunctionType::Chebyshev);
assert_eq!(config.basis_range, (-1.0, 1.0));
assert!(config.adaptive_grid);
assert_eq!(config.adaptation_threshold, 0.02);
assert_eq!(config.max_grid_size, 80);
assert!(config.enable_pruning);
assert_eq!(config.sparsity_target, 0.6);
assert!(config.enable_symbolic);
assert_eq!(config.symbolic_max_depth, 8);
}
}