In an introductory exercise, my goal is to generate patterns of 0, 1 values, subject to various constraints. While the code below works fine with the built-in sum/1 function, it fails (invalid_constraint_expression) with the hand crafted sum1/1 version.
import cp.
main => fill_0_1(4).
fill_0_1(CodeLen) =>
Codes = new_array(CodeLen),
Codes :: 0 .. 1,
sum1([Codes[I] : I in 1..CodeLen]) #= 3,
solve(Codes),
printf("%w\n", Codes).
sum1(FDVars) = N =>
N := 0,
foreach (V in FDVars)
N := N + V
end.
What's the correct way to create sum1?
The problem with your definition of
sum1/1is that you are mixing decision variables (defined with::) with "plain" non-decision variables (using:=). This don't work since you cannot re-assign the value of a decision variable (the variableNin your example).Also, unfortunately, you cannot use functions when defining constraints. It don't work to return a value of a decision variable (i.e. using
#=).Here's how I would do it. It's a little more complex since it supports when the decision variable are in a list and when they are in an array (as in your example). If it's an array (checked by
array(X)) then we have to convert it to a list, since the[H|T]stuff only works on lists, not arrays. The definition itself is a "standard" definition of sum from logic programming using an accumulator.A further comment: Note that the guard conditions in the head (
array(X)andlist(X)) only works with the Picat style of defining the predicates (?=>and=>) and not the Horn clause style (:-, introduced in v3.0), which means that one cannot rely on matching in the head of the predicates. Thus this don't work when using Picat style:The two
Sumvariables must instead be distinct in the head, and are then made equal in the body (using#=), i.e.Here's a variant - still recursive - that works for both lists and arrays without converting to lists. The main difference it that it use an index (
Ix) to access the value in the array/listX. It has two accumulators: one for the index (Ix) which is decremented for each step, and one for the intermediate sum (Sum0).Update: Here's a little more general version of this, and it has the similar form as
sum2/2-3above:fold2(X,Predicate,Init,Result). The input parameters is the list/arrayX, a predicatePred(of arity 3, see below for examples), and an initial valueInit; the output parameter is the resultResult. Here' we canNow we can defined both
sum/2as well asprod/2(for product of the elements in a list/array) like this, again checking if it's an array or list.