triska / clpz Goto Github PK
View Code? Open in Web Editor NEWConstraint Logic Programming over Integers
Home Page: https://www.metalevel.at/prolog/clpz
Constraint Logic Programming over Integers
Home Page: https://www.metalevel.at/prolog/clpz
If I have
A #= B + C + D, A #< 300, B #> 0, C #>0, D #> 0
A stronger propagation should be added stating that
B #< 300, C #< 300, D #< 300
Witness:
?- 2 #= -1 ^Z.
Should weighted_maximum/3 work together with multiple sat/1
constraints. I get the following result:
/* SWI-Prolog (threaded, 64 bits, version 8.3.20) */
ฬ?- sat(~(Y*X)), sat(Z=:=Y), weighted_maximum([7,2,5],[X,Y,Z],W).
false.
But was rather expecting:
?- sat(~(Y*X)), sat(Z=:=Y), weighted_maximum([7,2,5],[X,Y,Z],W).
Y = 1, X = 0, Z = 1, W = 7 ;
Y = 0, X = 1, Z = 0, W = 7
Verify the following propagation:
?- M #= 10^150, [B,E,P]ins 0..M, P #= B^E, B #>= 2, P #< E. M = 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, clpz:(B^E#=P), clpz:(P#=<E+ -1), clpz:(P in 512..999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999), clpz:(E in 513..1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000), clpz:(B in 2..1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) ? yes
Is this correct? Can it be made stronger?
In this Sicstus example,
pred(A, B, C) :-
Max = 10000,
[A, B] ins 0..Max,
B #= C+A-Max,
B #= C+1.
The predicate correctly fails since C+1
does not fit B
's Max
supremum (but C+0
would fit).
However, it is very slow to terminate (due to Max
being big).
What can be done to fasten this seemingly simple kind of code? (In practice, these constraints come from more complex predicates that are hard to relate)
(i3a#372)
| ?- B=1, B in 1..2, C in 1..2, #(A)#> #(B),nvalue(A,[B,B,C]),labeling([],[B,C]). B = 1, C = 2, A = 2 ? ; no | ?- B in 1..2, C in 1..2, #(A)#> #(B),nvalue(A,[B,B,C]),labeling([],[B,C]). no
It's not the labeling:
| ?- B=1, B in 1..2, C in 1..2, #(A)#> #(B),nvalue(A,[B,B,C]), A = 2, B = 1, C = 2. B = 1, C = 2, A = 2 ? ; no | ?- B in 1..2, C in 1..2, #(A)#> #(B),nvalue(A,[B,B,C]), A = 2, B = 1, C = 2. no
In fact, both can be generalized to:
| ?- B in 1..2, C in 1..2, #(A)#> #(B),nvalue(A,[B,B,C]). no
I've found that we have a situation where unification could fail if a custom attribute is added before a clpz constraint is posted.
Consider this code
:- use_module(library(atts)).
:- use_module(library(clpz)).
:- attribute some_attr/0.
verify_attributes(_Var1, _Var2, []). % to satisfy pred3
pred1 :- % fails
put_atts(A, some_attr),
B #= _,
A = B.
pred2 :- % works (but pointless)
B #= _,
_A = B.
pred3 :- % works
B #= _,
put_atts(A, some_attr),
A = B.
pred4 :- % works
A #= _,
put_atts(A, some_attr),
B #= _,
A = B.
Running this on sicstus, we see that only pred1
fails. However swapping a goal (like pred3) or declare the var as a clpz constraint first (like pred4) works!
Output:
[jeshan@pc ~]$ sicstus --nologo --noinfo -l hello.pl --goal pred1.
* user:pred1 - goal failed
| ?- [jeshan@pc ~]$ sicstus --nologo --noinfo -l hello.pl --goal pred2.
| ?- [jeshan@pc ~]$ sicstus --nologo --noinfo -l hello.pl --goal pred3.
| ?- [jeshan@pc ~]$ sicstus --nologo --noinfo -l hello.pl --goal pred4.
| ?- [jeshan@pc ~]$
I'm not familiar with the clpz code but could it be because you assumed that variables_same_queue([Var,Other])
would always hold here?
Lines 7385 to 7388 in 281aaf0
Am I reading this correctly or should the bug be fixed in my code?
Update: I've found that it makes a difference if the clpz var is either on left-hand side of X#=Y or right-hand side. If I have a specific example, I'll add it but I think you'll know that it's about verify_attributes
implementation which may not have been tested for if Var
and Other
have been swapped in verify_attributes(Var, Other, Gs)
.
This was raised by @JCumin:
X #\= Y
should become faster for ground X
or Y
, so that we can write for example:
R #\= N mod D
with acceptable performance.
Is there any available list of test cases to check changes made to CLPZ?
Currently, I'm using clpz with sicstus and not scryer. I see that there are many enhancements being done in the scryer fork but not in this repo.
https://github.com/mthom/scryer-prolog/pulls?q=is%3Apr+clpz
I understood that it's because of the differences in sicstus and scryer implementations. Is there anything the community/ @triska can do to align both versions?
I am sure this is completely my fault, but it is not entirely clear to me what this error means. Am I mixing clpz expressions in some non-compatible way?
I have a very "naive" implementation of the following predicate to detect what numeric position (0-based) an element is within a list which I have run using scryer prolog and clpz:
:- use_module(library(lists)).
:- use_module(library(clpz)).
member_at_position(L, [L|_], 0).
member_at_position(L, [_|Ls], N) :-
N #> 0,
length(Ls, Length),
Limit #= Length + 1,
N #< Limit,
member_at_position(L, Ls, M),
N #= M+1.
Here is some interaction with the console which shows this issue:
?- member_at_position(a, [a,b,c,a,d], P).
P = 0
; caught: error(domain_error(clpz_expression,[]),unknown([])-1)
?- member_at_position(a, L, P).
L = [a|_A], P = 0
; L = [_A,a], P = 1
; L = [_A,a,_B], P = 1
; L = [_A,_B,a], P = 2
; L = [_A,a,_B,_C], P = 1
; caught: error(domain_error(clpz_expression,[]),unknown([])-1)
?- member_at_position(E, L, P).
L = [E|_A], P = 0
; L = [_A,E], P = 1
; L = [_A,E,_B], P = 1
; L = [_A,_B,E], P = 2
; L = [_A,E,_B,_C], P = 1
; caught: error(domain_error(clpz_expression,[]),unknown([])-1)
I am sure I am at fault here, but could you explain what is wrong please?
Enhance propagation in the following cases, if possible:
?- X^Y #= 0. X^Y#=0.
and:
?- X in 0..100, 2^I #= X. X in 0..100, 2^I#=X.
Suggested by @JCumin. These cases must be checked carefully.
An interesting use case for the global constraint nvalue/2
is described by @hugoferreira at:
http://stackoverflow.com/questions/41496348/equivalent-of-nvalue-2-from-sicstus-in-swiprolog
For example, if one is trying to distribute stuff into bags of different sizes, and want to minimize the number of bags.
A working implementation of nvalue/2
is now available in CLP(Z).
It should become a bit stronger, and also detect entailment.
?- B = 0, B in -2..0, 0#<==>0#=0/(B*B),labeling([],[B]).
B = 0.
?- B in -2..0, 0#<==>0#=0/(B*B),labeling([],[B]).
B = 0, clpz:(_A in 0..1), clpz:(2#=0#<==>_A), clpz:(0/_A#=_B), clpz:(_C#/\0#=_B#<==>0), unexpected.
I see from your knight's tour example , you wrote a small utility to convert a list of values to a domain:
foldl(num_to_dom, Nexts, Next, Dom)
where:
num_to_dom(N, D0, D0\/N).
I think this should be part of the library itself. Something like:
Var in_set [1, 2, 3, 4]
Vars ins_set [1, 2, 3, 4]
Hello there! After watching/reading part of 'the Power of Prolog' (amazing material by the way!), I wanted to get some experience with using clpz
.
As challenge, I attempted to implement a variant of FizzBuzz that uses CLP(Z) rather than the builtin arithmetic predicates, as well as if_
(of library(reif)
), both with the idea in mind to write as 'logicaly pure' code as possible.
The issue arose that I tried to use (#=)/2
inside if_
, which did not work. After reading up on its definition in Scryer-Prolog's standard library as well as the 'indexing dif
'-paper, I realized that if_
uses an arity-3 version of the predicate that is passed as first argument.
I was able to adapt the example of (=)/3
given in the paper to (#=)
although I am not 100% if my implementation is correct.
To be precise, a call like ?- number_fizzbuzz(5, Res).
still produces a choicepoint:
?- number_fizzbuzz(5, Res).
Res = 'Buzz' ;
false.
Am I using clpz
and reif
correctly together here? Or am I making a mistake?
Is providing a reif
-compatible implementation of (#=)/3
(and maybe of the other similar predicates) something that would be worthwhile to add to clpz
, or not?
Thank you for your consideration,
~Qqwy/Marten
:- module(fizz_buzz, [main/0, number_fizzbuzz_below_100/2, number_fizzbuzz/2]).
:- use_module(library(reif)).
% for Scryer-Prolog:
:- use_module(library(clpz)).
:- use_module(library(between)).
:- use_module(library(iso_ext)).
% for SWI-Prolog:
% :- use_module(library(clpfd)).
% Prints all solutions to `number_fizzbuzz_below_100` each on a separate line, in order.
% Logically-impure shell.
main :-
forall(number_fizzbuzz_below_100(_, FizzBuzz), (write(FizzBuzz), write('\n'))).
% Constrains FizzBuzz results to the range 1 <= X <= 100,
% and (for the 'most general query' where neither X or FizzBuzz is concrete)
% ensures results are traversed in order low -> high X (with concrete X).
number_fizzbuzz_below_100(X, FizzBuzz) :-
between(1, 100, X),
number_fizzbuzz(X, FizzBuzz).
% States the relationship between a number
% and its FizzBuzz representation.
number_fizzbuzz(Num, FizzBuzz) :-
if_((Num mod 15 #= 0), FizzBuzz = 'FizzBuzz',
if_((Num mod 5 #= 0), FizzBuzz = 'Buzz',
if_((Num mod 3 #= 0), FizzBuzz = 'Fizz',
Num = FizzBuzz)
)
).
% Reifiable `#=`.
% Is this implementation correct?
% Can this implementation be improved to reduce useless choicepoints?
#=(X, Y, T) :-
(X #= Y, T = true)
; (X #\= Y, T = false).
Trying SWI-Prolog/swipl-devel#1160 in SICStus:
| ?- tuples_in([[A,B]],[[11,0],[12,1]]).
! Existence error in clpz:tuple_domain/2
! procedure clpz:tuple_domain/2 does not exist
! goal: clpz:tuple_domain([_229,_233],[[11,0],[12,1]])
| ?- X in 1..2.
clpz:(X in 1..2) ?
yes
The documentation reads:
?- assertz(clpz:monotonic).
true.
?- #(X) #= #(Y) + #(Z).
#(Y)+ #(Z)#= #(X). % inaccurate answer, given the operator definition for `(#)/1`
Yet, the functional notation is superfluous and the example is a bit irritating. E.g. in Scryer:
?- assertz(clpz:monotonic).
true.
?- #(X) #= #(Y) + #(Z).
clpz:(#Y+ #Z#= #X).
?- #X + #Y #= #Z.
clpz:(#X+ #Y#= #Z). % this example is preferable as the answer is not reformulated
?- X #= sign(-10).
caught: error(domain_error(clpz_expression,sign(-10)),unknown(sign(-10))-1)
Expected: X = -1.
See also this comment.
In SICStus:
| ?- call_residue_vars(X in 1..2,Vs).
Vs = [X,_A,_B,_C,_D,_E,_F],
clpz:(X in 1..2) ? ;
no
| ?- [user].
% compiling user...
| l :- X in 1..2, X = 1, l.
|
% compiled user in module user, 4 msec 2752 bytes
yes
| ?- catch(l,error(E,_),true).
E = resource_error(memory), unexpected.
$ ulimit -v
500000
Witness due to @jburse:
?- 3*X+2*Y #> 100, X in 10..20. X in 10..20, 101#=<3*X+2*Y, Y in 20..sup.
Y
can be reduced to 21..sup
.
(Tested on SWI's CLP(FD), though I assume the same problem exists in CLP(Z))
Weak propagation
?- X #= abs(X) + 1.
X in 2..sup,
_G2048+1#=X,
_G2048#=abs(X),
_G2048 in 2..sup.
This could directly fail. As a result it takes a long time for this to fail, even though it is obvious that it will:
?- time((X #= abs(X) + 1, X in 1..100000, indomain(X))).
% 17,551,054 inferences, 65.037 CPU in 65.061 seconds (100% CPU, 269863 Lips)
false.
Redundant constraint
?- X #= abs(X).
X in 0..sup,
X#=abs(X).
This correctly constrains X
to 0..sup
from inf..sup
, but then the X#=abs(X)
constraint is completely unnecessary.
?- A = 0, tuples_in([[A,A]],[[0,1],[2,0]]).
false.
?- tuples_in([[A,A]],[[0,1],[2,0]]).
A = 0, unexpected.
As followup to SWI-Prolog/swipl-devel#1160
I am not assuming that CLP(B) from SWI-Prolog uses branch and bound
for weighted_maximum/2. But theoreticall CLP(B) branch and bound should
work for any constraints, given as freeze/2, when/2, CLP(FD) etc..
I tried this theoretical claim. In my system I get:
/* Jekejeke Prolog 1.5.0 */
?- List = [Alice, Bob, Carla, David], weighted_maximum([5,-3,7,-9], List, Max).
List = [1, 0, 1, 0],
Max = 12 ;
No
?- List = [Alice, Bob, Carla, David], Alice*5-Bob*3+Carla*7-David*9 #<10,
weighted_maximum([5,-3,7,-9], List, Max).
List = [1, 1, 1, 0],
Max = 9 ;
No
But in SWI-Prolog I get:
/* SWI-Prolog 8.3.20 */
?- List = [Alice, Bob, Carla, David], weighted_maximum([5,-3,7,-9], List, Max).
List = [1, 0, 1, 0],
Max = 12.
?- List = [Alice, Bob, Carla, David], Alice*5-Bob*3+Carla*7-David*9 #<10,
weighted_maximum([5,-3,7,-9], List, Max).
false.
Is this unavoidable in SWI-Prolog? Does CLP(Z) fare better?
It would be nice if global_cardinality/3
supported CLP(Z) variables as keys. This would allow one to express more general permutations without needing to know the values of the list (e.g. two of one number and three of another).
If this cannot be done ATM because of missing libraries or open issues, then we can at least assemble a list of requirements SWI should meet in order to add compatibility in the future.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.