Closed braindeaf closed 3 years ago
Hey @braindeaf, try rewriting the expression to:
model.add(delta + sum_var == soft_min)
One difference in the Ruby library is ORTools variables must appear on the left side of the expression.
x + 1 # uses ORTools::SatIntVar + method (works)
1 + x # uses Integer + method (doesn't work)
Another way that would ideally work is:
model.add(delta == -sum_var + soft_min)
But it looks like the Should work on master.-
method isn't implemented for ORTools::SatIntVar
yet.
dang! I tried the latter first and it didn't work earlier. If I'd only thought about moving the sum_var to the opposite side of the equation :S
I'll certainly try it out w/the change anyway.
That then revealed
staff_scheduling_sat.rb:286:in add_soft_sum_constraint': undefined method
delta' for #
So I guess 'delta' isn't supported yet. Is it possible to add that too?
Many thanks Andrew.
I'm being a spanner, there is no delta method :)
Last one, I promise....
delta = model.new_int_var(-7, 7, '')
model.add(delta + soft_max == sum_var)
excess = model.new_int_var(0, 7, prefix + ': over_sum')
model.add_max_equality(excess, [delta, 0])
staff_scheduling_sat.rb:289:in `add_max_equality': Unable to convert Integer to operations_research::sat::IntVar (ArgumentError)
Ha, no worries, this is really useful for the library, so keep them coming. I don't see an immediate for fix this one, but you should be able to convert the constant to an int var manually for now.
model.add_max_equality(excess, [delta, model.new_int_var(0, 0, "zero")])
# or with master
model.add_max_equality(excess, [delta, model.new_constant(0)])
(I think you can just remove if it's zero though)
Edit 2: Just fyi, added many more constraint building methods on master - add_min_equality
, add_bool_and
, add_bool_xor
, and many others
This is looking great so far. I'm sure I've managed to mess up and I need to go back and debug, as it's taking three times as long as python and the solution is giving me differences in the debug output. I can't install latest version yet for some reason (battle for another day). I noticed that it's not possible to access the ObjectiveSolutionPrinter class as yet. Is it possible to add that?
Many thanks.
Looks like my example is ported across as much as I can. However, there is a distinct discrepancy I'm struggling with.
The python example stops after 10 seconds, with a FEASIBLE result, presumably some built in default solve time. Upping the max time doesn't seem to get to OPTIMAL, re-running at 5 and now 10 mins....
Solution 31, time = 160.86 s, objective = 52 Solution 32, time = 160.90 s, objective = 49
It seems to stall here.
My Ruby example finishes in 110 seconds with an OPTIMAL result which makes me more than suspicious. Would we expect Ruby to be this performant compared to Python or are strange things afoot?
Just added ObjectiveSolutionPrinter
as well as a better inspect
method for CpModel
. I haven't noticed a difference in runtime between the libraries (most of the work should be in C++ for both).
I'd see if the output matches between the new model.inspect
and Python's print(model)
. Also, make sure you're comparing with version 8.1 of the Python library.
fwiw, I just tried to run it and get the same results between Ruby and Python with master. Here they are after manually stopping after 5 seconds or so.
Edit: Ran it for 10 minutes as well, and they both get to Solution 32, objective = 62
.
Python
Solution 0, time = 0.20 s, objective = 248
Solution 1, time = 0.24 s, objective = 226
Solution 2, time = 3.07 s, objective = 213
Solution 3, time = 3.13 s, objective = 204
Solution 4, time = 3.18 s, objective = 195
Solution 5, time = 3.22 s, objective = 191
Solution 6, time = 3.28 s, objective = 182
Solution 7, time = 3.33 s, objective = 164
Solution 8, time = 3.37 s, objective = 162
Solution 9, time = 3.44 s, objective = 157
Solution 10, time = 3.48 s, objective = 156
Solution 11, time = 3.53 s, objective = 153
Solution 12, time = 3.57 s, objective = 152
Solution 13, time = 3.62 s, objective = 149
^C^C pressed 1 times. Interrupting the solver. Press 3 times to force termination.
M T W T F S S M T W T F S S M T W T F S S
worker 0: O M M N N O A A A O M A O A A A A A O N A
worker 1: O M N N A A A A A M M N N O M M A A A O N
worker 2: M A M A O N O N N A A O A O O N N A A A A
worker 3: M A N N O M M O A A A O N A A M N N N O M
worker 4: A A O M M N N O N N O M M M M A O M O N A
worker 5: A O A M N N O M M O O M N N N O M M M M O
worker 6: A O O A A A A A M M N N A O M O M N N A O
worker 7: N N A A M O O M N N A A O A A A M O M N O
Ruby
Solution 0, time = 0.18 s, objective = 248
Solution 1, time = 0.22 s, objective = 226
Solution 2, time = 3.06 s, objective = 213
Solution 3, time = 3.12 s, objective = 204
Solution 4, time = 3.16 s, objective = 195
Solution 5, time = 3.21 s, objective = 191
Solution 6, time = 3.26 s, objective = 182
Solution 7, time = 3.32 s, objective = 164
Solution 8, time = 3.35 s, objective = 162
Solution 9, time = 3.42 s, objective = 157
Solution 10, time = 3.46 s, objective = 156
Solution 11, time = 3.50 s, objective = 153
Solution 12, time = 3.54 s, objective = 152
Solution 13, time = 3.58 s, objective = 149
^CWARNING: Logging before InitGoogleLogging() is written to STDERR
I0610 19:41:55.818603 5154 sigint.cc:30] ^C pressed 1 times. Interrupting the solver. Press 3 times to force termination.
M T W T F S S M T W T F S S M T W T F S S
worker 0: O M M N N O A A A O M A O A A A A A O N A
worker 1: O M N N A A A A A M M N N O M M A A A O N
worker 2: M A M A O N O N N A A O A O O N N A A A A
worker 3: M A N N O M M O A A A O N A A M N N N O M
worker 4: A A O M M N N O N N O M M M M A O M O N A
worker 5: A O A M N N O M M O O M N N N O M M M M O
worker 6: A O O A A A A A M M N N A O M O M N N A O
worker 7: N N A A M O O M N N A A O A A A M O M N O
It's getting a bit confusing and a little disheartening that earlier I was getting OPTIMAL for one and FEASIBLE for another. It might be because I was flipping between 0.3.0 and now 0.4.0. However, now I'm the same as you when I set the timeout to 15 seconds for both. I'll take what I have and make a PR with these examples in tomorrow :)
Thank you again, this is great progress for us here.
Hi there,
I'm working on trying to port one Python example to Ruby and I've hit a roadblock. (I'll prefix that by saying this was reasonably smooth up until now). I am not going to pretend I understand how this works but my assumption is everytime you do something like
model.add(->> this is something that is evaluated later <<-)
In my port of a Staff Scheduling solution
https://github.com/braindeaf/staff_scheduling
I am now stuck.
causes
staff_scheduling_sat.rb:286:in `-': ORTools::SatIntVar can't be coerced into Integer (TypeError)
I know that soft_min is an Integer, so it's evaluating now and doesn't make sense. Is there a method for doing that subtraction in a way that is evaluated later?)
Many many thanks in advance :)
RobL