-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Adding implementation for Objectives (#2)
- Loading branch information
Showing
9 changed files
with
348 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
edition = "2021" | ||
reorder_modules = false | ||
use_small_heuristics = "Max" | ||
max_width = 140 | ||
imports_granularity = "Crate" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
use crate::Rule; | ||
use pest::iterators::Pair; | ||
|
||
pub trait IsNumeric { | ||
fn is_numeric(&self) -> bool; | ||
} | ||
|
||
impl IsNumeric for Rule { | ||
fn is_numeric(&self) -> bool { | ||
matches!(self, Self::FLOAT | Self::PLUS | Self::MINUS | Self::POS_INFINITY | Self::NEG_INFINITY) | ||
} | ||
} | ||
|
||
pub trait AsFloat { | ||
/// # Errors | ||
/// Returns an error if the rule cannot be converted to a float | ||
fn as_float(&self) -> anyhow::Result<f64>; | ||
} | ||
|
||
impl AsFloat for Pair<'_, Rule> { | ||
#[allow(clippy::unreachable, clippy::wildcard_enum_match_arm)] | ||
fn as_float(&self) -> anyhow::Result<f64> { | ||
match self.as_rule() { | ||
Rule::POS_INFINITY => Ok(f64::INFINITY), | ||
Rule::NEG_INFINITY => Ok(f64::NEG_INFINITY), | ||
Rule::FLOAT => Ok(self.as_str().trim().parse()?), | ||
Rule::PLUS => Ok(1.0), | ||
Rule::MINUS => Ok(-1.0), | ||
_ => unreachable!("Unexpected rule observed: {:?}", self.as_rule()), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
use std::collections::HashMap; | ||
|
||
use pest::iterators::Pairs; | ||
|
||
use crate::{ | ||
common::{AsFloat, IsNumeric}, | ||
Rule, | ||
}; | ||
|
||
#[derive(Debug, Default)] | ||
pub enum VariableType { | ||
#[default] | ||
Unbounded, | ||
Bounded(f64, f64), | ||
Free, | ||
Integer, | ||
Binary, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Objective { | ||
pub name: String, | ||
pub coefficients: Vec<Coefficient>, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Coefficient { | ||
pub name: String, | ||
pub coefficient: f64, | ||
} | ||
|
||
impl TryFrom<Pairs<'_, Rule>> for Coefficient { | ||
type Error = anyhow::Error; | ||
|
||
#[allow(clippy::unreachable, clippy::wildcard_enum_match_arm)] | ||
fn try_from(values: Pairs<'_, Rule>) -> anyhow::Result<Self> { | ||
let (mut value, mut name) = (1.0, String::new()); | ||
for item in values { | ||
match item.as_rule() { | ||
r if r.is_numeric() => { | ||
value *= item.as_float()?; | ||
} | ||
Rule::VARIABLE => { | ||
name = item.as_str().to_string(); | ||
} | ||
_ => unreachable!(), | ||
} | ||
} | ||
Ok(Self { name, coefficient: value }) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Constraint { | ||
pub name: String, | ||
pub coefficients: Vec<Coefficient>, | ||
pub sense: String, | ||
pub rhs: f64, | ||
} | ||
|
||
#[derive(Debug, Default, PartialEq, Eq)] | ||
pub enum Sense { | ||
#[default] | ||
Minimize, | ||
Maximize, | ||
} | ||
|
||
#[derive(Debug, Default)] | ||
pub struct LPDefinition { | ||
pub problem_sense: Sense, | ||
pub variables: HashMap<String, VariableType>, | ||
pub objectives: Vec<Objective>, | ||
pub constraints: Vec<Constraint>, | ||
} | ||
|
||
impl LPDefinition { | ||
#[must_use] | ||
pub fn with_sense(&mut self, problem_sense: Sense) -> Self { | ||
Self { problem_sense, ..Default::default() } | ||
} | ||
|
||
pub fn add_variable(&mut self, name: String) { | ||
self.variables.entry(name).or_default(); | ||
} | ||
|
||
pub fn set_var_bounds(&mut self, name: String, kind: VariableType) { | ||
self.variables.entry(name).and_modify(|bound_kind| *bound_kind = kind); | ||
} | ||
|
||
pub fn add_objective(&mut self, objectives: Vec<Objective>) { | ||
self.objectives = objectives; | ||
} | ||
|
||
pub fn add_constraints(&mut self, constraints: Vec<Constraint>) { | ||
self.constraints = constraints; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
use std::{ | ||
fs::File, | ||
io::{BufReader, Read}, | ||
path::Path, | ||
}; | ||
|
||
use crate::{ | ||
model::{Constraint, LPDefinition, Objective, Sense}, | ||
LParser, Rule, | ||
}; | ||
use pest::{iterators::Pair, Parser}; | ||
|
||
/// # Errors | ||
/// Returns an error if the `read_to_string` or `open` fails | ||
pub fn parse_file(path: &Path) -> anyhow::Result<String> { | ||
let Ok(file) = File::open(path) else { | ||
anyhow::bail!("Could not open file at {path:?}"); | ||
}; | ||
let mut buf_reader = BufReader::new(file); | ||
let mut contents = String::new(); | ||
buf_reader.read_to_string(&mut contents)?; | ||
|
||
Ok(contents) | ||
} | ||
|
||
/// # Errors | ||
/// Returns an error if the parse fails | ||
pub fn parse_lp_file(contents: &str) -> anyhow::Result<LPDefinition> { | ||
let mut parsed = LParser::parse(Rule::LP_FILE, contents)?; | ||
let Some(pair) = parsed.next() else { | ||
anyhow::bail!("Invalid LP file"); | ||
}; | ||
let mut parsed_contents = LPDefinition::default(); | ||
for pair in pair.clone().into_inner() { | ||
parsed_contents = build(pair, parsed_contents)?; | ||
} | ||
|
||
Ok(dbg!(parsed_contents)) | ||
} | ||
|
||
#[allow(clippy::unwrap_used)] | ||
fn build_objective(pair: Pair<'_, Rule>) -> anyhow::Result<Objective> { | ||
let mut components = pair.into_inner(); | ||
let name = components.next().unwrap().as_str().to_string(); | ||
let coefficients: anyhow::Result<Vec<_>> = components.map(|p| p.into_inner().try_into()).collect(); | ||
Ok(Objective { name, coefficients: coefficients? }) | ||
} | ||
|
||
fn build_constraint(_pair: Pair<'_, Rule>) -> anyhow::Result<Constraint> { | ||
// pub name: String, | ||
// pub coefficients: Vec<Coefficient>, | ||
// pub sense: String, | ||
// pub rhs: f64, | ||
unimplemented!() | ||
} | ||
|
||
#[allow(clippy::wildcard_enum_match_arm)] | ||
fn build(pair: Pair<'_, Rule>, mut parsed: LPDefinition) -> anyhow::Result<LPDefinition> { | ||
match pair.as_rule() { | ||
// Problem sense | ||
Rule::MIN_SENSE => Ok(parsed.with_sense(Sense::Minimize)), | ||
Rule::MAX_SENSE => Ok(parsed.with_sense(Sense::Maximize)), | ||
// Problem Objectives | ||
Rule::OBJECTIVES => { | ||
let objectives: anyhow::Result<Vec<Objective>> = pair.into_inner().map(|inner_pair| build_objective(inner_pair)).collect(); | ||
parsed.add_objective(objectives?); | ||
Ok(parsed) | ||
} | ||
// Problem Constraints | ||
// Rule::CONSTRAINTS => { | ||
// let constraints: anyhow::Result<Vec<Constraint>> = pair.into_inner().map(|inner_pair| build_constraint(inner_pair)).collect(); | ||
// parsed.add_constraints(constraints?); | ||
// Ok(parsed) | ||
// } | ||
// Problem Bounds | ||
// Problem Integers | ||
// Problem Generals | ||
// Problem Binaries | ||
_ => Ok(parsed), | ||
} | ||
} | ||
|
||
// Rule::CONSTRAINT_EXPR => todo!(), | ||
// Rule::CONSTRAINT_NAME => todo!(), | ||
// Rule::CONSTRAINT => todo!(), | ||
// Rule::CONSTRAINTS => todo!(), | ||
// // Problem Bounds | ||
// Rule::BOUND_PREFIX => todo!(), | ||
// Rule::BOUND => todo!(), | ||
// Rule::BOUNDS => todo!(), | ||
// // Problem Integers | ||
// Rule::INTEGER_PREFIX => todo!(), | ||
// Rule::INTEGERS => todo!(), | ||
// // Problem Generals | ||
// Rule::GENERALS_PREFIX => todo!(), | ||
// Rule::GENERALS => todo!(), | ||
// // Problem Binaries | ||
// Rule::BINARIES_PREFIX => todo!(), | ||
// Rule::BINARIES => todo!(), | ||
// // Other | ||
// Rule::WHITESPACE => todo!(), | ||
// Rule::POS_INFINITY => todo!(), | ||
// Rule::NEG_INFINITY => todo!(), | ||
// Rule::FLOAT => todo!(), | ||
// Rule::PLUS => todo!(), | ||
// Rule::MINUS => todo!(), | ||
// Rule::OPERATOR => todo!(), | ||
// Rule::COLON => todo!(), | ||
// Rule::ASTERIX => todo!(), | ||
// Rule::FREE => todo!(), | ||
// Rule::END => todo!(), | ||
// Rule::COMMENT_TEXT => todo!(), | ||
// Rule::COMMENTS => todo!(), | ||
// Rule::PROBLEM_SENSE => todo!(), | ||
// Rule::VALID_CHARS => todo!(), | ||
// Rule::CONSTRAINT_PREFIX => todo!(), | ||
// Rule::VARIABLE => todo!(), | ||
// Rule::GT => todo!(), | ||
// Rule::GTE => todo!(), | ||
// Rule::LT => todo!(), | ||
// Rule::LTE => todo!(), | ||
// Rule::EQ => todo!(), | ||
// Rule::CMP => todo!(), | ||
// Rule::EOF => todo!(), |
Oops, something went wrong.