-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds the ability to read QPLIB files to create OMMX instances. Some consideration has been given to adding the ability to output QPLIB format in the future but some aspects of it are still open.
- Loading branch information
Showing
11 changed files
with
1,152 additions
and
1 deletion.
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,81 @@ | ||
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>) -> anyhow::Result<crate::v1::Instance> { | ||
let data = QplibFile::from_file(path)?; | ||
let converted = convert::convert(data)?; | ||
Ok(converted) | ||
} | ||
|
||
pub fn load_file_bytes(path: impl AsRef<Path>) -> anyhow::Result<Vec<u8>> { | ||
let instance = load_file(path)?; | ||
Ok(instance.encode_to_vec()) | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
#[error("{reason} (at line {line_num})")] | ||
pub struct QplibParseError { | ||
line_num: usize, | ||
reason: ParseErrorReason, | ||
} | ||
|
||
impl QplibParseError { | ||
// generic "invalid line" error | ||
fn invalid_line(line_num: usize) -> Self { | ||
Self { | ||
line_num, | ||
reason: ParseErrorReason::InvalidLine(line_num), | ||
} | ||
} | ||
|
||
fn unexpected_eof(line_num: usize) -> Self { | ||
Self { | ||
line_num, | ||
reason: ParseErrorReason::UnexpectedEndOfFile(line_num), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum ParseErrorReason { | ||
#[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), | ||
} | ||
|
||
impl ParseErrorReason { | ||
// 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 { | ||
QplibParseError { | ||
line_num, | ||
reason: self, | ||
} | ||
} | ||
} | ||
|
||
// 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 ParseErrorReason { | ||
fn from(_: std::convert::Infallible) -> Self { | ||
unreachable!() | ||
} | ||
} |
Oops, something went wrong.