Python getting started

The Python package exposes a compact public API:

  • solve() solves an OMMX instance.

  • SolveOptions configures detailed solver behavior.

  • Structure enables structure-aware presets and handlers.

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:

  1. Solver defaults

  2. A Structure preset

  3. SolveOptions

  4. Convenience 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")

Use a structure preset

The following example builds a five-city traveling-salesperson problem with JijModeling. The binary variable x[i, j] represents whether the tour uses the directed edge from city i to city j.

import jijmodeling as jm

from jijzept_solver import Structure, solve

num_cities = 5
distances = [
    [float(abs(i - j)) for j in range(num_cities)]
    for i in range(num_cities)
]

# Keep self-loops out of the objective's attractive choices. The TSP
# structure handler also prohibits them when it builds the route constraints.
for city in range(num_cities):
    distances[city][city] = 1_000_000.0

problem = jm.Problem("TSP", sense=jm.ProblemSense.MINIMIZE)
distance = problem.Float("distance", ndim=2)
num_cities_expr = problem.NamedExpr("N", distance.len_at(0))
x = problem.BinaryVar("x", shape=(num_cities_expr, num_cities_expr))

problem += (distance * x).sum()

# Structure.tsp("x") supplies the in-degree, out-degree, self-loop, and
# subtour-elimination constraints, so the JijModeling problem only needs the
# edge-cost objective.
instance = problem.eval({"distance": distances})

structure = Structure.tsp("x")
solution = solve(
    instance,
    structure=structure,
    time_limit=60.0,
    verbosity=1,
)

print(f"feasible: {solution.feasible}")
print(f"tour length: {solution.objective}")

The string passed to Structure.tsp() must exactly match the JijModeling variable’s base name, x in this example. JijModeling preserves that name in the OMMX variable metadata, which lets the specialized route handler find the edge variables. The handler installs the route constraints and applies its recommended solver-option preset. Explicit options still take precedence.

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.