\* Rust · CLI · TUI · Python *\

A parser for LP files,and the four tools built on it.

Parse, analyse, modify, write, diff, and solve Linear Programming files. Built on a LALRPOP grammar covering the CPLEX 22.1.1, Gurobi, FICO Xpress, and Mosek LP specifications.

crates.io lp_parser_rs PyPI parse-lp
\* everything below round-trips:
   parse → modify → write → parse *\
Minimize
 cost: 0.1 x1 + 0.2 x2 + 3055.2 x3
Subject To
 capacity: 2 x1 + 4.5 x2 <= 200
 demand: x1 + x2 >= 30
Bounds
 0 <= x1 <= 40
 x2 free
General
 x3
End

What's here

Four ways to use it

lp_parser_rs — Rust crate

Parse, inspect, modify, and write LP problems programmatically, with optional diff, serde, and solver features.

lp_parser — CLI

Six subcommands: parse, info, analyze, diff, convert, solve. Text, JSON, or YAML out.

lp_diff — interactive TUI

Side-by-side coefficient-level diffs of LP/MPS files with fuzzy search, filters, and integrated HiGHS solving.

parse_lp — Python package

The same Rust engine from Python, with full type hints. pip install parse_lp.

The Rust library

Library capabilities

Parse and inspect

use lp_parser_rs::{parser::parse_file, problem::LpProblem};

let content = parse_file(Path::new("problem.lp"))?;
let problem = LpProblem::parse(&content)?;
println!("{} objectives, {} constraints, {} variables",
    problem.objective_count(), problem.constraint_count(), problem.variable_count());

Modify and write back

let mut problem = LpProblem::parse(&std::fs::read_to_string("problem.lp")?)?;

problem.update_objective_coefficient("profit", "x1", 5.0)?;
problem.rename_objective("profit", "total_profit")?;
problem.update_constraint_coefficient("capacity", "x1", 2.0)?;
problem.update_constraint_rhs("capacity", 200.0)?;
problem.rename_variable("x1", "production_a")?;
problem.update_variable_type("production_a", VariableType::Integer)?;

std::fs::write("modified.lp", write_lp_string(&problem)?)?;

The full modification surface on LpProblem: update_objective_coefficient, rename_objective, remove_objective, update_constraint_coefficient, update_constraint_rhs, rename_constraint, remove_constraint, rename_variable, update_variable_type, remove_variable. Writer knobs live on LpWriterOptions: include_problem_name, max_line_length, decimal_precision, include_section_spacing.

Cargo features

FeatureAdds
diffStructural comparison between two LP problems.
serdeJSON / YAML serialisation of problems, analyses, and diffs.
lp-solversExternal solver integration: CBC, GLPK, Gurobi, CPLEX.

The command line

CLI: lp_parser

Every subcommand writes to stdout or a file (-o) as text, JSON, or YAML (-f, plus --pretty). Global flags: -v/--verbose (repeatable), -q/--quiet.

parse — display file structure

lp_parser parse problem.lp
lp_parser parse problem.lp --format yaml -o problem.yaml

info — summary statistics

Counts plus optional full listings via --variables, --constraints, --objectives.

lp_parser info problem.lp --variables --constraints --objectives
lp_parser info problem.lp --format json --pretty

analyze — structural analysis & issue detection

OptionDefaultDescription
--issues-onlyoffSkip full analysis; show warnings/errors only
--large-coeff-threshold1e9Warn on coefficients larger than this
--small-coeff-threshold1e-9Warn on coefficients smaller than this
--ratio-threshold1e6Warn on coefficient scaling ratios above this
lp_parser analyze problem.lp --issues-only
lp_parser analyze problem.lp --large-coeff-threshold 1e8 --ratio-threshold 1e5
# example output
summary: { name: diet, sense: Minimize, objective_count: 1, constraint_count: 7, variable_count: 16, density: 0.571 }
variables: { type_distribution: { upper_bounded: 9, double_bounded: 7 }, discrete_variable_count: 0 }
constraints: { type_distribution: { equality: 7 }, rhs_range: { min: 30.0, max: 50000.0 } }
coefficients: { constraint_coeff_range: { min: 0.1, max: 3055.2 }, coefficient_ratio: 101840.0 }
issues: []

diff — compare two LP files

OptionDefaultDescription
--abs-tol0.0Absolute tolerance for numeric comparisons
--rel-tol0.0Relative tolerance: |a−b| ≤ rel_tol · max(|a|, |b|)
--rename P RRegex rewrite applied to names in both files before matching; repeatable
lp_parser diff old.lp new.lp --abs-tol 1e-6 --rel-tol 1e-9
lp_parser diff old.lp new.lp --rename '\[\d+\]$' '[N]' --format json --pretty

convert — translate to another format

Targets: lp, csv, json, yaml. LP output honours --precision, --max-line-length, --no-problem-name, and --compact; CSV writes constraints.csv, objectives.csv, and variables.csv to a directory.

lp_parser convert problem.lp --format lp --precision 4 --compact
lp_parser convert problem.lp --format csv --output ./out

solve — run an external solver

Solves with CBC (default) or GLPK from your PATH (-s/--solver). Multiple objectives and strict inequalities error; SOS constraints are ignored with a warning.

lp_parser solve problem.lp --solver glpk --format json --pretty

The terminal UI

TUI: lp_diff

An interactive ratatui explorer and diff viewer for LP and MPS files. One file opens a single-model explorer; two files diff them, in any mix of formats (lp_diff model.lp model.mps). It has grown well past a diff viewer: there is a background solver, in-memory what-if edits, solution-preserving rewrites, and a diagnostics pane that names the constraints slowing a solve down.

Python bindings

Python: parse_lp

The same Rust engine behind a typed Python API: parsing, data access, modification, LP writing, analysis with configurable thresholds, CSV export, and problem-to-problem comparison.

from parse_lp import LpParser

parser = LpParser("problem.lp")
parser.parse()

print(parser.name, parser.sense, parser.variable_count())

parser.update_objective_coefficient("OBJ", "x1", 5.0)
parser.rename_variable("x2", "production")
parser.update_constraint_rhs("C1", 100.0)

parser.save_to_file("modified_problem.lp")

analysis = parser.analyze()
print(analysis["summary"]["density"])

Getting it

Install

WhereCommand
Rust cratelp_parser_rs = { version = "4.1.0", features = ["serde", "diff"] }
CLIcargo install lp_parser_rs --all-features
TUIcargo install --path tui (from a clone)
Pythonpip install parse_lp

Develop with cargo insta test --all-features and review snapshot changes with cargo insta review. Contributions welcome: open a pull request.