+ All Categories
Home > Documents > Håkan Kjellerst rand (hakank@gmail) Independent Researcher, Malmö hakank

Håkan Kjellerst rand (hakank@gmail) Independent Researcher, Malmö hakank

Date post: 12-Jan-2016
Category:
Upload: nelia
View: 26 times
Download: 0 times
Share this document with a friend
Description:
What I (still) like about Constraint Programming. Håkan Kjellerst rand ([email protected]) Independent Researcher, Malmö http://www.hakank.org/ My Constraint Programming Blog: http://www.hakank.org/constraint_programming_blog/ This talk at SweConsNet 20130527: - PowerPoint PPT Presentation
44
Håkan Kjellerstrand ([email protected]) Independent Researcher, Malmö http://www.hakank.org/ My Constraint Programming Blog: http://www.hakank.org/constraint_programming_blog/ This talk at SweConsNet 20130527: http://www.hakank.org/constraint_programming/sweconsnet_talk_2013 What I (still) like about Constraint Programming
Transcript
Page 1: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Håkan Kjellerstrand ([email protected])Independent Researcher, Malmö

http://www.hakank.org/

My Constraint Programming Blog:http://www.hakank.org/constraint_programming_blog/

This talk at SweConsNet 20130527:http://www.hakank.org/constraint_programming/sweconsnet_talk_20130527.ppt

What I (still) like about Constraint Programming

Page 2: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Some more about me* Not a theory guy, much more a modeling guy I like to solve a good puzzle – with CP.

* I don't do CP professionally which might explain some things...

* Co-organizer of the CP 2013 Workshop (in Uppsala, September) “CP Solvers: Modeling, Applications, Integration, and Standardization” http://cp2013.a4cp.org/workshops/cpsolvers

Organizers: Jacob Feldman, Helmut Simonis, and me Constraint Solvers Catalog http://openjvm.jvmhost.net/CPSolvers/

Page 3: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Tested ~24 CP Systems

- Choco (21 models) - Comet (168 models) - ECLiPSe CLP (177 models) - Gecode (165 models) - Gecode/R (29 models) - JaCoP (18 models) - JaCoP/Scala (39 models) - MiniZinc (1031 models) - SICStus Prolog (152 models) - Essence'/Tailor (26 models) - Essence'/Savile Row (56 models) - Zinc (39 models) - Google or-tools/Python (202 models) - Google or-tools/Java (36 models) - Google or-tools/C# (129 models) - OscaR (Scala in OR) (146 models) - Java JSR-331 (42 models) - Numberjack (52 models) - AIMMS+CP (39 models) - B-Prolog (207 models) - Choco3 (90 models) - AMPL (100 "pure" CP models) - ILOG CP Optimizer OPL (as we speak, > 100 models) - Answer Set Programming ("related paradigm" 87 encodings)

In total > 3200 models.

Note: Not all of these are pure CP models, some are plain IP models.

Page 4: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Common Constraint Programming Problems

http://hakank.org/common_cp_models/

* I always start testing a CP system with a number of standard "learning" problems to get a feel for different constructs in the CP system.

* Also, I always report bugs and whine about things I don't like (or is inconvenient/weird/etc) to the developers.

* Forthcoming (perhaps) - Picat, http://picat-lang.org/ (available 20130531) - JaCoP v 4.0 - Gecode-python, https://launchpad.net/gecode-python (Python inteface to Gecode) - Copris, http://bach.istc.kobe-u.ac.jp/copris/ (Scala) - Clojure core.logic, https://github.com/clojure/core.logic - or-tools/C++ (no syntactic sugar at all...) - new MiniZinc solvers

Other suggestions?

Page 5: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

So, what do I (still) like about Constraint Programming?

In short: The modeling part, the ease of modeling many types of problems.

Some CP features:- element- reification- generating 1/N/all solution(s)- global constraints- reversibility/bidirection- nonlinear constraints- symmetry breaking- declarative (high level)- nifty language features (wish list)

Many of these features was what caught my interest (blew my mind) in 2008 after checking out mathematical programming some year earlier. Hence the “still”.

Page 6: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Element constraintThe “signum” of Constraint Programming.

* Good example (IMHO): x[y] = z; % MiniZinc, similar Essence' cp.add(x(y) == z, Strong); // OscaR (Scala)

* OK: rel(*this, z == element(x, y); // Gecode

* Not so good: solver.addConstraint( solver.makeEquality(z, solver.makeElement(x, y).var())); // or-tools/Java

Yes, I punish certain host languages (e.g. Java) that don't have operator overloading.

AMPL: can simulate element with “exists”. Here 1..n is the domain of y. s.t. c1: exists{j in 1..n} j = y and x[j] = z;

Page 7: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Reification

* alldifferent_except_0 Implementing a decomposition of this constraint is often a good proxy of the "syntactical sugarness" of a CP system. It's one of the first things I test.

* good example: MiniZinc, Zinc, Comet, Essence' % MiniZinc version forall(i, j in 1..length(x) where i < j) ( (x[i] != 0 /\ x[j] != 0) -> x[i] != x[j] )

* good example: Gecode for(int i = 0; i < x.size(); i++) { for(int j = i+1; j < x.size(); j++) { rel(space, (x[i] != 0 && x[j] != 0) >> (x[i] != x[j]), Icl); } }

Page 8: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Reification Choco3 (beta)// only half-reification (might be easier in fortcoming versions)BoolVar[] b = VariableFactory.boolArray("b_"+i+"_"+j, 3, solver);solver.post(IntConstraintFactory.implies(b[0], (IntConstraintFactory.arithm(v[i], "!=", c))));solver.post(IntConstraintFactory.implies(VariableFactory.not(b[0]), (IntConstraintFactory.arithm(v[i], "=", c))));solver.post(IntConstraintFactory.implies(b[1], (IntConstraintFactory.arithm(v[j], "!=", c))));solver.post(IntConstraintFactory.implies(VariableFactory.not(b[1]), (IntConstraintFactory.arithm(v[j], "=", c))));solver.post(IntConstraintFactory.implies(b[2], (IntConstraintFactory.arithm(v[i], "!=", v[j]))));solver.post(IntConstraintFactory.implies(VariableFactory.not(b[2]), (IntConstraintFactory.arithm(v[i], "=", v[j]))));ALogicTree t = Node.implies(Node.and(Literal.pos(b[0]),

Literal.pos(b[1])), Literal.pos(b[2]) );solver.post(IntConstraintFactory.clauses(t, solver));

Page 9: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Generating 1/N/all solution(s)

* Debugging Example: 8-queens should have 92 solutions, otherwise the model is wrong. Or - much rarer - the solver is wrong. Later: This assumes that no symmetry breaking is used. (Thanks Mats Carlsson.)

I use this very much. In the current AMPL+Gecode implementation this is not implemented which was quite strenuous..

* Ensure unicity of a solution, e.g. a Sudoku problem (set the numbet of solution to 2 and expect just 1)

* Certain combinatorial problems is about counting the number of solutions.

* Generating problem instances This is - kind of - the reverse of solving a problem, by letting all decision variable be free. E.g. "Drive Ya Nuts".

* Special case Search for optimal value and then generate all solutions with that value. No CP system has this as built-in. Hint, hint :-)

Page 10: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Global constraints

Global Constraint Catalog (364 listed global constraints)http://www.emn.fr/z-info/sdemasse/gccat/index.html

Advantages* Special tailored propagators This is the usual sale pitch for global constraints.

* For me: the modeling part High level concepts as "patterns" in modeling

* Some personal favorites all_different all_different_except_0 element ( x[y] = z) global_cardinality_count decrease/increase (sortedness) regular (finite state machine) cumulative (for scheduling like problems) circuit (Hamiltonian circuit) table (allowed assignments) inverse (useful in some puzzles :)

http://hakank.org/minizinc/#global (~170 decompositions in MiniZinc, more or less general)

Page 11: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Reversibility (bi-direction)

Simple example: convert a number ("num") to/from its digits ("x").

// MiniZinc (general predicate, base 10)predicate toNum(array[int] of var int: a, var int: n) = let { int: len = length(a) } in n = sum(i in 1..len) ( pow(10,len-i) * a[len-i+1] );

var 0..999: num;array[1..3] of var 0..9: x;

constraint toNum(x, num) /\ num % 2 = 1 /\ % constraint on num: is an odd number x[2] > 5; % constraint of digits: second digits > 5

- more general: channelling/dual model

Page 12: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Nonlinear constraints

* Compared to (traditional) IP modeling, there is much less need to reformulate/linearize nonlinear constraints.

* There is no need to remember which IP solver it is that handle that nonlinear constraints (quadratic etc).

* No big M's! Though we want as small domains as possible.

Page 13: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Symmetry breaking

Pruning the search tree.

Global constraints:- increasing/decreasing- lex family- precedence

Some system supports dynamic symmetry breaking, e.g.Chris Mears' Lightweight Dynamic Symmetry Breaking (LDSB)http://www.cmears.id.au/symmetry/

Supported by:- ECLiPSe CLP- Gecode

ECLiPSe CLP also has support for- Symmetry Breaking During Search (SBDS)- GAP-based Symmetry Breaking via Dominance Detection (SBDD)

Page 14: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Declarative, high level

Note: I'm not sure how to define "declarative", but I know it when I see it. :-)

http://en.wikipedia.org/wiki/Declarative_programmingThe introduction: “In computer science, declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow. Many languages applying this style attempt to minimize or eliminate side effects by describing what the program should accomplish, rather than describing how to go about accomplishing it (the how is left up to the language's implementation). This is in contrast with imperative programming, in which algorithms are implemented in terms of explicit steps.“

Sometimes it's hard claiming declarativness of CP modeling when the code is full of for(all) loops. Though, I still insist that it's declarative...

High level: The higher, the better (IMHO), at least for prototyping.

Page 15: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

- Can be hard to debug "Everything happens at once" is brilliant, but can also be hard to debug. I tend to rely on “printf-debugging“and removing all constraints and then put them back one after another (or testing after adding each single constraint).

- For more complex problems: must use search heuristics (or remodel) Getting the heuristics right is (still) “an art, not a science”.

There are some Black Box solvers/heuristics, but - IMHO - they need to be tweaked as well. Examples: - Gecode (v 4.0): AFC variants, Activity-Based - Choco 3: Activity-Based, Impact-Based - or-tools: Impact-Based - ILOG CP Optimizer: recommends to use no labeling at all first

Question: Is black box solvers a realistic/interesting/feasible goal for CP research?

What I (still) don't like about CP

Page 16: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Nifty language features I

Some of these features are not unique to CP, but are very handy.Many are just syntactic sugar.

- “syntactic sugar is everything” (hakank) If you want to make me happy, implement as many of these as possible. :-)

Some may be possible only for new dedicated CP languages or DSL.

- element as x[y] = z where x, y, and z (or at least y) are decision variables

- general matrix element Gecode and Choco2 has some support for this

Page 17: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Nifty language features II

- “exists”

Supported by MiniZinc and AMPL+CP. Useful when the range in a forall loop is dynamic; or instead of declaring a temporary decision variable (might give worse propagation).

Small example (MiniZinc): % z is a decision variable used elsewhere exists(i in 1..n) ( z = i /\ forall(j in i+1..n) ( x[j] = 0) )

- array/set comprehension % MiniZinc forall(i in 1..n) ( alldifferent([x[i,j] | j in 1..n where cost[i,j] > 0])

)

Page 18: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Nifty language features III

- “external loops” Which all host languages have (except some Prolog's). This is one of the feature I miss most in MiniZinc, i.e. the possibilty to do simple for loops, e.g. to create temporary variables, counting etc. - if-then-else on decision variables as well as non-decision variables

Example: AIMMS and (sometimes) AMPL.

In most other CP system one have to use reifications or dedicated methods like: ifThenElse(condition,thenclause, elseclause) or % MiniZinc (condition → then clause) /\ (not(condition) → else clause)

Choco3 - current beta version - only support half-reification which is a nuisance. (See earlier slide.)

Page 19: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

- predicates (functions) Is – surprisingly - not supported in some of the high level systems such as AMPL, OPL and Essence'.

- set variables Sometimes extremely useful. (I definitely respect the complexity of this.)

- Systematically testing all variable and value heuristics It would be nice to have a simple way of systematically testing all variable + value heuristics on a model, e.g. with a simple flag.

I haven't seen this in any system yet, and in some it's quite easy to implement.

Nifty language features IV

Page 20: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Thank you!

- Questions?- Comments?

Hakan Kjellerstrand ([email protected])http://www.hakank.org/

Page 21: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

CP bloggers

There are few CP bloggers (tweeters etc) compared to the OR bloggers.

- Jean-Charles Regin/Pierre Schaus: "CP is fun" http://cp-is-fun.blogspot.com/

- Jacob Feldman: "CP Standardization Blog" http://cpstandard.wordpress.com/

- Helmut Simonis: "CP Applications Blog" http://hsimonis.wordpress.com/

- Hakan Kjellerstrand: My Constraint Programming Blog http://www.hakank.org/constraint_programming_blog/

Some OR people that sometimes blog about CP. (See next slide.)

Page 22: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

Here are some great OR-bloggers that sometimes mention CP:

- Mike Trick: "Michael Trick’s Operations Research Blog" http://mat.gsia.cmu.edu/blog/

- Jean-Francois Puget: "IT Best Kept Secret Is Optimization" https://www.ibm.com/developerworks/community/blogs/jfp/?lang=en

- Erwin Kalvelagen: "Yet Another Math Programming Consultant" http://yetanothermathprogrammingconsultant.blogspot.com/

- Paul Rubin: “OR in an OB World” http://orinanobworld.blogspot.com

OR people that sometimes blog about CP

Page 23: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition in different CP systems

Implementation of all_different_except_0 in different CP systems:

- proxy for “ease of modelling”

- overloading of operators (if possible)

- logical operators

- reification

Note: It's not the only way to encode this constraint.

Page 24: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

G12 MiniZinc

forall(i, j in 1..length(x) where i < j) ( (x[i] > 0 /\ x[j] > 0) -> x[i] != x[j])

Comet

int n = x.getSize();forall(i in 1..n, j in i+1..n) { m.post(x[i] > 0 && x[j] > 0 => x[i] != x[j]);}

Page 25: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

Choco2

for(int i = 0; i < v.length; i++) { for(int j = i+1; j < v.length; j++) { m.addConstraint(ifThenElse( and( gt(v[i], 0), gt(v[j], 0) ), neq(v[i], v[j]), TRUE) ); }}

Page 26: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

JaCoP

for(int i = 0; i < v.length; i++) { for(int j = i+1; j < v.length; j++) { m.impose(new IfThen( new And( new XneqC(v[i], 0), new XneqC(v[j], 0) ), new XneqY(v[i], v[j]) ) ); }}

Page 27: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

JaCoP/Scala

for(i <- 0 until y.length; j <- 0 until i) { val b = new BoolVar("b") b <=> AND((y(i) #\= 0), (y(j) #\= 0)); b -> (y(i) #\= y(j))}

Page 28: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

Gecode

for(int i = 0; i < x.size(); i++) { for(int j = i+1; j < x.size(); j++) { rel(space, (x[i] != 0 && x[j] != 0) >> (x[i] != x[j]), icl); }}

Page 29: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

Gecode/R

n = x.lengthb1_is_an bool_var_matrix(n,n)b2_is_an bool_var_matrix(n,n)b3_is_an bool_var_matrix(n,n)n.times{|i| n.times{|j| if i != j then x[i].must_not.equal(0, :reify => b1[i,j]) x[i].must_not.equal(0, :reify => b2[i,j]) x[i].must_not.equal(x[j], :reify => b3[i,j]) (b1[i,j] & b2[i,j]).must.imply(b3[i,j]) else b1[i,j].must.true b2[i,j].must.true b3[i,j].must.true end } }

Page 30: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

ECLiPSe CLP

alldifferent_except_0(Xs) :- dim(Xs, [Len]), labeling(Xs), ( for(I, 1, Len) * for(J, 1, Len), param(Xs) do ( I \= J, Xs[I] #\= 0, Xs[J] #\= 0 ) -> Xs[I] #\= Xs[J] ; true ).

Page 31: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

SICStus Prolog

alldifferent_except_0(Xs) :- ( foreach(X,Xs) do indomain(X)), ( foreach(XI,Xs), count(I,1,_), param(Xs) do ( foreach(XJ,Xs), count(J,1,_), param(I,XI) do I < J, XI #\=0, XJ #\=0 -> XI #\= XJ ; true ) ).

Page 32: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

Essence'

forall i,j : int(1..n) . ( (i != j) => (((x[i] != 0) /\ (x[j] != 0)) => (x[i] != x[j]))),

Page 33: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

G12 Zinc

forall(i,j in index_set(x) where i != j) ( (x[i] > 0 /\ x[j] > 0) -> x[i] != x[j] )

Page 34: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

or-tools/Python

n = len(a)for i in range(n): for j in range(i): s.Add((a[i] != 0) * (a[j] != 0) <= (a[i] != a[j]))

Page 35: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

or-tools/Java

int n = a.length;for(int i = 0; i < n; i++) { for(int j = 0; j < i; j++) { IntVar bi = s.makeIsDifferentCstVar(a[i], 0); IntVar bj = s.makeIsDifferentCstVar(a[j], 0); IntVar bij = s.makeIsDifferentCstVar(a[i], a[j]); solver.addConstraint( s.makeLessOrEqual( s.makeProd(bi, bj).var(), bij)); }}

Page 36: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

or-tools/C#

int n = a.Length;for(int i = 0; i < n; i++) { for(int j = 0; j < i; j++) { s.Add((a[i] != 0) * (a[j] != 0) <= (a[i] != a[j])); }}

Page 37: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

AIMMS+CP

CONSTRAINT: identifier : CAllDifferentExcept0 index domain : (i,j) | i < j definition : if (x(i) <> 0 and x(j) <> 0) then x(i) <> x(j) endif;

Page 38: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

AMPL+CP

s.t. alldifferent_except0{i in dom,j in dom: i < j}: (x[i] > 0 && x[j] > 0) ==> (x[i] != x[j]);

Page 39: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

B-Prolog

alldifferent_except_0(Xs) :- Len @= Xs^length, foreach(I in 1..Len, J in 1..Len, (I #\=J #/\ Xs[I] #\= 0 #/\ Xs[J] #\= 0) #=> (Xs[I] #\= Xs[J])).

Page 40: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

Choco 3 (beta version)BoolVar[] b = VariableFactory.boolArray("b_"+i+"_"+j, 3, solver);solver.post(IntConstraintFactory.implies(b[0], (IntConstraintFactory.arithm(v[i], "!=", c))));solver.post(IntConstraintFactory.implies(VariableFactory.not(b[0]), (IntConstraintFactory.arithm(v[i], "=", c))));solver.post(IntConstraintFactory.implies(b[1], (IntConstraintFactory.arithm(v[j], "!=", c))));solver.post(IntConstraintFactory.implies(VariableFactory.not(b[1]), (IntConstraintFactory.arithm(v[j], "=", c))));solver.post(IntConstraintFactory.implies(b[2], (IntConstraintFactory.arithm(v[i], "!=", v[j]))));solver.post(IntConstraintFactory.implies(VariableFactory.not(b[2]), (IntConstraintFactory.arithm(v[i], "=", v[j]))));ALogicTree t = Node.implies(Node.and(Literal.pos(b[0]),

Literal.pos(b[1])), Literal.pos(b[2]) );solver.post(IntConstraintFactory.clauses(t, solver));

Page 41: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

Numberjack

return [ ( ((a != 0) & (b != 0)) <= (a != b ) ) for a, b in pair_of(x)]

Page 42: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

OscaR (Scala)

for(i <- 0 until y.length; j <- 0 until i) { cp.add( ((y(i) !== 0) && (y(j) !== 0)) ==> (y(i) !== y(j)) )}

Page 43: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

JSR-331

for(int i = 0; i < v.length; i++) { for(int j = i+1; j < v.length; j++) { Constraint c1 = p.linear(v[i],"!=", 0); Constraint c2 = p.linear(v[j],"!=", 0); Constraint c3 = p.linear(v[i],"!=", v[j]); p.postIfThen(c1.and(c2), c3); }}

Page 44: Håkan Kjellerst rand  (hakank@gmail) Independent Researcher, Malmö hakank

all_different_except_0: decomposition

Answer Set Programming (“related paradigm”)

#const n = 6.#const m = 9.

values(0..m).ix(1..n).

% unique indices of x, 1..n1 { x(I, Val) : values(Val) } 1 :- ix(I).

% alldifferent except 0:% If Val > 0 then there must be 0..1 % occurrences of Val in x.{ x(I, Val) : ix(I) } 1 :- values(Val), Val > 0.


Recommended