1/*  Part of SWI-Prolog
2
3    Author:        Markus Triska
4    E-mail:        triska@metalevel.at
5    WWW:           http://www.swi-prolog.org
6    Copyright (C): 2005-2016, Markus Triska
7    All rights reserved.
8
9    Redistribution and use in source and binary forms, with or without
10    modification, are permitted provided that the following conditions
11    are met:
12
13    1. Redistributions of source code must retain the above copyright
14       notice, this list of conditions and the following disclaimer.
15
16    2. Redistributions in binary form must reproduce the above copyright
17       notice, this list of conditions and the following disclaimer in
18       the documentation and/or other materials provided with the
19       distribution.
20
21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32    POSSIBILITY OF SUCH DAMAGE.
33*/
34
35
36:- module(simplex,
37        [
38                assignment/2,
39                constraint/3,
40                constraint/4,
41                constraint_add/4,
42                gen_state/1,
43                gen_state_clpr/1,
44                gen_state_clpr/2,
45                maximize/3,
46                minimize/3,
47                objective/2,
48                shadow_price/3,
49                transportation/4,
50                variable_value/3
51        ]).
52
53:- if(exists_source(library(clpr))).
54:- use_module(library(clpr)).
55:- endif.
56:- use_module(library(assoc)).
57:- use_module(library(pio)).
58
59/** <module> Solve linear programming problems
60
61## Introduction			{#simplex-intro}
62
63A *linear programming problem* or simply *linear program* (LP)
64consists of:
65
66  - a set of _linear_ **constraints**
67  - a set of **variables**
68  - a _linear_ **objective function**.
69
70The goal is to assign values to the variables so as to _maximize_ (or
71minimize) the value of the objective function while satisfying all
72constraints.
73
74Many optimization problems can be modeled in this way. As one basic
75example, consider a knapsack with fixed capacity C, and a number of
76items with sizes `s(i)` and values `v(i)`. The goal is to put as many
77items as possible in the knapsack (not exceeding its capacity) while
78maximizing the sum of their values.
79
80As another example, suppose you are given a set of _coins_ with
81certain values, and you are to find the minimum number of coins such
82that their values sum up to a fixed amount. Instances of these
83problems are solved below.
84
85All numeric quantities are converted to rationals via `rationalize/1`,
86and rational arithmetic is used throughout solving linear programs. In
87the current implementation, all variables are implicitly constrained
88to be _non-negative_. This may change in future versions, and
89non-negativity constraints should therefore be stated explicitly.
90
91
92## Example 1     {#simplex-ex-1}
93
94This is the "radiation therapy" example, taken from _Introduction to
95Operations Research_ by Hillier and Lieberman.
96
97[**Prolog DCG notation**](https://www.metalevel.at/prolog/dcg) is
98used to _implicitly_ thread the state through posting the constraints:
99
100==
101:- use_module(library(simplex)).
102
103radiation(S) :-
104	gen_state(S0),
105	post_constraints(S0, S1),
106	minimize([0.4*x1, 0.5*x2], S1, S).
107
108post_constraints -->
109	constraint([0.3*x1, 0.1*x2] =< 2.7),
110	constraint([0.5*x1, 0.5*x2] = 6),
111	constraint([0.6*x1, 0.4*x2] >= 6),
112	constraint([x1] >= 0),
113	constraint([x2] >= 0).
114==
115
116An example query:
117
118==
119?- radiation(S), variable_value(S, x1, Val1),
120		 variable_value(S, x2, Val2).
121Val1 = 15 rdiv 2,
122Val2 = 9 rdiv 2.
123==
124
125## Example 2     {#simplex-ex-2}
126
127Here is an instance of the knapsack problem described above, where `C
128= 8`, and we have two types of items: One item with value 7 and size
1296, and 2 items each having size 4 and value 4. We introduce two
130variables, `x(1)` and `x(2)` that denote how many items to take of
131each type.
132
133==
134:- use_module(library(simplex)).
135
136knapsack(S) :-
137	knapsack_constraints(S0),
138	maximize([7*x(1), 4*x(2)], S0, S).
139
140knapsack_constraints(S) :-
141	gen_state(S0),
142	constraint([6*x(1), 4*x(2)] =< 8, S0, S1),
143	constraint([x(1)] =< 1, S1, S2),
144	constraint([x(2)] =< 2, S2, S).
145==
146
147An example query yields:
148
149==
150?- knapsack(S), variable_value(S, x(1), X1),
151	        variable_value(S, x(2), X2).
152X1 = 1
153X2 = 1 rdiv 2.
154==
155
156That is, we are to take the one item of the first type, and half of one of
157the items of the other type to maximize the total value of items in the
158knapsack.
159
160If items can not be split, integrality constraints have to be imposed:
161
162==
163knapsack_integral(S) :-
164	knapsack_constraints(S0),
165	constraint(integral(x(1)), S0, S1),
166	constraint(integral(x(2)), S1, S2),
167	maximize([7*x(1), 4*x(2)], S2, S).
168==
169
170Now the result is different:
171
172==
173?- knapsack_integral(S), variable_value(S, x(1), X1),
174			 variable_value(S, x(2), X2).
175
176X1 = 0
177X2 = 2
178==
179
180That is, we are to take only the _two_ items of the second type.
181Notice in particular that always choosing the remaining item with best
182performance (ratio of value to size) that still fits in the knapsack
183does not necessarily yield an optimal solution in the presence of
184integrality constraints.
185
186## Example 3      {#simplex-ex-3}
187
188We are given:
189
190  - 3 coins each worth 1 unit
191  - 20 coins each worth 5 units and
192  - 10 coins each worth 20 units.
193
194The task is to find a _minimal_ number of these coins that amount to
195111 units in total. We introduce variables `c(1)`, `c(5)` and `c(20)`
196denoting how many coins to take of the respective type:
197
198==
199:- use_module(library(simplex)).
200
201coins(S) :-
202	gen_state(S0),
203	coins(S0, S).
204
205coins -->
206	constraint([c(1), 5*c(5), 20*c(20)] = 111),
207	constraint([c(1)] =< 3),
208	constraint([c(5)] =< 20),
209	constraint([c(20)] =< 10),
210	constraint([c(1)] >= 0),
211	constraint([c(5)] >= 0),
212	constraint([c(20)] >= 0),
213	constraint(integral(c(1))),
214	constraint(integral(c(5))),
215	constraint(integral(c(20))),
216	minimize([c(1), c(5), c(20)]).
217==
218
219An example query:
220
221==
222?- coins(S), variable_value(S, c(1), C1),
223	     variable_value(S, c(5), C5),
224	     variable_value(S, c(20), C20).
225
226C1 = 1,
227C5 = 2,
228C20 = 5.
229==
230
231@author [Markus Triska](https://www.metalevel.at)
232*/
233
234
235/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236   CLP(R) bindings
237   the (unsolved) state is stored as a structure of the form
238      clpr_state(Options, Cs, Is)
239   Options: list of Option=Value pairs, currently only eps=Eps
240   Cs: list of constraints, i.e., structures of the form
241      c(Name, Left, Op, Right)
242      anonymous constraints have Name == 0
243   Is: list of integral variables
244- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
245
246gen_state_clpr(State) :- gen_state_clpr([], State).
247
248gen_state_clpr(Options, State) :-
249        ( memberchk(eps=_, Options) -> Options1 = Options
250        ;   Options1 = [eps=1.0e-6|Options]
251        ),
252        State = clpr_state(Options1, [], []).
253
254clpr_state_options(clpr_state(Os, _, _), Os).
255clpr_state_constraints(clpr_state(_, Cs, _), Cs).
256clpr_state_integrals(clpr_state(_, _, Is), Is).
257clpr_state_add_integral(I, clpr_state(Os, Cs, Is), clpr_state(Os, Cs, [I|Is])).
258clpr_state_add_constraint(C, clpr_state(Os, Cs, Is), clpr_state(Os, [C|Cs], Is)).
259clpr_state_set_constraints(Cs, clpr_state(Os,_,Is), clpr_state(Os, Cs, Is)).
260
261clpr_constraint(Name, Constraint, S0, S) :-
262        (   Constraint = integral(Var) -> clpr_state_add_integral(Var, S0, S)
263        ;   Constraint =.. [Op,Left,Right],
264            coeff_one(Left, Left1),
265            clpr_state_add_constraint(c(Name, Left1, Op, Right), S0, S)
266        ).
267
268clpr_constraint(Constraint, S0, S) :-
269        clpr_constraint(0, Constraint, S0, S).
270
271clpr_shadow_price(clpr_solved(_,_,Duals,_), Name, Value) :-
272        memberchk(Name-Value0, Duals),
273        Value is Value0.
274        %( var(Value0) ->
275        %       Value = 0
276        %;
277        %       Value is Value0
278        %).
279
280
281
282clpr_make_variables(Cs, Aliases) :-
283        clpr_constraints_variables(Cs, Variables0, []),
284        sort(Variables0, Variables1),
285        pairs_keys(Aliases, Variables1).
286
287clpr_constraints_variables([]) --> [].
288clpr_constraints_variables([c(_, Left, _, _)|Cs]) -->
289        variables(Left),
290        clpr_constraints_variables(Cs).
291
292clpr_set_up(Aliases, c(_Name, Left, Op, Right)) :-
293        clpr_translate_linsum(Left, Aliases, LinSum),
294        CLPRConstraint =.. [Op, LinSum, Right],
295        clpr:{ CLPRConstraint }.
296
297clpr_set_up_noneg(Aliases, Var) :-
298        memberchk(Var-CLPVar, Aliases),
299        { CLPVar >= 0 }.
300
301clpr_translate_linsum([], _, 0).
302clpr_translate_linsum([Coeff*Var|Ls], Aliases, LinSum) :-
303        memberchk(Var-CLPVar, Aliases),
304        LinSum = Coeff*CLPVar + LinRest,
305        clpr_translate_linsum(Ls, Aliases, LinRest).
306
307clpr_dual(Objective0, S0, DualValues) :-
308        clpr_state_constraints(S0, Cs0),
309        clpr_constraints_variables(Cs0, Variables0, []),
310        sort(Variables0, Variables1),
311        maplist(clpr_standard_form, Cs0, Cs1),
312        clpr_include_all_vars(Cs1, Variables1, Cs2),
313        clpr_merge_into(Variables1, Objective0, Objective, []),
314        clpr_unique_names(Cs2, 0, Names),
315        maplist(clpr_constraint_coefficient, Cs2, Coefficients),
316        lists_transpose(Coefficients, TCs),
317        maplist(clpr_dual_constraints(Names), TCs, Objective, DualConstraints),
318        phrase(clpr_nonneg_constraints(Cs2, Names), DualNonNeg),
319        append(DualConstraints, DualNonNeg, DualConstraints1),
320        maplist(clpr_dual_objective, Cs2, Names, DualObjective),
321        clpr_make_variables(DualConstraints1, Aliases),
322        maplist(clpr_set_up(Aliases), DualConstraints1),
323        clpr_translate_linsum(DualObjective, Aliases, LinExpr),
324        minimize(LinExpr),
325        Aliases = DualValues.
326
327
328
329clpr_dual_objective(c(_, _, _, Right), Name, Right*Name).
330
331clpr_nonneg_constraints([], _) --> [].
332clpr_nonneg_constraints([C|Cs], [Name|Names]) -->
333        { C = c(_, _, Op, _) },
334        (   { Op == (=<) } -> [c(0, [1*Name], (>=), 0)]
335        ;   []
336        ),
337        clpr_nonneg_constraints(Cs, Names).
338
339
340clpr_dual_constraints(Names, Coeffs, O*_, Constraint) :-
341        maplist(clpr_dual_linsum, Coeffs, Names, Linsum),
342        Constraint = c(0, Linsum, (>=), O).
343
344clpr_dual_linsum(Coeff, Name, Coeff*Name).
345
346clpr_constraint_coefficient(c(_, Left, _, _), Coeff) :-
347        maplist(coeff_, Left, Coeff).
348
349all_coeffs(Coeff*_, Coeff).
350
351clpr_unique_names([], _, []).
352clpr_unique_names([C0|Cs0], Num, [N|Ns]) :-
353        C0 = c(Name, _, _, _),
354        (   Name == 0 -> N = Num, Num1 is Num + 1
355        ;   N = Name, Num1 = Num
356        ),
357        clpr_unique_names(Cs0, Num1, Ns).
358
359clpr_include_all_vars([], _, []).
360clpr_include_all_vars([C0|Cs0], Variables, [C|Cs]) :-
361        C0 = c(Name, Left0, Op, Right),
362        clpr_merge_into(Variables, Left0, Left, []),
363        C = c(Name, Left, Op, Right),
364        clpr_include_all_vars(Cs0, Variables, Cs).
365
366clpr_merge_into([], _, Ls, Ls).
367clpr_merge_into([V|Vs], Left, Ls0, Ls) :-
368        ( member(Coeff*V, Left) ->
369                Ls0 = [Coeff*V|Rest]
370        ;
371                Ls0 = [0*V|Rest]
372        ),
373        clpr_merge_into(Vs, Left, Rest, Ls).
374
375
376
377
378clpr_maximize(Expr0, S0, S) :-
379        coeff_one(Expr0, Expr),
380        clpr_state_constraints(S0, Cs),
381        clpr_make_variables(Cs, Aliases),
382        maplist(clpr_set_up(Aliases), Cs),
383        clpr_constraints_variables(Cs, Variables0, []),
384        sort(Variables0, Variables1),
385        maplist(clpr_set_up_noneg(Aliases), Variables1),
386        clpr_translate_linsum(Expr, Aliases, LinExpr),
387        clpr_state_integrals(S0, Is),
388        ( Is == [] ->
389                maximize(LinExpr),
390                Sup is LinExpr,
391                clpr_dual(Expr, S0, DualValues),
392                S = clpr_solved(Sup, Aliases, DualValues, S0)
393        ;
394                clpr_state_options(S0, Options),
395                memberchk(eps=Eps, Options),
396                maplist(clpr_fetch_var(Aliases), Is, Vars),
397                bb_inf(Vars, -LinExpr, Sup, Vertex, Eps),
398                pairs_keys_values(Values, Is, Vertex),
399                % what about the dual in MIPs?
400                Sup1 is -Sup,
401                S = clpr_solved(Sup1, Values, [], S0)
402        ).
403
404clpr_minimize(Expr0, S0, S) :-
405        coeff_one(Expr0, Expr1),
406        maplist(linsum_negate, Expr1, Expr2),
407        clpr_maximize(Expr2, S0, S1),
408        S1 = clpr_solved(Sup, Values, Duals, S0),
409        Inf is -Sup,
410        S = clpr_solved(Inf, Values, Duals, S0).
411
412clpr_fetch_var(Aliases, Var, X) :- memberchk(Var-X, Aliases).
413
414clpr_variable_value(clpr_solved(_, Aliases, _, _), Variable, Value) :-
415        memberchk(Variable-Value0, Aliases),
416        Value is Value0.
417        %( var(Value0) ->
418        %       Value = 0
419        %;
420        %       Value is Value0
421        %).
422
423clpr_objective(clpr_solved(Obj, _, _, _), Obj).
424
425clpr_standard_form(c(Name, Left, Op, Right), S) :-
426        clpr_standard_form_(Op, Name, Left, Right, S).
427
428clpr_standard_form_((=), Name, Left, Right, c(Name, Left, (=), Right)).
429clpr_standard_form_((>=), Name, Left, Right, c(Name, Left1, (=<), Right1)) :-
430        Right1 is -Right,
431        maplist(linsum_negate, Left, Left1).
432clpr_standard_form_((=<), Name, Left, Right, c(Name, Left, (=<), Right)).
433
434
435/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
436   General Simplex Algorithm
437   Structures used:
438
439   tableau(Objective, Variables, Indicators, Constraints)
440     *) objective function, represented as row
441     *) list of variables corresponding to columns
442     *) indicators denoting which variables are still active
443     *) constraints as rows
444
445   row(Var, Left, Right)
446     *) the basic variable corresponding to this row
447     *) coefficients of the left-hand side of the constraint
448     *) right-hand side of the constraint
449- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
450
451
452find_row(Variable, [Row|Rows], R) :-
453        Row = row(V, _, _),
454        (   V == Variable -> R = Row
455        ;   find_row(Variable, Rows, R)
456        ).
457
458%% variable_value(+State, +Variable, -Value)
459%
460% Value is unified with the value obtained for Variable. State must
461% correspond to a solved instance.
462
463variable_value(State, Variable, Value) :-
464        functor(State, F, _),
465        (   F == solved ->
466            solved_tableau(State, Tableau),
467            tableau_rows(Tableau, Rows),
468            (   find_row(Variable, Rows, Row) -> Row = row(_, _, Value)
469            ;   Value = 0
470            )
471        ;   F == clpr_solved -> clpr_variable_value(State, Variable, Value)
472        ).
473
474var_zero(State, _Coeff*Var) :- variable_value(State, Var, 0).
475
476list_first(Ls, F, Index) :- once(nth0(Index, Ls, F)).
477
478%% shadow_price(+State, +Name, -Value)
479%
480% Unifies Value with the shadow price corresponding to the linear
481% constraint whose name is Name. State must correspond to a solved
482% instance.
483
484shadow_price(State, Name, Value) :-
485        functor(State, F, _),
486        (   F == solved ->
487            solved_tableau(State, Tableau),
488            tableau_objective(Tableau, row(_,Left,_)),
489            tableau_variables(Tableau, Variables),
490            solved_names(State, Names),
491            memberchk(user(Name)-Var, Names),
492            list_first(Variables, Var, Nth0),
493            nth0(Nth0, Left, Value)
494        ;   F == clpr_solved -> clpr_shadow_price(State, Name, Value)
495        ).
496
497%% objective(+State, -Objective)
498%
499% Unifies Objective with the result of the objective function at the
500% obtained extremum. State must correspond to a solved instance.
501
502objective(State, Obj) :-
503        functor(State, F, _),
504        (   F == solved ->
505            solved_tableau(State, Tableau),
506            tableau_objective(Tableau, Objective),
507            Objective = row(_, _, Obj)
508        ;   clpr_objective(State, Obj)
509        ).
510
511   % interface functions that access tableau components
512
513tableau_objective(tableau(Obj, _, _, _), Obj).
514tableau_rows(tableau(_, _, _, Rows), Rows).
515tableau_indicators(tableau(_, _, Inds, _), Inds).
516tableau_variables(tableau(_, Vars, _, _), Vars).
517
518/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
519   interface functions that access and modify state components
520
521   state is a structure of the form
522       state(Num, Names, Cs, Is)
523   Num: used to obtain new unique names for slack variables in a side-effect
524        free way (increased by one and threaded through)
525   Names: list of Name-Var, correspondence between constraint-names and
526        names of slack/artificial variables to obtain shadow prices later
527   Cs: list of constraints
528   Is: list of integer variables
529
530   constraints are initially represented as c(Name, Left, Op, Right),
531   and after normalizing as c(Var, Left, Right). Name of unnamed constraints
532   is 0. The distinction is important for merging constraints (mainly in
533   branch and bound) with existing ones.
534- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
535
536
537constraint_name(c(Name, _, _, _), Name).
538constraint_op(c(_, _, Op, _), Op).
539constraint_left(c(_, Left, _, _), Left).
540constraint_right(c(_, _, _, Right), Right).
541
542%% gen_state(-State)
543%
544% Generates an initial state corresponding to an empty linear program.
545
546gen_state(state(0,[],[],[])).
547
548state_add_constraint(C, S0, S) :-
549        (   constraint_name(C, 0), constraint_left(C, [_Coeff*_Var]) ->
550            state_merge_constraint(C, S0, S)
551        ;   state_add_constraint_(C, S0, S)
552        ).
553
554state_add_constraint_(C, state(VID,Ns,Cs,Is), state(VID,Ns,[C|Cs],Is)).
555
556state_merge_constraint(C, S0, S) :-
557        constraint_left(C, [Coeff0*Var0]),
558        constraint_right(C, Right0),
559        constraint_op(C, Op),
560        (   Coeff0 =:= 0 ->
561            (   Op == (=) -> Right0 =:= 0, S0 = S
562            ;   Op == (=<) -> S0 = S
563            ;   Op == (>=) -> Right0 =:= 0, S0 = S
564            )
565        ;   Coeff0 < 0 -> state_add_constraint_(C, S0, S)
566        ;   Right is Right0 rdiv Coeff0,
567            state_constraints(S0, Cs),
568            (   select(c(0, [1*Var0], Op, CRight), Cs, RestCs) ->
569                (   Op == (=) -> CRight =:= Right, S0 = S
570                ;   Op == (=<) ->
571                    NewRight is min(Right, CRight),
572                    NewCs = [c(0, [1*Var0], Op, NewRight)|RestCs],
573                    state_set_constraints(NewCs, S0, S)
574                ;   Op == (>=) ->
575                    NewRight is max(Right, CRight),
576                    NewCs = [c(0, [1*Var0], Op, NewRight)|RestCs],
577                    state_set_constraints(NewCs, S0, S)
578                )
579            ;   state_add_constraint_(c(0, [1*Var0], Op, Right), S0, S)
580            )
581        ).
582
583
584state_add_name(Name, Var), [state(VID,[Name-Var|Ns],Cs,Is)] -->
585        [state(VID,Ns,Cs,Is)].
586
587state_add_integral(Var, state(VID,Ns,Cs,Is), state(VID,Ns,Cs,[Var|Is])).
588
589state_constraints(state(_, _, Cs, _), Cs).
590state_names(state(_,Names,_,_), Names).
591state_integrals(state(_,_,_,Is), Is).
592state_set_constraints(Cs, state(VID,Ns,_,Is), state(VID,Ns,Cs,Is)).
593state_set_integrals(Is, state(VID,Ns,Cs,_), state(VID,Ns,Cs,Is)).
594
595
596state_next_var(VarID0), [state(VarID1,Names,Cs,Is)] -->
597        [state(VarID0,Names,Cs,Is)],
598        { VarID1 is VarID0 + 1 }.
599
600solved_tableau(solved(Tableau, _, _), Tableau).
601solved_names(solved(_, Names,_), Names).
602solved_integrals(solved(_,_,Is), Is).
603
604% User-named constraints are wrapped with user/1 to also allow "0" in
605% constraint names.
606
607%% constraint(+Constraint, +S0, -S)
608%
609% Adds a linear or integrality constraint to the linear program
610% corresponding to state S0. A linear constraint is of the form =|Left
611% Op C|=, where `Left` is a list of `Coefficient*Variable` terms
612% (variables in the context of linear programs can be atoms or
613% compound terms) and `C` is a non-negative numeric constant. The list
614% represents the sum of its elements. `Op` can be `=`, `=<` or `>=`.
615% The coefficient `1` can be omitted. An integrality constraint is of
616% the form integral(Variable) and constrains Variable to an integral
617% value.
618
619
620constraint(C, S0, S) :-
621        functor(S0, F, _),
622        (   F == state ->
623            (   C = integral(Var) -> state_add_integral(Var, S0, S)
624            ;   constraint_(0, C, S0, S)
625            )
626        ;   F == clpr_state -> clpr_constraint(C, S0, S)
627        ).
628
629%% constraint(+Name, +Constraint, +S0, -S)
630%
631% Like constraint/3, and attaches the name Name (an atom or compound
632% term) to the new constraint.
633
634constraint(Name, C, S0, S) :- constraint_(user(Name), C, S0, S).
635
636constraint_(Name, C, S0, S) :-
637        functor(S0, F, _),
638        (   F == state ->
639            (   C = integral(Var) -> state_add_integral(Var, S0, S)
640            ;   C =.. [Op, Left0, Right0],
641                coeff_one(Left0, Left),
642                Right0 >= 0,
643                Right is rationalize(Right0),
644                state_add_constraint(c(Name, Left, Op, Right), S0, S)
645            )
646        ;   F == clpr_state -> clpr_constraint(Name, C, S0, S)
647        ).
648
649%% constraint_add(+Name, +Left, +S0, -S)
650%
651% Left is a list of `Coefficient*Variable` terms. The terms are added
652% to the left-hand side of the constraint named Name. S is unified
653% with the resulting state.
654
655constraint_add(Name, A, S0, S) :-
656        functor(S0, F, _),
657        (   F == state ->
658            state_constraints(S0, Cs),
659            add_left(Cs, user(Name), A, Cs1),
660            state_set_constraints(Cs1, S0, S)
661        ;   F == clpr_state ->
662            clpr_state_constraints(S0, Cs),
663            add_left(Cs, Name, A, Cs1),
664            clpr_state_set_constraints(Cs1, S0, S)
665        ).
666
667
668add_left([c(Name,Left0,Op,Right)|Cs], V, A, [c(Name,Left,Op,Right)|Rest]) :-
669        (   Name == V -> append(A, Left0, Left), Rest = Cs
670        ;   Left0 = Left, add_left(Cs, V, A, Rest)
671        ).
672
673branching_variable(State, Variable) :-
674        solved_integrals(State, Integrals),
675        member(Variable, Integrals),
676        variable_value(State, Variable, Value),
677        \+ integer(Value).
678
679
680worth_investigating(ZStar0, _, _) :- var(ZStar0).
681worth_investigating(ZStar0, AllInt, Z) :-
682        nonvar(ZStar0),
683        (   AllInt =:= 1 -> Z1 is floor(Z)
684        ;   Z1 = Z
685        ),
686        Z1 > ZStar0.
687
688
689branch_and_bound(Objective, Solved, AllInt, ZStar0, ZStar, S0, S, Found) :-
690        objective(Solved, Z),
691        (   worth_investigating(ZStar0, AllInt, Z) ->
692            (   branching_variable(Solved, BrVar) ->
693                variable_value(Solved, BrVar, Value),
694                Value1 is floor(Value),
695                Value2 is Value1 + 1,
696                constraint([BrVar] =< Value1, S0, SubProb1),
697                (   maximize_(Objective, SubProb1, SubSolved1) ->
698                    Sub1Feasible = 1,
699                    objective(SubSolved1, Obj1)
700                ;   Sub1Feasible = 0
701                ),
702                constraint([BrVar] >= Value2, S0, SubProb2),
703                (   maximize_(Objective, SubProb2, SubSolved2) ->
704                    Sub2Feasible = 1,
705                    objective(SubSolved2, Obj2)
706                ;   Sub2Feasible = 0
707                ),
708                (   Sub1Feasible =:= 1, Sub2Feasible =:= 1 ->
709                    (   Obj1 >= Obj2 ->
710                        First = SubProb1,
711                        Second = SubProb2,
712                        FirstSolved = SubSolved1,
713                        SecondSolved = SubSolved2
714                    ;   First = SubProb2,
715                        Second = SubProb1,
716                        FirstSolved = SubSolved2,
717                        SecondSolved = SubSolved1
718                    ),
719                    branch_and_bound(Objective, FirstSolved, AllInt, ZStar0, ZStar1, First, Solved1, Found1),
720                    branch_and_bound(Objective, SecondSolved, AllInt, ZStar1, ZStar2, Second, Solved2, Found2)
721                ;   Sub1Feasible =:= 1 ->
722                    branch_and_bound(Objective, SubSolved1, AllInt, ZStar0, ZStar1, SubProb1, Solved1, Found1),
723                    Found2 = 0
724                ;   Sub2Feasible =:= 1 ->
725                    Found1 = 0,
726                    branch_and_bound(Objective, SubSolved2, AllInt, ZStar0, ZStar2, SubProb2, Solved2, Found2)
727                ;   Found1 = 0, Found2 = 0
728                ),
729                (   Found1 =:= 1, Found2 =:= 1 -> S = Solved2, ZStar = ZStar2
730                ;   Found1 =:= 1 -> S = Solved1, ZStar = ZStar1
731                ;   Found2 =:= 1 -> S = Solved2, ZStar = ZStar2
732                ;   S = S0, ZStar = ZStar0
733                ),
734                Found is max(Found1, Found2)
735            ;   S = Solved, ZStar = Z, Found = 1
736            )
737        ;   ZStar = ZStar0, S = S0, Found = 0
738        ).
739
740%% maximize(+Objective, +S0, -S)
741%
742% Maximizes the objective function, stated as a list of
743% `Coefficient*Variable` terms that represents the sum of its
744% elements, with respect to the linear program corresponding to state
745% S0. \arg{S} is unified with an internal representation of the solved
746% instance.
747
748maximize(Z0, S0, S) :-
749        coeff_one(Z0, Z1),
750        functor(S0, F, _),
751        (   F == state -> maximize_mip(Z1, S0, S)
752        ;   F == clpr_state -> clpr_maximize(Z1, S0, S)
753        ).
754
755maximize_mip(Z, S0, S) :-
756        maximize_(Z, S0, Solved),
757        state_integrals(S0, Is),
758        (   Is == [] -> S = Solved
759        ;   % arrange it so that branch and bound branches on variables
760            % in the same order the integrality constraints were stated in
761            reverse(Is, Is1),
762            state_set_integrals(Is1, S0, S1),
763            (   all_integers(Z, Is1) -> AllInt = 1
764            ;   AllInt = 0
765            ),
766            branch_and_bound(Z, Solved, AllInt, _, _, S1, S, 1)
767        ).
768
769all_integers([], _).
770all_integers([Coeff*V|Rest], Is) :-
771        integer(Coeff),
772        memberchk(V, Is),
773        all_integers(Rest, Is).
774
775%% minimize(+Objective, +S0, -S)
776%
777% Analogous to maximize/3.
778
779minimize(Z0, S0, S) :-
780        coeff_one(Z0, Z1),
781        functor(S0, F, _),
782        (   F == state ->
783            maplist(linsum_negate, Z1, Z2),
784            maximize_mip(Z2, S0, S1),
785            solved_tableau(S1, tableau(Obj, Vars, Inds, Rows)),
786            solved_names(S1, Names),
787            Obj = row(z, Left0, Right0),
788            all_times(Left0, -1, Left),
789            Right is -Right0,
790            Obj1 = row(z, Left, Right),
791            state_integrals(S0, Is),
792            S = solved(tableau(Obj1, Vars, Inds, Rows), Names, Is)
793        ;   F == clpr_state -> clpr_minimize(Z1, S0, S)
794        ).
795
796op_pendant(>=, =<).
797op_pendant(=<, >=).
798
799constraints_collapse([]) --> [].
800constraints_collapse([C|Cs]) -->
801        { C = c(Name, Left, Op, Right) },
802        (   { Name == 0, Left = [1*Var], op_pendant(Op, P) } ->
803            { Pendant = c(0, [1*Var], P, Right) },
804            (   { select(Pendant, Cs, Rest) } ->
805                [c(0, Left, (=), Right)],
806                { CsLeft = Rest }
807            ;   [C],
808                { CsLeft = Cs }
809            )
810        ;   [C],
811            { CsLeft = Cs }
812        ),
813        constraints_collapse(CsLeft).
814
815% solve a (relaxed) LP in standard form
816
817maximize_(Z, S0, S) :-
818        state_constraints(S0, Cs0),
819        phrase(constraints_collapse(Cs0), Cs1),
820        phrase(constraints_normalize(Cs1, Cs, As0), [S0], [S1]),
821        flatten(As0, As1),
822        (   As1 == [] ->
823            make_tableau(Z, Cs, Tableau0),
824            simplex(Tableau0, Tableau),
825            state_names(S1, Names),
826            state_integrals(S1, Is),
827            S = solved(Tableau, Names, Is)
828        ;   state_names(S1, Names),
829            state_integrals(S1, Is),
830            two_phase_simplex(Z, Cs, As1, Names, Is, S)
831        ).
832
833make_tableau(Z, Cs, Tableau) :-
834        ZC = c(_, Z, _),
835        phrase(constraints_variables([ZC|Cs]), Variables0),
836        sort(Variables0, Variables),
837        constraints_rows(Cs, Variables, Rows),
838        linsum_row(Variables, Z, Objective1),
839        all_times(Objective1, -1, Obj),
840        length(Variables, LVs),
841        length(Ones, LVs),
842        all_one(Ones),
843        Tableau = tableau(row(z, Obj, 0), Variables, Ones, Rows).
844
845all_one(Ones) :- maplist(=(1), Ones).
846
847proper_form(Variables, Rows, _Coeff*A, Obj0, Obj) :-
848        (   find_row(A, Rows, PivotRow) ->
849            list_first(Variables, A, Col),
850            row_eliminate(Obj0, PivotRow, Col, Obj)
851        ;   Obj = Obj0
852        ).
853
854
855two_phase_simplex(Z, Cs, As, Names, Is, S) :-
856        % phase 1: minimize sum of articifial variables
857        make_tableau(As, Cs, Tableau0),
858        Tableau0 = tableau(Obj0, Variables, Inds, Rows),
859        foldl(proper_form(Variables, Rows), As, Obj0, Obj),
860        simplex(tableau(Obj, Variables, Inds, Rows), Tableau1),
861        maplist(var_zero(solved(Tableau1, _, _)), As),
862        % phase 2: remove artificial variables and solve actual LP.
863        tableau_rows(Tableau1, Rows2),
864        eliminate_artificial(As, As, Variables, Rows2, Rows3),
865        list_nths(As, Variables, Nths0),
866        nths_to_zero(Nths0, Inds, Inds1),
867        linsum_row(Variables, Z, Objective),
868        all_times(Objective, -1, Objective1),
869        foldl(proper_form(Variables, Rows3), Z, row(z, Objective1, 0), ObjRow),
870        simplex(tableau(ObjRow, Variables, Inds1, Rows3), Tableau),
871        S = solved(Tableau, Names, Is).
872
873/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
874   If artificial variables are still in the basis, replace them with
875   non-artificial variables if possible. If that is not possible, the
876   constraint is ignored because it is redundant.
877- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
878
879eliminate_artificial([], _, _, Rows, Rows).
880eliminate_artificial([_Coeff*A|Rest], As, Variables, Rows0, Rows) :-
881        (   select(row(A, Left, 0), Rows0, Others) ->
882            (   nth0(Col, Left, Coeff),
883                Coeff =\= 0,
884                nth0(Col, Variables, Var),
885                \+ memberchk(_*Var, As) ->
886                row_divide(row(A, Left, 0), Coeff, Row),
887                gauss_elimination(Rows0, Row, Col, Rows1),
888                swap_basic(Rows1, A, Var, Rows2)
889            ;   Rows2 = Others
890            )
891        ;   Rows2 = Rows0
892        ),
893        eliminate_artificial(Rest, As, Variables, Rows2, Rows).
894
895nths_to_zero([], Inds, Inds).
896nths_to_zero([Nth|Nths], Inds0, Inds) :-
897        nth_to_zero(Inds0, 0, Nth, Inds1),
898        nths_to_zero(Nths, Inds1, Inds).
899
900nth_to_zero([], _, _, []).
901nth_to_zero([I|Is], Curr, Nth, [Z|Zs]) :-
902        (   Curr =:= Nth -> [Z|Zs] = [0|Is]
903        ;   Z = I,
904            Next is Curr + 1,
905            nth_to_zero(Is, Next, Nth, Zs)
906        ).
907
908
909list_nths([], _, []).
910list_nths([_Coeff*A|As], Variables, [Nth|Nths]) :-
911        list_first(Variables, A, Nth),
912        list_nths(As, Variables, Nths).
913
914
915linsum_negate(Coeff0*Var, Coeff*Var) :- Coeff is -Coeff0.
916
917linsum_row([], _, []).
918linsum_row([V|Vs], Ls, [C|Cs]) :-
919        (   member(Coeff*V, Ls) -> C is rationalize(Coeff)
920        ;   C = 0
921        ),
922        linsum_row(Vs, Ls, Cs).
923
924constraints_rows([], _, []).
925constraints_rows([C|Cs], Vars, [R|Rs]) :-
926        C = c(Var, Left0, Right),
927        linsum_row(Vars, Left0, Left),
928        R = row(Var, Left, Right),
929        constraints_rows(Cs, Vars, Rs).
930
931constraints_normalize([], [], []) --> [].
932constraints_normalize([C0|Cs0], [C|Cs], [A|As]) -->
933        { constraint_op(C0, Op),
934          constraint_left(C0, Left),
935          constraint_right(C0, Right),
936          constraint_name(C0, Name),
937          Con =.. [Op, Left, Right] },
938        constraint_normalize(Con, Name, C, A),
939        constraints_normalize(Cs0, Cs, As).
940
941constraint_normalize(As0 =< B0, Name, c(Slack, [1*Slack|As0], B0), []) -->
942        state_next_var(Slack),
943        state_add_name(Name, Slack).
944constraint_normalize(As0 = B0, Name, c(AID, [1*AID|As0], B0), [-1*AID]) -->
945        state_next_var(AID),
946        state_add_name(Name, AID).
947constraint_normalize(As0 >= B0, Name, c(AID, [-1*Slack,1*AID|As0], B0), [-1*AID]) -->
948        state_next_var(Slack),
949        state_next_var(AID),
950        state_add_name(Name, AID).
951
952coeff_one([], []).
953coeff_one([L|Ls], [Coeff*Var|Rest]) :-
954        (   L = A*B -> Coeff = A, Var = B
955        ;   Coeff = 1, Var = L
956        ),
957        coeff_one(Ls, Rest).
958
959
960tableau_optimal(Tableau) :-
961        tableau_objective(Tableau, Objective),
962        tableau_indicators(Tableau, Indicators),
963        Objective = row(_, Left, _),
964        all_nonnegative(Left, Indicators).
965
966all_nonnegative([], []).
967all_nonnegative([Coeff|As], [I|Is]) :-
968        (   I =:= 0 -> true
969        ;   Coeff >= 0
970        ),
971        all_nonnegative(As, Is).
972
973pivot_column(Tableau, PCol) :-
974        tableau_objective(Tableau, row(_, Left, _)),
975        tableau_indicators(Tableau, Indicators),
976        first_negative(Left, Indicators, 0, Index0, Val, RestL, RestI),
977        Index1 is Index0 + 1,
978        pivot_column(RestL, RestI, Val, Index1, Index0, PCol).
979
980first_negative([L|Ls], [I|Is], Index0, N, Val, RestL, RestI) :-
981        Index1 is Index0 + 1,
982        (   I =:= 0 -> first_negative(Ls, Is, Index1, N, Val, RestL, RestI)
983        ;   (   L < 0 -> N = Index0, Val = L, RestL = Ls, RestI = Is
984            ;   first_negative(Ls, Is, Index1, N, Val, RestL, RestI)
985            )
986        ).
987
988
989pivot_column([], _, _, _, N, N).
990pivot_column([L|Ls], [I|Is], Coeff0, Index0, N0, N) :-
991        (   I =:= 0 -> Coeff1 = Coeff0, N1 = N0
992        ;   (   L < Coeff0 -> Coeff1 = L, N1 = Index0
993            ;   Coeff1 = Coeff0, N1 = N0
994            )
995        ),
996        Index1 is Index0 + 1,
997        pivot_column(Ls, Is, Coeff1, Index1, N1, N).
998
999
1000pivot_row(Tableau, PCol, PRow) :-
1001        tableau_rows(Tableau, Rows),
1002        pivot_row(Rows, PCol, false, _, 0, 0, PRow).
1003
1004pivot_row([], _, Bounded, _, _, Row, Row) :- Bounded.
1005pivot_row([Row|Rows], PCol, Bounded0, Min0, Index0, PRow0, PRow) :-
1006        Row = row(_Var, Left, B),
1007        nth0(PCol, Left, Ae),
1008        (   Ae > 0 ->
1009            Bounded1 = true,
1010            Bound is B rdiv Ae,
1011            (   Bounded0 ->
1012                (   Bound < Min0 -> Min1 = Bound, PRow1 = Index0
1013                ;   Min1 = Min0, PRow1 = PRow0
1014                )
1015            ;   Min1 = Bound, PRow1 = Index0
1016            )
1017        ;   Bounded1 = Bounded0, Min1 = Min0, PRow1 = PRow0
1018        ),
1019        Index1 is Index0 + 1,
1020        pivot_row(Rows, PCol, Bounded1, Min1, Index1, PRow1, PRow).
1021
1022
1023row_divide(row(Var, Left0, Right0), Div, row(Var, Left, Right)) :-
1024        all_divide(Left0, Div, Left),
1025        Right is Right0 rdiv Div.
1026
1027
1028all_divide([], _, []).
1029all_divide([R|Rs], Div, [DR|DRs]) :-
1030        DR is R rdiv Div,
1031        all_divide(Rs, Div, DRs).
1032
1033gauss_elimination([], _, _, []).
1034gauss_elimination([Row0|Rows0], PivotRow, Col, [Row|Rows]) :-
1035        PivotRow = row(PVar, _, _),
1036        Row0 = row(Var, _, _),
1037        (   PVar == Var -> Row = PivotRow
1038        ;   row_eliminate(Row0, PivotRow, Col, Row)
1039        ),
1040        gauss_elimination(Rows0, PivotRow, Col, Rows).
1041
1042row_eliminate(row(Var, Ls0, R0), row(_, PLs, PR), Col, row(Var, Ls, R)) :-
1043        nth0(Col, Ls0, Coeff),
1044        (   Coeff =:= 0 -> Ls = Ls0, R = R0
1045        ;   MCoeff is -Coeff,
1046            all_times_plus([PR|PLs], MCoeff, [R0|Ls0], [R|Ls])
1047        ).
1048
1049all_times_plus([], _, _, []).
1050all_times_plus([A|As], T, [B|Bs], [AT|ATs]) :-
1051        AT is A * T + B,
1052        all_times_plus(As, T, Bs, ATs).
1053
1054all_times([], _, []).
1055all_times([A|As], T, [AT|ATs]) :-
1056        AT is A * T,
1057        all_times(As, T, ATs).
1058
1059simplex(Tableau0, Tableau) :-
1060        (   tableau_optimal(Tableau0) -> Tableau0 = Tableau
1061        ;   pivot_column(Tableau0, PCol),
1062            pivot_row(Tableau0, PCol, PRow),
1063            Tableau0 = tableau(Obj0,Variables,Inds,Matrix0),
1064            nth0(PRow, Matrix0, Row0),
1065            Row0 = row(Leaving, Left0, _Right0),
1066            nth0(PCol, Left0, PivotElement),
1067            row_divide(Row0, PivotElement, Row1),
1068            gauss_elimination([Obj0|Matrix0], Row1, PCol, [Obj|Matrix1]),
1069            nth0(PCol, Variables, Entering),
1070            swap_basic(Matrix1, Leaving, Entering, Matrix),
1071            simplex(tableau(Obj,Variables,Inds,Matrix), Tableau)
1072        ).
1073
1074swap_basic([Row0|Rows0], Old, New, Matrix) :-
1075        Row0 = row(Var, Left, Right),
1076        (   Var == Old -> Matrix = [row(New, Left, Right)|Rows0]
1077        ;   Matrix = [Row0|Rest],
1078            swap_basic(Rows0, Old, New, Rest)
1079        ).
1080
1081constraints_variables([]) --> [].
1082constraints_variables([c(_,Left,_)|Cs]) -->
1083        variables(Left),
1084        constraints_variables(Cs).
1085
1086variables([]) --> [].
1087variables([_Coeff*Var|Rest]) --> [Var], variables(Rest).
1088
1089
1090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092
1093/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1094   A dual algorithm ("algorithm alpha-beta" in Papadimitriou and
1095   Steiglitz) is used for transportation and assignment problems. The
1096   arising max-flow problem is solved with Edmonds-Karp, itself a dual
1097   algorithm.
1098- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1099
1100/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1101   An attributed variable is introduced for each node. Attributes:
1102   node: Original name of the node.
1103   edges: arc_to(To,F,Capacity) (F has an attribute "flow") or
1104          arc_from(From,F,Capacity)
1105   parent: used in breadth-first search
1106- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1107
1108arcs([], Assoc, Assoc).
1109arcs([arc(From0,To0,C)|As], Assoc0, Assoc) :-
1110        (   get_assoc(From0, Assoc0, From) -> Assoc1 = Assoc0
1111        ;   put_assoc(From0, Assoc0, From, Assoc1),
1112            put_attr(From, node, From0)
1113        ),
1114        (   get_attr(From, edges, Es) -> true
1115        ;   Es = []
1116        ),
1117        put_attr(F, flow, 0),
1118        put_attr(From, edges, [arc_to(To,F,C)|Es]),
1119        (   get_assoc(To0, Assoc1, To) -> Assoc2 = Assoc1
1120        ;   put_assoc(To0, Assoc1, To, Assoc2),
1121            put_attr(To, node, To0)
1122        ),
1123        (   get_attr(To, edges, Es1) -> true
1124        ;   Es1 = []
1125        ),
1126        put_attr(To, edges, [arc_from(From,F,C)|Es1]),
1127        arcs(As, Assoc2, Assoc).
1128
1129
1130edmonds_karp(Arcs0, Arcs) :-
1131        empty_assoc(E),
1132        arcs(Arcs0, E, Assoc),
1133        get_assoc(s, Assoc, S),
1134        get_assoc(t, Assoc, T),
1135        maximum_flow(S, T),
1136        % fetch attvars before deleting visited edges
1137        term_attvars(S, AttVars),
1138        phrase(flow_to_arcs(S), Ls),
1139        arcs_assoc(Ls, Arcs),
1140        maplist(del_attrs, AttVars).
1141
1142flow_to_arcs(V) -->
1143        (   { get_attr(V, edges, Es) } ->
1144            { del_attr(V, edges),
1145              get_attr(V, node, Name) },
1146            flow_to_arcs_(Es, Name)
1147        ;   []
1148        ).
1149
1150flow_to_arcs_([], _) --> [].
1151flow_to_arcs_([E|Es], Name) -->
1152        edge_to_arc(E, Name),
1153        flow_to_arcs_(Es, Name).
1154
1155edge_to_arc(arc_from(_,_,_), _) --> [].
1156edge_to_arc(arc_to(To,F,C), Name) -->
1157        { get_attr(To, node, NTo),
1158          get_attr(F, flow, Flow) },
1159        [arc(Name,NTo,Flow,C)],
1160        flow_to_arcs(To).
1161
1162arcs_assoc(Arcs, Hash) :-
1163        empty_assoc(E),
1164        arcs_assoc(Arcs, E, Hash).
1165
1166arcs_assoc([], Hs, Hs).
1167arcs_assoc([arc(From,To,F,C)|Rest], Hs0, Hs) :-
1168        (   get_assoc(From, Hs0, As) -> Hs1 = Hs0
1169        ;   put_assoc(From, Hs0, [], Hs1),
1170            empty_assoc(As)
1171        ),
1172        put_assoc(To, As, arc(From,To,F,C), As1),
1173        put_assoc(From, Hs1, As1, Hs2),
1174        arcs_assoc(Rest, Hs2, Hs).
1175
1176
1177/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1178   Strategy: Breadth-first search until we find a free right vertex in
1179   the value graph, then find an augmenting path in reverse.
1180- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1181
1182maximum_flow(S, T) :-
1183        (   augmenting_path([[S]], Levels, T) ->
1184            phrase(augmenting_path(S, T), Path),
1185            Path = [augment(_,First,_)|Rest],
1186            path_minimum(Rest, First, Min),
1187            % format("augmenting path: ~w\n", [Min]),
1188            maplist(augment(Min), Path),
1189            maplist(maplist(clear_parent), Levels),
1190            maximum_flow(S, T)
1191        ;   true
1192        ).
1193
1194clear_parent(V) :- del_attr(V, parent).
1195
1196augmenting_path(Levels0, Levels, T) :-
1197        Levels0 = [Vs|_],
1198        Levels1 = [Tos|Levels0],
1199        phrase(reachables(Vs), Tos),
1200        Tos = [_|_],
1201        (   member(To, Tos), To == T -> Levels = Levels1
1202        ;   augmenting_path(Levels1, Levels, T)
1203        ).
1204
1205reachables([])     --> [].
1206reachables([V|Vs]) -->
1207        { get_attr(V, edges, Es) },
1208        reachables_(Es, V),
1209        reachables(Vs).
1210
1211reachables_([], _)     --> [].
1212reachables_([E|Es], V) -->
1213        reachable(E, V),
1214        reachables_(Es, V).
1215
1216reachable(arc_from(V,F,_), P) -->
1217        (   { \+ get_attr(V, parent, _),
1218              get_attr(F, flow, Flow),
1219              Flow > 0 } ->
1220            { put_attr(V, parent, P-augment(F,Flow,-)) },
1221            [V]
1222        ;   []
1223        ).
1224reachable(arc_to(V,F,C), P) -->
1225        (   { \+ get_attr(V, parent, _),
1226              get_attr(F, flow, Flow),
1227              (   C == inf ; Flow < C )} ->
1228            { ( C == inf -> Diff = inf
1229              ;   Diff is C - Flow
1230              ),
1231              put_attr(V, parent, P-augment(F,Diff,+)) },
1232            [V]
1233        ;   []
1234        ).
1235
1236
1237path_minimum([], Min, Min).
1238path_minimum([augment(_,A,_)|As], Min0, Min) :-
1239        (   A == inf -> Min1 = Min0
1240        ;   Min1 is min(Min0,A)
1241        ),
1242        path_minimum(As, Min1, Min).
1243
1244augment(Min, augment(F,_,Sign)) :-
1245        get_attr(F, flow, Flow0),
1246        flow_(Sign, Flow0, Min, Flow),
1247        put_attr(F, flow, Flow).
1248
1249flow_(+, F0, A, F) :- F is F0 + A.
1250flow_(-, F0, A, F) :- F is F0 - A.
1251
1252augmenting_path(S, V) -->
1253        (   { V == S } -> []
1254        ;   { get_attr(V, parent, V1-Augment) },
1255            [Augment],
1256            augmenting_path(S, V1)
1257        ).
1258
1259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1260
1261naive_init(Supplies, _, Costs, Alphas, Betas) :-
1262        same_length(Supplies, Alphas),
1263        maplist(=(0), Alphas),
1264        lists_transpose(Costs, TCs),
1265        maplist(min_list, TCs, Betas).
1266
1267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268
1269lists_transpose([], []).
1270lists_transpose([L|Ls], Ts) :- foldl(transpose_, L, Ts, [L|Ls], _).
1271
1272transpose_(_, Fs, Lists0, Lists) :-
1273        maplist(list_first_rest, Lists0, Fs, Lists).
1274
1275list_first_rest([L|Ls], L, Ls).
1276
1277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1279   TODO: use attributed variables throughout
1280- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1281
1282
1283%% transportation(+Supplies, +Demands, +Costs, -Transport)
1284%
1285% Solves a transportation problem. Supplies and Demands must be lists
1286% of non-negative integers. Their respective sums must be equal. Costs
1287% is a list of lists representing the cost matrix, where an entry
1288% (_i_,_j_) denotes the integer cost of transporting one unit from _i_
1289% to _j_. A transportation plan having minimum cost is computed and
1290% unified with Transport in the form of a list of lists that
1291% represents the transportation matrix, where element (_i_,_j_)
1292% denotes how many units to ship from _i_ to _j_.
1293
1294transportation(Supplies, Demands, Costs, Transport) :-
1295        length(Supplies, LAs),
1296        length(Demands, LBs),
1297        naive_init(Supplies, Demands, Costs, Alphas, Betas),
1298        network_head(Supplies, 1, SArcs, []),
1299        network_tail(Demands, 1, DArcs, []),
1300        numlist(1, LAs, Sources),
1301        numlist(1, LBs, Sinks0),
1302        maplist(make_sink, Sinks0, Sinks),
1303        append(SArcs, DArcs, Torso),
1304        alpha_beta(Torso, Sources, Sinks, Costs, Alphas, Betas, Flow),
1305        flow_transport(Supplies, 1, Demands, Flow, Transport).
1306
1307flow_transport([], _, _, _, []).
1308flow_transport([_|Rest], N, Demands, Flow, [Line|Lines]) :-
1309        transport_line(Demands, N, 1, Flow, Line),
1310        N1 is N + 1,
1311        flow_transport(Rest, N1, Demands, Flow, Lines).
1312
1313transport_line([], _, _, _, []).
1314transport_line([_|Rest], I, J, Flow, [L|Ls]) :-
1315        (   get_assoc(I, Flow, As), get_assoc(p(J), As, arc(I,p(J),F,_)) -> L = F
1316        ;   L = 0
1317        ),
1318        J1 is J + 1,
1319        transport_line(Rest, I, J1, Flow, Ls).
1320
1321
1322make_sink(N, p(N)).
1323
1324network_head([], _) --> [].
1325network_head([S|Ss], N) -->
1326        [arc(s,N,S)],
1327        { N1 is N + 1 },
1328        network_head(Ss, N1).
1329
1330network_tail([], _) --> [].
1331network_tail([D|Ds], N) -->
1332        [arc(p(N),t,D)],
1333        { N1 is N + 1 },
1334        network_tail(Ds, N1).
1335
1336network_connections([], _, _, _) --> [].
1337network_connections([A|As], Betas, [Cs|Css], N) -->
1338        network_connections(Betas, Cs, A, N, 1),
1339        { N1 is N + 1 },
1340        network_connections(As, Betas, Css, N1).
1341
1342network_connections([], _, _, _, _) --> [].
1343network_connections([B|Bs], [C|Cs], A, N, PN) -->
1344        (   { C =:= A + B } -> [arc(N,p(PN),inf)]
1345        ;   []
1346        ),
1347        { PN1 is PN + 1 },
1348        network_connections(Bs, Cs, A, N, PN1).
1349
1350alpha_beta(Torso, Sources, Sinks, Costs, Alphas, Betas, Flow) :-
1351        network_connections(Alphas, Betas, Costs, 1, Cons, []),
1352        append(Torso, Cons, Arcs),
1353        edmonds_karp(Arcs, MaxFlow),
1354        mark_hashes(MaxFlow, MArcs, MRevArcs),
1355        all_markable(MArcs, MRevArcs, Markable),
1356        mark_unmark(Sources, Markable, MarkSources, UnmarkSources),
1357        (   MarkSources == [] -> Flow = MaxFlow
1358        ;   mark_unmark(Sinks, Markable, MarkSinks0, UnmarkSinks0),
1359            maplist(un_p, MarkSinks0, MarkSinks),
1360            maplist(un_p, UnmarkSinks0, UnmarkSinks),
1361            MarkSources = [FirstSource|_],
1362            UnmarkSinks = [FirstSink|_],
1363            theta(FirstSource, FirstSink, Costs, Alphas, Betas, TInit),
1364            theta(MarkSources, UnmarkSinks, Costs, Alphas, Betas, TInit, Theta),
1365            duals_add(MarkSources, Alphas, Theta, Alphas1),
1366            duals_add(UnmarkSinks, Betas, Theta, Betas1),
1367            Theta1 is -Theta,
1368            duals_add(UnmarkSources, Alphas1, Theta1, Alphas2),
1369            duals_add(MarkSinks, Betas1, Theta1, Betas2),
1370            alpha_beta(Torso, Sources, Sinks, Costs, Alphas2, Betas2, Flow)
1371        ).
1372
1373mark_hashes(MaxFlow, Arcs, RevArcs) :-
1374        assoc_to_list(MaxFlow, FlowList),
1375        maplist(un_arc, FlowList, FlowList1),
1376        flatten(FlowList1, FlowList2),
1377        empty_assoc(E),
1378        mark_arcs(FlowList2, E, Arcs),
1379        mark_revarcs(FlowList2, E, RevArcs).
1380
1381un_arc(_-Ls0, Ls) :-
1382        assoc_to_list(Ls0, Ls1),
1383        maplist(un_arc_, Ls1, Ls).
1384
1385un_arc_(_-Ls, Ls).
1386
1387mark_arcs([], Arcs, Arcs).
1388mark_arcs([arc(From,To,F,C)|Rest], Arcs0, Arcs) :-
1389        (   get_assoc(From, Arcs0, As) -> true
1390        ;   As = []
1391        ),
1392        (   C == inf -> As1 = [To|As]
1393        ;   F < C -> As1 = [To|As]
1394        ;   As1 = As
1395        ),
1396        put_assoc(From, Arcs0, As1, Arcs1),
1397        mark_arcs(Rest, Arcs1, Arcs).
1398
1399mark_revarcs([], Arcs, Arcs).
1400mark_revarcs([arc(From,To,F,_)|Rest], Arcs0, Arcs) :-
1401        (   get_assoc(To, Arcs0, Fs) -> true
1402        ;   Fs = []
1403        ),
1404        (   F > 0 -> Fs1 = [From|Fs]
1405        ;   Fs1 = Fs
1406        ),
1407        put_assoc(To, Arcs0, Fs1, Arcs1),
1408        mark_revarcs(Rest, Arcs1, Arcs).
1409
1410
1411un_p(p(N), N).
1412
1413duals_add([], Alphas, _, Alphas).
1414duals_add([S|Ss], Alphas0, Theta, Alphas) :-
1415        add_to_nth(1, S, Alphas0, Theta, Alphas1),
1416        duals_add(Ss, Alphas1, Theta, Alphas).
1417
1418add_to_nth(N, N, [A0|As], Theta, [A|As]) :- !,
1419        A is A0 + Theta.
1420add_to_nth(N0, N, [A|As0], Theta, [A|As]) :-
1421        N1 is N0 + 1,
1422        add_to_nth(N1, N, As0, Theta, As).
1423
1424
1425theta(Source, Sink, Costs, Alphas, Betas, Theta) :-
1426        nth1(Source, Costs, Row),
1427        nth1(Sink, Row, C),
1428        nth1(Source, Alphas, A),
1429        nth1(Sink, Betas, B),
1430        Theta is (C - A - B) rdiv 2.
1431
1432theta([], _, _, _, _, Theta, Theta).
1433theta([Source|Sources], Sinks, Costs, Alphas, Betas, Theta0, Theta) :-
1434        theta_(Sinks, Source, Costs, Alphas, Betas, Theta0, Theta1),
1435        theta(Sources, Sinks, Costs, Alphas, Betas, Theta1, Theta).
1436
1437theta_([], _, _, _, _, Theta, Theta).
1438theta_([Sink|Sinks], Source, Costs, Alphas, Betas, Theta0, Theta) :-
1439        theta(Source, Sink, Costs, Alphas, Betas, Theta1),
1440        Theta2 is min(Theta0, Theta1),
1441        theta_(Sinks, Source, Costs, Alphas, Betas, Theta2, Theta).
1442
1443
1444mark_unmark(Nodes, Hash, Mark, Unmark) :-
1445        mark_unmark(Nodes, Hash, Mark, [], Unmark, []).
1446
1447mark_unmark([], _, Mark, Mark, Unmark, Unmark).
1448mark_unmark([Node|Nodes], Markable, Mark0, Mark, Unmark0, Unmark) :-
1449        (   memberchk(Node, Markable) ->
1450            Mark0 = [Node|Mark1],
1451            Unmark0 = Unmark1
1452        ;   Mark0 = Mark1,
1453            Unmark0 = [Node|Unmark1]
1454        ),
1455        mark_unmark(Nodes, Markable, Mark1, Mark, Unmark1, Unmark).
1456
1457all_markable(Flow, RevArcs, Markable) :-
1458        phrase(markable(s, [], _, Flow, RevArcs), Markable).
1459
1460all_markable([], Visited, Visited, _, _) --> [].
1461all_markable([To|Tos], Visited0, Visited, Arcs, RevArcs) -->
1462        (   { memberchk(To, Visited0) } -> { Visited0 = Visited1 }
1463        ;   markable(To, [To|Visited0], Visited1, Arcs, RevArcs)
1464        ),
1465        all_markable(Tos, Visited1, Visited, Arcs, RevArcs).
1466
1467markable(Current, Visited0, Visited, Arcs, RevArcs) -->
1468        { (   Current = p(_) ->
1469              (   get_assoc(Current, RevArcs, Fs) -> true
1470              ;   Fs = []
1471              )
1472          ;   (   get_assoc(Current, Arcs, Fs) -> true
1473              ;   Fs = []
1474              )
1475          ) },
1476        [Current],
1477        all_markable(Fs, [Current|Visited0], Visited, Arcs, RevArcs).
1478
1479/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1480   solve(File) -- read input from File.
1481
1482   Format (NS = number of sources, ND = number of demands):
1483
1484   NS
1485   ND
1486   S1 S2 S3 ...
1487   D1 D2 D3 ...
1488   C11 C12 C13 ...
1489   C21 C22 C23 ...
1490   ... ... ... ...
1491- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1492
1493input(Ss, Ds, Costs) -->
1494        integer(NS),
1495        integer(ND),
1496        n_integers(NS, Ss),
1497        n_integers(ND, Ds),
1498        n_kvectors(NS, ND, Costs).
1499
1500n_kvectors(0, _, []) --> !.
1501n_kvectors(N, K, [V|Vs]) -->
1502        n_integers(K, V),
1503        { N1 is N - 1 },
1504        n_kvectors(N1, K, Vs).
1505
1506n_integers(0, []) --> !.
1507n_integers(N, [I|Is]) --> integer(I), { N1 is N - 1 }, n_integers(N1, Is).
1508
1509
1510number([D|Ds]) --> digit(D), number(Ds).
1511number([D])    --> digit(D).
1512
1513digit(D) --> [D], { between(0'0, 0'9, D) }.
1514
1515integer(N) --> number(Ds), !, ws, { name(N, Ds) }.
1516
1517ws --> [W], { W =< 0' }, !, ws.  % closing quote for syntax highlighting: '
1518ws --> [].
1519
1520solve(File) :-
1521        time((phrase_from_file(input(Supplies, Demands, Costs), File),
1522              transportation(Supplies, Demands, Costs, Matrix),
1523              maplist(print_row, Matrix))),
1524        halt.
1525
1526print_row(R) :- maplist(print_row_, R), nl.
1527
1528print_row_(N) :- format("~w ", [N]).
1529
1530
1531% ?- call_residue_vars(transportation([12,7,14], [3,15,9,6], [[20,50,10,60],[70,40,60,30],[40,80,70,40]], Ms), Vs).
1532%@ Ms = [[0, 3, 9, 0], [0, 7, 0, 0], [3, 5, 0, 6]],
1533%@ Vs = [].
1534
1535
1536%?- call_residue_vars(simplex:solve('instance_80_80.txt'), Vs).
1537
1538%?- call_residue_vars(simplex:solve('instance_3_4.txt'), Vs).
1539
1540%?- call_residue_vars(simplex:solve('instance_100_100.txt'), Vs).
1541
1542
1543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1544
1545%% assignment(+Cost, -Assignment)
1546%
1547%  Solves a linear assignment problem. Cost is a list of lists
1548%  representing the quadratic cost matrix, where element (i,j) denotes
1549%  the integer cost of assigning entity $i$ to entity $j$. An
1550%  assignment with minimal cost is computed and unified with
1551%  Assignment as a list of lists, representing an adjacency matrix.
1552
1553
1554% Assignment problem - for now, reduce to transportation problem
1555assignment(Costs, Assignment) :-
1556        length(Costs, LC),
1557        length(Supply, LC),
1558        all_one(Supply),
1559        transportation(Supply, Supply, Costs, Assignment).
1560
1561/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1562   Messages
1563- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1564
1565:- multifile prolog:message//1.
1566
1567prolog:message(simplex(bounded)) -->
1568        ['Using library(simplex) with bounded arithmetic may yield wrong results.'-[]].
1569
1570warn_if_bounded_arithmetic :-
1571        (   current_prolog_flag(bounded, true) ->
1572            print_message(warning, simplex(bounded))
1573        ;   true
1574        ).
1575
1576:- initialization(warn_if_bounded_arithmetic).
1577