From rubyforge at lokorin.org Mon Jul 21 08:11:11 2008 From: rubyforge at lokorin.org (Andreas Launila) Date: Mon, 21 Jul 2008 14:11:11 +0200 Subject: [gecoder-devel] Syntax for extensional constraints In-Reply-To: <4800A965.3030305@lokorin.org> References: <4800A965.3030305@lokorin.org> Message-ID: <48847CDF.9030403@lokorin.org> Andreas Launila wrote: > I would like to start adding support for extensional constraints. The > syntax for the tuple variant seems straightforward, but the DFA variant > is not as simple. > I asked the ruby-talk mailing list and got some interesting suggestions[1]. I especially like Eivind Eklund's suggestion[2] of using arrays in conjunction with a few operators to describe the regular expressions. It would seem to be as rather good trade-off between being economic, readable and easy to learn. [1] http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/300648 [2] http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/300657 -- Andreas Launila From rubyforge at lokorin.org Tue Jul 29 03:48:32 2008 From: rubyforge at lokorin.org (Andreas Launila) Date: Tue, 29 Jul 2008 09:48:32 +0200 Subject: [gecoder-devel] Sugar for "composite" constraints - a.k.a. "extended sugar" Message-ID: <488ECB50.2030304@lokorin.org> > The idea would be to extend that further so that any constraint which > can be used to constrain equality with a variable of type T is able to > replace a variable of type T when used elsewhere. For example: > > (int_enum.min + int_enum.max).must > int_var > > would be sugar for > > tmp1, tmp2 = int_var_array(2, int_enum_range) > int_enum.min.must == tmp1 > int_enum.max.must == tmp2 > (tmp1 + tmp2).must > int_var I have thought about it and I think that it should be possible to implement by having the "composite constraints" (such as int_enum.min) and the integer variables define a #to_int_var method. Whenever an integer variable is expected one would check whether the provided object responds to #to_int_var and then call that method. If this change is made then I would also like to make a clear separation between the different types of constraints in the documentation. The sugar would further emphasize the difference between the two major groups that the Gecode constraints are currently divided into (with a roughly 40%/60% split): * The ones that produces new variables (those constraints currently called composites in the quick ref[1]). * The ones that constrain variables (the rest). I feel that separating these groups in the documentation, i.e. teaching the user about these "composites" separately from the other constraints, would make things more manageable for the user. With such a separation only the "non-composite" constraints would actually be called constraints. The goal would be to make the user think of set.min.must == 17 as selecting the minimum of the set and then placing a relation constraint on that minimum (rather than directly placing a constraint that constrains the minimum of a set). The name "composite constraint" is hence not especially well suited for this mindset (and probably wasn't a good name anyway), another name is needed. The best I can think of at the moment would be something generic like "int var accessor", "int var selector" or perhaps "set var property" (to describe "set.min" above). The ideal situation would be if the user can think of everything to the left of "must" as accessors/... and everything to the right as constraints placed on the variables accessed on the left. Achieving this would require allowing some of the current constraints, such as the set operation constraint set_var_or_constant_1.set_operation( set_var_or_constant_2).must.set_relation( set_var_or_constant_3) , to act as accessors/... when only the part before "must" is given. Or rather it would mean converting set_var_or_constant_1.set_operation(set_var_or_constant_2) into an accessor/... that deals with #must.set_relation in a special way (similar to how the current composites deal with #== in a special way to avoid additional variables). The other constraints that fall into the above group of non-composite constraints with parts before "must" are: * The set elements constraint: set_var.elements.must.int_relation(int_var_or_constant) * The set enumeration select disjoint constraint: set_enum[set_var].must_be.disjoint The former could receive treatment similar to the set set operation constraint. The latter can't be treated in such a way since it doesn't have an equality constraint. The option to completely change the latter's syntax still remains, but it might be best to leave it as an exception to the concept (or be stringent and say that it is an accessor/... for a virtual variable (selected set enum) with only one constraint and one accessor/... (#union), which would be true). Another question is whether such a separation should be reflected in the code, i.e. whether these accessors/selectors/properties should be moved out of the interface/constraints directory and namespace. It would probably be a good idea in order to keep the code organised, but it would probably require a bit of work and the two groups still need to share some code. It would have been beneficial to have started off with this design in mind, as rewriting some of the fundamentals of the code for organisational purposes might not be worth the effort. Reorganisations shouldn't be feared though as the complete C0 coverage of the specs is a great aid (which I can attest to during the move to Gecode 2.* and other major changes). I guess it might be a case of making an attempt and then giving up on the code reorganisation if it proves to have too many complications. In conclusion I think that: * The sugar should be straightforward enough to implement. * The documentation changes would be beneficial and should be implemented if the sugar is implemented. * The code reorganisation is optional at best, only to be done if it proves rather painless. [1] http://gecoder.rubyforge.org/documentation/constraints/quick-reference.html -- Andreas Launila From rubyforge at lokorin.org Tue Jul 29 05:11:55 2008 From: rubyforge at lokorin.org (Andreas Launila) Date: Tue, 29 Jul 2008 11:11:55 +0200 Subject: [gecoder-devel] Simpler ways to create models, access variables and perform searches Message-ID: <488EDEDB.8060100@lokorin.org> The models I have written typically have the following form in common: class Foo < Gecode::Model attr :vars def initialize @numbers = int_var_array(17, 0..4711) # Insert constraints branch_on @numbers end end solution = Foo.new.solve! if solution.nil? puts 'failed' else do_something solution.numbers.values end It should be possible to add some sugar/convenience to cut down on the amount of repetition. == Accessible variables I think one of the most annoying thing is to have to make variables accessible from the outside by using class variables and accessors. The reason it's currently not done automatically is that the model doesn't know the name of the (Ruby) variable that is assigned the Gecode variable. One way to get around that would be to allow the user to name the variables upon creation: int_var_array(:numbers, 17, 0..4711) and then have the model automatically define an accessor for it, turning the example into class Foo < Gecode::Model def initialize int_var_array(:numbers, 17, 0..4711) # Insert constraints branch_on numbers end end solution = Foo.new.solve! if solution.nil? puts 'failed' else do_something solution.numbers.values end The non-named syntax would remain and still function as normal. The syntax for naming could also be different. A few other possibilities off the top of my head: name int_var_array(17, 0..4711), :numbers declare int_var_array(17, 0..4711), :as => :numbers numbers_is int_var_array(17, 0..4711) numbers_is_an int_var_array(17, 0..4711) numbers.is int_var_array(17, 0..4711) numbers :is, int_var_array(17, 0..4711) call int_var_array(17, 0..4711), :numbers let :numbers, :be => int_var_array(17, 0..4711) let_numbers_be int_var_array(17, 0..4711) "numbers_is_an int_var_array(17, 0..4711)" sounds pretty good to me, but there should be better ones. == Skip having to define classes Having to define classes should not be needed unless the model is rather camplex. One could provide some method that takes a block, initializes an anonymous class, performs a search and returns the results. Applied to the above example: solution = Gecode.solve do numbers_is_an int_var_array(17, 0..4711) # Insert constraints branch_on numbers end if solution.nil? puts 'failed' else do_something solution.numbers.values end Gecode#minimize and Gecode#maximize could also be provided. == Accessing solution values If a variable has been assigned then there is no interest in anything other than the assigned value (correct me if I'm wrong). The returned solution could hence forgo the user by calling #value and #values. The obvious problem with that is that variables that have not been branched over may not be assigned. It is probably more hassle than it's worth. == Handling failure Perhaps it would be more fitting to throw an exception if the search failed (for the above sugar as well as Model#solve!, Model#optimize!, Model#minimize! and Model#maximize!) rather than have everyone explicitly check for nil. Those that want to handle search failure would just have to catch the exception. This would turn the above example into: do_something Gecode.solve do numbers_is_an int_var_array(17, 0..4711) # Insert constraints branch_on numbers end.numbers.values Which I think is concise enough. -- Andreas Launila From rubyforge at lokorin.org Tue Jul 29 09:41:25 2008 From: rubyforge at lokorin.org (Andreas Launila) Date: Tue, 29 Jul 2008 15:41:25 +0200 Subject: [gecoder-devel] Simpler ways to create models, access variables and perform searches In-Reply-To: <488EDEDB.8060100@lokorin.org> References: <488EDEDB.8060100@lokorin.org> Message-ID: <488F1E05.9030405@lokorin.org> Andreas Launila wrote: > The non-named syntax would remain and still function as normal. The > syntax for naming could also be different. A few other possibilities off > the top of my head: > > name int_var_array(17, 0..4711), :numbers > declare int_var_array(17, 0..4711), :as => :numbers > numbers_is int_var_array(17, 0..4711) > numbers_is_an int_var_array(17, 0..4711) > numbers.is int_var_array(17, 0..4711) > numbers :is, int_var_array(17, 0..4711) > call int_var_array(17, 0..4711), :numbers > let :numbers, :be => int_var_array(17, 0..4711) > let_numbers_be int_var_array(17, 0..4711) > Or possibly numbers :is_an => int_var_array(17, 0..4711) declare :numbers => int_var_array(17, 0..4711) variables :numbers => int_var_array(17, 0..4711), :other_numbers => int_var_array(9, 0..4714), ... declare_variables :numbers => int_var_array(17, 0..4711), :other_numbers => int_var_array(9, 0..4714), ... -- Andreas Launila