503 lines
15 KiB
MiniZinc
503 lines
15 KiB
MiniZinc
/*
|
|
% FlatZinc built-in redefinitions for linear solvers.
|
|
%
|
|
% AUTHORS
|
|
% Sebastian Brand
|
|
% Gleb Belov
|
|
*/
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Linear equations and inequations
|
|
% TODO Use indicators for (half)reifs.
|
|
% Otherwise and more using unary encoding for reasonable domains
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Domains: reduce all to aux_ stuff?
|
|
%% never use Concert's reif feature
|
|
|
|
%% var, var
|
|
predicate int_le_reif(var int: x, var int: y, var bool: b) =
|
|
%% trace(" int_le_reif VV: \(x), \(y), \(b) \n") /\
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x<=y else x>y endif
|
|
elseif ub(x)<=lb(y) then b==true
|
|
elseif lb(x)>ub(y) then b==false
|
|
elseif fPostprocessDomains /\ fPostproDom_DIFF then
|
|
int_le_reif__POST(x-y, 0, b)
|
|
else
|
|
int_le_reif__NOPOST(x, y, b)
|
|
endif
|
|
;
|
|
|
|
%% const, var
|
|
predicate int_le_reif(int: x, var int: y, var bool: b) =
|
|
%% trace(" int_le_reif CV: \(x), \(y), \(b) \n") /\
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x<=y else x>y endif
|
|
elseif ub(x)<=lb(y) then b==true
|
|
elseif lb(x)>ub(y) then b==false
|
|
elseif not (x in dom(y)) then %% dom(y) has holes
|
|
let {
|
|
set of int: sDom2 = dom(y) intersect x+1..infinity,
|
|
constraint assert( card( sDom2 ) > 0,
|
|
"Variable: dom(\(y)) = \(dom(y)), but dom() intersect \(x)..inf: \(sDom2)\n" ),
|
|
} in
|
|
b <-> min( sDom2 ) <= y
|
|
elseif fPostprocessDomains then
|
|
int_ge_reif__POST(y, x, b)
|
|
else
|
|
int_le_reif(-y, -x, b)
|
|
endif
|
|
;
|
|
|
|
%% var, const
|
|
predicate int_le_reif(var int: x, int: y, var bool: b) =
|
|
%% trace(" int_le_reif VC: \(x), \(y), \(b) \n") /\
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x<=y else x>y endif
|
|
elseif ub(x)<=lb(y) then b==true
|
|
elseif lb(x)>ub(y) then b==false
|
|
elseif not (y in dom(x)) then %% dom(x) has holes
|
|
let {
|
|
set of int: sDom2 = dom(x) intersect -infinity..y-1,
|
|
constraint assert( card( sDom2 ) > 0,
|
|
"Variable: dom(\(x)) = \(dom(x)), but dom() intersect -inf..\(y): \(sDom2)\n" ),
|
|
} in
|
|
b <-> x <= max( sDom2 )
|
|
else
|
|
if fPostprocessDomains then
|
|
int_le_reif__POST(x, y, b)
|
|
else
|
|
int_le_reif__NOPOST(x, y, b)
|
|
endif
|
|
endif
|
|
;
|
|
|
|
%% var int, var int
|
|
predicate int_le_reif__NOPOST(var int: x, var int: y, var bool: b) =
|
|
aux_int_le_if_1(x, y, b) /\ %% This can call POSTs... TODO
|
|
aux_int_gt_if_0(x, y, b)
|
|
;
|
|
|
|
%% var, var
|
|
predicate int_lt_reif(var int: x, var int: y, var bool: b) =
|
|
%% int_le_reif(x-y, -1, b); %% This would produce a new variable x-y and possibly POST it
|
|
%% but it looks like differences should not be
|
|
if is_fixed( x ) then
|
|
int_le_reif(x+1, y, b)
|
|
else
|
|
int_le_reif(x, y-1, b)
|
|
endif;
|
|
|
|
%% var, var
|
|
predicate int_ne(var int: x, var int: y) =
|
|
if fPostproDom_DIFF then
|
|
int_ne(x-y, 0)
|
|
else
|
|
int_ne__SIMPLE(x-y, 0)
|
|
endif;
|
|
|
|
%% var, const
|
|
predicate int_ne(var int: x, int: y) =
|
|
if y in dom(x) then
|
|
if y==ub(x) then
|
|
x <= y-1
|
|
elseif y==lb(x) then
|
|
x >= y+1
|
|
elseif fPostprocessDomains then
|
|
int_ne__POST(x, y)
|
|
elseif card(dom(x))<nMZN__UnaryLenMax_neq then
|
|
let {
|
|
array[int] of var int: p = eq_encode(x);
|
|
} in
|
|
p[y]==false
|
|
else int_ne__SIMPLE(x, y)
|
|
endif
|
|
else
|
|
true
|
|
endif;
|
|
|
|
%% var, const. No postprocessing
|
|
predicate int_ne__SIMPLE(var int: x, int: y) =
|
|
if true then
|
|
let { var 0..1: p }
|
|
in
|
|
aux_int_lt_if_1(x, y, p) /\
|
|
aux_int_gt_if_0(x, y, p)
|
|
else %TODO: Why is this not half-reified?
|
|
1 == (x<y) + (x>y)
|
|
endif;
|
|
|
|
%% var, var
|
|
predicate int_eq_reif(var int: x, var int: y, var bool: b) =
|
|
%% trace(" int_eq_reif VV: \(x), \(y), \(b) \n") /\
|
|
if is_fixed(b) then
|
|
if fix(b) then x==y else x!=y endif
|
|
elseif card(dom(x) intersect dom(y))>0 then
|
|
if is_fixed(x) then
|
|
if is_fixed(y) then
|
|
b <-> fix(x)==fix(y)
|
|
else
|
|
int_eq_reif(y, fix(x), b)
|
|
endif
|
|
elseif is_fixed(y) then
|
|
int_eq_reif(x, fix(y), b)
|
|
elseif fPostprocessDomains /\ fPostproDom_DIFF then
|
|
int_eq_reif(x-y, 0, b)
|
|
else
|
|
aux_int_eq_iff_1(x, y, b)
|
|
endif
|
|
else
|
|
not b
|
|
endif;
|
|
|
|
%% var, const
|
|
predicate int_eq_reif(var int: x, int: y, var bool: b) =
|
|
%% trace(" int_eq_reif VC: \(x), \(y), \(b) \n") /\
|
|
if is_fixed(b) then
|
|
if fix(b) then x==y else x!=y endif
|
|
elseif y in dom(x) then
|
|
if is_fixed(x) then
|
|
b <-> y==fix(x)
|
|
elseif card(dom(x))==2 then
|
|
x == max(dom(x) diff {y}) + b*(y - max(dom(x) diff {y})) %% This should directly connect b to var 0..1: x
|
|
elseif fPostprocessDomains then
|
|
int_eq_reif__POST(x, y, b)
|
|
%%% THIS seems pretty complex, especially for binaries, and does not connect to eq_encoding (/ MIPD?)
|
|
%% elseif y==lb(x) then
|
|
%% int_lt_reif(y, x, not b)
|
|
%% elseif y==ub(x) then
|
|
%% int_lt_reif(x, y, not b)
|
|
elseif card(dom(x))<nMZN__UnaryLenMax_eq then
|
|
let {
|
|
array[int] of var int: p = eq_encode(x);
|
|
} in
|
|
p[y]==b
|
|
else
|
|
aux_int_eq_iff_1(x, y, bool2int(b))
|
|
endif
|
|
else
|
|
b==false
|
|
endif;
|
|
|
|
|
|
predicate int_ne_reif(var int: x, var int: y, var bool: b) =
|
|
int_eq_reif(x, y, not b);
|
|
|
|
predicate int_ne_reif(var int: x, int: y, var bool: b) =
|
|
int_eq_reif(x, y, not b);
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%% To avoid creating a new int var = sum...
|
|
function var float: sum2float( array[int] of int: c, array[int] of var int: x ) =
|
|
let {
|
|
array[int] of float: cF = c;
|
|
array[int] of var float: xF = x;
|
|
} in
|
|
sum(i in index_set(xF))( cF[i]*xF[i] );
|
|
|
|
%% lin_expr, const
|
|
predicate int_lin_le_reif(array[int] of int: c, array[int] of var int: x,
|
|
int: d, var bool: b) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
int_le_reif(sum(i in index_set(x))( c[i] * x[i] ), d, b)
|
|
elseif fAvoidNI then
|
|
aux_float_le_if_1(sum2float( c, x ), d, b) /\
|
|
aux_float_ge_if_0(sum2float( c, x ), d+1, b)
|
|
else
|
|
int_le_reif__NOPOST(sum(i in index_set(x))( c[i] * x[i] ), d, b)
|
|
endif;
|
|
|
|
|
|
predicate int_lin_lt_reif(array[int] of int: c, array[int] of var int: x,
|
|
int: d, var bool: b) =
|
|
if true then
|
|
abort("int_lin_lt_reif not supposed to be called")
|
|
else
|
|
int_lin_le_reif(c, x, d - 1, b)
|
|
endif;
|
|
|
|
%% lin_expr, const
|
|
predicate int_lin_eq_reif(array[int] of int: c, array[int] of var int: x,
|
|
int: d, var bool: b) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
int_eq_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b)
|
|
elseif fAvoidNI /\ fAuxIntEqOLD00 then
|
|
aux_int_eq_iff_1__float(sum2float( c, x ), d, b)
|
|
else
|
|
aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, b)
|
|
endif;
|
|
|
|
%% lin_expr, const
|
|
predicate int_lin_ne(array[int] of int: c, array[int] of var int: x, int: d) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
int_ne(sum(i in index_set(x))( c[i]*x[i] ),d)
|
|
elseif fAvoidNI then
|
|
int_lin_eq_reif(c, x, d, false)
|
|
else
|
|
int_ne__SIMPLE(sum(i in index_set(x))( c[i]*x[i] ),d)
|
|
endif;
|
|
|
|
%% lin_expr, const
|
|
predicate int_lin_ne_reif(array[int] of int: c, array[int] of var int: x,
|
|
int: d, var bool: b) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
int_ne_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b)
|
|
else
|
|
int_lin_eq_reif(c, x, d, not (b))
|
|
endif;
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
%% var float, const
|
|
predicate float_le_reif(var float: x, float: y, var bool: b) =
|
|
%% trace( "float_le_reif(\(x), \(y), \(b))\n" ) /\
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x<=y else x>y endif
|
|
elseif false then
|
|
float_lin_le_reif__IND( [1.0, -1.0], [x, y], 0.0, b)
|
|
elseif ub(x) <= y then
|
|
b == true
|
|
elseif lb(x) > y then
|
|
b == false
|
|
elseif fPostprocessDomains then
|
|
float_le_reif__POST(x, y, b, float_lt_EPS)
|
|
else
|
|
float_le_reif__NOPOST(x, y, b)
|
|
endif;
|
|
|
|
%% const, var float
|
|
predicate float_le_reif(float: x, var float: y, var bool: b) =
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x<=y else x>y endif
|
|
elseif false then
|
|
float_lin_le_reif__IND( [1.0, -1.0], [x, y], 0.0, b)
|
|
elseif ub(x) <= lb(y) then
|
|
b == true
|
|
elseif lb(x) > ub(y) then
|
|
b == false
|
|
elseif fPostprocessDomains then
|
|
float_ge_reif__POST(y, x, b, float_lt_EPS)
|
|
else
|
|
float_le_reif(-y, -x, b)
|
|
endif;
|
|
|
|
%% var float, var float
|
|
predicate float_le_reif(var float: x, var float: y, var bool: b) =
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x<=y else x>y endif
|
|
elseif ub(x)<=lb(y) then b==true
|
|
elseif lb(x)>ub(y) then b==false
|
|
elseif fPostprocessDomains /\ fPostproDom_DIFF then
|
|
float_le_reif(x-y, 0.0, b)
|
|
else
|
|
float_le_reif__NOPOST(x-y, 0, b)
|
|
endif
|
|
;
|
|
|
|
%% var float, var float
|
|
predicate float_le_reif__NOPOST(var float: x, var float: y, var bool: b) =
|
|
aux_float_le_if_1(x, y, (b)) /\ %% Can call __POSTs TODO
|
|
aux_float_gt_if_0(x, y, (b))
|
|
;
|
|
|
|
%% TODO
|
|
predicate float_lt_reif(var float: x, var float: y, var bool: b) =
|
|
%% Actually = float_le_reif(x, y-eps, b).
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x<y else x>=y endif
|
|
elseif fPostprocessDomains /\ fPostproDom_DIFF then
|
|
aux_float_lt_zero_iff_1__POST(x - y, b, float_lt_EPS)
|
|
else
|
|
aux_float_lt_if_1(x, y, (b)) /\
|
|
aux_float_ge_if_0(x, y, (b))
|
|
endif;
|
|
|
|
%% var, const
|
|
predicate float_ne(var float: x, float: y) =
|
|
if fPostprocessDomains then
|
|
float_ne__POST(x, y, float_lt_EPS)
|
|
else
|
|
float_ne__SIMPLE(x, y)
|
|
endif;
|
|
|
|
predicate float_ne__SIMPLE(var float: x, var float: y) =
|
|
if true then
|
|
let { var 0..1: p }
|
|
in
|
|
aux_float_lt_if_1(x, y, (p)) /\
|
|
aux_float_gt_if_0(x, y, (p))
|
|
else %TODO: Why is this not half-reified?
|
|
1 == (x>y) + (x<y)
|
|
endif;
|
|
|
|
%% var, var
|
|
predicate float_ne(var float: x, var float: y) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
float_ne(x-y, 0.0)
|
|
else
|
|
float_ne__SIMPLE(x-y, 0.0)
|
|
endif;
|
|
|
|
%% TODO Why is disequality with EPS but equality not??
|
|
|
|
%% var, var? TODO
|
|
predicate float_eq_reif(var float: x, var float: y, var bool: b) =
|
|
if is_fixed(b) then
|
|
if true==fix(b) then x==y else x!=y endif
|
|
elseif ub(x)<lb(y) \/ lb(x)>ub(y) then
|
|
b == false
|
|
elseif is_fixed(x) /\ is_fixed(y) then
|
|
b == (fix(x) == fix(y))
|
|
elseif fPostprocessDomains /\ fPostproDom_DIFF then
|
|
float_eq_reif__POST(x-y, 0, b, float_lt_EPS)
|
|
else
|
|
aux_float_eq_iff_1(x, y, (bool2int(b)))
|
|
endif;
|
|
|
|
predicate float_ne_reif(var float: x, var float: y, var bool: b) =
|
|
float_eq_reif(x, y, not (b));
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
predicate float_lin_eq_reif(array[int] of float: c, array[int] of var float: x,
|
|
float: d, var bool: b) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
float_eq_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b)
|
|
else
|
|
aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, b)
|
|
endif;
|
|
|
|
predicate float_lin_ne_reif(array[int] of float: c, array[int] of var float: x,
|
|
float: d, var bool: b) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
float_ne_reif(sum(i in index_set(x))( c[i]*x[i] ), d, not b)
|
|
else
|
|
aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, not b)
|
|
endif;
|
|
|
|
predicate float_lin_le_reif(array[int] of float: c, array[int] of var float: x,
|
|
float: d, var bool: b) =
|
|
if fPostprocessDomains /\ fPostproDom_DIFF then
|
|
float_le_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b)
|
|
else
|
|
float_le_reif__NOPOST(sum(i in index_set(x))( c[i]*x[i] ), d, b)
|
|
endif;
|
|
|
|
predicate float_lin_lt_reif(array[int] of float: c, array[int] of var float: x,
|
|
float: d, var bool: b) =
|
|
float_lt_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b);
|
|
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
% Auxiliary: equality reified onto a 0/1 variable
|
|
|
|
predicate aux_int_eq_iff_1(var int: x, var int: y, var int: p) =
|
|
if is_fixed(p) then
|
|
if 1==fix(p) then x==y else x!=y endif
|
|
elseif fPostprocessDomains /\ fPostproDom_DIFF then
|
|
abort(" aux_int_eq_iff_1 should not be used with full domain postprocessing")
|
|
elseif false then
|
|
true
|
|
elseif fAuxIntEqOLD00 then
|
|
let { array[1..2] of var 0..1: q }
|
|
in
|
|
aux_int_le_if_1(x, y, p) /\
|
|
aux_int_ge_if_1(x, y, p) /\
|
|
aux_int_lt_if_0(x, y, q[1]) /\
|
|
aux_int_gt_if_0(x, y, q[2]) /\
|
|
sum(q) == p + 1
|
|
else
|
|
%% redundant p == (x<=y /\ y<=x) /\
|
|
1+p == (x<=y) + (y<=x)
|
|
endif;
|
|
|
|
predicate aux_int_eq_iff_1__float(var float: x, float: y, var int: p) =
|
|
if fAuxIntEqOLD00 then
|
|
assert( false, "Don't use aux_int_eq_iff_1__float" )
|
|
/* let { array[1..2] of var 0..1: q }
|
|
in
|
|
aux_int_le_if_1(x, y, p) /\
|
|
aux_int_ge_if_1(x, y, p) /\
|
|
aux_int_lt_if_0(x, y, q[1]) /\
|
|
aux_int_gt_if_0(x, y, q[2]) /\
|
|
sum(q) == p + 1 */
|
|
else
|
|
assert( false, "Don't use aux_int_eq_iff_1__float with fAuxIntEqOLD00" )
|
|
endif;
|
|
|
|
% Alternative 2
|
|
predicate aux_int_eq_iff_1__WEAK1(var int: x, var int: y, var int: p) =
|
|
let { array[1..2] of var 0..1: q_458 }
|
|
in
|
|
aux_int_lt_if_0(x - p, y, q_458[1]) /\
|
|
aux_int_gt_if_0(x + p, y, q_458[2]) /\
|
|
sum(q_458) <= 2 - 2*p /\
|
|
sum(q_458) <= 1 + p;
|
|
|
|
% Alternative 1
|
|
predicate alt_1_aux_int_eq_iff_1(var int: x, var int: y, var int: p) =
|
|
let { array[1..2] of var 0..1: q }
|
|
in
|
|
aux_int_lt_if_0(x - p, y, q[1]) /\
|
|
aux_int_gt_if_0(x + p, y, q[2]) /\
|
|
q[1] <= 1 - p /\
|
|
q[2] <= 1 - p /\
|
|
sum(q) <= 1 + p;
|
|
|
|
predicate aux_float_eq_iff_1(var float: x, var float: y, var int: p) =
|
|
if is_fixed(p) then
|
|
if 1==fix(p) then x==y else x!=y endif
|
|
elseif fPostprocessDomains /\ fPostproDom_DIFF then
|
|
abort(" aux_float_eq_iff_1 should not be used with full domain postprocessing")
|
|
elseif fAuxFloatEqOLD00 then
|
|
let { array[1..2] of var 0..1: q }
|
|
in
|
|
aux_float_le_if_1(x, y, p) /\
|
|
aux_float_ge_if_1(x, y, p) /\
|
|
aux_float_lt_if_0(x, y, (q[1])) /\
|
|
aux_float_gt_if_0(x, y, (q[2])) /\
|
|
sum(i in 1..2)((q[i])) == 1 + p
|
|
else
|
|
%% redundant p == (x<=y /\ y<=x) /\
|
|
1+p == (x<=y) + (y<=x)
|
|
endif;
|
|
|
|
|
|
|
|
% ----------------------------- Domains postpro ---------------------------
|
|
|
|
%%%%%%%%%%%%%%%%%% predicate int_le_reif__POST(var int: x, var int: y, var int: b);
|
|
%%%%%%%%%%%%%%%%%% predicate int_le_reif__POST(int: x, var int: y, var int: b);
|
|
%%%%%%% var int: b: bool2int is a reverse_map, not passed to .fzn
|
|
%% var, const
|
|
predicate int_le_reif__POST(var int: x, int: y, var int: b);
|
|
%% var, const
|
|
predicate int_ge_reif__POST(var int: x, int: y, var int: b);
|
|
|
|
%% var, const
|
|
predicate int_eq_reif__POST(var int: x, int: y, var int: b);
|
|
%% var, const
|
|
predicate int_ne__POST(var int: x, int: y);
|
|
|
|
%%%%%%%%%%%%%%%%%% predicate float_le_reif__POST(var float: x, var float: y, var int: b);
|
|
%%%%%%%%%%%%%%%%%% predicate float_le_reif__POST(float: x, var float: y, var int: b);
|
|
%% var, const
|
|
predicate float_le_reif__POST(var float: x, float: y, var int: b, float: epsRel);
|
|
%% var, const
|
|
predicate float_ge_reif__POST(var float: x, float: y, var int: b, float: epsRel);
|
|
|
|
%% var, var
|
|
predicate aux_float_lt_zero_iff_1__POST(var float: x, var int: b, float: epsRel);
|
|
|
|
%% var, const
|
|
predicate float_eq_reif__POST(var float: x, float: y, var int: b, float: epsRel);
|
|
%% var, const
|
|
predicate float_ne__POST(var float: x, float: y, float: epsRel);
|
|
|
|
|