diff: lpQ - new module for linear programming over Q
Vanessa Joy TEAGUE
vjteag at students.cs.mu.OZ.AU
Fri Mar 13 08:21:14 AEDT 1998
Hi Tom,
Could you review this please?
Estimated hours taken: 240
Add some functions to lp and change number representation to rationals.
compiler/lpQ.m:
Change the constraint solver so that it gives different
responses for inconsistent constraints and constraints for
which the objective function isn't bounded.
Add a predicate for Fourier-Motzkin elimination, which
projects a set of linear inequalities onto a subset of
the variables in the inequalities.
Add a predicate for calculating the convex hull of two
polyhedra.
Add a test for whether a linear inequality is entailed
by a conjunction of other linear inequalities.
Add a widening predicate, which takes two polyhedra,
represented by sets of linear inequalities, and returns
the inequalities in the first polyhedron which are
entailed by the second.
compiler/rat.m:
Implement a rational number type, predicates for comparison
of rats, functions for arithmetic operations on rats and
simplification of rats using greatest common divisor.
%-----------------------------------------------------------------------------%
% Copyright (C) 1997-1998 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% file: lpQ.m
% main author: conway, Oct 1997
% rewrite by vjteag, Feb 1998
%
% This module implements:
%
% A linear constraint solver that finds an optimal solution to a set of
% linear [in]equalities with respect to some objective function. It does
% this using the simplex method.
%
% The form of an [in]equation is
% a1.x1 + a2.x2 + ... + an.xn {=<,=,>=} b
% where all the numbers are rats, a variable xn may occur multiple
% times in an equation (or the objective function) - the solver simplifies
% all the inequations.
% By defaut, there is an additional constraint on each of the `xn's:
% xn >= 0
% If you want xn to take on any value, you can include it in the list
% of URS (UnRestricted in Sign) variables.
%
% The objective function is simply a weighted sum of the variables.
%
% The `x's are represented by `term__var's. The varset from which
% they are allocated is passed to the solver because it needs to
% introduce new variables as part of the solving algorithm.
%
%
% Fourier-Motzkin elimination, which takes a polyhedron represented by
% a list of inequations (represented in the same way as in the
% constraint solver) and projects it onto a subset of the variables
% contained in the inequations.
%
%
% A convex hull algorithm, which takes a list of polyhedrons (represented
% by lists of inequations), and computes the smallest convex set which is
% a superset of all of them.
% For details of the algorithm, see Benoy and King.
%
%
% A test for entailment of one constraint (a single inequality) by a
% conjunction of other inequalities (a polyhedron). This uses the constraint
% solver to find the point on the polyhedron closest to the boundary of the
% constraint, then checks whether it is on the correct side of the boundary.
% It assumes that all variables are non-negative.
%
% A widening algorithm, which takes two polyhedra and retains only the
% inequalities in the first polyhedron which are entailed by the second.
%
%------------------------------------------------------------------------------%
:- module lpQ.
:- interface.
%------------------------------------------------------------------------------%
:- import_module rat, list, map, std_util, term, varset.
:- type coeff == pair(var, rat).
:- type equation
---> eqn(list(coeff), operator, rat).
:- type operator
---> (=<) ; (=) ; (>=) .
:- type equations == list(equation).
:- type objective == list(coeff).
:- type direction
---> max ; min .
:- type lpQ__result
---> unbounded
; inconsistent
; satisfiable(rat, map(var, rat))
.
:- type vector ---> pair(map(var, rat), rat).
:- type matrix == list(vector).
%------------------------------------------------------------------------------%
% lp_solve(Inequations, MaxOrMin, Objective, Varset, URSVars,
% Result)
% maximize (or minimize - depending on `MaxOrMin') `Objective'
% subject to the constraints `Inequations'. The variables in
% the objective and inequations are from `Varset' which is passed
% so that the solver can allocate fresh variables as required.
% URSVars is the list of variable that are unrestricted in sign.
% lp_solve binds Result to `unbounded' if the the objective
% function is not bounded by the constraints, to 'inconsistent'
% if the given constraints are inconsistent, or to
% `satisfiable(ObjVal, MapFromObjVarsToVals)'.
:- pred lp_solve(equations, direction, objective, varset, list(var),
lpQ__result).
:- mode lp_solve(in, in, in, in, in, out) is det.
:- pred project(equations, list(var), equations).
:- mode project(in, in, out) is det.
:- pred convex_hull(list(equations), equations, varset, varset).
:- mode convex_hull(in, out, in, out) is det.
:- pred widen(equations, equations, varset, equations).
:- mode widen(in, in, in, out) is det.
:- pred entailed(equation, equations, varset).
:- mode entailed(in, in, in) is semidet.
:- pred is_false(equation).
:- mode is_false(in) is semidet.
:- pred false_eqn(equation).
:- mode false_eqn(out) is det.
% Exported for testing purposes.
:- pred eqns_to_vectors(equations, list(vector)).
:- mode eqns_to_vectors(in, out) is det.
:- pred fm_standardize_equations(equations, equations).
:- mode fm_standardize_equations(in, out) is det.
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
:- implementation.
:- import_module bool, int, io, require, set, string, array.
:- type lp_info
---> lp(
varset,
map(var, pair(var)), % map from variables with URS to
% the corresponding pair of variables
% that represent that variable in
% the standard form (x = x' - x'',
% x', x'' >= 0).
list(var), % slack variables
list(var) % artificial variables
).
is_false(eqn([], (>=), Const)) :-
Const > zero.
is_false(eqn([], (=<), Const)) :-
Const < zero.
is_false(eqn([], (=), Const)) :-
not (Const = zero).
false_eqn(eqn([], (=<), rat(-1,1))).
lp_solve(Eqns, Dir, Obj, Varset, URSVars, Result) :-
lp_info_init(Varset, URSVars, Info0),
lp_solve2(Eqns, Dir, Obj, Result, Info0, _).
%
% lp_solve2(Eqns, Dir, Obj, Res, LPInfo0, LPInfo) takes a list
% of inequations `Eqns', a direction for optimization `Dir', an objective
% function `Obj', an I/O state `IO0' and an lp_info structure `LPInfo0'.
% See inline comments for details on the algorithm.
%
:- pred lp_solve2(equations, direction, objective, lpQ__result,
lp_info, lp_info).
:- mode lp_solve2(in, in, in, out, in, out) is det.
lp_solve2(Eqns0, Dir, Obj0, Result, Info0, Info) :-
% simplify the inequations and convert them
% to standard form by introducing slack/excess/
% artificial variables. We also expand URS variables
% by replacing them with the difference of two
% fresh variables.
standardize_equations(Eqns0, Eqns, Info0, Info1),
% If we're maximizing the objective function then we need
% to negate all the coefficients in the objective.w
(
Dir = max,
negate_equation(eqn(Obj0, (=), zero), eqn(Obj1, _, _))
;
Dir = min,
Obj1 = Obj0
),
simplify_coeffs(Obj1, Obj2),
get_urs_vars(URS, Info1, _),
expand_urs_vars(Obj2, URS, Obj),
list__length(Eqns, Rows),
collect_vars(Eqns, Obj, Vars),
set__to_sorted_list(Vars, VarList),
list__length(VarList, Cols),
map__init(VarNums0),
number_vars(VarList, 0, VarNums0, VarNums),
get_art_vars(ArtVars, Info1, Info),
init_tableau(Rows, Cols, VarNums, URS, Tableau0),
insert_equations(Eqns, 1, Cols, VarNums,
Tableau0, Tableau1),
(
ArtVars = [_|_],
% There are one or more artificial variables, so we use
% the two-phase method for solving the system.
two_phase(Obj0, Obj, ArtVars, VarNums, Tableau1, Result0)
;
ArtVars = [],
one_phase(Obj0, Obj, VarNums, Tableau1, Result0)
),
(
Dir = max,
Result = Result0
;
Dir = min,
(
Result0 = unbounded,
Result = Result0
;
Result0 = inconsistent,
Result = Result0
;
Result0 = satisfiable(NOptVal, OptCoffs),
OptVal is -NOptVal,
Result = satisfiable(OptVal, OptCoffs)
)
).
%------------------------------------------------------------------------------%
:- pred one_phase(list(coeff), list(coeff), map(var, int), tableau,lpQ__result).
:- mode one_phase(in, in, in, in, out) is det.
one_phase(Obj0, Obj, VarNums, Tableau0, Result) :-
insert_coeffs(Obj, 0, VarNums, Tableau0, Tableau1),
GetObjVar = lambda([V::out] is nondet, (
list__member(X, Obj0),
X = V - _Cof
)),
solutions(GetObjVar, ObjVars),
optimize(ObjVars, Tableau1, _, Result).
%------------------------------------------------------------------------------%
:- pred two_phase(list(coeff), list(coeff), list(var), map(var, int),
tableau, lpQ__result).
:- mode two_phase(in, in, in, in, in, out) is det.
two_phase(Obj0, Obj, ArtVars, VarNums, Tableau0, Result) :-
% Do phase 1:
% minimize the sum of the artificial variables
construct_art_objective(ArtVars, ArtObj),
insert_coeffs(ArtObj, 0, VarNums, Tableau0, Tableau1a),
ensure_zero_obj_coeffs(ArtVars, Tableau1a, Tableau1b),
optimize(ArtVars, Tableau1b, Tableau1c, Res0),
(
Res0 = unbounded,
Result = unbounded
;
Res0 = inconsistent,
Result = inconsistent
;
Res0 = satisfiable(Val, _ArtRes),
( Val \= zero ->
Result = inconsistent
;
fix_basis_and_rem_cols(ArtVars, Tableau1c, Tableau2),
% Do phase 2:
% insert the real objective,
% zero the objective coefficients of the
% basis variables,
% optimize the objective.
insert_coeffs(Obj, 0, VarNums, Tableau2, Tableau3),
get_basis_vars(Tableau3, BasisVars),
ensure_zero_obj_coeffs(BasisVars,
Tableau3, Tableau4),
GetObjVar = lambda([V::out] is nondet, (
list__member(X, Obj0),
X = V - _Cof
)),
solutions(GetObjVar, ObjVars),
optimize(ObjVars, Tableau4, _, Result)
)
).
%------------------------------------------------------------------------------%
:- pred construct_art_objective(list(var), list(coeff)).
:- mode construct_art_objective(in, out) is det.
construct_art_objective([], []).
construct_art_objective([V|Vs], [V - (one)|Rest]) :-
construct_art_objective(Vs, Rest).
%------------------------------------------------------------------------------%
:- pred standardize_equations(equations, equations, lp_info, lp_info).
:- mode standardize_equations(in, out, in, out) is det.
standardize_equations(Eqns0, Eqns) -->
list__map_foldl(standardize_equation, Eqns0, Eqns).
% standardize_equation peforms the following operations on an
% equation:
% - ensures the constant is >= 0 (multiplying by -1 if
% necessary)
% - introduces slack, excess and artificial variables
% - replace the URS variables with their corresponding
% difference pair
:- pred standardize_equation(equation, equation, lp_info, lp_info).
:- mode standardize_equation(in, out, in, out) is det.
standardize_equation(Eqn0, Eqn) -->
{ Eqn0 = eqn(Coeffs0, (=<), Const0) },
( { Const0 < zero } ->
{ negate_equation(Eqn0, Eqn1) },
standardize_equation(Eqn1, Eqn)
;
new_slack_var(Var),
{ Coeffs = [Var - one|Coeffs0] },
{ simplify(eqn(Coeffs, (=<), Const0), Eqn1) },
get_urs_vars(URS),
{ expand_urs_vars_e(Eqn1, URS, Eqn) }
).
standardize_equation(Eqn0, Eqn) -->
{ Eqn0 = eqn(Coeffs0, (=), Const0) },
( { Const0 < zero } ->
{ negate_equation(Eqn0, Eqn1) },
standardize_equation(Eqn1, Eqn)
;
new_art_var(Var),
{ Coeffs = [Var - one|Coeffs0] },
{ simplify(eqn(Coeffs, (=<), Const0), Eqn1) },
get_urs_vars(URS),
{ expand_urs_vars_e(Eqn1, URS, Eqn) }
).
standardize_equation(Eqn0, Eqn) -->
{ Eqn0 = eqn(Coeffs0, (>=), Const0) },
( { Const0 < zero } ->
{ negate_equation(Eqn0, Eqn1) },
standardize_equation(Eqn1, Eqn)
;
new_slack_var(SVar),
new_art_var(AVar),
{ Coeffs = [SVar - (-one), AVar - (one)|Coeffs0] },
{ simplify(eqn(Coeffs, (>=), Const0), Eqn1) },
get_urs_vars(URS),
{ expand_urs_vars_e(Eqn1, URS, Eqn) }
).
:- pred negate_equation(equation, equation).
:- mode negate_equation(in, out) is det.
negate_equation(eqn(Coeffs0, Op0, Const0), eqn(Coeffs, Op, Const)) :-
(
Op0 = (=<), Op = (>=)
;
Op0 = (=), Op = (=)
;
Op0 = (>=), Op = (=<)
),
Neg = lambda([Pair0::in, Pair::out] is det, (
Pair0 = V - X0,
X is -X0,
Pair = V - X
)),
list__map(Neg, Coeffs0, Coeffs),
Const is -Const0.
:- pred simplify(equation, equation).
:- mode simplify(in, out) is det.
simplify(eqn(Coeffs0, Op, Const), eqn(Coeffs, Op, Const)) :-
simplify_coeffs(Coeffs0, Coeffs).
:- pred simplify_coeffs(list(coeff), list(coeff)).
:- mode simplify_coeffs(in, out) is det.
simplify_coeffs(Coeffs0, Coeffs) :-
map__init(CoeffMap0),
AddCoeff = lambda([Pair::in, Map0::in, Map::out] is det, (
Pair = Var - Coeff,
add_var(Map0, Var, Coeff, Map)
)),
list__foldl(AddCoeff, Coeffs0, CoeffMap0, CoeffMap),
map__to_assoc_list(CoeffMap, Coeffs).
:- pred add_var(map(var, rat), var, rat, map(var, rat)).
:- mode add_var(in, in, in, out) is det.
add_var(Map0, Var, Coeff, Map) :-
( map__search(Map0, Var, Acc0) ->
Acc1 = Acc0
;
Acc1 = zero
),
Acc is Acc1 + Coeff,
map__set(Map0, Var, Acc, Map).
:- pred expand_urs_vars_e(equation, map(var, pair(var)), equation).
:- mode expand_urs_vars_e(in, in, out) is det.
expand_urs_vars_e(eqn(Coeffs0, Op, Const), Vars, eqn(Coeffs, Op, Const)) :-
expand_urs_vars(Coeffs0, Vars, Coeffs).
:- pred expand_urs_vars(list(coeff), map(var, pair(var)), list(coeff)).
:- mode expand_urs_vars(in, in, out) is det.
expand_urs_vars(Coeffs0, Vars, Coeffs) :-
expand_urs_vars(Coeffs0, Vars, [], Coeffs1),
list__reverse(Coeffs1, Coeffs).
:- pred expand_urs_vars(list(coeff), map(var, pair(var)),
list(coeff), list(coeff)).
:- mode expand_urs_vars(in, in, in, out) is det.
expand_urs_vars([], _Vars, Coeffs, Coeffs).
expand_urs_vars([Var - Coeff|Rest], Vars, Coeffs0, Coeffs) :-
( map__search(Vars, Var, PVar - NVar) ->
NCoeff is -Coeff,
Coeffs1 = [NVar - NCoeff, PVar - Coeff|Coeffs0]
;
Coeffs1 = [Var - Coeff|Coeffs0]
),
expand_urs_vars(Rest, Vars, Coeffs1, Coeffs).
%------------------------------------------------------------------------------%
:- pred collect_vars(equations, objective, set(var)).
:- mode collect_vars(in, in, out) is det.
collect_vars(Eqns, Obj, Vars) :-
GetVar = lambda([Var::out] is nondet, (
(
list__member(Eqn, Eqns),
Eqn = eqn(Coeffs, _, _),
list__member(Pair, Coeffs),
Pair = Var - _
;
list__member(Pair, Obj),
Pair = Var - _
)
)),
solutions(GetVar, VarList),
set__list_to_set(VarList, Vars).
:- pred number_vars(list(var), int, map(var, int), map(var, int)).
:- mode number_vars(in, in, in, out) is det.
number_vars([], _, VarNums, VarNums).
number_vars([Var|Vars], N, VarNums0, VarNums) :-
map__det_insert(VarNums0, Var, N, VarNums1),
N1 is N + 1,
number_vars(Vars, N1, VarNums1, VarNums).
:- pred insert_equations(equations, int, int, map(var, int), tableau, tableau).
:- mode insert_equations(in, in, in, in, in, out) is det.
insert_equations([], _, _, _, Tableau, Tableau).
insert_equations([Eqn|Eqns], Row, ConstCol, VarNums, Tableau0, Tableau) :-
Eqn = eqn(Coeffs, _Op, Const),
insert_coeffs(Coeffs, Row, VarNums, Tableau0, Tableau1),
set_index(Tableau1, Row, ConstCol, Const, Tableau2),
Row1 is Row + 1,
insert_equations(Eqns, Row1, ConstCol, VarNums, Tableau2, Tableau).
:- pred insert_coeffs(list(coeff), int, map(var, int), tableau, tableau).
:- mode insert_coeffs(in, in, in, in, out) is det.
insert_coeffs([], _Row, _VarNums, Tableau, Tableau).
insert_coeffs([Coeff|Coeffs], Row, VarNums, Tableau0, Tableau) :-
Coeff = Var - Const,
map__lookup(VarNums, Var, Col),
set_index(Tableau0, Row, Col, Const, Tableau1),
insert_coeffs(Coeffs, Row, VarNums, Tableau1, Tableau).
%------------------------------------------------------------------------------%
:- pred optimize(list(var), tableau, tableau, lpQ__result).
:- mode optimize(in, in, out, out) is det.
optimize(ObjVars, A0, A, Result) :-
simplex(A0, A, Res0),
(
Res0 = no ,
Result = unbounded
;
Res0 = yes,
rhs_col(A, M),
index(A, 0, M, ObjVal),
extract_objective(ObjVars, A, ObjMap),
Result = satisfiable(ObjVal, ObjMap)
).
:- pred extract_objective(list(var), tableau, map(var, rat)).
:- mode extract_objective(in, in, out) is det.
extract_objective(ObjVars, Tab, Res) :-
map__init(Res0),
list__foldl(extract_obj_var(Tab), ObjVars, Res0, Res).
:- pred extract_obj_var(tableau, var, map(var, rat), map(var, rat)).
:- mode extract_obj_var(in, in, in, out) is det.
extract_obj_var(Tab, Var, Map0, Map) :-
urs_vars(Tab, Vars),
( map__search(Vars, Var, Pos - Neg) ->
extract_obj_var2(Tab, Pos, PosVal),
extract_obj_var2(Tab, Neg, NegVal),
Val is PosVal - NegVal
;
extract_obj_var2(Tab, Var, Val)
),
map__set(Map0, Var, Val, Map).
:- pred extract_obj_var2(tableau, var, rat).
:- mode extract_obj_var2(in, in, out) is det.
extract_obj_var2(Tab, Var, Val) :-
var_col(Tab, Var, Col),
GetCell = lambda([Val0::out] is nondet, (
all_rows(Tab, Row),
index(Tab, Row, Col, one),
rhs_col(Tab, RHS),
index(Tab, Row, RHS, Val0)
)),
solutions(GetCell, Solns),
( Solns = [Val1] ->
Val = Val1
;
Val = zero
).
:- pred simplex(tableau, tableau, bool).
:- mode simplex(in, out, out) is det.
simplex(A0, A, Result) :-
AllColumns = all_cols(A0),
MinAgg = lambda([Col::in, Min0::in, Min::out] is det, (
(
Min0 = no,
index(A0, 0, Col, MinVal),
( MinVal < zero ->
Min = yes(Col - MinVal)
;
Min = no
)
;
Min0 = yes(_ - MinVal0),
index(A0, 0, Col, CellVal),
( CellVal < MinVal0 ->
Min = yes(Col - CellVal)
;
Min = Min0
)
)
)),
aggregate(AllColumns, MinAgg, no, MinResult),
(
MinResult = no,
A = A0,
Result = yes
;
MinResult = yes(Q - _Val),
AllRows = all_rows(A0),
MaxAgg = lambda([Row::in, Max0::in, Max::out] is det, (
(
Max0 = no,
index(A0, Row, Q, MaxVal),
( MaxVal > zero ->
rhs_col(A0, RHSC),
index(A0, Row, RHSC, MVal),
require(nz(MaxVal), "simplex: zero
divisor MaxVal"),
CVal is MVal/MaxVal,
Max = yes(Row - CVal)
;
Max = no
)
;
Max0 = yes(_ - MaxVal0),
index(A0, Row, Q, CellVal),
rhs_col(A0, RHSC),
index(A0, Row, RHSC, MVal),
( CellVal =< zero ->
% CellVal = 0 indicates multiple optimal sol'ns
Max = Max0
;
require(nz(CellVal), "simplex: zero
divisor CellVal"),
MaxVal1 is MVal/CellVal,
( MaxVal1 =< MaxVal0 ->
Max = yes(Row - MaxVal1)
;
Max = Max0
)
)
)
)),
aggregate(AllRows, MaxAgg, no, MaxResult),
(
MaxResult = no,
A = A0,
Result = no
;
MaxResult = yes(P - _),
pivot(P, Q, A0, A1),
simplex(A1, A, Result)
)
).
%------------------------------------------------------------------------------%
:- pred ensure_zero_obj_coeffs(list(var), tableau, tableau).
:- mode ensure_zero_obj_coeffs(in, in, out) is det.
ensure_zero_obj_coeffs([], Tableau, Tableau).
ensure_zero_obj_coeffs([V|Vs], Tableau0, Tableau) :-
var_col(Tableau0, V, Col),
index(Tableau0, 0, Col, Val),
( Val = zero ->
ensure_zero_obj_coeffs(Vs, Tableau0, Tableau)
;
FindOne = lambda([P::out] is nondet, (
all_rows(Tableau0, R),
index(Tableau0, R, Col, ValF0),
ValF0 \= zero,
P = R - ValF0
)),
solutions(FindOne, Ones),
(
Ones = [Row - Fac0|_],
require(nz(Fac0), "ensure_zero_obj_coeffs:
zero divisor Fac0"),
Fac is -Val/Fac0,
row_op(Fac, Row, 0, Tableau0, Tableau1),
ensure_zero_obj_coeffs(Vs, Tableau1, Tableau)
;
Ones = [],
error("problem with artificial variable")
)
).
:- pred fix_basis_and_rem_cols(list(var), tableau, tableau).
:- mode fix_basis_and_rem_cols(in, in, out) is det.
fix_basis_and_rem_cols([], Tab, Tab).
fix_basis_and_rem_cols([V|Vs], Tab0, Tab) :-
var_col(Tab0, V, Col),
BasisAgg = lambda([R::in, Ones0::in, Ones::out] is det, (
index(Tab0, R, Col, Val),
( Val = zero ->
Ones = Ones0
;
Ones = [Val - R|Ones0]
)
)),
aggregate(all_rows(Tab0), BasisAgg, [], Res),
(
Res = [one - Row]
->
PivGoal = lambda([Col1::out] is nondet, (
all_cols(Tab0, Col1),
Col \= Col1,
index(Tab0, Row, Col1, Zz),
Zz \= zero
)),
solutions(PivGoal, PivSolns),
(
PivSolns = [],
remove_col(Col, Tab0, Tab0a),
remove_row(Row, Tab0a, Tab1)
;
PivSolns = [Col2|_],
pivot(Row, Col2, Tab0, Tab0a),
remove_col(Col, Tab0a, Tab1)
)
;
Tab1 = Tab0
),
remove_col(Col, Tab1, Tab2),
fix_basis_and_rem_cols(Vs, Tab2, Tab).
%------------------------------------------------------------------------------%
:- type cell ---> cell(int, int).
:- pred pivot(int, int, tableau, tableau).
:- mode pivot(in, in, in, out) is det.
pivot(P, Q, A0, A) :-
index(A0, P, Q, Apq),
MostCells = lambda([Cell::out] is nondet, (
all_rows0(A0, J),
J \= P,
all_cols0(A0, K),
K \= Q,
Cell = cell(J, K)
)),
ScaleCell = lambda([Cell::in, T0::in, T::out] is det, (
Cell = cell(J, K),
index(T0, J, K, Ajk),
index(T0, J, Q, Ajq),
index(T0, P, K, Apk),
require(nz(Apq), "ScaleCell: zero divisor"),
NewAjk is Ajk - Apk * Ajq / Apq,
set_index(T0, J, K, NewAjk, T)
)),
aggregate(MostCells, ScaleCell, A0, A1),
QColumn = lambda([Cell::out] is nondet, (
all_rows0(A1, J),
Cell = cell(J, Q)
)),
Zero = lambda([Cell::in, T0::in, T::out] is det, (
Cell = cell(J, K),
set_index(T0, J, K, zero, T)
)),
aggregate(QColumn, Zero, A1, A2),
PRow = all_cols0(A2),
ScaleRow = lambda([K::in, T0::in, T::out] is det, (
index(T0, P, K, Apk),
require(nz(Apq), "ScaleRow: zero divisor"),
NewApk is Apk / Apq,
set_index(T0, P, K, NewApk, T)
)),
aggregate(PRow, ScaleRow, A2, A3),
set_index(A3, P, Q, one, A).
:- pred row_op(rat, int, int, tableau, tableau).
:- mode row_op(in, in, in, in, out) is det.
row_op(Scale, From, To, A0, A) :-
AllCols = all_cols0(A0),
AddRow = lambda([Col::in, T0::in, T::out] is det, (
index(T0, From, Col, X),
index(T0, To, Col, Y),
Z is Y + (Scale * X),
set_index(T0, To, Col, Z, T)
)),
aggregate(AllCols, AddRow, A0, A).
%------------------------------------------------------------------------------%
:- type tableau
---> tableau(
int,
int,
map(var, int),
map(var, pair(var)),
list(int), % shunned rows
list(int), % shunned cols
map(pair(int), rat)
).
:- pred init_tableau(int::in, int::in, map(var, int)::in,
map(var, pair(var))::in, tableau::out) is det.
init_tableau(Rows, Cols, VarNums, URSVars, Tableau) :-
map__init(Cells),
Tableau = tableau(Rows, Cols, VarNums, URSVars, [], [], Cells).
:- pred index(tableau, int, int, rat).
:- mode index(in, in, in, out) is det.
index(Tableau, J, K, R) :-
Tableau = tableau(_, _, _, _, SR, SC, Cells),
(
( list__member(J, SR)
; list__member(K, SC)
)
->
error("attempt to address shunned row/col")
;
true
),
(
map__search(Cells, J - K, R0)
->
R = R0
;
R = zero
).
:- pred set_index(tableau, int, int, rat, tableau).
:- mode set_index(in, in, in, in, out) is det.
set_index(Tableau0, J, K, R, Tableau) :-
Tableau0 = tableau(Rows, Cols, VarNums, URS, SR, SC, Cells0),
(
( list__member(J, SR)
; list__member(K, SC)
)
->
error("attempt to write shunned row/col")
;
true
),
( R = zero ->
map__delete(Cells0, J - K, Cells)
;
map__set(Cells0, J - K, R, Cells)
),
Tableau = tableau(Rows, Cols, VarNums, URS, SR, SC, Cells).
:- pred rhs_col(tableau, int).
:- mode rhs_col(in, out) is det.
rhs_col(tableau(_, RHS, _, _, _, _, _), RHS).
:- pred all_rows0(tableau, int).
:- mode all_rows0(in, out) is nondet.
all_rows0(Tableau, Row) :-
Tableau = tableau(Rows, _Cols, _, _, SR, _, _),
between(0, Rows, Row),
\+ list__member(Row, SR).
:- pred all_rows(tableau, int).
:- mode all_rows(in, out) is nondet.
all_rows(Tableau, Row) :-
Tableau = tableau(Rows, _Cols, _, _, SR, _, _),
between(1, Rows, Row),
\+ list__member(Row, SR).
:- pred all_cols0(tableau, int).
:- mode all_cols0(in, out) is nondet.
all_cols0(Tableau, Col) :-
Tableau = tableau(_Rows, Cols, _, _, _, SC, _),
between(0, Cols, Col),
\+ list__member(Col, SC).
:- pred all_cols(tableau, int).
:- mode all_cols(in, out) is nondet.
all_cols(Tableau, Col) :-
Tableau = tableau(_Rows, Cols, _, _, _, SC, _),
Cols1 is Cols - 1,
between(0, Cols1, Col),
\+ list__member(Col, SC).
:- pred var_col(tableau, var, int).
:- mode var_col(in, in, out) is det.
var_col(Tableau, Var, Col) :-
Tableau = tableau(_, _, VarCols, _, _, _, _),
map__lookup(VarCols, Var, Col).
:- pred urs_vars(tableau, map(var, pair(var))).
:- mode urs_vars(in, out) is det.
urs_vars(Tableau, URS) :-
Tableau = tableau(_, _, _, URS, _, _, _).
:- pred remove_row(int, tableau, tableau).
:- mode remove_row(in, in, out) is det.
remove_row(R, Tableau0, Tableau) :-
Tableau0 = tableau(Rows, Cols, VarNums, URS, SR, SC, Cells),
Tableau = tableau(Rows, Cols, VarNums, URS, [R|SR], SC, Cells).
:- pred remove_col(int, tableau, tableau).
:- mode remove_col(in, in, out) is det.
remove_col(C, Tableau0, Tableau) :-
Tableau0 = tableau(Rows, Cols, VarNums, URS, SR, SC, Cells),
Tableau = tableau(Rows, Cols, VarNums, URS, SR, [C|SC], Cells).
:- pred get_basis_vars(tableau, list(var)).
:- mode get_basis_vars(in, out) is det.
get_basis_vars(Tab, Vars) :-
BasisCol = lambda([C::out] is nondet, (
all_cols(Tab, C),
NonZeroGoal = lambda([P::out] is nondet, (
all_rows(Tab, R),
index(Tab, R, C, Z),
Z \= zero,
P = R - Z
)),
solutions(NonZeroGoal, Solns),
Solns = [_ - one]
)),
solutions(BasisCol, Cols),
BasisVars = lambda([V::out] is nondet, (
list__member(Col, Cols),
Tab = tableau(_, _, VarCols, _, _, _, _),
map__member(VarCols, V, Col)
)),
solutions(BasisVars, Vars).
%------------------------------------------------------------------------------%
% For debugging ....
:- pred show_tableau(tableau, io__state, io__state).
:- mode show_tableau(in, di, uo) is det.
show_tableau(Tableau) -->
{ Tableau = tableau(N, M, _, _, _, _, _) },
{ string__format("Tableau (%d, %d):\n", [i(N), i(M)], Str) },
io__write_string(Str),
aggregate(all_rows0(Tableau), show_row(Tableau)).
:- pred show_row(tableau, int, io__state, io__state).
:- mode show_row(in, in, di, uo) is det.
show_row(Tableau, Row) -->
aggregate(all_cols0(Tableau), show_cell(Tableau, Row)),
io__write_string("\n").
:- pred show_cell(tableau, int, int, io__state, io__state).
:- mode show_cell(in, in, in, di, uo) is det.
show_cell(Tableau, Row, Col) -->
{ index(Tableau, Row, Col, Val) },
{ string__format("%2.2f\t", [f(float(Val))], Str) },
io__write_string(Str),
io__write(Val).
%------------------------------------------------------------------------------%
:- pred lp_info_init(varset, list(var), lp_info).
:- mode lp_info_init(in, in, out) is det.
lp_info_init(Varset0, URSVars, LPInfo) :-
Introduce = lambda([Orig::in, VP0::in, VP::out] is det, (
VP0 = VS0 - VM0,
varset__new_var(VS0, V1, VS1),
varset__new_var(VS1, V2, VS),
map__set(VM0, Orig, V1 - V2, VM),
VP = VS - VM
)),
map__init(URSMap0),
list__foldl(Introduce, URSVars, Varset0 - URSMap0, Varset - URSMap),
LPInfo = lp(Varset, URSMap, [], []).
:- pred new_slack_var(var::out, lp_info::in, lp_info::out) is det.
new_slack_var(Var) -->
get_varset(Varset0),
{ varset__new_var(Varset0, Var, Varset) },
set_varset(Varset),
get_slack_vars(Vars),
set_slack_vars([Var|Vars]).
:- pred new_art_var(var::out, lp_info::in, lp_info::out) is det.
new_art_var(Var) -->
get_varset(Varset0),
{ varset__new_var(Varset0, Var, Varset) },
set_varset(Varset),
get_art_vars(Vars),
set_art_vars([Var|Vars]).
:- pred get_varset(varset::out, lp_info::in, lp_info::out) is det.
get_varset(Varset, Info, Info) :-
Info = lp(Varset, _URSVars, _Slack, _Art).
:- pred set_varset(varset::in, lp_info::in, lp_info::out) is det.
set_varset(Varset, Info0, Info) :-
Info0 = lp(_Varset, URSVars, Slack, Art),
Info = lp(Varset, URSVars, Slack, Art).
:- pred get_urs_vars(map(var, pair(var))::out, lp_info::in, lp_info::out) is det.
get_urs_vars(URSVars, Info, Info) :-
Info = lp(_Varset, URSVars, _Slack, _Art).
:- pred set_urs_vars(map(var, pair(var))::in, lp_info::in, lp_info::out) is det.
set_urs_vars(URSVars, Info0, Info) :-
Info0 = lp(Varset, _URSVars, Slack, Art),
Info = lp(Varset, URSVars, Slack, Art).
:- pred get_slack_vars(list(var)::out, lp_info::in, lp_info::out) is det.
get_slack_vars(Slack, Info, Info) :-
Info = lp(_Varset, _URSVars, Slack, _Art).
:- pred set_slack_vars(list(var)::in, lp_info::in, lp_info::out) is det.
set_slack_vars(Slack, Info0, Info) :-
Info0 = lp(Varset, URSVars, _Slack, Art),
Info = lp(Varset, URSVars, Slack, Art).
:- pred get_art_vars(list(var)::out, lp_info::in, lp_info::out) is det.
get_art_vars(Art, Info, Info) :-
Info = lp(_Varset, _URSVars, _Slack, Art).
:- pred set_art_vars(list(var)::in, lp_info::in, lp_info::out) is det.
set_art_vars(Art, Info0, Info) :-
Info0 = lp(Varset, URSVars, Slack, _Art),
Info = lp(Varset, URSVars, Slack, Art).
%------------------------------------------------------------------------------%
:- pred between(int, int, int).
:- mode between(in, in, out) is nondet.
between(Min, Max, I) :-
Min =< Max,
(
I = Min
;
Min1 is Min + 1,
between(Min1, Max, I)
).
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
% Fourier-Motzkin elimination.
% project(Equations0, Variables, Equations) takes a list of inequalities
% and eliminates the variables in the input list, i.e. it projects onto
% those variables not in the list.
project(Eqns, [], Eqns).
project(Eqns0, Vars, Eqns) :-
Vars = [_|_],
fm_standardize_equations(Eqns0, Eqns1),
eqns_to_vectors(Eqns1, Vecs),
eliminate_vars(Vars, Vecs, Matrix),
vectors_to_eqns(Matrix, Eqns).
% fm_standardize_equations ensures all inequations are of the form
% a1x1 + ... + anxn =< C. Inequalities containing >= are multiplied
% through by -1, and equations are converted into two inequations
% of the form a1x1 + ... + anxn =< C, -a1x1 - ... - anxn =< -C.
fm_standardize_equations(Eqns0, Eqns1) :-
fm_standardize_equations_acc(Eqns0, [], Eqns1).
:- pred fm_standardize_equations_acc(equations, equations, equations).
:- mode fm_standardize_equations_acc(in, in, out) is det.
fm_standardize_equations_acc([], Acc, Acc).
fm_standardize_equations_acc([Eqn0|Eqns], Acc0, Acc) :-
(
Eqn0 = eqn(_, (=<), _),
Acc1 = [Eqn0|Acc0]
;
Eqn0 = eqn(Coeffs, (=), Const),
Acc1 = [eqn(Coeffs, (=<), Const), Neg_eqn | Acc0],
negate_equation(eqn(Coeffs, (>=), Const), Neg_eqn)
;
Eqn0 = eqn(Coeffs, (>=), Const),
Acc1 = [Neg_eqn | Acc0],
negate_equation(eqn(Coeffs, (>=), Const), Neg_eqn)
),
fm_standardize_equations_acc(Eqns, Acc1, Acc).
eqns_to_vectors(Eqns, Vecs) :-
Eqn_to_vec = lambda([eqn(List, Op, Num)::in,
pair(Map, Num)::out] is det, (
( Op = (=<) ->
map__init(Map0),
coeff_list_to_map(List, Map0, Map)
;
error("Equality or >= passed to eqns_to_vectors\n")
)
)),
list__map(Eqn_to_vec, Eqns, Vecs).
:- pred vectors_to_eqns(list(vector), equations).
:- mode vectors_to_eqns(in, out) is det.
vectors_to_eqns(Vectors, Equations) :-
Vec_to_eqn = lambda([pair(Map,Rat)::in, Eqn::out] is det, (
map__to_assoc_list(Map, List),
Eqn = eqn(List, (=<), Rat)
)),
list__map(Vec_to_eqn, Vectors, Equations).
% Ensures that there are no zeros in the resulting map.
:- pred coeff_list_to_map(list(coeff), map(var, rat), map(var, rat)).
:- mode coeff_list_to_map(in, in, out) is det.
coeff_list_to_map([], Map, Map).
coeff_list_to_map([Var-Num | Coeffs], Map0, Map) :-
( Num = zero ->
Map1 = Map0
;
( map__search(Map0, Var, Num1) ->
( Num1 + Num = zero ->
map__delete(Map0, Var, Map1)
;
map__det_update(Map0, Var, Num+Num1, Map1)
)
;
map__det_insert(Map0, Var, Num, Map1)
)
),
coeff_list_to_map(Coeffs, Map1, Map).
% normalize_vector(Vec0, Var, Vec1) divides all coefficients in
% Vec0 by the absolute value of the coefficient of Var.
% It is an error if the map contains a zero coefficient.
:- pred normalize_vector(vector, var, vector).
:- mode normalize_vector(in, in, out) is det.
normalize_vector(pair(Map0, Num0), Var0, pair(Map, Num)) :-
( map__search(Map0, Var0, Coeff) ->
Is_map_key = lambda([Var::out] is nondet, (
map__member(Map0, Var, _)
)),
Divide_map_val = lambda([Var::in, Map1::in, Map2::out] is det, (
map__lookup(Map1, Var, Coeff0),
( Coeff0 = zero ->
error("Divide by zero in normalize_vector")
;
require(nz(Coeff),
"normalize_vector: zero divisor"),
Coeff1 = Coeff0 / abs(Coeff),
map__det_update(Map1, Var, Coeff1, Map2)
)
)),
aggregate(Is_map_key, Divide_map_val, Map0, Map),
require(nz(Coeff), "normalize_vector: zero divisor"),
Num = Num0 / abs(Coeff)
;
Map = Map0,
Num = Num0
).
% separate_vectors(List1, Variable, Posititves, Negatives, Zeroes):
% Breaks a list of vectors up into three lists of vectors according to
% whether the coefficient of Variable is positive, negative or zero.
% Applies normalize_vector to each vector.
:- pred separate_vectors(matrix, var, matrix, matrix, matrix).
:- mode separate_vectors(in, in, out, out, out) is det.
separate_vectors(Matrix, Var, Pos, Neg, Zero) :-
separate_vectors_acc(Matrix, Var, [], [], [], Pos, Neg, Zero).
:- pred separate_vectors_acc(matrix, var, matrix, matrix,
matrix, matrix, matrix, matrix).
:- mode separate_vectors_acc(in, in, in, in, in, out, out, out) is det.
separate_vectors_acc([], _, Pos, Neg, Zeros, Pos, Neg, Zeros).
separate_vectors_acc([Vec0|Vectors], Var, Pos0, Neg0, Zeros0, Pos, Neg, Zeros):-
( get_var_coeff(Vec0, Var, Coeff) ->
normalize_vector(Vec0, Var, Vec1),
( Coeff > zero ->
Pos1 = [Vec1 | Pos0],
Neg1 = Neg0,
Zeros1 = Zeros0
;
Pos1 = Pos0,
Neg1 = [Vec1 | Neg0],
Zeros1 = Zeros0
)
;
Pos1 = Pos0,
Neg1 = Neg0,
Zeros1 = [Vec0 | Zeros0]
),
separate_vectors_acc(Vectors, Var, Pos1, Neg1, Zeros1, Pos, Neg, Zeros).
% Fails if the variable searched for is not in the vector
% (i.e. if it has coefficient zero).
:- pred get_var_coeff(vector, var, rat).
:- mode get_var_coeff(in, in, out) is semidet.
get_var_coeff(pair(Varmap, _), Var, Num) :-
map__search(Varmap, Var, Num).
:- pred add_vectors(vector, vector, vector).
:- mode add_vectors(in, in, out) is det.
add_vectors(pair(Map0, Float0), pair(Map1, Float1), pair(Map2, Float2)) :-
Float2 = Float0 + Float1,
Is_map_key = lambda([Var::out] is nondet, (
map__member(Map0, Var, _)
)),
Add_val = lambda([Var::in, Map_i::in, Map_o::out] is det, (
map__lookup(Map0, Var, Num0),
( map__search(Map_i, Var, Num1) ->
( Num0 + Num1 = zero ->
map__delete(Map_i, Var, Map_o)
;
map__det_update(Map_i, Var, Num0+Num1, Map_o)
)
;
map__det_insert(Map_i, Var, Num0, Map_o)
)
)),
aggregate(Is_map_key, Add_val, Map1, Map2).
% Eliminates a variable V from three sets of linear inequalities,
% assuming that the inequations in the first list have a positive
% coefficient for V, and that the second and third lists contain negative
% and zero coefficients respectively.
:- pred eliminate_var(var, matrix, matrix, matrix, matrix).
:- mode eliminate_var(in, in, in, in, out) is det.
eliminate_var(_, [], _, Matrix, Matrix).
eliminate_var(Var, [Vec_P|Pos], Neg, Zeros, Result) :-
Add_vec = lambda([Vec0::in, Vec1::out] is semidet, (
add_vectors(Vec_P, Vec0, Vec1),
Vec1 = pair(Map1, Const),
not ( map__is_empty(Map1), Const >= zero )
)),
list__filter_map(Add_vec, Neg, Zeros1),
list__append(Zeros1, Zeros, New_zeros),
eliminate_var(Var, Pos, Neg, New_zeros, Result).
:- pred eliminate_vars(list(var), matrix, matrix).
:- mode eliminate_vars(in, in, out) is det.
eliminate_vars([], Matrix, Matrix).
eliminate_vars([Var|Vars], Matrix, Result) :-
separate_vectors(Matrix, Var, Pos, Neg, Zeros),
eliminate_var(Var, Pos, Neg, Zeros, New_zeros1),
list__sort_and_remove_dups(New_zeros1, New_zeros2),
remove_some_redundancies(New_zeros2, [], New_zeros),
eliminate_vars(Vars, New_zeros, Result).
% Removes all constraints of the form a1x1 + ... + anxn <= A
% where there exists another constraint a1x1 + ... + anxn <= B
% and B <= A.
:- pred remove_some_redundancies(matrix, matrix, matrix).
:- mode remove_some_redundancies(in, in, out) is det.
remove_some_redundancies([], Constraints, Constraints).
remove_some_redundancies([Constr0|Equations0], Constrs, Equations) :-
find_strongest_remove_weaker(Constr0, Constr, Equations0, Equations1),
remove_some_redundancies(Equations1, [Constr|Constrs], Equations).
% Removes all equations in the given list which are
% weaker than the given constraint Vec0. Returns the strongest vector
% which is comparable to Vec0, and the list with the redundant
% constraints removed.
:- pred find_strongest_remove_weaker(vector, vector, matrix, matrix).
:- mode find_strongest_remove_weaker(in, out, in, out) is det.
find_strongest_remove_weaker(Vec0, Vec, Matrix0, Matrix) :-
Comp_constraints = lambda([Vec1::in, Str_vec0::in, Str_vec1::out,
List1::in, List2::out] is det, (
( compare_constraints(Str_vec0, Vec1, Str_vec) ->
( Str_vec = Str_vec0 ->
List2 = List1,
Str_vec1 = Str_vec0
;
List2 = List1,
Str_vec1 = Vec1
)
;
List2 = [Vec1|List1],
Str_vec1 = Str_vec0
)
)),
list__foldl2(Comp_constraints, Matrix0, Vec0, Vec, [], Matrix).
% Fails if neither constraint implies the other.
% Otherwise, returns the stronger constraint.
:- pred compare_constraints(vector, vector, vector).
:- mode compare_constraints(in, in, out) is semidet.
compare_constraints(pair(Map0,Float0), pair(Map1,Float1), pair(Map3,Float)) :-
map__keys(Map0, Keylist),
Del_same_keys = lambda([Var0::in, Map4::in, Map5::out] is semidet, (
map__lookup(Map0, Var0, Num0),
map__remove(Map4, Var0, Num4, Map5),
Num4 = Num0
)),
list__foldl(Del_same_keys, Keylist, Map1, Map2),
map__is_empty(Map2),
Map3 = Map0,
( Float0 < Float1 ->
Float = Float0
;
Float = Float1
).
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
% Convex Hull
:- type var_info
---> var_info(
list(map(var,var)), % Maps from original variables
% to new (temporary) ones.
% A variables which occurs in
% more than one polygon is
% mapped to a separate variable
% for each one.
% This list contains one map
% for each polygon.
list(var), % List of sigma variables.
varset
).
:- type eqn_info
---> eqn_info(
map(var, var),
varset
).
% convex_hull takes a list of polyhedra (represented as lists of constraints)
% and computes the smallest convex set which is a superset of all of them.
% For details of the algorithm, see Benoy and King.
convex_hull(Polys0, Poly, Vars1, Vars) :-
list__map(fm_standardize_equations, Polys0, Polys),
transform_polys(Polys, Eqns1, var_info([], [], Vars1),
var_info(Map_list, Sigmas, Vars)),
add_sigma_eqns(Eqns1, Sigmas, Eqns2),
add_last_eqns(Eqns2, Map_list, Eqns3),
Append_values = lambda([Map::in, Varlist0::in, Varlist::out] is det, (
map__values(Map, List),
append(List, Varlist0, Varlist)
)),
list__foldl(Append_values, Map_list, [], Values),
append(Sigmas, Values, Variables),
project(Eqns3, Variables, Poly).
:- pred transform_polys(list(equations), equations, var_info, var_info).
:- mode transform_polys(in, out, in, out) is det.
transform_polys(Polys, Eqns, Var_info0, Var_info) :-
list__foldl2(transform_poly, Polys, [], Eqns, Var_info0, Var_info).
% transform_poly: takes a polygon (with original variables), a list
% of the polygons transformed so far and variable information.
% It substitutes new (temporary) variables for all of the variables
% in the polygon while constructing a map from old variables to new
% ones. The new polygon is then added to the list of transformed ones.
% It then updates the variable info by adding the new map and a new
% sigma variable.
:- pred transform_poly(equations, equations, equations, var_info, var_info).
:- mode transform_poly(in, in, out, in, out) is det.
transform_poly(Poly, Polys0, Polys, var_info(Maps0, Sigmas0, Vars0), Var_info):-
map__init(Newmap),
varset__new_var(Vars0, Sigma, Vars1),
Trans_eqn = lambda([Eqn0::in, Eqn::out, Eq_inf0::in,
Eq_inf1::out] is det, (
transform_eqn(Eqn0, Eqn, Sigma, Eq_inf0, Eq_inf1)
)),
list__map_foldl(Trans_eqn, Poly, New_eqns, eqn_info(Newmap, Vars1),
eqn_info(Map, Vars)),
append(New_eqns, Polys0, Polys),
Var_info = var_info([Map|Maps0], [Sigma|Sigmas0], Vars).
:- pred transform_eqn(equation, equation, var, eqn_info, eqn_info).
:- mode transform_eqn(in, out, in, in, out) is det.
transform_eqn(eqn(Coeffs0, Op, Num0), eqn(Coeffs, Op, Num), Sigma,
eqn_info(Map0, Vars0), eqn_info(Map, Vars)) :-
list__map_foldl(change_var, Coeffs0, Coeffs1,
eqn_info(Map0, Vars0), eqn_info(Map, Vars)),
Coeffs = [Sigma - -Num0|Coeffs1],
Num = zero.
% change_var: takes a Var-Num pair with an old variable and returns one with
% a new variable which the old variable maps to. Updates the map of old to
% new variables if necessary.
:- pred change_var(coeff, coeff, eqn_info, eqn_info).
:- mode change_var(in, out, in, out) is det.
change_var(Var0-Num, Var-Num, eqn_info(Map0, Varset0), eqn_info(Map, Varset)) :-
% Have we already mapped this original variable to a new one?
( map__search(Map0, Var0, Var2) ->
Var = Var2,
Map = Map0,
Varset = Varset0
;
varset__new_var(Varset0, Nvar, Varset),
map__det_insert(Map0, Var0, Nvar, Map),
Var = Nvar
).
% add_sigma_eqns: Takes the list of equations so far and appends
% the non-negativity equation for each sigma variable,
% and an equation stating that the sum of the sigmas is one.
:- pred add_sigma_eqns(equations, list(var), equations).
:- mode add_sigma_eqns(in, in, out) is det.
add_sigma_eqns(Eqns0, Sigmas, Eqns) :-
Non_neg = lambda([Var::in, Eqn::out] is det, (
Eqn = eqn([Var - -one], (=<), zero)
)),
list__map(Non_neg, Sigmas, Eqns1),
append(Eqns1, Eqns0, Eqns2),
Var_to_coeff = lambda([Var::in, Coeff::out] is det, (
Coeff = Var-one
)),
list__map(Var_to_coeff, Sigmas, Coefflist),
fm_standardize_equations([eqn(Coefflist, (=), one)], Eqns3),
append(Eqns3, Eqns2, Eqns).
% add_last_eqns: Adds the equations stating that each original
% variable is the sum of the temporary variables to which it has
% been mapped.
:- pred add_last_eqns(equations, list(map(var,var)), equations).
:- mode add_last_eqns(in, in, out) is det.
add_last_eqns(Eqns0, Old_to_new_var_maps, Eqns) :-
get_map_keys(Old_to_new_var_maps, [], Keys1),
VarComp = lambda([Var1::in, Var2::in, Res::out] is det, (
compare(Res, Var1, Var2)
)),
list__sort_and_remove_dups(VarComp, Keys1, Keys),
Original_var_to_eqn = lambda([Orig_var::in, Eqn::out] is semidet, (
original_var_to_eqn(Orig_var, Old_to_new_var_maps, Eqn)
)),
list__filter_map(Original_var_to_eqn, Keys, Eqns1),
fm_standardize_equations(Eqns1, Eqns2),
append(Eqns2, Eqns0, Eqns).
:- pred original_var_to_eqn(var, list(map(var,var)), equation).
:- mode original_var_to_eqn(in, in, out) is semidet.
original_var_to_eqn(Original_var, Maplist, Eqn) :-
Corresp_vars = lambda([Map::in, Coeffs0::in, Coeffs::out] is semidet, (
map__search(Map, Original_var, Newvar),
Coeffs = [Newvar - -one|Coeffs0]
)),
list__foldl(Corresp_vars, Maplist, [], Coefflist),
Eqn = eqn([Original_var-one|Coefflist], (=), zero).
:- pred get_map_keys(list(map(var,var)), list(var), list(var)).
:- mode get_map_keys(in, in, out) is det.
get_map_keys([], Keys, Keys).
get_map_keys([Map|Maps], Keys0, Keys) :-
map__keys(Map, Keys1),
append(Keys1, Keys0, NewKeys),
get_map_keys(Maps, NewKeys, Keys).
%-----------------------------------------------------------------------------%
% Widening
% widen(Cs1, Cs2, Cs, Vars): Relaxes the constraints Cs1 by selecting
% those constraints from Cs1 which are implied by Cs2.
widen(Poly1, Poly2, Vars, Wide_poly) :-
Entailed_by_Poly2 = lambda([Constraint::in] is semidet, (
entailed(Constraint, Poly2, Vars)
)),
list__filter(Entailed_by_Poly2, Poly1, Wide_poly).
% entailed(C, Cs, Vars): Determines whether the constraint C is
% implied by the set of constraints Cs.
% Uses the simplex method to find the point P satisfying Cs
% which maximises (or minimises if C contains >= ) a function parallel
% to C. Then tests whether P satisfies C.
% This assumes that all the variables are non-negative.
entailed(eqn(Coeff_list, (=<), Const), Poly, Vars) :-
Objective = Coeff_list,
lp_solve(Poly, max, Objective, Vars, [], Result),
(
Result = satisfiable(Max_val, _),
Max_val =< Const
;
Result = inconsistent,
error("inconsistent polygon passed to entailed")
).
entailed(eqn(Coeff_list, (>=), Const), Poly, Vars) :-
Objective = Coeff_list,
lp_solve(Poly, min, Objective, Vars, [], Result),
(
Result = satisfiable(Min_val, _),
Min_val >= Const
;
Result = inconsistent,
error("inconsistent polygon passed to entails")
).
entailed(eqn(Coeff_list, (=), Const), Poly, Vars) :-
entailed(eqn(Coeff_list, (=<), Const), Poly, Vars),
entailed(eqn(Coeff_list, (>=), Const), Poly, Vars).
:- pred nz(rat::in) is semidet.
nz(X) :- X \= zero.
%-----------------------------------------------------------------------------%
% Copyright (C) 1997-1998 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% file: rat.m
% authors: conway, vjteag Jan 1998.
%
% Implements a rational number type and a set of basic operations on
% rational numbers.
:- module rat.
:- interface.
:- import_module float, int.
:- type rat.
:- pred rat:'<'(rat, rat).
:- mode rat:'<'(in, in) is semidet.
:- pred rat:'>'(rat, rat).
:- mode rat:'>'(in, in) is semidet.
:- pred rat:'=<'(rat, rat).
:- mode rat:'=<'(in, in) is semidet.
:- pred rat:'>='(rat, rat).
:- mode rat:'>='(in, in) is semidet.
:- pred rat:'='(rat, rat).
:- mode rat:'='(in, in) is semidet.
:- func rat(int, int) = rat.
:- func float(rat) = float.
:- func rat:'+'(rat) = rat.
:- func rat:'-'(rat) = rat.
:- func rat:'+'(rat, rat) = rat.
:- func rat:'-'(rat, rat) = rat.
:- func rat:'*'(rat, rat) = rat.
:- func rat:'/'(rat, rat) = rat.
:- func rat:numer(rat) = int.
:- func rat:denom(rat) = int.
:- func rat:abs(rat) = rat.
:- func one = rat.
:- func zero = rat.
:- implementation.
:- import_module require.
:- type rat ---> r(int, int).
rat:'<'(r(An, Ad), r(Bn, Bd)) :-
An*Bd < Bn*Ad.
rat:'>'(r(An, Ad), r(Bn, Bd)) :-
An*Bd > Bn*Ad.
rat:'=<'(r(An, Ad), r(Bn, Bd)) :-
An*Bd =< Bn*Ad.
rat:'>='(r(An, Ad), r(Bn, Bd)) :-
An*Bd >= Bn*Ad.
:- func ratnorm(rat) = rat.
rat(Num, Den) = ratnorm(r(Num, Den)).
rat:'='(r(An, Ad), r(Bn, Bd)) :-
ratnorm(r(An, Ad)) = ratnorm(r(Bn, Bd)).
rat:float(r(Num, Den)) = float:'/'(float:float(Num), float:float(Den)).
one = r(1, 1).
zero = r(0, 1).
rat:'+'(Rat) = Rat.
rat:'-'(r(Num, Den)) = r(-Num, Den).
rat:'+'(r(An, Ad), r(Bn, Bd)) = ratnorm(r(int:'+'(An*Bd, Bn*Ad), Ad*Bd)).
rat:'-'(r(An, Ad), r(Bn, Bd)) = ratnorm(r(int:'-'(An*Bd, Bn*Ad), Ad*Bd)).
rat:'*'(r(An, Ad), r(Bn, Bd)) = ratnorm(r(An*Bn, Ad*Bd)).
rat:'/'(r(An, Ad), r(Bn, Bd)) = Rat :-
( Bn \= 0 ->
Rat = ratnorm(r(An*Bd, Ad*Bn))
;
error("divide by zero")
).
rat:numer(r(Num, _)) = Num.
rat:denom(r(_, Den)) = Den.
rat:abs(Rat) = ( Rat < zero -> rat:'-'(Rat) ; Rat ).
ratnorm(r(Num, Den)) = r(Sgn*iabs(Num)//Gcd, iabs(Den)//Gcd) :-
(
Num > 0,
Den > 0
->
Sgn = 1
;
Num > 0
->
Sgn = -1
;
Den > 0
->
Sgn = -1
;
Sgn = 1
),
( Den = 0 -> error("zero denominator!") ; true),
Gcd0 = gcd(iabs(Num), iabs(Den)),
( Gcd0 = 0 -> error("zero gcd!") ; Gcd = Gcd0 ).
:- func iabs(int) = int.
iabs(X) = ( X < 0 -> -X ; X ).
:- func gcd(int, int) = int.
gcd(A, B) = Res :-
( A = 0 ->
Res = B
; A < 0 ->
error("A < 0")
; B =< 0 ->
error("B =< 0")
;
Res = gcd0(A, B)
).
:- func gcd0(int, int) = int.
gcd0(A, B) = (
A = 1 -> 1
; B = 1 -> 1
; A = B -> A
; A > B -> gcd0(A - B, B)
; gcd0(B - A, A)
).
More information about the developers
mailing list