#[allow(unused_imports)]
use crate::prelude::*;
use num_rational::BigRational;
use num_traits::One;
pub type VarId = usize;
#[derive(Debug, Clone)]
pub struct Basis {
pub basic_vars: Vec<VarId>,
pub nonbasic_vars: Vec<VarId>,
pub basis_matrix: FxHashMap<(usize, usize), BigRational>,
}
impl Basis {
pub fn new(basic_vars: Vec<VarId>, nonbasic_vars: Vec<VarId>) -> Self {
Self {
basic_vars,
nonbasic_vars,
basis_matrix: FxHashMap::default(),
}
}
pub fn size(&self) -> usize {
self.basic_vars.len()
}
pub fn is_basic(&self, var: VarId) -> bool {
self.basic_vars.contains(&var)
}
}
#[derive(Debug, Clone)]
pub struct EtaMatrix {
pub column: usize,
pub eta_vector: FxHashMap<usize, BigRational>,
}
#[derive(Debug, Clone)]
pub struct BasisUpdateConfig {
pub use_forrest_tomlin: bool,
pub refactorize_freq: usize,
pub steepest_edge: bool,
}
impl Default for BasisUpdateConfig {
fn default() -> Self {
Self {
use_forrest_tomlin: true,
refactorize_freq: 100,
steepest_edge: true,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct BasisUpdateStats {
pub updates: u64,
pub refactorizations: u64,
pub eta_matrices: u64,
pub instabilities: u64,
}
#[derive(Debug)]
pub struct BasisUpdater {
basis: Basis,
eta_matrices: Vec<EtaMatrix>,
updates_since_refactor: usize,
config: BasisUpdateConfig,
stats: BasisUpdateStats,
}
impl BasisUpdater {
pub fn new(basis: Basis, config: BasisUpdateConfig) -> Self {
Self {
basis,
eta_matrices: Vec::new(),
updates_since_refactor: 0,
config,
stats: BasisUpdateStats::default(),
}
}
pub fn default_config(basis: Basis) -> Self {
Self::new(basis, BasisUpdateConfig::default())
}
pub fn pivot(&mut self, entering: VarId, leaving: VarId) {
let leaving_pos = self.basis.basic_vars.iter().position(|&v| v == leaving);
if let Some(pos) = leaving_pos {
self.basis.basic_vars[pos] = entering;
self.basis.nonbasic_vars.retain(|&v| v != entering);
self.basis.nonbasic_vars.push(leaving);
if self.config.use_forrest_tomlin {
self.forrest_tomlin_update(pos, entering);
}
self.stats.updates += 1;
self.updates_since_refactor += 1;
if self.updates_since_refactor >= self.config.refactorize_freq {
self.refactorize();
}
}
}
fn forrest_tomlin_update(&mut self, column: usize, _entering: VarId) {
let mut eta_vector = FxHashMap::default();
eta_vector.insert(column, BigRational::one());
self.eta_matrices.push(EtaMatrix { column, eta_vector });
self.stats.eta_matrices += 1;
}
fn refactorize(&mut self) {
self.eta_matrices.clear();
self.updates_since_refactor = 0;
self.stats.refactorizations += 1;
}
pub fn check_stability(&mut self) -> bool {
true
}
pub fn basis(&self) -> &Basis {
&self.basis
}
pub fn stats(&self) -> &BasisUpdateStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = BasisUpdateStats::default();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basis_creation() {
let basis = Basis::new(vec![0, 1], vec![2, 3]);
assert_eq!(basis.size(), 2);
assert!(basis.is_basic(0));
assert!(!basis.is_basic(2));
}
#[test]
fn test_updater_creation() {
let basis = Basis::new(vec![0, 1], vec![2, 3]);
let updater = BasisUpdater::default_config(basis);
assert_eq!(updater.stats().updates, 0);
}
#[test]
fn test_pivot() {
let basis = Basis::new(vec![0, 1], vec![2, 3]);
let mut updater = BasisUpdater::default_config(basis);
updater.pivot(2, 0);
assert!(updater.basis().is_basic(2));
assert!(!updater.basis().is_basic(0));
assert_eq!(updater.stats().updates, 1);
}
#[test]
fn test_refactorization() {
let basis = Basis::new(vec![0, 1], vec![2, 3]);
let config = BasisUpdateConfig {
refactorize_freq: 2,
..Default::default()
};
let mut updater = BasisUpdater::new(basis, config);
updater.pivot(2, 0);
updater.pivot(3, 1);
updater.pivot(0, 2);
assert!(updater.stats().refactorizations > 0);
}
#[test]
fn test_eta_matrices() {
let basis = Basis::new(vec![0, 1], vec![2, 3]);
let mut updater = BasisUpdater::default_config(basis);
updater.pivot(2, 0);
assert_eq!(updater.stats().eta_matrices, 1);
}
#[test]
fn test_stability_check() {
let basis = Basis::new(vec![0, 1], vec![2, 3]);
let mut updater = BasisUpdater::default_config(basis);
assert!(updater.check_stability());
}
#[test]
fn test_stats() {
let basis = Basis::new(vec![0, 1], vec![2, 3]);
let mut updater = BasisUpdater::default_config(basis);
updater.pivot(2, 0);
assert_eq!(updater.stats().updates, 1);
updater.reset_stats();
assert_eq!(updater.stats().updates, 0);
}
}