Clp is the linear programming code of the COIN-OR project: a simplex implementation with a barrier alternative, presolve, and warm starts. This package binds its callable library.
A small product mix problem. Two products, three resource constraints, maximise the contribution.
A <- rbind(material = c(120, 210),
labour = c(110, 30),
capacity = c( 1, 1))
b <- c(15000, 4000, 75)
fit <- clp_solve(c(143, 60), A, "<=", b, max = TRUE,
col_names = c("x", "y"), row_names = rownames(A))
fit
#> <clp_solution>
#> status: 0 (optimal)
#> objective: 6315.625 [max]
#> solution: 21.875 53.125
#> iterations: 2The solution, the shadow prices and the reduced costs come back together.
fit$solution
#> x y
#> 21.875 53.125
fit$duals
#> material labour capacity
#> 0.0000 1.0375 28.8750
fit$reduced_costs
#> [1] 0 0A zero dual marks a resource that is not binding: here the material constraint has slack, while labour and capacity are tight.
data.frame(row = rownames(A), activity = fit$row_activity, limit = b,
dual = fit$duals)
#> row activity limit dual
#> material material 13781.25 15000 0.0000
#> labour labour 4000.00 4000 1.0375
#> capacity capacity 75.00 75 28.8750dir and rhs cover one-sided rows. For a row
bounded on both sides, give row_lower and
row_upper instead. Variable bounds default to
[0, Inf) and are set with lower and
upper; -Inf makes a variable free.
Real models are sparse. Any of a Matrix sparse matrix, a
slam::simple_triplet_matrix or plain triplets can be passed
straight in; nothing is densified on the way to the solver.
clp_solve() builds a model, solves it and throws it
away. When a problem is solved repeatedly with small changes – a
parametric study, a column generation loop, a rolling horizon – build
the model once and re-solve it, so Clp can start from the basis it
already has.
model <- clp_model()
clp_set_log_level(model, 0)
clp_load_problem(model, ncols = 2, nrows = 3,
start = c(0L, 3L, 6L),
index = c(0L, 1L, 2L, 0L, 1L, 2L),
value = c(120, 110, 1, 210, 30, 1),
obj = c(-143, -60),
rowub = b)
clp_initial_solve(model)
#> [1] 0
c(objective = clp_objective_value(model), iterations = clp_iterations(model))
#> objective iterations
#> -6315.625 2.000The problem is loaded column by column in compressed sparse column
form: start says where each column begins,
index holds 0-based row positions and value
the coefficients. Because the low level functions follow the C API,
positions are 0-based here, as in the Clp documentation. Maximisation is
expressed by negating the objective, or by
clp_set_optimization_direction(model, -1).
Keep the basis, change the model, hand the basis back:
basis <- clp_status_array(model)
clp_set_row_upper(model, c(15000, 4000, 70))
clp_copyin_status(model, basis)
clp_dual_simplex(model)
#> [1] 0
c(objective = clp_objective_value(model), iterations = clp_iterations(model))
#> objective iterations
#> -6171.25 0.00The re-solve takes no iterations at all: the old basis is still optimal for the tightened problem.
for (alg in c("auto", "primal", "dual", "barrier")) {
fit <- clp_solve(c(143, 60), A, "<=", b, max = TRUE,
control = clp_control(algorithm = alg))
cat(sprintf("%-8s %.4f\n", alg, fit$objval))
}
#> auto 6315.6250
#> primal 6315.6250
#> dual 6315.6250
#> barrier 6315.6250clp_control() also carries the tolerances, the iteration
and time limits, the scaling mode and whether to presolve. For finer
control over presolve there is a clp_options() object with
the individual transformations (clp_options_set_do_dupcol()
and friends), passed to
clp_initial_solve_with_options().
path <- system.file("extdata", "productmix.mps", package = "coinclp")
from_file <- clp_model()
clp_set_log_level(from_file, 0)
clp_read_mps(from_file, path)
clp_col_names(from_file)
#> [1] "x" "y"
clp_initial_solve(from_file)
#> [1] 0
clp_objective_value(from_file)
#> [1] -6315.625Writing works the other way with clp_write_mps(). Clp
only gained an MPS writer in its C API after the 1.17 series, so on a
Clp without one – the version Rtools ships, for instance – the package
writes the file itself. clp_features() says which entry
points the current build has.
The archived clpAPI package is reproduced function for function, so code written against it runs here unchanged.
lp <- initProbCLP()
setLogLevelCLP(lp, 0)
loadProblemCLP(lp, 2, 3, c(0, 3, 6), c(0, 1, 2, 0, 1, 2),
c(120, 110, 1, 210, 30, 1),
lb = c(0, 0), ub = c(1e30, 1e30), obj_coef = c(143, 60),
rlb = rep(-1e30, 3), rub = b)
setObjDirCLP(lp, -1)
solveInitialCLP(lp)
#> [1] 0
status_codeCLP(getSolStatusCLP(lp))
#> [1] "solution is optimal"
getObjValCLP(lp)
#> [1] 6315.625
delProbCLP(lp)Note that clpAPI used 1e30 for an infinite bound, which is Clp’s own
convention and what clp_inf() returns. The
clp_solve() interface accepts Inf and converts
it.