domino-lib 2.0.0

Domino library, create sequences and puzzles, solve and validate them in rust
Documentation
use std::collections::HashMap;

use good_lp::{
    default_solver, variable, Constraint, Expression, ProblemVariables, ResolutionError,
    SolverModel, Variable, VariableDefinition,
};

use super::{
    parser::{BoundType, ObjectiveType},
    translator::ResultTranslator,
    ModelParser,
};

pub struct Adapter {
    vars: ProblemVariables,
    pub variable_map: HashMap<String, (VariableDefinition, Variable)>,
    var_names: HashMap<Variable, String>,
    objective_direction: ObjectiveType,
    objective: Expression,
    bounds: Vec<Constraint>,
}

impl Adapter {
    pub fn new() -> Adapter {
        Adapter {
            vars: ProblemVariables::new(),
            variable_map: HashMap::new(),
            var_names: HashMap::new(),
            objective_direction: ObjectiveType::Minimize,
            objective: Expression::from_other_affine(0.0),
            bounds: Vec::new(),
        }
    }

    pub fn adapt(mut self, parser: &ModelParser) -> Result<ResultTranslator, ResolutionError> {
        self.adapt_variables(parser);
        self.adapt_objective(parser);
        self.adapt_bounds(parser);
        let mut problem = if self.objective_direction == ObjectiveType::Minimize {
            self.vars.minimise(self.objective.clone())
        } else {
            self.vars.maximise(self.objective.clone())
        }
        .using(default_solver);
        self.bounds.into_iter().for_each(|constraint| {
            problem.add_constraint(constraint);
        });
        problem.solve().map(|solution| {
            ResultTranslator::new(
                self.objective,
                self.var_names,
                self.variable_map.clone(),
                solution,
            )
        })
    }

    fn adapt_variables(&mut self, parser: &ModelParser) {
        for variable_name in parser.binary_variables.clone() {
            let def_variable = variable().binary().name(variable_name.clone());
            let variable = self.vars.add(def_variable.clone());
            self.variable_map
                .insert(variable_name.clone(), (def_variable, variable));
            self.var_names.insert(variable, variable_name);
        }
    }

    fn adapt_objective(&mut self, parser: &ModelParser) {
        let objective = parser.objective.clone();
        self.objective_direction = objective.objective_type;
        self.objective = Expression::from_other_affine(0.0);
        for term in objective.terms.0 {
            if let Some(variable) = self.variable_map.get(&term.1) {
                self.objective
                    .add_mul(term.0, Expression::from_other_affine(variable.1));
            }
        }
        self.objective += objective.terms.1;
    }

    fn adapt_bounds(&mut self, parser: &ModelParser) {
        for bound in &parser.bounds {
            let mut lhs_expr = Expression::from_other_affine(0.0);
            bound.lhs.0.iter().for_each(|(coef, var_name)| {
                if let Some(variable) = self.variable_map.get(var_name) {
                    lhs_expr.add_mul(*coef, variable.1);
                }
            });
            lhs_expr += bound.lhs.1;
            let mut rhs_expr = Expression::from_other_affine(0.0);
            bound.rhs.0.iter().for_each(|(coef, var_name)| {
                if let Some(variable) = self.variable_map.get(var_name) {
                    rhs_expr.add_mul(*coef, variable.1);
                }
            });
            rhs_expr += bound.rhs.1;

            let constraint = match bound.bound_type {
                BoundType::Equality => lhs_expr.eq(rhs_expr),
                BoundType::LessThanOrEqual => lhs_expr.leq(rhs_expr),
                BoundType::GreaterThanOrEqual => lhs_expr.geq(rhs_expr),
            };
            self.bounds.push(constraint);
        }
    }
}