use std::iter::Iterator;
use relp_num::{Field, OrderedField, OrderedFieldRef};
use relp_num::NonZeroSign;
pub use scale::Scalable as Prescalable;
use crate::data::linear_algebra::traits::SparseElement;
use crate::data::linear_program::elements::{BoundDirection, LinearProgramType, RangedConstraintRelation};
use crate::data::linear_program::general_form::{GeneralForm, RemovedVariable};
use crate::data::linear_program::general_form::presolve::counters::Counters;
use crate::data::linear_program::general_form::presolve::queues::Queues;
use crate::data::linear_program::general_form::presolve::updates::Updates;
mod rule;
mod queues;
pub(super) mod updates;
mod counters;
pub(super) mod scale;
pub(super) struct Index<'a, F: Field> {
queues: Queues,
pub updates: Updates<'a, F>,
counters: Counters<'a, F>,
activity_bounds: Vec<(Option<F>, Option<F>)>,
general_form: &'a GeneralForm<F>,
}
#[derive(Eq, PartialEq, Debug)]
pub(super) enum Change {
Meaningful,
NotMeaningful,
None,
}
impl<'a, OF> Index<'a, OF>
where
OF: OrderedField + SparseElement<OF>,
for<'r> &'r OF: OrderedFieldRef<OF>,
{
pub(super) fn new(general_form: &'a GeneralForm<OF>) -> Result<Self, LinearProgramType<OF>> {
let counters = Counters::new(general_form);
let updates = Updates::new(general_form, &counters)?;
let queues = Queues::new(general_form, &counters);
Ok(Self {
queues,
updates,
counters,
activity_bounds: vec![(None, None); general_form.nr_constraints()],
general_form,
})
}
pub(super) fn presolve_step(&mut self) -> Result<Change, LinearProgramType<OF>> {
if let Some(variable) = self.queues.substitution.pop() {
if self.counters.is_variable_still_active(variable) {
return self.presolve_fixed_variable(variable)
.map(|()| Change::Meaningful);
}
}
while let Some(constraint) = self.queues.bound.pop() {
if self.counters.is_constraint_still_active(constraint) {
return self.presolve_bound_constraint(constraint)
.map(|()| Change::Meaningful);
}
}
while let Some(variable) = self.queues.slack.pop() {
if self.counters.is_variable_still_active(variable) {
return self.presolve_slack(variable)
.map(|()| Change::Meaningful);
}
}
while let Some((constraint, direction)) = self.queues.activity.pop() {
if self.counters.is_constraint_still_active(constraint) {
debug_assert!(self.counters.constraint[constraint] > 1, "was {}", self.counters.constraint[constraint]);
return self.presolve_domain_propagation(constraint, direction);
}
}
Ok(Change::NotMeaningful)
}
fn after_bound_change(
&mut self,
variable: usize,
direction: BoundDirection,
change: Option<OF>,
) {
debug_assert!(self.updates.removed_variables.iter().all(|&(j, _)| variable != j));
debug_assert!(match direction {
BoundDirection::Lower => change.as_ref().map_or(true, |v| v.is_positive()),
BoundDirection::Upper => change.as_ref().map_or(true, |v| v.is_negative()),
});
if self.updates.is_variable_fixed(variable).is_some() && self.counters.is_variable_still_active(variable) {
self.queues.substitution.push(variable);
}
match change {
Some(difference) => self.update_activity_bounds(variable, direction, difference),
None => self.update_activity_counters(variable, direction),
}
}
fn update_activity_bounds(
&mut self,
variable: usize,
direction: BoundDirection,
by_how_much: OF,
) {
debug_assert!(match direction {
BoundDirection::Lower => by_how_much > OF::zero(),
BoundDirection::Upper => by_how_much < OF::zero(),
});
let rows_to_check = self.counters.iter_active_column(variable).collect::<Vec<_>>();
for (row, coefficient) in rows_to_check {
if !self.counters.is_constraint_still_active(row) {
continue;
}
let bound_to_edit = direction * coefficient.non_zero_signum();
if let Some(ref mut bound) = match bound_to_edit {
BoundDirection::Lower => &mut self.activity_bounds[row].0,
BoundDirection::Upper => &mut self.activity_bounds[row].1,
} {
*bound += &by_how_much * coefficient;
debug_assert!(match bound_to_edit {
BoundDirection::Lower => self.counters.activity[row].0,
BoundDirection::Upper => self.counters.activity[row].1,
} <= 1);
self.queues.activity.push((row, bound_to_edit));
}
}
}
fn update_activity_counters(
&mut self,
variable: usize,
direction: BoundDirection,
) {
let constraints_to_check = self.counters.iter_active_column(variable)
.map(|(i, v)| (i, v.clone())).collect::<Vec<_>>();
for (constraint, coefficient) in constraints_to_check {
let activity_direction = direction * coefficient.non_zero_signum();
let counter = match activity_direction {
BoundDirection::Lower => &mut self.counters.activity[constraint].0,
BoundDirection::Upper => &mut self.counters.activity[constraint].1,
};
*counter -= 1;
if *counter <= 1 {
self.queues.activity.push((constraint, activity_direction));
}
}
}
fn remove_constraint_values(
&mut self,
constraint: usize,
) -> Result<(), LinearProgramType<OF>> {
let variables_to_scan = self.counters.iter_active_row(constraint)
.map(|(j, _)| j)
.collect::<Vec<_>>();
for variable in variables_to_scan {
self.counters.constraint[constraint] -= 1;
self.counters.variable[variable] -= 1;
self.queue_variable_by_counter(variable)?;
}
debug_assert_eq!(self.counters.constraint[constraint], 0);
Ok(())
}
fn queue_variable_by_counter(&mut self, variable: usize) -> Result<(), LinearProgramType<OF>> {
match self.counters.variable[variable] {
0 => {
debug_assert!(self.updates.variable_feasible_value(variable).is_some());
let value = if self.general_form.variables[variable].cost.is_zero() {
RemovedVariable::Solved(self.updates.variable_feasible_value(variable).unwrap())
} else {
self.updates.optimize_column_independently(variable)?
};
self.remove_variable(variable, value);
},
1 => if self.general_form.variables[variable].cost.is_zero() {
self.queues.slack.push(variable);
},
_ => (),
}
Ok(())
}
fn queue_constraint_by_counter(&mut self, constraint: usize) -> Result<Change, LinearProgramType<OF>> {
match self.counters.constraint[constraint] {
0 => {
let right_hand_side = self.updates.b(constraint);
let constraint_type = self.updates.constraint_type(constraint);
if is_empty_constraint_feasible(right_hand_side, constraint_type) {
self.remove_constraint(constraint);
Ok(Change::Meaningful)
} else {
Err(LinearProgramType::Infeasible)
}
}
1 => {
self.queues.bound.push(constraint);
Ok(Change::None)
},
_ => Ok(Change::None),
}
}
fn remove_constraint(&mut self, constraint: usize) {
debug_assert_eq!(self.counters.constraint[constraint], 0);
self.updates.constraints_marked_removed.push(constraint);
}
fn remove_variable(&mut self, variable: usize, solution: RemovedVariable<OF>) {
debug_assert_eq!(self.counters.variable[variable], 0);
self.updates.removed_variables.push((variable, solution));
}
pub fn are_queues_empty(&self) -> bool {
self.queues.are_empty()
}
}
fn is_empty_constraint_feasible<OF>(
right_hand_side: &OF,
constraint_type: &RangedConstraintRelation<OF>,
) -> bool
where
OF: OrderedField,
for<'r> &'r OF: OrderedFieldRef<OF>,
{
match constraint_type {
RangedConstraintRelation::Equal => {
right_hand_side == &OF::zero()
},
RangedConstraintRelation::Range(range) => {
right_hand_side >= &OF::zero() && right_hand_side - range <= OF::zero()
},
RangedConstraintRelation::Less => {
right_hand_side >= &OF::zero()
},
RangedConstraintRelation::Greater => {
right_hand_side <= &OF::zero()
},
}
}
#[cfg(test)]
mod test;