From 5bae8fcaf80c91a52d860b3e3b316c859786a3c0 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 28 Jan 2023 09:42:26 +0100 Subject: [PATCH 1/3] FIXED: use lsb/2 and msb/2 from library(arithmetic) This addresses #1720. --- src/lib/clpz.pl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 04ba0a79..14a790fc 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2962,8 +2962,8 @@ expr_conds(A0>>B0, A>>B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(A0/\B0, A/\B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(A0\/B0, A\/B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(xor(A0,B0), xor(A,B)) --> expr_conds(A0, A), expr_conds(B0, B). -expr_conds(lsb(A0), lsb(A)) --> expr_conds(A0, A). -expr_conds(msb(A0), msb(A)) --> expr_conds(A0, A). +% expr_conds(lsb(A0), lsb(A)) --> expr_conds(A0, A). +% expr_conds(msb(A0), msb(A)) --> expr_conds(A0, A). expr_conds(popcount(A0), Count) --> expr_conds(A0, A), [I is A, arithmetic:popcount(I, Count)]. @@ -3539,8 +3539,8 @@ parse_reified(E, R, D, m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], % bitwise operations m(\A) => [function(D,\,A,R)], - m(msb(A)) => [function(D,msb,A,R)], - m(lsb(A)) => [function(D,lsb,A,R)], + m(msb(A)) => [g(#A#>0) ,function(D,msb,A,R)], + m(lsb(A)) => [g(#A#>0), function(D,lsb,A,R)], m(popcount(A)) => [function(D,popcount,A,R)], m(sign(A)) => [function(D,sign,A,R)], m(A< [function(D,<<,A,B,R)], @@ -5683,8 +5683,11 @@ run_propagator(pfunction(Op,A,B,R), MState) --> run_propagator(pfunction(Op,A,R), MState) --> ( integer(A) -> kill(MState), - Expr =.. [Op,A], - R is Expr + ( Op == msb -> { msb(A, R) } + ; Op == lsb -> { lsb(A, R) } + ; Expr =.. [Op,A], + R is Expr + ) ; [] ). From 0d8c7f8785b60a03a9c40a699660c07015f69651 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 28 Jan 2023 10:03:23 +0100 Subject: [PATCH 2/3] small documentation adjustments --- src/lib/clpz.pl | 81 +++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 14a790fc..88e78d78 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -282,14 +282,14 @@ exclude_([L|Ls0], Goal, Ls) :- :- op(700, xfx, cis_lt). :- op(1200, xfx, ++>). -/** Constraint Logic Programming over Integers +/** Constraint Logic Programming over Integers ## Introduction This library provides CLP(ℤ): Constraint Logic Programming over Integers. -CLP(ℤ) is an instance of the general CLP(.) scheme, extending logic +CLP(ℤ) is an instance of the general CLP(_X_) scheme, extending logic programming with reasoning over specialised domains. CLP(ℤ) lets us reason about *integers* in a way that honors the relational nature of Prolog. @@ -303,7 +303,7 @@ There are two major use cases of CLP(ℤ) constraints: The predicates of this library can be classified as: - * _arithmetic_ constraints like `#=/2`, `#>/2` and `#\=/2` + * _arithmetic_ constraints like `(#=)/2`, `(#>)/2` and `(#\=)/2` * the _membership_ constraints `in/2` and `ins/2` * the _enumeration_ predicates `indomain/1`, `label/1` and `labeling/2` * _combinatorial_ constraints like `all_distinct/1` and `global_cardinality/2` @@ -314,7 +314,7 @@ In most cases, [_arithmetic constraints_](<#clpz-arith-constraints>) are the only predicates you will ever need from this library. When reasoning over integers, simply replace low-level arithmetic predicates like `(is)/2` and `(>)/2` by the corresponding CLP(ℤ) -constraints like #=/2 and #>/2 to honor and preserve declarative +constraints like `(#=)/2` and `(#>)/2` to honor and preserve declarative properties of your programs. For satisfactory performance, arithmetic constraints are implicitly rewritten at compilation time so that low-level fallback predicates are automatically used whenever @@ -323,7 +323,7 @@ possible. Almost all Prolog programs also reason about integers. Therefore, it is highly advisable that you make CLP(ℤ) constraints available in all your programs. One way to do this is to put the following directive in -your =|~/.scryerrc|= initialisation file: +your `~/.scryerrc` initialisation file: ``` :- use_module(library(clpz)). @@ -410,12 +410,12 @@ supported. ## Declarative integer arithmetic {#clpz-integer-arith} -The [_arithmetic constraints_](<#clpz-arith-constraints>) #=/2, #>/2 -etc. are meant to be used _instead_ of the primitives `(is)/2`, -`(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog programs also -reason about integers. Therefore, it is recommended that you put the -following directive in your =|~/.scryerrc|= initialisation file to make -CLP(ℤ) constraints available in all your programs: +The [_arithmetic constraints_](#clpz-arith-constraints) `(#=)/2`, +`(#>)/2` etc. are meant to be used _instead_ of the primitives +`(is)/2`, `(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog +programs also reason about integers. Therefore, it is recommended that +you put the following directive in your =|~/.scryerrc|= initialisation +file to make CLP(ℤ) constraints available in all your programs: ``` :- use_module(library(clpz)). @@ -499,7 +499,7 @@ used instead. ## Example: Factorial relation {#clpz-factorial} -We illustrate the benefit of using #=/2 for more generality with a +We illustrate the benefit of using `(#=)/2` for more generality with a simple example. Consider first a rather conventional definition of `n_factorial/2`, @@ -560,7 +560,7 @@ us from _all_ procedural phenomena. For example, the two programs do not even have the same _termination properties_ in all cases. Instead, the primary benefit of CLP(ℤ) constraints is that they allow you to try different execution orders and apply [*declarative -debugging*](https://www.metalevel.at/prolog/debugging.html) +debugging*](https://www.metalevel.at/prolog/debugging) techniques _at all_! Reordering goals (and clauses) can significantly impact the performance of Prolog programs, and you are free to try different variants if you use declarative approaches. Moreover, since @@ -568,27 +568,27 @@ all CLP(ℤ) constraints _always terminate_, placing them earlier can at most _improve_, never worsen, the termination properties of your programs. An additional benefit of CLP(ℤ) constraints is that they eliminate the complexity of introducing `(is)/2` and `(=:=)/2` to -beginners, since _both_ predicates are subsumed by #=/2 when reasoning -over integers. +beginners, since _both_ predicates are subsumed by `(#=)/2` when +reasoning over integers. ## Combinatorial constraints {#clpz-combinatorial} In addition to subsuming and replacing low-level arithmetic predicates, CLP(ℤ) constraints are often used to solve combinatorial problems such as planning, scheduling and allocation tasks. Among the -most frequently used *combinatorial constraints* are all_distinct/1, -global_cardinality/2 and cumulative/2. This library also provides -several other constraints like disjoint2/1 and automaton/8, which are +most frequently used *combinatorial constraints* are `all_distinct/1`, +`global_cardinality/2` and `cumulative/2`. This library also provides +several other constraints like `disjoint2/1` and `automaton/8`, which are useful in more specialized applications. ## Domains {#clpz-domains} Each CLP(ℤ) variable has an associated set of admissible integers, which we call the variable's *domain*. Initially, the domain of each -CLP(ℤ) variable is the set of _all_ integers. CLP(ℤ) constraints -like #=/2, #>/2 and #\=/2 can at most reduce, and never extend, the -domains of their arguments. The constraints in/2 and ins/2 let us -explicitly state domains of CLP(ℤ) variables. The process of +CLP(ℤ) variable is the set of _all_ integers. CLP(ℤ) constraints like +`(#=)/2`, `(#>)/2` and `(#\=)/2` can at most reduce, and never extend, +the domains of their arguments. The constraints `(in)/2` and `(ins)/2` +let us explicitly state domains of CLP(ℤ) variables. The process of determining and adjusting domains of variables is called constraint *propagation*, and it is performed automatically by this library. When the domain of a variable contains only one element, then the variable @@ -683,10 +683,10 @@ goals, it is clear that the constraint solver has deduced additional domain restrictions in many cases. To inspect residual goals, it is best to let the toplevel display them -for us. Wrap the call of your predicate into call_residue_vars/2 to +for us. Wrap the call of your predicate into `call_residue_vars/2` to make sure that all constrained variables are displayed. To make the constraints a variable is involved in available as a Prolog term for -further reasoning within your program, use copy_term/3. For example: +further reasoning within your program, use `copy_term/3`. For example: ``` ?- X #= Y + Z, X in 0..5, copy_term([X,Y,Z], [X,Y,Z], Gs). @@ -695,8 +695,8 @@ X in 0..5, Y+Z#=X. ``` -This library also provides _reflection_ predicates (like fd_dom/2, -fd_size/2 etc.) with which we can inspect a variable's current +This library also provides _reflection_ predicates (like `fd_dom/2`, +`fd_size/2` etc.) with which we can inspect a variable's current domain. These predicates can be useful if you want to implement your own labeling strategies. @@ -732,7 +732,7 @@ puzzle([S,E,N,D] + [M,O,R,E] = [M,O,N,E,Y]) :- M #\= 0, S #\= 0. ``` -Notice that we are _not_ using labeling/2 in this predicate, so that +Notice that we are _not_ using `labeling/2` in this predicate, so that we can first execute and observe the modeling part in isolation. Sample query and its result (actual variables replaced for readability): @@ -853,7 +853,7 @@ separated the core relation from the actual search. ## Optimisation {#clpz-optimisation} -We can use labeling/2 to minimize or maximize the value of a CLP(ℤ) +We can use `labeling/2` to minimize or maximize the value of a CLP(ℤ) expression, and generate solutions in increasing or decreasing order of the value. See the labeling options `min(Expr)` and `max(Expr)`, respectively. @@ -868,7 +868,7 @@ If necessary, we can use `once/1` to commit to the first optimal solution. However, it is often very valuable to see alternative solutions that are _also_ optimal, so that we can choose among optimal solutions by other criteria. For the sake of -[*purity*](https://www.metalevel.at/prolog/purity.html) and +[*purity*](https://www.metalevel.at/prolog/purity) and completeness, we recommend to avoid `once/1` and other constructs that lead to impurities in CLP(ℤ) programs. @@ -878,10 +878,11 @@ numbers. ## Reification {#clpz-reification} -The constraints in/2, #=/2, #\=/2, #/2, #==/2 can be -_reified_, which means reflecting their truth values into Boolean -values represented by the integers 0 and 1. Let P and Q denote -reifiable constraints or Boolean variables, then: +The constraints `(in)/2`, `(#=)/2`, `(#\=)/2`, `(#<)/2`, `(#>)/2`, +`(#=<)/2`, and `(#>=)/2` can be _reified_, which means reflecting +their truth values into Boolean values represented by the integers 0 +and 1. Let P and Q denote reifiable constraints or Boolean variables, +then: | #\ Q | True iff Q is false | | P #\/ Q | True iff either P or Q | @@ -958,19 +959,19 @@ clpz:run_propagator(oneground(X, Y, Z), MState) :- ). ``` -First, clpz:make_propagator/2 is used to transform a user-defined +First, `clpz:make_propagator/2` is used to transform a user-defined representation of the new constraint to an internal form. With -clpz:init_propagator/2, this internal form is then attached to X and +`clpz:init_propagator/2`, this internal form is then attached to X and Y. From now on, the propagator will be invoked whenever the domains of -X or Y are changed. Then, clpz:trigger_once/1 is used to give the +X or Y are changed. Then, `clpz:trigger_once/1` is used to give the propagator its first chance for propagation even though the variables' -domains have not yet changed. Finally, clpz:run_propagator/2 is +domains have not yet changed. Finally, `clpz:run_propagator/2` is extended to define the actual propagator. As explained, this predicate is automatically called by the constraint solver. The first argument is the user-defined representation of the constraint as used in -clpz:make_propagator/2, and the second argument is a mutable state +`clpz:make_propagator/2`, and the second argument is a mutable state that can be used to prevent further invocations of the propagator when -the constraint has become entailed, by using clpz:kill/1. An example +the constraint has become entailed, by using `clpz:kill/1`. An example of using the new constraint: ``` @@ -2922,7 +2923,7 @@ X #=< Y :- Y #>= X. %% #=(?X, ?Y) % % The arithmetic expression X equals Y. When reasoning over integers, -% replace is/2 by #=/2 to obtain more general relations. +% replace `(is)/2` by `(#=)/2` to obtain more general relations. X #= Y :- clpz_equal(X, Y). From 814b631543fc83e1829789e6c36d86c84860eb2f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 28 Jan 2023 10:43:42 +0100 Subject: [PATCH 3/3] use DocLog syntax for section anchors and links within the document --- src/lib/clpz.pl | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 88e78d78..ebcaa249 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -296,7 +296,7 @@ of Prolog. There are two major use cases of CLP(ℤ) constraints: - 1. [*declarative integer arithmetic*](<#clpz-integer-arith>) + 1. [*declarative integer arithmetic*](#clpz-integer-arith) 2. solving *combinatorial problems* such as planning, scheduling and allocation tasks. @@ -310,7 +310,7 @@ The predicates of this library can be classified as: * _reification_ predicates such as `#<==>/2` * _reflection_ predicates such as `fd_dom/2` -In most cases, [_arithmetic constraints_](<#clpz-arith-constraints>) +In most cases, [_arithmetic constraints_](#clpz-arith-constraints) are the only predicates you will ever need from this library. When reasoning over integers, simply replace low-level arithmetic predicates like `(is)/2` and `(>)/2` by the corresponding CLP(ℤ) @@ -362,7 +362,8 @@ constraints is to use the dedicated `clpz` tag on foremost CLP(ℤ) experts regularly participate in these discussions and will help you for free on this platform. -## Arithmetic constraints {#clpz-arith-constraints} +{#clpz-arith-constraints} +## Arithmetic constraints In modern Prolog systems, *arithmetic constraints* subsume and supersede low-level predicates over integers. The main advantage of @@ -408,7 +409,8 @@ The bitwise operations `(\)/1`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`, `lsb/1`, `msb/1`, `popcount/1` and `(xor)/2` are also supported. -## Declarative integer arithmetic {#clpz-integer-arith} +{#clpz-integer-arith} +## Declarative integer arithmetic The [_arithmetic constraints_](#clpz-arith-constraints) `(#=)/2`, `(#>)/2` etc. are meant to be used _instead_ of the primitives @@ -460,7 +462,7 @@ and should therefore be deferred to more advanced lectures. For supported expressions, CLP(ℤ) constraints are drop-in replacements of these low-level arithmetic predicates, often yielding -more general programs. See [`n_factorial/2`](<#clpz-factorial>) for an +more general programs. See [`n_factorial/2`](#clpz-factorial) for an example. This library uses goal_expansion/2 to automatically rewrite @@ -497,7 +499,8 @@ primitives by providing declarative alternatives that are meant to be used instead. -## Example: Factorial relation {#clpz-factorial} +{#clpz-factorial} +## Example: Factorial relation We illustrate the benefit of using `(#=)/2` for more generality with a simple example. @@ -571,7 +574,8 @@ eliminate the complexity of introducing `(is)/2` and `(=:=)/2` to beginners, since _both_ predicates are subsumed by `(#=)/2` when reasoning over integers. -## Combinatorial constraints {#clpz-combinatorial} +{#clpz-combinatorial} +## Combinatorial constraints In addition to subsuming and replacing low-level arithmetic predicates, CLP(ℤ) constraints are often used to solve combinatorial @@ -581,7 +585,8 @@ most frequently used *combinatorial constraints* are `all_distinct/1`, several other constraints like `disjoint2/1` and `automaton/8`, which are useful in more specialized applications. -## Domains {#clpz-domains} +{#clpz-domains} +## Domains Each CLP(ℤ) variable has an associated set of admissible integers, which we call the variable's *domain*. Initially, the domain of each @@ -597,7 +602,8 @@ is automatically unified to that element. Domains are taken into account when further constraints are stated, and by enumeration predicates like labeling/2. -## Example: Sudoku {#clpz-sudoku} +{#clpz-sudoku} +## Example: Sudoku As another example, consider _Sudoku_: It is a popular puzzle over integers that can be easily solved with CLP(ℤ) constraints. @@ -650,7 +656,8 @@ In this concrete case, the constraint solver is strong enough to find the unique solution without any search. -## Residual goals {#clpz-residual-goals} +{#clpz-residual-goals} +## Residual goals Here is an example session with a few queries and their answers: @@ -700,7 +707,8 @@ This library also provides _reflection_ predicates (like `fd_dom/2`, domain. These predicates can be useful if you want to implement your own labeling strategies. -## Core relations and search {#clpz-search} +{#clpz-search} +## Core relations and search Using CLP(ℤ) constraints to solve combinatorial tasks typically consists of two phases: @@ -772,7 +780,8 @@ to reduce the domains of remaining variables to singleton sets. In general though, it is necessary to label all variables to obtain ground solutions. -## Example: Eight queens puzzle {#clpz-n-queens} +{#clpz-n-queens} +## Example: Eight queens puzzle We illustrate the concepts of the preceding sections by means of the so-called _eight queens puzzle_. The task is to place 8 queens on an @@ -851,7 +860,8 @@ separated the core relation from the actual search. -## Optimisation {#clpz-optimisation} +{#clpz-optimisation} +## Optimisation We can use `labeling/2` to minimize or maximize the value of a CLP(ℤ) expression, and generate solutions in increasing or decreasing order @@ -876,7 +886,8 @@ Related to optimisation with CLP(ℤ) constraints are `library(simplex)` and CLP(Q) which reason about _linear_ constraints over rational numbers. -## Reification {#clpz-reification} +{#clpz-reification} +## Reification The constraints `(in)/2`, `(#=)/2`, `(#\=)/2`, `(#<)/2`, `(#>)/2`, `(#=<)/2`, and `(#>=)/2` can be _reified_, which means reflecting @@ -897,7 +908,8 @@ The constraints of this table are reifiable as well. When reasoning over Boolean variables, also consider using CLP(B) constraints as provided by `library(clpb)`. -## Enabling monotonic CLP(ℤ) {#clpz-monotonicity} +{#clpz-monotonicity} +## Enabling monotonic CLP(ℤ) In the default execution mode, CLP(ℤ) constraints still exhibit some non-relational properties. For example, _adding_ constraints can yield @@ -933,7 +945,8 @@ expressions with the functor `(?)/1` or `(#)/1`. For example: The wrapper can be omitted for variables that are already constrained to integers. -## Custom constraints {#clpz-custom-constraints} +{#clpz-custom-constraints} +## Custom constraints We can define custom constraints. The mechanism to do this is not yet finalised, and we welcome suggestions and descriptions of use cases