Python getting started
The Python package exposes a compact public API:
solve()solves an OMMX instance.SolveOptionsconfigures detailed solver behavior.
Build and solve a model with JijModeling
Define a binary knapsack model with JijModeling, evaluate it with concrete
data to create an OMMX instance, and pass that instance directly to solve:
import jijmodeling as jm
from jijzept_solver import solve
problem = jm.Problem("knapsack", sense=jm.ProblemSense.MAXIMIZE)
values = problem.Float("values", ndim=1)
weights = problem.Float("weights", ndim=1)
capacity = problem.Float("capacity")
selected = problem.BinaryVar("selected", shape=values.len_at(0))
problem += (values * selected).sum()
problem += problem.Constraint(
"capacity",
(weights * selected).sum() <= capacity,
)
instance = problem.eval(
{
"values": [10, 13, 18, 31],
"weights": [11, 15, 20, 35],
"capacity": 47,
}
)
solution = solve(
instance,
time_limit=60.0,
gap_limit=0.01,
verbosity=1,
)
print(f"feasible: {solution.feasible}")
print(f"objective: {solution.objective}")
Problem.eval() evaluates the placeholders and produces the OMMX instance
accepted by JijZept Solver. There is no separate JijModeling-to-OMMX
conversion step.
Solve an MPS model
If a model is already available as MPS, load it as an OMMX instance and call
solve in the same way:
from ommx.v1 import Instance
from jijzept_solver import solve
instance = Instance.load_mps("model.mps")
solution = solve(
instance,
time_limit=60.0,
gap_limit=0.01,
verbosity=1,
)
print(f"feasible: {solution.feasible}")
print(f"objective: {solution.objective}")
gap_limit=0.01 stops once the relative optimality gap reaches one percent.
Omit time_limit, or pass it explicitly as None, to run without a time
limit.
Configure detailed options
Use SolveOptions for settings that are not part of the convenience
arguments on solve:
from jijzept_solver import SolveOptions, solve
options = SolveOptions(
branching="reliability",
node_selector="depth-first",
enable_feasibility_pump=True,
num_threads=4,
)
solution = solve(instance, options=options, time_limit=60.0)
Settings have the following precedence, from lowest to highest:
Solver defaults
SolveOptionsConvenience arguments passed directly to
solve
This makes it possible to reuse an options object while overriding a small number of values for one solve.
Save and load options
Options can be shared as a TOML file:
from jijzept_solver import SolveOptions
options = SolveOptions(time_limit=120.0, num_threads=8)
options.to_file("solver-options.toml")
loaded = SolveOptions.from_file("solver-options.toml")
To create a documented template containing every option:
SolveOptions.write_template("solver-options.toml")
Handle interruption
solve supports Ctrl+C. If an incumbent exists, the solver returns the best
solution found so far and emits a warning. If no solution has been found, it
raises KeyboardInterrupt.
See the Python API reference for the complete signatures and option descriptions.
Reusing an initial solution across sessions
Decision-variable IDs can change when a model is compiled again. To reuse
values across compilations, pass names and subscript tuples to initial_state:
initial = {
"x": {(0,): 1.0, (1,): 0.0},
"scalar_variable": {(): 2.0},
}
solution = solve(current_instance, initial_state=initial)
For a variable with multiple indices, such as x[0, 1, 2, 3], use the
entire index tuple. The following nested and flat dictionaries are equivalent:
# Nested form: variable name, then the four indices.
initial = {"x": {(0, 1, 2, 3): 1.0}}
solution = solve(current_instance, initial_state=initial)
# Flat form: (variable name, index tuple) as one key.
initial = {("x", (0, 1, 2, 3)): 1.0}
solution = solve(current_instance, initial_state=initial)
The equivalent flat form is {("x", (0,)): 1.0}. Use () for scalar
variables and (i,) for one-dimensional subscripts. These are Python dictionaries,
not a JSON wire format. Resolve names against the instance being solved, not a
previous instance’s numeric IDs. Names and subscripts must still identify the
same modeling variables in the new problem.
Partial dictionaries are allowed: omitted variables retain the solver’s existing
default initialization. Every supplied name/subscript pair must resolve uniquely,
but unrelated variables need not have names. Variables without names can still
be initialized using their current numeric IDs or an existing ommx.v1.State.
An empty subscript tuple is valid for a named scalar; it is not missing metadata.
Unknown names/subscripts and ambiguous duplicate
name/subscript pairs raise ValueError; malformed keys or nonnumeric values
raise TypeError. Distinct Python keys that become identical after native
name or integer conversion raise ValueError rather than overwriting values.
Mapping values must be finite real numbers, not booleans.
An infeasible start is allowed, and out-of-bounds values retain the solver’s
existing clamping behavior. Always check solution.feasible.
Existing ommx.v1.State objects remain supported unchanged. Numeric ID dictionaries
such as {0: 1.0} are also accepted, but only use these with IDs from the current
instance. Extra numeric IDs retain the existing behavior of being ignored.
Do not mix ID, flat named, and nested named keys within one dictionary. This
convenience API does not change the Rust or serialized OMMX State format, nor
does it guarantee that presolve behaves identically across platforms.