Andreas Launila wrote:
> James Edward Gray II wrote:
>> On Jun 6, 2007, at 5:21 AM, Andreas Launila wrote:
>>> As an example there's a domain
>>> constraint which basically says that x must be in a given range.
>>> Negating the constraint is possible and might be written as follows.
>>>
>>> constrain x.in(1..17)
>>> constrain x.not_in(1..17)
>> This seems like a very viable solution to me.  If it's possible to just
>> negate all of the tests, that should work:
>>
>> equal/not_equal
>> in/not_in
>> includes/does_not_include
>>
>
> Yes, that could work. I was probably locking myself in a bit too much
> trying to make everything read as must/must_not. I will sketch how other
> constraints could be expressed on a similar form to see if there are any
> consistency problems.
>

If I understood the suggestion of the syntax correctly then constraints
would basically be given on the form "constrain
variable.predicate(argument)" when variable is a single (finite-domain)
variable and "constrain function(variable).predicate(argument)" when
variable is something enumerable.

I have written down some examples of how constraints could look with
that syntax at http://gecoder.lokorin.org/dev/wiki/Syntax_test . There
are some constraints there which I couldn't get especially readable or
self-evident. I will go through them below.

== Element constraints ==

This is basically the array access of constraints. It takes an array of
constant integers, selects the i:th one where i is decided by a
variable, and constrains that variable to be equal to another variable.
This can typically be used to convert e.g. an identifier to a quantity
of some sort, for instance the price of a fruit.

A short example:

fruit_selection = IntVar.new(0..3) # We can pick one of four fruits.
prices = [45, 60, 764, 45] # The prices of each fruit.
price_to_pay = IntVar.new((prices.min)..(prices.max))
constrain prices[fruit_selection].equal(price_to_pay)
constrain price_to_pay > 500

The above example will force fruit_selection to equal 2 (no branching
required) since it's the only fruit with a price > 500.

I would imagine the above syntax of "constrain array[x].equal(y)" to be
easily understandable, but it would require modification to Enumerable
to define/wrap []. Something more on the form of the rest of the syntax
would probably be

constrain element(array, x).equal(y)

It just doesn't read as well to me. Maybe there's a better way of
representing constraints where the function has to take multiple
arguments, such as returning an helper with only one method, i.e. as
follows.

constrain element(x).in(array).equal(y)

== Channel constraints ==

This is a constraint that I have little idea of how to produce something
readable for. The constraint links two enumerables with variables to
each other so that the xs_i = j <=> ys_j = i forall i where xs and ys
are enumerables.

It's used to represent a model from two different viewpoints. Lets say
that you have a problem involving a sequence of length n with numbers in
0..(n-1) that all have to be distinct (for instance magic sequence).
Naturally you might model this as an array where element i is the value
at position i (so if you print the array you get the actual sequence).
You could also model it as an array where element i is the position of
value i in the sequence. These are two different viewpoints of the same
problem. Below what the two arrays might contain when the problem is solved.

First viewpoint: xs = [2,0,1,3] (the actual sequence)
Second viewpoint: ys = [2,1,0,3] (the positions of the different
numbers, e.g. the position of 0 in xs is 2, hence ys[0] == 2)

The important part is that some constraints might be easier to model
using the first viewpoint and others might be easier in the second one.
The channel constraint is used to give the person modeling access to
both viewpoints at the same time. The second viewpoint can be
constructed from the first one by using the channel constraint. The best
syntax I can think of for it is.

constrain channel(xs, ys)

That is probably because I can't think of any common concept that
conveys the intention in a few words.

== Sort constraints ==

There's a special form of sort constraint that operates on three
enumerables: xs, ys and zs. What it does is that it constrains e.g. xs
to be ys sorted using the positions specified in zs. Once again it's the
problem of conveying a function with multiple arguments. Using the
suggestion for element constraints this could become something like

constrain sort(xs).with_indices(zs).equal(ys)

I'm not sure how readable that is.

== Cardinality constraints ==

This is a fairly straightforward constraint, it counts the number of
occurrences in an array. For instance I might specify that an array of
variables must contain 3 instances of 5 (3 and 5 can also be replaced by
variables).

Again it's multiple arguments for a function. Maybe it's more readable
as the following.

constrain number_of(y).in(xs).equal(z)

== Beyond ==

There are more constraints to worry about, but the ones in the syntax
test page represents the commonly used ones for finite domain integers
and booleans. Hence it's probably best to hammer out a good syntax for
them and then adapt it for the other constraints and set variables.

--
Andreas Launila