gecoder 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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