-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
QPLIB format parser #250
Open
Zengor
wants to merge
7
commits into
main
Choose a base branch
from
feat/qplib_parser
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,152
−1
Open
QPLIB format parser #250
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e8b77ab
Initial parser implementation
Zengor 0452b63
Convert qplib to ommx
Zengor af5a7d8
Remove prune_constraints
Zengor 79a09d0
Merge branch 'main' into feat/qplib_parser
Zengor 0792e88
Add python interface
Zengor 10eb8a9
Add basic python test
Zengor 8f64f08
Restructure QplibParseError
Zengor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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 @@ | ||
from .v1 import Instance | ||
|
||
|
||
def load_file(path: str) -> Instance: | ||
return Instance.load_qplib(path) |
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,15 @@ | ||
use anyhow::Result; | ||
use pyo3::{ | ||
prelude::*, | ||
types::{PyBytes, PyString}, | ||
}; | ||
|
||
#[cfg_attr(feature = "stub_gen", pyo3_stub_gen::derive::gen_stub_pyfunction)] | ||
#[pyfunction(name = "load_qplib_bytes")] | ||
pub fn load_qplib_bytes<'py>( | ||
py: Python<'py>, | ||
path: Bound<PyString>, | ||
) -> Result<Bound<'py, PyBytes>> { | ||
let instance = ommx::qplib::load_file_bytes(path.to_str()?)?; | ||
Ok(PyBytes::new_bound(py, &instance)) | ||
} |
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,44 @@ | ||
! --------------- | ||
! example problem | ||
! --------------- | ||
MIPBAND # problem name | ||
QML # problem is a mixed-integer quadratic program | ||
Minimize # minimize the objective function | ||
3 # variables | ||
2 # general linear constraints | ||
5 # nonzeros in lower triangle of Q^0 | ||
1 1 2.0 5 lines row & column index & value of nonzero in lower triangle Q^0 | ||
2 1 -1.0 | | ||
2 2 2.0 | | ||
3 2 -1.0 | | ||
3 3 2.0 | | ||
-0.2 default value for entries in b_0 | ||
1 # non default entries in b_0 | ||
2 -0.4 1 line of index & value of non-default values in b_0 | ||
0.0 value of q^0 | ||
4 # nonzeros in vectors b^i (i=1,...,m) | ||
1 1 1.0 4 lines constraint, index & value of nonzero in b^i (i=1,...,m) | ||
1 2 1.0 | | ||
2 1 1.0 | | ||
2 3 1.0 | | ||
1.0E+20 infinity | ||
1.0 default value for entries in c_l | ||
0 # non default entries in c_l | ||
1.0E+20 default value for entries in c_u | ||
0 # non default entries in c_u | ||
0.0 default value for entries in l | ||
0 # non default entries in l | ||
1.0 default value for entries in u | ||
1 # non default entries in u | ||
2 2.0 1 line of non-default indices and values in u | ||
0 default variable type is continuous | ||
1 # non default variable types | ||
3 2 variable 3 is binary | ||
1.0 default value for initial values for x | ||
0 # non default entries in x | ||
0.0 default value for initial values for y | ||
0 # non default entries in y | ||
0.0 default value for initial values for z | ||
0 # non default entries in z | ||
0 # non default names for variables | ||
0 # non default names for 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
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,72 @@ | ||
use prost::Message; | ||
use std::path::Path; | ||
|
||
mod convert; | ||
mod parser; | ||
|
||
pub use parser::QplibFile; | ||
|
||
/// Reads and parses the file at the given path as a gzipped MPS file. | ||
pub fn load_file(path: impl AsRef<Path>) -> Result<crate::v1::Instance, QplibParseError> { | ||
let data = QplibFile::from_file(path)?; | ||
convert::convert(data) | ||
} | ||
|
||
pub fn load_file_bytes(path: impl AsRef<Path>) -> Result<Vec<u8>, QplibParseError> { | ||
let instance = load_file(path)?; | ||
Ok(instance.encode_to_vec()) | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum QplibParseError { | ||
#[error("Invalid problem type: {0}")] | ||
InvalidProblemType(String), | ||
#[error("Invalid OBJSENSE: {0}")] | ||
InvalidObjSense(String), | ||
#[error("Invalid variable type: {0}")] | ||
InvalidVarType(String), | ||
#[error("Unexpected end of file at line {0}")] | ||
UnexpectedEndOfFile(usize), | ||
#[error("Line {0} did not match expected formatting")] | ||
InvalidLine(usize), | ||
#[error(transparent)] | ||
ParseInt(#[from] std::num::ParseIntError), | ||
#[error(transparent)] | ||
ParseFloat(#[from] std::num::ParseFloatError), | ||
#[error(transparent)] | ||
Io(#[from] std::io::Error), | ||
|
||
// a little hack to allow us to add the line numbers | ||
// as context to errors generated in `FromStr` impls | ||
#[error("{inner} (at line {line_num})")] | ||
WithLine { | ||
line_num: usize, | ||
inner: Box<QplibParseError>, | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be better to convert |
||
} | ||
|
||
impl QplibParseError { | ||
// This is a method to make it easier to add the line number at which an | ||
// error occurred in the qplib parser. | ||
pub(crate) fn with_line(self, line_num: usize) -> QplibParseError { | ||
use QplibParseError::*; | ||
match self { | ||
e @ UnexpectedEndOfFile(_) => e, | ||
e @ InvalidLine(_) => e, | ||
WithLine { inner, .. } => WithLine { line_num, inner }, | ||
e => WithLine { | ||
line_num, | ||
inner: Box::new(e), | ||
}, | ||
} | ||
} | ||
} | ||
|
||
// Workaround to the fact that `String`'s `FromStr` impl has error | ||
// type `Infallible`. As the conversion can't fail, by definition, | ||
// this will never be called and no panic will ever happen | ||
impl From<std::convert::Infallible> for QplibParseError { | ||
fn from(_: std::convert::Infallible) -> Self { | ||
unreachable!() | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IO error is not a parse error. Since
std::io::Error
does not contains which file is tried to opened, returning this error is not user-friendly. We should rather returnanyhow::Result
from function which may returns IO error withanyhow::Context
to tell which file is opened.For the actual parse errors like invalid var types, creating this
ParseError
and returned withinanyhow::Error
. Users can get the error asParseError
viadowncast
https://docs.rs/anyhow/latest/anyhow/struct.Error.html#method.downcast