But there is no difference between A::real(1,10)
and {A>=1,A=<10}
:
?- A::real(1,10).
A::real(1, 10).
?- {A>=1,A=<10}.
A::real(1, 10).
?- {(1=<A) and (A=<10)}.
A::real(1, 10).
They’re exactly the same constraint on A
(an attributed variable). There is nothing special about the “time of declaring” - constraints can be defined in any order.
But there is no equivalent of clpfd’s union of (sub)domains (intervals with holes), and providing them does require “generic support for disjoint intervals in constraints” because that’s really all there is. Furthermore the combinatorial explosion you referred to using an explicit boolean equally applies here, e.g., a binary operation on two intervals, each with two sub-domains possibly yields an interval with 4 sub-domains. And with continuous domains, there’s no practical limit on the number of sub-domains, no matter how narrow the union is.
Given this it’s very unlikely that unions of sub-domains will be “generically supported”. Furthermore, I haven’t seen any support for disjoint intervals in comparable systems supporting continuous domains (Numerica, Newton, CLIP, to name a few) so I’m even more wary of going down that path.
The question I would like to answer is whether something roughly equivalent could be accomplished within the current conceptual framework of Prolog+clpBNR although possibly not as elegantly as clpfd.
One possible interpretation of A::[real(1,3), real(8,10)].
is:
?- A::real(1,3) ; A::real(8,10).
A::real(1, 3) ;
A::real(8, 10).
This really just maps disjoint domains to Prolog disjunction with choices handled via standard backtracking. Note that this doesn’t necessarily have to be done before A
is otherwise constrained:
?- B::real(0,10), {A==B+2}, (A::real(1,3) ; A::real(8,10)).
B::real(0, 1),
A::real(2, 3) ;
B::real(6, 8),
A::real(8, 10).
or equivalently:
?- B::real(0,10),{A==B+2}, ({A>=1,A=<3} ; {A>=8,A=<10}).
B::real(0, 1),
A::real(2, 3) ;
B::real(6, 8),
A::real(8, 10).
The next question is whether some or all of this can be pushed into the clpBNR constraints using boolean operations and, if so, what’s the advantage of doing so. I’m thinking along the lines of:
?- {((1=<X) and (X=<3)) or ((8=<X) and (X=<10))}.
X::real(-1.0Inf, 1.0Inf).
This is a perfectly good constraint but the weakness here is that until one of the or
terms becomes false, nothing narrows (not even to the bounds of the “union”). But when that occurs, the constraint becomes active:
?- {((1=<X) and (X=<3)) or ((8=<X) and (X=<10))}, {X=<5}.
X::real(1, 3).
?- {((1=<X) and (X=<3)) or ((8=<X) and (X=<10))}, {X>=5}.
X::real(8, 10).
?- {((1=<X) and (X=<3)) or ((8=<X) and (X=<10))}, X::real(5,6).
false.
?- {((1=<X) and (X=<3)) or ((8=<X) and (X=<10))}, X::real(2,9).
X::real(2, 9).
?- {((1=<X) and (X=<3)) or ((8=<X) and (X=<10))}, X::real(2,7).
X::real(2, 3).
So this seems to be an adequate way to constrain an interval to a union of sub-domains. However local propagation is inadequate to drive any narrowing (or
is weak); additional constraints, perhaps via labelling of some kind, are required. Maybe there’s a way of strengthening this but it’s not immediately obvious to me.
I also think clpfd’s effectiveness in dealing with disjoint domains has little to do with how they’re “declared”. Rather it’s due to clpfd’s “generic support” , i.e., I’m pretty sure A in 1..5, A#\=3
will result in the domain of A
becoming disjoint. And B#=A+1
will subsequently define a disjoint domain for B
. So I’m still interested in a clpfd solution to your problem and what answers you would deem acceptable.
One of clpfd’s strengths IMO is its ability to deal with “holes” in domains but it’s not something that can be easily ported to continuous (non-finite) domains. If you have a problem that can be easily expressed using disjoint sub-domains in clpfd, why wouldn’t you do that?