Linear Programming Solver - osvaldoandrade/ova-lib GitHub Wiki

Linear Programming Solver

Read this page when the problem is a linear objective with <= constraints and you want the simplex implementation shipped in the library.

Supported Solver Types

The public enum lists 4 solver types:

SOLVER_BRANCH_AND_CUT

SOLVER_BRANCH_AND_BOUND

SOLVER_LAGRANGEAN_SIMPLEX

SOLVER_SIMPLEX

The current source implements only SOLVER_SIMPLEX. The other enum values exist in the public header but do not install working algorithms in the current tree.

Build the Problem

The problem constructor is:

lp_problem *create_problem(int numVariables, int numConstraints);

Construction allocates:

Stored object Shape
constraints numConstraints x numVariables matrix
objective vector of numVariables coefficients
bounds vector of numConstraints bounds

If numVariables <= 0, construction fails.

If numConstraints < 1, construction raises the capacity to 1.

The problem tracks constraint_count separately from constraint_capacity, which is why you can add constraints incrementally after construction.

Problem Mutators

The public method table on lp_problem exposes:

Field Meaning
add_constraint(self, coefficients, bound) appends one <= row
set_objective(self, coefficients, type) copies objective coefficients and stores PROBLEM_MAX or PROBLEM_MIN
set_bounds(self, bounds) overwrites the active bounds
print(self) prints the objective and active constraints

add_constraint grows capacity when needed by resizing the constraint matrix rows and the bounds vector.

The current problem printer renders constraints as <= constraints, and the current simplex solver is written around that shape.

Create and Run the Solver

The solver constructor is:

solver *create_solver(SolverType type);

The solver method table exposes:

Field Meaning
solve(self, problem, &out_tableau) runs the algorithm and returns a status code
free(self) frees the solver wrapper

The status codes are:

Status Value
OPTIMAL 0
UNBOUNDED -1
INFEASIBLE -2

What Solve Writes Back

On success, the simplex solver stores results internally. Access the primal solution with problem->solution_value(problem, index) and the objective value with problem->objective_value(problem).

out_tableau is a caller-owned matrix * that must be freed explicitly with tableau->free(tableau).

Example

#include "solver.h"

int main(void) {
    lp_problem *problem = create_problem(2, 2);
    solver *simplex = create_solver(SOLVER_SIMPLEX);
    matrix *tableau = NULL;

    double objective[] = {1, 1};
    double c1[] = {1, 0};
    double c2[] = {0, 1};

    problem->set_objective(problem, objective, PROBLEM_MAX);
    problem->add_constraint(problem, c1, 1.0);
    problem->add_constraint(problem, c2, 1.0);

    int status = simplex->solve(simplex, problem, &tableau);

    if (tableau) {
        tableau->free(tableau);
    }
    simplex->free(simplex);
    problem->free(problem);
    return status == OPTIMAL ? 0 : 1;
}

Helper Functions

The header also exports:

Function Meaning
problem->is_feasible(problem, solution) method-table field on lp_problem; checks whether a vector satisfies the stored constraints
problem->improves_objective(problem, solution, old_value, index) method-table field on lp_problem; checks whether a candidate value improves the objective
is_integer(value) standalone function; checks whether a double is close to an integer

These helpers matter when you build additional solver logic around the current simplex path.

Ownership and Cleanup

Destroy the solver with solver->free(solver).

Destroy the problem with problem->free(problem).

Destroy the returned tableau with matrix->free(matrix).

The problem object owns the constraint matrix, objective vector, bounds vector, and solution array.

Read Next

If you need the underlying matrix and vector contract first, move to Matrix-and-Vector.