A tester which provides initial tests for CapacitatedFacilityLocationBlock,
possibly MCFBlock and MCFSolver, possibly BinaryKnapsackBlock and any
specialised Solver for it, possibly LagrangianDualSolver, LagBFunction,
BendersBFunction, any CDASolver able to handle C05Function in the
Objective (such as BundleSolver), any Solver able to handle
(Mixed Integer) Linear Programs (such as MILPSolver and its derived classes
CPXMILPSolver , GRBMILPSolver, etc.), as well as for quite a lot of the
mechanics of the "core" SMS++ library.
This executable, given the filename and (optionally) filetype of one
Capacitated Facility Location (CFL) instance in one of the several supported
file formats, reads the instance in a CapacitatedFacilityLocationBlock. It
then creates one or more other Block via the R3Block mechanism; each can be
either another CapacitatedFacilityLocationBlock or a MCFBlock
representing the continuous relaxation of CFL (as a Min-Cost Flow problem).
What they are is the list of formulations given with -F, a
SimpleConfiguration< std::vector< Configuration * > >: its first element is
the pair BlockConfig + BlockSolverConfig of the original Block, each of
the others the vector of three Configuration of an R3Block, the one it is
made with [see get_R3_Block()], its BlockConfig and its
BlockSolverConfig. Also, an UpdateSolver is manually registered to the
original CapacitatedFacilityLocationBlock for each R3Block, so that any
change on it is automatically map_forward-ed to the other Block (but not
vice-versa).
According to the value of the -s parameter, the Slope Scaling iterations,
this is all: the Solver are supposed to be exact (but it may well be that
they are all exactly solving the continuous relaxation), and the lower
bounds of all of them, those of the original Block and those of every
R3Block, are compared with each other.
Otherwise, the first Solver attached to each R3Block is supposed to be solving
some kind of continuous relaxation and a certain number of rounds of a
simple slope scaling heuristic are ran on that Block: the continuous
solution is map_back-ed to the original CapacitatedFacilityLocationBlock,
it is used to temporarily change the facility costs according to the standard
slope scaling idea, and then it is rounded up to obtain a feasible solution
(in this case expecting that the CapacitatedFacilityLocationBlock is in the
"splittable" version where continuous X[ i ][ j ] variables are feasible).
This is repeated for a maximum number of iterations, or until the solution
(value) of the relaxation stops changing. In this case the results (both
lower and upper bound) are compared with the optimal value of the other
Solver, but not expecting them to be equal. Note that there are at least
four significantly different implementations for the R3Block and its
Solver:
-
The R3Block is a
CapacitatedFacilityLocationBlock(in any formulation) and theSolveris, say, a:MILPSolversolving its continuous relaxation by standard LP tools, possibly with the dynamic generation of the strong forcing constraints to improve the bound. -
The R3Block is a
MCFBlocksolved by aMCFSolver: a much weaker bound, and the much worse/more fractional solution that comes with it, but obtained (possibly, much) faster. -
The R3Block is a
CapacitatedFacilityLocationBlockin the KF and theSolveris aLagrangianDualSolver, say usingBundleSolverto drive the solution of the Lagrangian Dual andDPBinaryKnapsackSolverto solve the Lagrangian subproblems: a much stronger bound, and the much better/less fractional solution that comes with it, but obtained at a (much) higher computational cost. -
The R3Block is a
CapacitatedFacilityLocationBlockin the "Benders friendly" formulation (BenForm), whose hiddenBendersBFunctionseparates Benders optimality/feasibility cuts from an innerMCFBlock; solved as a pure LP with an explicit user-cut separation loop (MILPSolverwithintRelaxIntVars == 2), it reproduces the natural-formulation LP bound.
This shows how these completely different arrangements can be obtained with
exactly the same executable by just changing the configuration files. Each
arrangement is one list of formulations, all in this directory: CFLBForm.txt
(natural-formulation LP vs flow relaxation), CFLBForm-mcf.txt (the same with
the LEMON solvers first), CFLBForm-cuts.txt (LP + user cuts),
CFLBForm-ld.txt and CFLBForm-ldvscuts.txt (Lagrangian dual of the knapsack
relaxation) and CFLBForm-ben.txt (the Benders formulation), which name the
CFLBCfg-*.txt and MCFBCfg.txt BlockConfig, the CFLBSCfg-*.txt and
MCFBSCfg*.txt BlockSolverConfig and the R3BCfg-copy.txt and
R3BCfg-MCF.txt R3Block Configuration. All the batch scripts live in
batches; each one runs from this directory and gives its list.
All this is possibly repeated a number of times in a loop where data of the CFLproblem (fixed and transportation costs, demands, capacities, facilities being open, closed or fixed-open, and the problem being splittable or not) are modified at random. Note that:
-
changing the problem type will not go well with the slope scaling heuristic, since that only works with the splittable version of the problem;
-
the MCF R3Block has an unavoidable issue with negative facility costs, that should therefore be avoided if one wants to be sure that the optimal values of the continuous relaxation coincide (see
NEGATIVE\_F\_COSTSin test.cpp); -
Many
MCFSolverare based on the algorithms of the rather old MCFClass project, that have a questionable habit of requiring well-scaled absolute numerical tolerances to work; this may be necessary, again, when testing theMCFBlockR3Block (seeSET\_EPSin test.cpp, and please don't complain that this is notSolver-independent as it should be: it's theMCFClasssolvers that are not properly implemented).
The usage of the executable is the following:
./CFL_test name -F formulations [-t typ] [-s niter] [-e seed] [-k wchg]
[-n #rounds] [-m #chng] [-q %chng] [-T cmptol]
typ = [C], F, L, ignored if name ends in .nc4
niter: how many Slope Scaling iterations [0]
seed: seed of the random generator [1]
wchg: what to change, coded bit-wise [31]
0 = facility cost, 1 = transportation cost
2 = capacities, 3 = demands
4 = close, 5 = re-open, 6 = fix-open fac.
7 = change problem type (split/unsplit)
8 (+256) = change abstract representation
9 (+512) = set eps & no negative design
#rounds: number of changing rounds [40]
#chng: average number of elements to change [10]
%chng: probability of any single change [0.5]
cmptol: relative tolerance of the comparison of the bounds [1e-5]
The batches folder collects all the batch scripts; each runs the
test on a set of CFL instances taken from the data/ folder of the
CapacitatedFacilityLocationBlock
repo (not the very
large ones, so that the tests end in reasonable time):
- batch / batch-s: the default arrangement (natural-formulation LP vs the MCF flow relaxation), on a largish / small set of instances respectively;
- batch-mcf: the MCF flow-relaxation arrangement;
- batch-ben: the Benders-formulation arrangement;
- batch-cuts: the LP + user-cuts arrangement;
- batch-ld / batch-ldvscuts: the Lagrangian-dual (knapsack relaxation) arrangement.
The -s scripts are restricted to instances small and easy enough that the
test can be run while solving them to integer optimality (required, e.g., to
test changing the problem type from splittable to unsplittable). Whenever the
R3Block is the MCF flow relaxation (the default and batch-mcf) the wchg
bitmask must include the +512 bit ("set eps & no negative design"), otherwise
the natural-LP and MCF bounds diverge after a few modification rounds. Only
the scenarios that currently pass are registered with ctest (see the
CMakeLists.txt for the per-batch status); the batch-ld and batch-ldvscuts
arrangements are known to be broken and are kept off.
A makefile is also provided that builds the executable including the
LagrangianDualSolver module, the BundleSolver module, the MILPSolver
module, the MCFBlock module, the BinaryKnapsackBlock module, together of
course with the core SMS++ library.
A second tester, CFL_BDS_test, puts the two Benders decompositions of the
same instance one against the other: the ad hoc one the Block carries in
its BenForm formulation, where the BendersBFunction separating the cuts is
hidden inside it and its subproblem is an MCFBlock, and the generic one of
BendersDecompositionSolver, which is given the same problem written as a
master holding the facility-opening Variable and one transportation
sub-Block per customer. The two describe one problem, hence what is checked
is that they agree with each other and with the monolithic solve, and what is
reported is what each of them costs, in the convex regime and in the MILP one,
with and without the Pareto-optimal cuts. It is built only where
BendersDecompositionSolver is in the build, the rest of the suite running
without it, and it reads one of the ORLib instances the Block downloads and
unpacks.
All the tests passing give some confidence that no regressions have been
done for the involved Block (CapacitatedFacilityLocationBlock,
MCFBlock, and BinaryKnapsackBlock) and the Solver used in the
solution process.
CFLScenarioGenerator reads a facility location instance and writes a set of
scenarios out of it, the uncertainty being the demand of the customers, plus
the TwoStageStochasticBlock that carries them, which is what the reduction
reads:
./CFLScenarioGenerator -i <instance.nc4> -o <scenarios.nc4>
--tssb-output <tssb.nc4> -n 50 -v 0.5 -s 1
--no-validate
-i is the instance (required), -o where the scenarios go,
--tssb-output the file the reduction reads, -n how many scenarios, -v
how much the demand varies between them, -s the seed, and --no-validate
skips a slow feasibility check, which also makes what is written depend on
the seed alone. How many scenarios there are is fixed when the file is
written, so a sweep over that number is a file each.
The reduction is asked of TSSB_scenred_test, the generic tester of the
scenario reduction of the TwoStageStochasticBlock suite, which reads the
same file whatever Block wrote it:
./TSSB_scenred_test -i <tssb.nc4> -m cssc -r 5 -c BSCfg-scenred.txt
with -m the method (baseline, dupacova, bestfit, firstfit, cssc),
-r how many representatives to keep and -c the BlockSolverConfig. What
it prints is the value of the whole set, the value of the reduced one and the
relative difference between the two, which is how well the representatives
stand for the whole.
batches-scenred/batch walks the two steps over instances, numbers of scenarios and of representatives and methods, writing one file per number of scenarios since the reduction cannot take a smaller set out of a larger pool, and collects what each run gives into a CSV.
- Antonio Frangioni
Dipartimento di Informatica
Università di Pisa
This code is provided free of charge under the GNU Lesser General Public License version 3.0 - see the LICENSE file for details.