gecoder 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/CHANGES +16 -3
  2. data/example/magic_sequence.rb +1 -1
  3. data/example/queens.rb +1 -1
  4. data/example/send_more_money.rb +1 -1
  5. data/example/sudoku.rb +1 -1
  6. data/ext/missing.cpp +18 -4
  7. data/ext/missing.h +8 -0
  8. data/lib/gecoder/bindings.rb +30 -3
  9. data/lib/gecoder/bindings/bindings.rb +22 -0
  10. data/lib/gecoder/interface/binding_changes.rb +81 -107
  11. data/lib/gecoder/interface/branch.rb +65 -14
  12. data/lib/gecoder/interface/constraints.rb +1 -0
  13. data/lib/gecoder/interface/constraints/bool_enum/boolean.rb +16 -12
  14. data/lib/gecoder/interface/constraints/int/arithmetic.rb +7 -3
  15. data/lib/gecoder/interface/constraints/int/linear.rb +19 -16
  16. data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +8 -4
  17. data/lib/gecoder/interface/constraints/int_enum/channel.rb +14 -6
  18. data/lib/gecoder/interface/constraints/int_enum/element.rb +7 -5
  19. data/lib/gecoder/interface/constraints/int_enum/sort.rb +1 -4
  20. data/lib/gecoder/interface/constraints/set/cardinality.rb +6 -3
  21. data/lib/gecoder/interface/constraints/set/connection.rb +136 -0
  22. data/lib/gecoder/interface/constraints/set_enum/channel.rb +18 -0
  23. data/lib/gecoder/interface/constraints/set_enum/distinct.rb +61 -0
  24. data/lib/gecoder/interface/constraints/set_enum_constraints.rb +32 -0
  25. data/lib/gecoder/interface/constraints/set_var_constraints.rb +1 -0
  26. data/lib/gecoder/interface/enum_wrapper.rb +12 -3
  27. data/lib/gecoder/interface/model.rb +77 -56
  28. data/lib/gecoder/interface/search.rb +74 -5
  29. data/lib/gecoder/interface/variables.rb +117 -15
  30. data/lib/gecoder/version.rb +1 -1
  31. data/specs/binding_changes.rb +9 -5
  32. data/specs/bool_var.rb +8 -12
  33. data/specs/branch.rb +85 -19
  34. data/specs/constraints/arithmetic.rb +99 -71
  35. data/specs/constraints/bool_enum.rb +26 -18
  36. data/specs/constraints/boolean.rb +53 -49
  37. data/specs/constraints/cardinality.rb +33 -26
  38. data/specs/constraints/channel.rb +77 -6
  39. data/specs/constraints/connection.rb +352 -0
  40. data/specs/constraints/constraints.rb +10 -1
  41. data/specs/constraints/count.rb +79 -39
  42. data/specs/constraints/distinct.rb +128 -9
  43. data/specs/constraints/element.rb +26 -19
  44. data/specs/constraints/equality.rb +2 -1
  45. data/specs/constraints/int_domain.rb +19 -12
  46. data/specs/constraints/int_relation.rb +12 -6
  47. data/specs/constraints/linear.rb +30 -30
  48. data/specs/constraints/reification_sugar.rb +8 -4
  49. data/specs/constraints/set_domain.rb +24 -18
  50. data/specs/constraints/set_relation.rb +38 -23
  51. data/specs/constraints/sort.rb +12 -10
  52. data/specs/enum_wrapper.rb +9 -3
  53. data/specs/int_var.rb +8 -4
  54. data/specs/logging.rb +24 -0
  55. data/specs/model.rb +25 -7
  56. data/specs/search.rb +41 -1
  57. data/specs/set_var.rb +36 -7
  58. data/specs/spec_helper.rb +3 -10
  59. data/vendor/rust/rust/templates/FunctionDefinition.rusttpl +1 -1
  60. metadata +12 -3
  61. data/specs/tmp +0 -22
@@ -339,3 +339,4 @@ require 'gecoder/interface/constraints/int_enum_constraints'
339
339
  require 'gecoder/interface/constraints/bool_var_constraints'
340
340
  require 'gecoder/interface/constraints/bool_enum_constraints'
341
341
  require 'gecoder/interface/constraints/set_var_constraints'
342
+ require 'gecoder/interface/constraints/set_enum_constraints'
@@ -25,13 +25,15 @@ module Gecode
25
25
  variable = @model.bool_var
26
26
  end
27
27
 
28
- if variable.respond_to? :bind
29
- bound = variable.bind
30
- else
31
- bound = variable
28
+ @model.add_interaction do
29
+ if variable.respond_to? :bind
30
+ bound = variable.bind
31
+ else
32
+ bound = variable
33
+ end
34
+ Gecode::Raw::bool_and(@model.active_space, enum.to_bool_var_array,
35
+ bound, strength)
32
36
  end
33
- Gecode::Raw::bool_and(@model.active_space, enum.to_bool_var_array,
34
- bound, strength)
35
37
  return variable
36
38
  end
37
39
  end
@@ -45,13 +47,15 @@ module Gecode
45
47
  variable = @model.bool_var
46
48
  end
47
49
 
48
- if variable.respond_to? :bind
49
- bound = variable.bind
50
- else
51
- bound = variable
50
+ @model.add_interaction do
51
+ if variable.respond_to? :bind
52
+ bound = variable.bind
53
+ else
54
+ bound = variable
55
+ end
56
+ Gecode::Raw::bool_or(@model.active_space, enum.to_bool_var_array,
57
+ bound, strength)
52
58
  end
53
- Gecode::Raw::bool_or(@model.active_space, enum.to_bool_var_array,
54
- bound, strength)
55
59
  return variable
56
60
  end
57
61
  end
@@ -29,7 +29,9 @@ module Gecode::Constraints::Int::Arithmetic
29
29
  variable = @model.int_var(lhs.min..lhs.max)
30
30
  end
31
31
 
32
- Gecode::Raw::abs(@model.active_space, lhs.bind, variable.bind, strength)
32
+ @model.add_interaction do
33
+ Gecode::Raw::abs(@model.active_space, lhs.bind, variable.bind, strength)
34
+ end
33
35
  return variable
34
36
  end
35
37
  end
@@ -46,8 +48,10 @@ module Gecode::Constraints::Int::Arithmetic
46
48
  variable = @model.int_var(products.min..products.max)
47
49
  end
48
50
 
49
- Gecode::Raw::mult(@model.active_space, lhs.bind, lhs2.bind,
50
- variable.bind, strength)
51
+ @model.add_interaction do
52
+ Gecode::Raw::mult(@model.active_space, lhs.bind, lhs2.bind,
53
+ variable.bind, strength)
54
+ end
51
55
  return variable
52
56
  end
53
57
  end
@@ -85,11 +85,9 @@ module Gecode
85
85
  if lhs.kind_of? Gecode::FreeIntVar
86
86
  lhs = lhs * 1 # Convert to Gecode::Raw::LinExp
87
87
  end
88
- if right_hand_side.respond_to? :to_minimodel_lin_exp
89
- right_hand_side = right_hand_side.to_minimodel_lin_exp
90
- elsif right_hand_side.kind_of? Gecode::FreeIntVar
91
- right_hand_side = right_hand_side.bind * 1
92
- elsif not right_hand_side.kind_of? Fixnum
88
+ if not (right_hand_side.respond_to? :to_minimodel_lin_exp or
89
+ right_hand_side.kind_of? Gecode::FreeIntVar or
90
+ right_hand_side.kind_of? Fixnum)
93
91
  raise TypeError, 'Invalid right hand side of linear equation.'
94
92
  end
95
93
 
@@ -102,12 +100,6 @@ module Gecode
102
100
  # relation type (as specified by Gecode) in relation to the specifed
103
101
  # element.
104
102
  def add_relation_constraint(relation_type, element)
105
- # Bind parameters.
106
- @params[:lhs] = @params[:lhs].bind
107
- if element.kind_of? FreeIntVar
108
- element = element.bind
109
- end
110
-
111
103
  @model.add_constraint Linear::SimpleRelationConstraint.new(@model,
112
104
  @params.update(:relation_type => relation_type, :element => element))
113
105
  end
@@ -122,6 +114,11 @@ module Gecode
122
114
  lhs, rhs, relation_type, reif_var, strength = @params.values_at(:lhs,
123
115
  :rhs, :relation_type, :reif, :strength)
124
116
  reif_var = reif_var.bind if reif_var.respond_to? :bind
117
+ if rhs.respond_to? :to_minimodel_lin_exp
118
+ rhs = rhs.to_minimodel_lin_exp
119
+ elsif rhs.kind_of? Gecode::FreeIntVar
120
+ rhs = rhs.bind * 1
121
+ end
125
122
 
126
123
  final_exp = (lhs.to_minimodel_lin_exp - rhs)
127
124
  if reif_var.nil?
@@ -136,11 +133,17 @@ module Gecode
136
133
  class SimpleRelationConstraint < Gecode::Constraints::ReifiableConstraint
137
134
  def post
138
135
  # Fetch the parameters to Gecode.
139
- params = @params.values_at(:lhs, :relation_type, :element, :reif,
140
- :strength)
141
- params[3] = params[3].bind unless params[3].nil? # Bind reification var.
142
- params.delete_if{ |x| x.nil? }
143
- Gecode::Raw::rel(@model.active_space, *params)
136
+ lhs, relation, rhs, reif_var, strength = @params.values_at(:lhs,
137
+ :relation_type, :element, :reif, :strength)
138
+
139
+ rhs = rhs.bind if rhs.respond_to? :bind
140
+ if reif_var.nil?
141
+ Gecode::Raw::rel(@model.active_space, lhs.bind, relation, rhs,
142
+ strength)
143
+ else
144
+ Gecode::Raw::rel(@model.active_space, lhs.bind, relation, rhs,
145
+ reif_var.bind, strength)
146
+ end
144
147
  end
145
148
  end
146
149
 
@@ -24,8 +24,10 @@ module Gecode::Constraints::IntEnum::Arithmetic
24
24
  variable = @model.int_var(enum.domain_range)
25
25
  end
26
26
 
27
- Gecode::Raw::max(@model.active_space, enum.to_int_var_array,
28
- variable.bind, strength)
27
+ @model.add_interaction do
28
+ Gecode::Raw::max(@model.active_space, enum.to_int_var_array,
29
+ variable.bind, strength)
30
+ end
29
31
  return variable
30
32
  end
31
33
  end
@@ -38,8 +40,10 @@ module Gecode::Constraints::IntEnum::Arithmetic
38
40
  variable = @model.int_var(enum.domain_range)
39
41
  end
40
42
 
41
- Gecode::Raw::min(@model.active_space, enum.to_int_var_array,
42
- variable.bind, strength)
43
+ @model.add_interaction do
44
+ Gecode::Raw::min(@model.active_space, enum.to_int_var_array,
45
+ variable.bind, strength)
46
+ end
43
47
  return variable
44
48
  end
45
49
  end
@@ -1,13 +1,17 @@
1
1
  module Gecode::Constraints::IntEnum
2
2
  class Expression
3
3
  # Posts a channel constraint on the variables in the enum with the specified
4
- # other enum.
4
+ # other set or int enum.
5
5
  def channel(enum, options = {})
6
6
  if @params[:negate]
7
7
  raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
8
8
  'is not implemented.'
9
9
  end
10
-
10
+ unless enum.respond_to?(:to_int_var_array) or
11
+ enum.respond_to?(:to_set_var_array)
12
+ raise TypeError, "Expected int or set enum, got #{enum.class}."
13
+ end
14
+
11
15
  @params.update(Gecode::Constraints::Util.decode_options(options))
12
16
  @params.update(:rhs => enum)
13
17
  @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
@@ -21,11 +25,15 @@ module Gecode::Constraints::IntEnum
21
25
  def post
22
26
  lhs, rhs, strength = @params.values_at(:lhs, :rhs, :strength)
23
27
 
24
- # Bind both sides.
25
28
  lhs = lhs.to_int_var_array
26
- rhs = rhs.to_int_var_array
27
-
28
- Gecode::Raw::channel(@model.active_space, lhs, rhs, strength)
29
+ if rhs.respond_to? :to_int_var_array
30
+ # Int var array.
31
+ Gecode::Raw::channel(@model.active_space, lhs, rhs.to_int_var_array,
32
+ strength)
33
+ else
34
+ # Set var array, no strength.
35
+ Gecode::Raw::channel(@model.active_space, lhs, rhs.to_set_var_array)
36
+ end
29
37
  end
30
38
  end
31
39
  end
@@ -8,11 +8,13 @@ module Gecode::Constraints::IntEnum::Element
8
8
  if variable.nil?
9
9
  variable = @model.int_var(enum.domain_range)
10
10
  end
11
-
12
- # The enum can be a constant array.
13
- enum = enum.to_int_var_array if enum.respond_to? :to_int_var_array
14
- Gecode::Raw::element(@model.active_space, enum,
15
- position.bind, variable.bind, strength)
11
+
12
+ @model.add_interaction do
13
+ # The enum can be a constant array.
14
+ enum = enum.to_int_var_array if enum.respond_to? :to_int_var_array
15
+ Gecode::Raw::element(@model.active_space, enum,
16
+ position.bind, variable.bind, strength)
17
+ end
16
18
  return variable
17
19
  end
18
20
  end
@@ -92,10 +92,7 @@ module Gecode::Constraints::IntEnum
92
92
  first.must_be.less_than_or_equal_to(second, rel_options)
93
93
  end
94
94
  if using_reification
95
- # TODO use the interface's all constraint when available.
96
- # reification_variables.all.must == reif_var
97
- Gecode::Raw::bool_and(@model.active_space,
98
- reification_variables.to_bool_var_array, reif_var.bind, strength)
95
+ reification_variables.conjunction.must == reif_var
99
96
  end
100
97
  end
101
98
  negate_using_reification
@@ -1,6 +1,6 @@
1
1
  module Gecode
2
2
  class FreeSetVar
3
- # Starts a constraint on all the size of the set.
3
+ # Starts a constraint on the size of the set.
4
4
  def size
5
5
  params = {:lhs => self}
6
6
  Gecode::Constraints::Set::Cardinality::SizeExpressionStub.new(@model, params)
@@ -46,9 +46,12 @@ module Gecode::Constraints::Set
46
46
  def constrain_equal(variable, params)
47
47
  lhs = @params[:lhs]
48
48
  if variable.nil?
49
- variable = @model.int_var(lhs.glb_size, lhs.lub_size)
49
+ variable = @model.int_var(lhs.lower_bound.size, lhs.upper_bound.size)
50
+ end
51
+
52
+ @model.add_interaction do
53
+ Gecode::Raw::cardinality(@model.active_space, lhs.bind, variable.bind)
50
54
  end
51
- Gecode::Raw::cardinality(@model.active_space, lhs.bind, variable.bind)
52
55
  return variable
53
56
  end
54
57
  end
@@ -0,0 +1,136 @@
1
+ module Gecode
2
+ class FreeSetVar
3
+ # Starts a constraint on the minimum value of the set.
4
+ def min
5
+ params = {:lhs => self}
6
+ Gecode::Constraints::Set::Connection::MinExpressionStub.new(@model, params)
7
+ end
8
+
9
+ # Starts a constraint on the maximum value of the set.
10
+ def max
11
+ params = {:lhs => self}
12
+ Gecode::Constraints::Set::Connection::MaxExpressionStub.new(@model, params)
13
+ end
14
+
15
+ # Starts a constraint on the sum of the set. The option :weights may
16
+ # optionally be given with a hash of weights as value. If it is then the
17
+ # weighted sum, using the hash as weight function, will be constrained. The
18
+ # option :substitutions may also be given (with a hash as value), if it is
19
+ # then the sum of the set with all elements replaced according to the hash
20
+ # is constrained. Elements mapped to nil by the weights or substitutions
21
+ # hash are removed from the upper bound of the set. Only one of the two
22
+ # options may be given at the same time.
23
+ def sum(options = {:weights => weights = Hash.new(1)})
24
+ if options.empty? or options.keys.size > 1
25
+ raise ArgumentError, 'One of the options :weights and :substitutions, ' +
26
+ 'or neither, must be specified.'
27
+ end
28
+ params = {:lhs => self}
29
+ unless options.empty?
30
+ case options.keys.first
31
+ when :substitutions: params.update(options)
32
+ when :weights:
33
+ weights = options[:weights]
34
+ substitutions = Hash.new do |hash, key|
35
+ if weights[key].nil?
36
+ hash[key] = nil
37
+ else
38
+ hash[key] = key * weights[key]
39
+ end
40
+ end
41
+ params.update(:substitutions => substitutions)
42
+ else raise ArgumentError, "Unrecognized option #{options.keys.first}."
43
+ end
44
+ end
45
+ Gecode::Constraints::Set::Connection::SumExpressionStub.new(@model, params)
46
+ end
47
+ end
48
+ end
49
+
50
+ module Gecode::Constraints::Set
51
+ class Expression
52
+ # Adds a constraint that forces specified values to be included in the
53
+ # set. This constraint has the side effect of sorting the variables in
54
+ # non-descending order.
55
+ def include(variables)
56
+ unless variables.respond_to? :to_int_var_array
57
+ raise TypeError, "Expected int var enum, got #{variables.class}."
58
+ end
59
+ if @params[:negate]
60
+ raise Gecode::MissingConstraintError, 'A negated include is not ' +
61
+ 'implemented.'
62
+ end
63
+
64
+ @params.update(:variables => variables)
65
+ @model.add_constraint Connection::IncludeConstraint.new(@model, @params)
66
+ end
67
+ end
68
+
69
+ # A module that gathers the classes and modules used in connection
70
+ # constraints.
71
+ module Connection
72
+ # Describes an expression stub started with an int var following by #min.
73
+ class MinExpressionStub < Gecode::Constraints::Int::CompositeStub
74
+ def constrain_equal(variable, params)
75
+ set = params[:lhs]
76
+ if variable.nil?
77
+ variable = @model.int_var(set.upper_bound.min, set.lower_bound.min)
78
+ end
79
+
80
+ @model.add_interaction do
81
+ Gecode::Raw::min(@model.active_space, set.bind, variable.bind)
82
+ end
83
+ return variable
84
+ end
85
+ end
86
+
87
+ # Describes an expression stub started with an int var following by #max.
88
+ class MaxExpressionStub < Gecode::Constraints::Int::CompositeStub
89
+ def constrain_equal(variable, params)
90
+ set = params[:lhs]
91
+ if variable.nil?
92
+ variable = @model.int_var(set.upper_bound.max, set.lower_bound.max)
93
+ end
94
+
95
+ @model.add_interaction do
96
+ Gecode::Raw::max(@model.active_space, set.bind, variable.bind)
97
+ end
98
+ return variable
99
+ end
100
+ end
101
+
102
+ # Describes an expression stub started with an int var following by #max.
103
+ class SumExpressionStub < Gecode::Constraints::Int::CompositeStub
104
+ def constrain_equal(variable, params)
105
+ set, subs = params.values_at(:lhs, :substitutions)
106
+ lub = set.upper_bound.to_a
107
+ lub.delete_if{ |e| subs[e].nil? }
108
+ substituted_lub = lub.map{ |e| subs[e] }
109
+ if variable.nil?
110
+ # Compute the theoretical bounds of the weighted sum. This is slightly
111
+ # sloppy since we could also use the contents of the greatest lower
112
+ # bound.
113
+ min = substituted_lub.find_all{ |e| e < 0}.inject(0){ |x, y| x + y }
114
+ max = substituted_lub.find_all{ |e| e > 0}.inject(0){ |x, y| x + y }
115
+ variable = @model.int_var(min..max)
116
+ end
117
+
118
+ @model.add_interaction do
119
+ Gecode::Raw::weights(@model.active_space, lub, substituted_lub,
120
+ set.bind, variable.bind)
121
+ end
122
+ return variable
123
+ end
124
+ end
125
+
126
+ # Describes a constraint that constrains a set to include a number of
127
+ # integer variables.
128
+ class IncludeConstraint < Gecode::Constraints::Constraint
129
+ def post
130
+ set, variables = @params.values_at(:lhs, :variables)
131
+ Gecode::Raw::match(@model.active_space, set.bind,
132
+ variables.to_int_var_array)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,18 @@
1
+ module Gecode::Constraints::SetEnum
2
+ class Expression
3
+ # Posts a channel constraint on the variables in the enum with the specified
4
+ # int enum.
5
+ def channel(enum)
6
+ unless enum.respond_to? :to_int_var_array
7
+ raise TypeError, "Expected integer variable enum, for #{enum.class}."
8
+ end
9
+
10
+ # Just provide commutativity to the corresponding int enum constraint.
11
+ if @params[:negate]
12
+ enum.must_not.channel(@params[:lhs])
13
+ else
14
+ enum.must.channel(@params[:lhs])
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,61 @@
1
+ module Gecode::Constraints::SetEnum
2
+ class Expression
3
+ # Adds a distinct constraint on the sets in the enum. The "option" :size
4
+ # must be specified, the sets will be constrained to that size.
5
+ def distinct(options = {})
6
+ unless options.has_key? :size
7
+ raise ArgumentError, 'Option :size has to be specified.'
8
+ end
9
+ unless options.size == 1
10
+ raise ArgumentError, 'Only the option :size is accepted, got ' +
11
+ "#{options.keys.join(', ')}."
12
+ end
13
+ if @params[:negate]
14
+ raise Gecode::MissingConstraintError, 'A negated distinct is not ' +
15
+ 'implemented.'
16
+ end
17
+
18
+ @model.add_constraint Distinct::DistinctConstraint.new(
19
+ @model, @params.update(options))
20
+ end
21
+
22
+ # Adds a constraint on the sets that specifies that they must have at most
23
+ # one element in common. The "option" :size must be specified, the sets
24
+ # will be constrained to that size.
25
+ def at_most_share_one_element(options = {})
26
+ unless options.has_key? :size
27
+ raise ArgumentError, 'Option :size has to be specified.'
28
+ end
29
+ unless options.size == 1
30
+ raise ArgumentError, 'Only the option :size is accepted, got ' +
31
+ "#{options.keys.join(', ')}."
32
+ end
33
+ if @params[:negate]
34
+ raise Gecode::MissingConstraintError, 'A negated atmost one ' +
35
+ 'constrain is not implemented.'
36
+ end
37
+
38
+ @model.add_constraint Distinct::AtMostOneConstraint.new(
39
+ @model, @params.update(options))
40
+ end
41
+ end
42
+
43
+ # A module that gathers the classes and modules used in distinct constraints.
44
+ module Distinct
45
+ # Describes a set distinct constraint.
46
+ class DistinctConstraint < Gecode::Constraints::Constraint
47
+ def post
48
+ sets, size = @params.values_at(:lhs, :size)
49
+ Gecode::Raw::distinct(@model.active_space, sets.to_set_var_array, size)
50
+ end
51
+ end
52
+
53
+ # Describes an at most one set constraint.
54
+ class AtMostOneConstraint < Gecode::Constraints::Constraint
55
+ def post
56
+ sets, size = @params.values_at(:lhs, :size)
57
+ Gecode::Raw::atmostOne(@model.active_space, sets.to_set_var_array, size)
58
+ end
59
+ end
60
+ end
61
+ end