Home     |     .Net Programming    |     cSharp Home    |     Sql Server Home    |     Javascript / Client Side Development     |     Ajax Programming

Ruby on Rails Development     |     Perl Programming     |     C Programming Language     |     C++ Programming     |     IT Jobs

Python Programming Language     |     Laptop Suggestions?    |     TCL Scripting     |     Fortran Programming     |     Scheme Programming Language


 
 
Cervo Technologies
The Right Source to Outsource

MS Dynamics CRM 3.0

Ruby Programming Language

Gecode/R - Request for syntax feedback


Hello, I'm working on Gecode/R, a Ruby interface to Gecode[0], allowing
constraint programming in Ruby. I'm currently trying to set some
goals/direction for the syntax, and would appreciate feedback from
anyone that has the interest and time. The interface is intended for
people with no previous experience of constraint programming, and should
be fairly easy for an average ruby programmer (i.e. someone familiar
with Ruby) to pick up and use.

I begin with a short introduction and example, I will then highlight
some problems and things to consider. I have formulated some actual
questions in the end of some paragraph, but those are mostly there as
aid for the reader. Feel free to disregard them and comment on any thing
that you think of, any sort of feedback is welcome.

== Introduction and example ==

Using constraint programming basically means that you model the problem
by specifying the constraints that have to hold for something to be a
solution to the problem. The underlying engine then solves the problem
by searching for solutions satisfying all the constraints. Lets take
Ruby Quiz #124 (Magic Squares)[1] as an example. For something to be a
solution to that problem we require that the elements are distinct,
between 1 and n^2, and that the sum of the rows, columns and two main
diagonals are equal. So we model that with some code and then let the
underlying engine do a search.

The following is an example of how solving the problem might look.

# A nave model of the magic square problem with square size n.
class MagicSquare < Gecode::Model
  # n is the size of the magic square.
  def initialize(n)
    # This models that all elements are in 1..(n**2) and are all
    # distinct. IntVar.matrix produces an instance of Matrix filled
    # with instances of IntVar.
    squares = IntVar.matrix(n, n, 1..(n**2))
    all_distinct squares

    # The following models the part about various sums being equal. We
    # know the magic sum (row sum) since we know the sum of all
    # elements.
    magic_sum = n*(1 + n**2) / 2
    n.times do |i|
      squares.row(i).to_a.sum == magic_sum
      squares.column(i).to_a.sum == magic_sum
    end
    squares.diagonal(0, 0).sum == magic_sum
    squares.diagonal(0, squares.column_size - 1).sum == magic_sum

    # We need to select a branching strategy, this is used when we can
    # no longer prune impossible values by deduction and have to make a
    # guess.
    branch_on squares, :variable => :min_size, :value => :min
  end
end

# A couple of utility methods for Array and Matrix to make the above a
# bit more readable.
class Array
  # Folds the elements in the array using the + method.
  def sum
    inject{ |sum, element| sum + element }
  end
end
class Matrix
  # Returns an array containing the element found in the diagonal which
  # contains the element in the specified row and column and contains no
  # element with a strictly smaller row-index. nil is returned if such a
  # diagonal does not exist.
  def diagonal(row, column)
    # Out of bounds.
    return nil unless row < row_size and column < column_size
    # Does not exist.
    return nil if row > 0 and column != 0 and column != column_size - 1

    diag_length = [row_size - row, column_size - column].min
    elements = []
    diag_length.times do |offset|
      elements << self[row + offset, column + offset]
    end
    return elements
  end
end

# Print a solution (the first we find). This assumes that the solution
# has some decent default to_s method.
puts MagicSquare.new(9).solution.to_s

With the above syntax each problem is described as a class inheriting
from some common superclass. The actual model is defined in the
constructor. Each model has a number of variables that define the space
that the engine should search through, in our case we have a n*n matrix
with integers which can take values in the range 1..n**2 . Ruby's Array
and Matrix are used as collections (so IntVar.matrix is just a
convenience method for creating an instance of Matrix and filling it
with instances of IntVar). The advantage with that is that we get all
the utility that we're used to, along with Array's special syntax for
construction.

== Issues ==

=== Describing (linear) constraints ===

In the above example we describe our constraints with lines such as
"squares.diagonal(0, 0).sum == magic_sum". In this case we define a
linear equation that has to hold. These are not lines that are evaluated
to true or false, rather they have the side effect of creating a
constraint that has to hold. The instances of IntVar define arithmetic
methods to create a temporary representation which can then be further
added on to by e.g. more use of arithmetic methods. The representations
are kept track of behind the scenes and translated to real constraints
in the end. Lets take an example:

x,y,z = IntVar.array(3, 0..9) # Three variables with domain 0..9.
x + y == z

"x + y" would evaluate to a temporary representation which is stored and
then has "== z" applied to it.

A problem with the above is that people might look at it and
instinctively think that it's something that does not have a side
effect. Therefore prepending some sort of method might convey that
intention better. For instance we could allow people to write the following.

constrain x + y == z

Fitting words other than "constrain" might be "assert", "post" and
"add". One could also go a bit further and make using such a prepended
method compulsory for consistency. I guess the real question is how
disturbing it is for an average Ruby programmer to have arithmetic
methods that have side effects. Do you see it as natural, disturbing or
something in between? Can you think of any way of making the intention
of there being a side effect clearer?

Using arithmetic and comparison methods is of course only one way of
many to describe these (linear) constraints. One drawback with it is
consistency. Since != can't be redefined to be something other than !(x
== y) inequality has to be treated differently. In this case I'm
considering using something similar to "x.not_equal(y)", i.e. just
replace "!=" with "not_equal". There are a lot of constraints, but these
linear constraints are amongst the most common. Can you think of better
ways to represent them?

=== Variable access ===

Something that the above example doesn't really bother with is some way
to allow the variables to be accessed as something other than local
variables. For instance we might want to write a better to_s method
where we need access to the variables in the squares matrix (which hold
that information).

One way that should be familiar is to have the user save squares in an
instance variable (e.g. @squares) that can then be accessed other where.
Other approaches might include trying to save all instance variables
created in the constructor that refer to instances of e.g. IntVar and
then create methods for accessing them. Yet another would be to have the
user explicitly declare variables (possibly on a class level), which
would then be accessible anywhere in the instance. What would you suggest?

=== Describing distinct constraints ===

The "all_distinct squares" line adds a constraint that says that all
variables contained in squares have to be pairwise inequal. Other names
than "all_distinct" could be used, for example "distinct" and
"all_different" (Gecode uses "distinct"). Other ways of specifying the
constraint could also be imagined, such as "squares.all_distinct". The
latter could make things a bit more consistent by allowing a method to
be prepended, such as "constrain squares.all_distinct" (assuming such a
prepended method is used for the linear constraints). What feels more
natural/readable?

=== Overall syntax ===

Every model has at least three parts:
* Declaration of variables (in the example that would be "squares = ...")
* Definition of constraints (all the lines from "all_distinct ..." down
to "squares.diagonal..." in the example).
* Selection of branching strategy (the line starting with "branch_on" in
the example)

Maybe that could be used to produce some other overall syntax? E.g.
something other than throwing it all into a constructor.

== Conclusion ==

Any feedback (pointing out flaws, suggesting modifications, suggesting a
completely different syntax, anything) is appreciated. Questions about
the project, constraint programming, what has to be covered by the
syntax etc are also welcome.

There are more issues that I would like feedback on, but I figured that
it might be best to start with these and then introduce other areas as
the discussion progresses. I'm trying to collect syntax ideas on a
wiki-page[2], that page gives an overview of the majority of the areas
that the syntax will have to cover. It's more of a collection of notes
and thoughts than proposals though, so it might be hard to read but
possibly interesting to someone.

[0] http://www.gecode.org/
[1] http://www.rubyquiz.com/quiz124.html
[2] http://gecoder.lokorin.org/dev/wiki/Syntax_ideas

--
Andreas Launila

On Jun 5, 2007, at 1:43 PM, Andreas Launila wrote:

would you mind posting something about this on sciruby?

http://sciruby.codeforpeople.com/

??

-a
--
we can deny everything, except that we have the possibility of being  
better. simply reflect on that.
h.h. the 14th dalai lama

On Jun 5, 2007, at 3:08 PM, ara.t.howard wrote:

Perhaps that would be a better idea after the Google SoC project is a  
little farther along with definite syntax to show off?  Just a thought.

James Edward Gray II

I can, when the project is something beyond mere plans (i.e. when it can
be used for something).

--
Andreas Launila

On Jun 5, 2007, at 2:43 PM, Andreas Launila wrote:

> == Issues ==

> === Describing (linear) constraints ===
> A problem with the above is that people might look at it and
> instinctively think that it's something that does not have a side
> effect. Therefore prepending some sort of method might convey that
> intention better. For instance we could allow people to write the  
> following.

> constrain x + y == z

RSpec works a lot like this.  It has you define expectations with  
syntax like:

   some_obj.should     equal(whatever)
   some_obj.should_not equal(whatever)

> Fitting words other than "constrain" might be "assert", "post" and
> "add".

I like the word "constrain."  If you go the RSpec route though, you  
will probably also want a negative form and I don't know what that  
would be.

> One could also go a bit further and make using such a prepended
> method compulsory for consistency.

I think that's a sound strategy.  That's pretty much the point of  
should/should_not in RSpec.  It gives the user a visual queue that  
says something like, "We're now switching into defining constraints  
mode."

> I guess the real question is how
> disturbing it is for an average Ruby programmer to have arithmetic
> methods that have side effects. Do you see it as natural,  
> disturbing or
> something in between?

I don't think it's too shocking.  I think most programmers can accept  
that objects can override methods.  You see that the variables are  
created as IntVar objects, and it's not a far leap that IntVar  
redefines some operators.

If I had to choose though, I think I prefer the RSpec-like context  
changing method prefix.

> One drawback with it is consistency. Since != can't be redefined to  
> be something other than !(x == y) inequality has to be treated  
> differently.

I do like consistency, so, for what it's worth, I prefer equal/
not_equal since we can't have both == and !=.

> === Variable access ===
> One way that should be familiar is to have the user save squares in an
> instance variable (e.g. @squares) that can then be accessed other  
> where.

This feels pretty natural to me.  It works on the knowledge we  
already have.

> Yet another would be to have the user explicitly declare variables  
> (possibly on a class level), which would then be accessible  
> anywhere in the instance. What would you suggest?

This could be another interesting option.  See my comment below about  
a DSL...

> === Describing distinct constraints ===
> Other ways of specifying the constraint could also be imagined,  
> such as "squares.all_distinct". The latter could make things a bit  
> more consistent by allowing a method to be prepended, such as  
> "constrain squares.all_distinct" (assuming such a prepended method  
> is used for the linear constraints). What feels more natural/readable?

I worry that the syntax purposed above requires you to add some  
methods to Array and/or Matrix.  This seems like a slope you want to  
avoid.

As an idea, again using RSpec for inspiration:

   constrain distinct(some_enum)

> === Overall syntax ===

> Every model has at least three parts:
> * Declaration of variables (in the example that would be "squares  
> = ...")
> * Definition of constraints (all the lines from "all_distinct ..."  
> down
> to "squares.diagonal..." in the example).
> * Selection of branching strategy (the line starting with  
> "branch_on" in
> the example)

> Maybe that could be used to produce some other overall syntax? E.g.
> something other than throwing it all into a constructor.

Reading this definitely made me wonder over a DSL.  That's pretty  
much what you have with the class system though.  If constrain()  
becomes the constraint definer and branch_on() represents the  
branching strategy, the only thing you haven't really wrapped is the  
variable declaration.

I don't know what that would look like, but maybe something like:

   variable :some_name, :int, accepted_range_of_values

Then I guess you could later refer to them by name.  Of course,  
that's just not as convenient when it comes to accessing them.  
Iterating over rows and columns shows this well.

Whatever way you go with this though, you may want to consider  
providing a DSL wrapper for your classes, something like:

   magic_square = problem do |model|
     # ...
   end

That just might be nice when you don't need to define a full class.

James Edward Gray II

James Edward Gray II wrote:

That would be a nice solution. The best word I can think of for
"constrain" is "constrain_negation", which is probably far from perfect.

constrain_negation x + y == z

or maybe

constrain (x + y == z).negate

Another variation would be to use a combination of constrain and RSpec's
should/should_not.

constrain (x + y).must == z
constrain (x + y).must_not == z

It might chop up the constraint a bit, but it reads well. One could
possibly also drop the constraint part. It would be nicer with a
negative form of constrain though (or some other fitting word) since it
would make it easier to be consistent. 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)

The above takes a different form than the linear constraints. To make it
consistent with the mixed variation of linear constraint syntax one
could rewrite it as.

constrain (1..17).must(include(x))
constrain (1..17).must_not(include(x))

But that feels a bit clunky and requires changes to Range.

With a negative form of constraint the constraints would look rather
good while being consistent.

constrain x + y == z
constrain_negation x + y == z
constrain x.in(1..17)
constrain_negation x.in(1..17)

>> === Describing distinct constraints ===

>> Other ways of specifying the constraint could also be imagined, such
>> as "squares.all_distinct". The latter could make things a bit more
>> consistent by allowing a method to be prepended, such as "constrain
>> squares.all_distinct" (assuming such a prepended method is used for
>> the linear constraints). What feels more natural/readable?

> I worry that the syntax purposed above requires you to add some methods
> to Array and/or Matrix.  This seems like a slope you want to avoid.

> As an idea, again using RSpec for inspiration:

>   constrain distinct(some_enum)

Sounds good to me.

I don't understand the part about not being as convenient to access. I'm
assuming that one could define matrices just as well as single variables
with such a syntax, which would basically give the same level of
convenience. An example to see if I understood you correctly (possibly
with some keys for the parameters beyond the name and type):

class MagicSquare < Gecode::Model
  def initialize(n)
    variable :squares, :int_matrix, n, n, 1..(n**2)
    squares.row(0)
    ...
  end
end

I mentioned declaring these kinds of variables on a class level before,
but I see now that doing so would make it hard to initialize the matrix
size in the example. One could however change that part so that the
problem parameters are used to create the class rather than instances of
the class.

> Whatever way you go with this though, you may want to consider providing
> a DSL wrapper for your classes, something like:

>   magic_square = problem do |model|
>     # ...
>   end

> That just might be nice when you don't need to define a full class.

Aye, thanks for the idea.

--
Andreas Launila

Not necessarily. It could also be written as

constrain x.must(be_in(1..17))
constrain x.must_not(be_in(1..17))

Which is both consistent and reads well (possibly apart from the
parenthesis).

Another way of making it clear that we're specifying constraints could
be to place them all in a block. E.g. something like the following.

in_a_solution do
  (x + y).must == z
  x.must > z
  y.must_not be_in(1..4)
  distinct x,y,z
end

Possibly also sending the variables through the method with the block
(which would clearly declare which variables that constitute a solution).

in_a_solution_with(x,y,z) do |x,y,z|
  (x + y).must == z
  x.must > z
  y.must_not be_in(1..4)
  distinct x,y,z
end

--
Andreas Launila

On Jun 6, 2007, at 5:21 AM, Andreas Launila wrote:

Yuck.  And I mean that in the nicest possible way.  ;)

> Another variation would be to use a combination of constrain and  
> RSpec's
> should/should_not.

> constrain (x + y).must == z
> constrain (x + y).must_not == z

That's not too bad.  I think my biggest complaint there is that it  
makes the flow of the line a little hard to follow.  Perhaps must/
must_not could replace constrain altogether though:

must     x + y == z
must_not x + y == z

That doesn't read too well though, does it?

> 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

I think this makes it more complicated to support custom data  
structures.  What if I want a MyUniqueDataStructure full of IntVars,  
for example?

Beyond that, it doesn't seem in anyway superior to:

   class MagicSquare < Gecode::Model
     def initialize(n)
       @squares = ...
     end
     attr_reader :squares
   end

I think it's probably best not to override core Rubyisms if we can't  
significantly add to them.

James Edward Gray II

On Jun 6, 2007, at 7:42 AM, Andreas Launila wrote:

This could possibly be an option.  You could choose to write:

constrain ...
constrain ...
constrain ...

or:

constrain do
   ...
   ...
   ...
end

James Edward Gray II

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.

There doesn't exist support for negating all constraints. One example is
a sorting constraint, which basically says that some variables, when
sorted, are equal to some other variables. Which could be expressed
something like:

constrain xs.sorted.equal(ys)
or
constrain sorted(xs).equal(ys)
or
constrain sort(xs).equal(ys)

The last couple avoid having to modify Enumerable. Using this form I
prefer the second version.

The point however is that there's no simple way to negate that
constraint using Gecode, beyond hacking together several constraints
that express that a single element is out of order and saying that at
least one of them must hold. To me it would seem accepatable to simply
throw an exception in those cases (or translate it to something
inefficient behind the scenes, but I would rather not do that).

True, I didn't think about custom data structures.

--
Andreas Launila

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

On Jun 6, 2007, at 12:35 PM, Andreas Launila wrote:

> 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.

I'm liking this direction, for what it's worth. I'm worried about the  
ones that require changes to core classes though and I think we  
generally want to avoid that.  That may be an argument for the  
earlier proposed variable(), since it could wrap the data structures  
in proxy objects supporting the extra calls.

Random thoughts from that page:

* We probably shouldn't use sort(), because a class my inherit a sort
() from Enumerable.
* Could all_equal() be same()?  I was just wondering if that paired  
better with distinct().  I don't hate all_equal() though.  Just a  
thought.

> == 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))

Could this line also be:

price_to_pay = IntVar.new(*prices)

?  Just curious as it made more sense to me that way.

> 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 [].

The []() method is defined in Array, actually.

> 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)

I would say avoiding core class modification is important.  If that  
means we need element(), that's probably just a price we need to pay,  
in my opinion.

See my earlier comment about proxy objects for another option though.

> == Channel constraints ==
> 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.

Looks reasonable to me, at least as far as I understood this one.  ;)

> == 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.

What about taking a page from the Rails book and doing something like:

constrain sorted(xs, :indices => zs).equal(ys)

?

> == 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)

Here again parameters seem like they might be OK.  What's nice is  
that we can get more variations this way.  For example count(enum)  
could count all elements while count(enum, :element => y) would be  
just the y elements in enum.

James Edward Gray II

James Edward Gray II wrote:

True, I have switched to sorted.

> * Could all_equal() be same()?  I was just wondering if that paired
> better with distinct().  I don't hate all_equal() though.  Just a thought.

Certainly, I have updated the page with this suggestion and all the
others in your mail.

Yes.

I will write up some similar syntax examples with proxy objects to see
how big the difference is.

>> == Channel constraints ==

>> 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.

> Looks reasonable to me, at least as far as I understood this one.  ;)

Actually I made a mistake in the examples of the two different
viewpoints which might have confused a bit. It should be the following.

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

Yes, especially since indices is optional.

I'm not sure if count(enum) would be used since it's basically the same
as counting the number of elements in enum and constraining z to be
equal to it (and since you constructed the enum you already know the
size and could thereby replace z with that constant). But the form with
an hash option is more readable than the form with just two arguments.

--
Andreas Launila

Examples: http://gecoder.lokorin.org/dev/wiki/Syntax_test#Proxy_objects

I have assumed that it's in this case okay to shadow methods such as
#sort since if the enum is passed to a wrapping method then it should
only be used with finite-domain variables, which do not have any defined
way of comparison (similarly for min and max).

--
Andreas Launila

On Jun 7, 2007, at 3:58 AM, Andreas Launila wrote:

That page talks about how we loose some convenience in that we need  
to now wrap simple IntVars.  I'm not sure that's 100% necessary  
though.  If we built the class, we should be able to count on it  
having what we need.  The wrapper could just be for Enumerables and/
or custom data structures, I think.  Double-check my logic there though.

James Edward Gray II

James Edward Gray II wrote:

Your conclusion is correct. But my intent with the text is that we have
to wrap enumerables before use, I'm not sure how to reach the other
interpretation.

--
Andreas Launila

On Jun 7, 2007, at 8:35 AM, Andreas Launila wrote:

Oh yes, I misread.  Sorry.

I'm not giddy with the idea either.  Maybe we're getting too  
complicated here.  Let's back up.

Our current pattern is:

   constrain function(...).predicate(...)

First of all, can't function() always return the needed proxy object,  
without an need to wrap?  Or is the issue when we don't use the  
function()?

If we want to get rid of the need to wrap, we need to eliminate the  
calls on random objects.  That's not so bad, because we really want  
or constraint builders worrying about the details for us.

Maybe the issue is that I led us down the wrong path with my RSpec  
suggestions.  What if we tried something more like Test::Unit's  
assertions.  I'm now thinking of a pattern like:

   constrain_...(args, hash_style_options)

Translating the examples I come up with:

   constrain_equal     x, y
   constrain_not_equal x, y
   constrain_in        x, enum
   constrain_not_in    x, enum
   constrain_same      enum
   constrain_distinct  enum

The operators are a little clumsier.  We could do constrain_less_than
(), constrain_greater_than(), etc.  This might be a better option  
though:

   constrain_relationship x, :>, y

The symbol could be replaced with the other logical comparisons as  
needed.

For things like sorted(), I'm wondering if we could add that as an  
option:

   constrain_equal enum, other_enum, :sorted => true

What do we think about this direction, on the whole?

James Edward Gray II

James Edward Gray II wrote:

I think that must/must_not would make a fairly interesting syntax in the
sense that it reads very well. Therefore I have tried some examples at
http://gecoder.lokorin.org/dev/wiki/Syntax_test#must.2Fmust_not .

They probably require skipping "constrain" though and replacing it with
something block-like, it just doesn't read right otherwise.

--
Andreas Launila

On Jun 7, 2007, at 9:10 AM, Andreas Launila wrote:

I like how this reads in the early examples and worry about that  
amount of core hacking we have to do to support the later examples:

   whatever.with_offsets   ...
   whatever.sorted         ...
   whatever.sorted_with    ...
   whatever.occurrences_of ...
   whatever.any            ...
   whatever.all            ...
   whatever.implies        ...
   whatever.equivalent_to  ...

James Edward Gray II

James Edward Gray II wrote:

Unless the enumerables are wrapped before use as in the other example
(or the variables are sent through as parameters alongs with the block
as in some earlier example).

--
Andreas Launila

James Edward Gray II wrote:

> Our current pattern is:

>   constrain function(...).predicate(...)

> First of all, can't function() always return the needed proxy object,
> without an need to wrap?  Or is the issue when we don't use the function()?

Yes, wrapping was to use

  constrain variable.relation(arguments)

consistently for everything (the examples for that are under the header
"Proxy objects").

I have added that syntax to the examples to get a feel for it:
http://gecoder.lokorin.org/dev/wiki/Syntax_test#constrain_.2A_arguments

It's a bit bulkier at times, but is probably more conventional (possibly
easier to get to grips with?). My primary concern with it is probably
things like sort constraints where it isn't necessarily immediately
clear which of the enumerables that will be sorted (i.e. it forces one
to look into the API documentation). Those constraints can probably be
eliminated/mitigated with some other choice of words or option names
though. I will see what it leads to.

--
Andreas Launila

On Jun 7, 2007, at 10:07 AM, Andreas Launila wrote:

> I have added that syntax to the examples to get a feel for it:
> http://gecoder.lokorin.org/dev/wiki/Syntax_test#constrain_.
> 2A_arguments

> It's a bit bulkier at times, but is probably more conventional  
> (possibly
> easier to get to grips with?).

I agree that it lacks the sex appeal of the RSpec approach.  However,  
it does allow us to avoid wrapping all variables and core class  
hacking, which I consider big wins.  I would be willing to pay the  
price.

James Edward Gray II

James Edward Gray II wrote:

> On Jun 7, 2007, at 10:07 AM, Andreas Launila wrote:

>> I have added that syntax to the examples to get a feel for it:
>> http://gecoder.lokorin.org/dev/wiki/Syntax_test#constrain_.2A_arguments

>> It's a bit bulkier at times, but is probably more conventional (possibly
>> easier to get to grips with?).

> I agree that it lacks the sex appeal of the RSpec approach.  However, it
> does allow us to avoid wrapping all variables and core class hacking,
> which I consider big wins.  I would be willing to pay the price.

I'm not really sure that the cost compared to the others is that much
lower. The following are the changes necessary to make the others work
as far as I'm aware, please correct me if I'm missing something.

* Custom enums with variables have to be wrapped before use (arrays and
matrices created through the convenience methods are wrapped there).
* It becomes harder to create temporary arrays of variables, e.g. [x,y].
Instead the user has to use variable([x,y]) or something similar (or
possibly IntVar.array(x,y) if that's better).

Beyond that I can not see anything that requires wrapping or altering
core classes (or any other non-gecode classes for that matter).

What I see as a bigger problem of must/must_not is where to fit the
constraint options. It might be necessary to change it around a bit to

variable.<must|must_not> relation(argument)

or something similar for that to work.

--
Andreas Launila

On Jun 8, 2007, at 3:18 AM, Andreas Launila wrote:

You are probably right.

If we go with the wrapping approach, I just think the most important  
thing is to make sure we are careful to support custom data  
structures as painlessly as possible.

James Edward Gray II

Add to del.icio.us | Digg this | Stumble it | Powered by Megasolutions Inc