#[allow(unused_imports)]
use crate::prelude::*;
use num_rational::BigRational;
pub type VarId = usize;
#[derive(Debug, Clone)]
pub struct CuttingPlane {
pub coeffs: FxHashMap<VarId, BigRational>,
pub rhs: BigRational,
pub cut_type: CutType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CutType {
Gomory,
MixedIntegerGomory,
Cover,
Clique,
LiftAndProject,
}
#[derive(Debug, Clone)]
pub struct ExtendedCuttingPlanesConfig {
pub enable_mig: bool,
pub enable_cover: bool,
pub enable_clique: bool,
pub enable_lift_project: bool,
pub max_cuts_per_round: usize,
}
impl Default for ExtendedCuttingPlanesConfig {
fn default() -> Self {
Self {
enable_mig: true,
enable_cover: true,
enable_clique: true,
enable_lift_project: false, max_cuts_per_round: 100,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ExtendedCuttingPlanesStats {
pub mig_cuts: u64,
pub cover_cuts: u64,
pub clique_cuts: u64,
pub lift_project_cuts: u64,
}
#[derive(Debug)]
pub struct ExtendedCuttingPlaneGenerator {
integer_vars: FxHashSet<VarId>,
binary_vars: FxHashSet<VarId>,
config: ExtendedCuttingPlanesConfig,
stats: ExtendedCuttingPlanesStats,
}
impl ExtendedCuttingPlaneGenerator {
pub fn new(config: ExtendedCuttingPlanesConfig) -> Self {
Self {
integer_vars: FxHashSet::default(),
binary_vars: FxHashSet::default(),
config,
stats: ExtendedCuttingPlanesStats::default(),
}
}
pub fn default_config() -> Self {
Self::new(ExtendedCuttingPlanesConfig::default())
}
pub fn add_integer_var(&mut self, var: VarId) {
self.integer_vars.insert(var);
}
pub fn add_binary_var(&mut self, var: VarId) {
self.binary_vars.insert(var);
self.integer_vars.insert(var);
}
pub fn generatorerate_mig_cut(
&mut self,
_row: &FxHashMap<VarId, BigRational>,
_rhs: &BigRational,
) -> Option<CuttingPlane> {
if !self.config.enable_mig {
return None;
}
self.stats.mig_cuts += 1;
None
}
pub fn generatorerate_cover_cut(
&mut self,
_weights: &FxHashMap<VarId, BigRational>,
_capacity: &BigRational,
) -> Option<CuttingPlane> {
if !self.config.enable_cover {
return None;
}
self.stats.cover_cuts += 1;
None
}
pub fn generatorerate_clique_cut(&mut self, _vars: &[VarId]) -> Option<CuttingPlane> {
if !self.config.enable_clique {
return None;
}
self.stats.clique_cuts += 1;
None
}
pub fn generatorerate_lift_project_cut(
&mut self,
_constraint: &FxHashMap<VarId, BigRational>,
_lift_var: VarId,
) -> Option<CuttingPlane> {
if !self.config.enable_lift_project {
return None;
}
self.stats.lift_project_cuts += 1;
None
}
pub fn generatorerate_cuts(
&mut self,
_solution: &FxHashMap<VarId, BigRational>,
) -> Vec<CuttingPlane> {
Vec::new()
}
pub fn stats(&self) -> &ExtendedCuttingPlanesStats {
&self.stats
}
pub fn reset_stats(&mut self) {
self.stats = ExtendedCuttingPlanesStats::default();
}
}
impl Default for ExtendedCuttingPlaneGenerator {
fn default() -> Self {
Self::default_config()
}
}
#[cfg(test)]
mod tests {
use super::*;
use num_traits::Zero;
#[test]
fn test_generatorerator_creation() {
let generator = ExtendedCuttingPlaneGenerator::default_config();
assert_eq!(generator.stats().mig_cuts, 0);
}
#[test]
fn test_add_vars() {
let mut generator = ExtendedCuttingPlaneGenerator::default_config();
generator.add_integer_var(0);
generator.add_binary_var(1);
assert!(generator.integer_vars.contains(&0));
assert!(generator.binary_vars.contains(&1));
}
#[test]
fn test_generatorerate_mig_cut() {
let mut generator = ExtendedCuttingPlaneGenerator::default_config();
let row = FxHashMap::default();
let rhs = BigRational::zero();
let cut = generator.generatorerate_mig_cut(&row, &rhs);
assert_eq!(generator.stats().mig_cuts, 1);
assert!(cut.is_none());
}
#[test]
fn test_stats() {
let mut generator = ExtendedCuttingPlaneGenerator::default_config();
generator.stats.mig_cuts = 5;
generator.stats.cover_cuts = 3;
assert_eq!(generator.stats().mig_cuts, 5);
assert_eq!(generator.stats().cover_cuts, 3);
generator.reset_stats();
assert_eq!(generator.stats().mig_cuts, 0);
}
}