The oximo-io crate writes Models to the standard text formats MPS, LP, and NL, and can read all three formats back into a Model. All I/O is gated on the io Cargo feature, which is on by default.
Use this when you want to:
- Hand a
Modelto a solver oximo doesn't bundle (COPT, SCIP, CPLEX, ...) - Feed a nonlinear model to an AMPL-compatible solver via NL
- Reproduce a bug report against a third-party tool
- Archive the exact problem instance for later inspection
- Inspect the model
🔗To a string
use oximo::io;
let mps = io::to_mps_string(&m)?;
let lp = io::to_lp_string(&m)?;
let nl = io::to_nl_string(&m)?;
Each returns a String you can log, hash, or feed into something else in memory.
🔗To a file
use oximo::io;
io::write_mps(&m, "model.mps")?;
io::write_lp(&m, "model.lp")?;
io::write_nl(&m, "model.nl")?;
All three accept anything that implements AsRef<Path>.
🔗Picking a format
MPS and LP describe linear and quadratic models. In general, you should use LP and reach for NL when the model has nonlinear expressions that MPS and LP cannot represent.
| Format | Pros | When to pick |
|---|---|---|
| MPS | Universal, column-oriented, fixed historical format | Maximum solver compatibility, archival |
| LP | Human-readable, row-oriented, mirrors algebraic notation | Quick inspection, sharing in bug reports |
| NL | Compact, carries nonlinear structure | NLP/MINLP models, AMPL-compatible solvers |
🔗NL options
The NL writer is the most configurable of the three. write_nl_with and to_nl_string_with take a WriteOptions to select the NlFormat (binary or ASCII) and attach solver metadata: suffixes, defined variables, imported functions, and complementarity pairs.
use oximo::io::{NlFormat, WriteOptions, write_nl_with};
let opts = WriteOptions::default().format(NlFormat::Ascii);
write_nl_with(&m, "model.nl", &opts)?;
write_nl_files emits the .nl alongside its companion .col/.row name files, which is what most AMPL-compatible solvers expect when you want readable names in the solution.
🔗Names round-trip
All writers preserve the Variable and constraint names from your model, so exported files cross-reference cleanly with SolverResult lookups such as dual_of and reduced_costs (see Results).
🔗Reading MPS models
Use read_mps_file for a path or read_mps for any text stream:
use oximo::io::{read_mps, read_mps_file};
use std::fs::File;
let model = read_mps_file("model.mps")?;
let model_from_stream = read_mps(File::open("model.mps")?)?;
The reader accepts the standard linear sections, range rows, integer markers,
binary and semi-variable bounds, and the QUADOBJ, QMATRIX, QCMATRIX, and
QSECTION quadratic extensions. MPS does not identify the coefficient scaling
used by quadratic constraints, so the default is the Gurobi convention. Select
CPLEX or MOSEK scaling explicitly when needed:
use oximo::io::{
MpsQuadraticFormat, MpsReadOptions, read_mps_file_with,
};
let options = MpsReadOptions {
quadratic_format: MpsQuadraticFormat::Cplex,
};
let model = read_mps_file_with("cplex-model.mps", &options)?;
Malformed input returns IoError::InvalidMps. Multiple alternative RHS, range, or bounds vectors and semantics not represented by oximo-core, such as SOS and indicator constraints, return IoError::UnsupportedMps.
🔗Reading NL models
The NL reader imports models produced by oximo or compatible AMPL-style tools. Use read_nl_file for a path or read_nl for any byte stream:
use oximo::io::{read_nl, read_nl_file};
use std::fs::File;
let model = read_nl_file("model.nl")?;
let model_from_stream = read_nl(File::open("model.nl")?)?;
Both ASCII and little-endian binary NL encodings are accepted. When a .row or .col sidecar exists beside the file, it supplies the original row and column names; otherwise deterministic names are generated. Interval rows and initial values are preserved when they can be represented by the core model.
The reader rejects malformed input with IoError::InvalidNl and well-formed NL sections that the core model cannot represent with IoError::UnsupportedNl. Imported functions, defined variables, logical/network constraints, complementarity sections, and unsupported expression opcodes are intentionally rejected.
🔗Reading LP models
LP files can be imported from a byte stream or a path with read_lp and read_lp_file:
use oximo::io::{read_lp, read_lp_file};
use std::fs::File;
let model = read_lp_file("model.lp")?;
let model_from_stream = read_lp(File::open("model.lp")?)?;
The reader supports the CPLEX LP linear and quadratic subset represented by the core model, including objectives, constraints, bounds, integer/binary and semicontinuous domains, and quadratic terms. Malformed input returns IoError::InvalidLp with its source line and column. Unsupported LP sections return IoError::UnsupportedLp.
🔗Skipping the writers
If you don't need file export, opt out of the io feature to drop the dependency:
[dependencies]
oximo = { version = "0.6", default-features = false, features = ["highs"] }