linear_programming
The linear_programming library provides immutable construction and
solving of small linear and mixed-integer linear programs. The
simplex backend is a portable dense two-phase tableau implementation
supporting Bland’s and Dantzig’s pivot rules. The
milp_branch_and_bound backend provides deterministic depth-first
branch-and-bound over simplex LP relaxations.
The model API supports continuous, integer, and binary variable
declarations. The simplex backend solves continuous models only and
throws a domain_error(simplex_variable_type, Variable-Type) error
for other variable types. Use milp_branch_and_bound to solve models
containing integer or binary variables.
API documentation
Open the ../../apis/library_index.html#linear-programming link in a web browser.
Loading
To load all entities in this library, load the loader.lgt file:
| ?- logtalk_load(linear_programming(loader)).
Testing
To test this library, load the tester.lgt file:
| ?- logtalk_load(linear_programming(tester)).
Model representation
Variables are identified by unique ground terms. Linear expressions are
lists of Coefficient*Variable terms. Repeated variables are combined
and zero coefficients are removed. Arithmetic expression trees such as
[2*x + 3*y] are not accepted; write [2*x, 3*y] instead.
Continuous and integer variables default to bounds 0 and inf.
Binary variables default to bounds 0 and 1. Explicit bounds are
numbers, -inf for an absent lower bound, or inf for an absent
upper bound.
Continuous solving
The simplex backend solves continuous linear programs using a
portable dense two-phase tableau implementation. The supported options
are:
max_iterations(PositiveInteger)- Maximum number of iterations shared by both simplex phases (default:10000).tolerance(PositiveNumber)- Numerical tolerance (default:1.0e-9).pivot_rule(Rule)- Pivot rule, eitherbland(the default), which selects the first eligible entering column, ordantzig, which selects the eligible column with the most negative reduced cost, breaking ties by the lowest column index.| ?- simplex::( new_problem(P0), variable(x, continuous, P0, P1), variable(y, continuous, P1, P2), constraint([1x, 2y], =<, 14, P2, P3), constraint([3x, -1y], >=, 0, P3, P4), objective([3x, 4y], maximize, P4, P), solve(P, Result), status(Result, Status), variable_value(Result, x, X), variable_value(Result, y, Y), objective_value(Result, Value) ). Status = optimal, X = 14.0, Y = 0, Value = 42.0.
Dense matrix conversion
The problem_from_matrices/8 predicate converts a dense standard-form
input to the same immutable model. Equality rows are represented by
Aeq and Beq; inequality rows are represented by Aineq and
Bineq and always mean Aineq*x =< Bineq. Variables are named by
one-based column indices.
| ?- simplex::problem_from_matrices(
[3,4], maximize,
[], [],
[[1,2],[-3,1]], [14,0],
[0-inf,0-inf], Problem
),
simplex::solve(Problem, Result).
Results and errors
Solver statuses are optimal, infeasible, unbounded,
iteration_limit, node_limit, and numerical_error. Only an
optimal result contains variable values and an objective value. The
corresponding inspector predicates fail for other statuses. Malformed
problems and unsupported backend capabilities throw errors instead of
being reported as mathematical solver statuses.
Mixed-integer solving
The milp_branch_and_bound backend supports mixed continuous,
integer, and binary models. Integer and binary variables must have
finite bounds and each integer domain must contain at least one integer.
The solver proves optimality by exhausting or bounding the search tree;
it does not use cutting planes or primal heuristics.
The supported options are:
max_nodes(PositiveInteger)- Maximum number of search nodes (default:10000).integrality_tolerance(PositiveNumber)- Integrality tolerance (default:1.0e-9).simplex_max_iterations(PositiveInteger)- Maximum simplex iterations per LP relaxation (default:10000).simplex_tolerance(PositiveNumber)- Simplex numerical tolerance (default:1.0e-9).simplex_pivot_rule(Rule)- Simplex pivot rule for all LP relaxations (default:bland).branching_rule(Rule)- Branching rule, eitherfirst_fractional(the default), which selects the first fractional discrete variable in declaration order, ormost_fractional, which selects the variable whose value is farthest from its nearest integer, breaking ties by declaration order.branch_order(Order)- Child traversal order, eitherlower_first(the default) orupper_first.| ?- milp_branch_and_bound:: new_problem(P0), variable(x, binary, P0, P1), variable(y, binary, P1, P2), constraint([2x, 2y], =<, 3, P2, P3), objective([1x, 1y], maximize, P3, P), solve(P, Result ).
Limitations
Both backends use dense lists and are intended for small problems.
The library does not currently implement presolve beyond bound and standard-form normalization, sparse storage, cutting planes, dual values, reduced costs, basis export, warm starts, time limits, callbacks, or parallel search.