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.
- data/CHANGES +16 -3
- data/example/magic_sequence.rb +1 -1
- data/example/queens.rb +1 -1
- data/example/send_more_money.rb +1 -1
- data/example/sudoku.rb +1 -1
- data/ext/missing.cpp +18 -4
- data/ext/missing.h +8 -0
- data/lib/gecoder/bindings.rb +30 -3
- data/lib/gecoder/bindings/bindings.rb +22 -0
- data/lib/gecoder/interface/binding_changes.rb +81 -107
- data/lib/gecoder/interface/branch.rb +65 -14
- data/lib/gecoder/interface/constraints.rb +1 -0
- data/lib/gecoder/interface/constraints/bool_enum/boolean.rb +16 -12
- data/lib/gecoder/interface/constraints/int/arithmetic.rb +7 -3
- data/lib/gecoder/interface/constraints/int/linear.rb +19 -16
- data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +8 -4
- data/lib/gecoder/interface/constraints/int_enum/channel.rb +14 -6
- data/lib/gecoder/interface/constraints/int_enum/element.rb +7 -5
- data/lib/gecoder/interface/constraints/int_enum/sort.rb +1 -4
- data/lib/gecoder/interface/constraints/set/cardinality.rb +6 -3
- data/lib/gecoder/interface/constraints/set/connection.rb +136 -0
- data/lib/gecoder/interface/constraints/set_enum/channel.rb +18 -0
- data/lib/gecoder/interface/constraints/set_enum/distinct.rb +61 -0
- data/lib/gecoder/interface/constraints/set_enum_constraints.rb +32 -0
- data/lib/gecoder/interface/constraints/set_var_constraints.rb +1 -0
- data/lib/gecoder/interface/enum_wrapper.rb +12 -3
- data/lib/gecoder/interface/model.rb +77 -56
- data/lib/gecoder/interface/search.rb +74 -5
- data/lib/gecoder/interface/variables.rb +117 -15
- data/lib/gecoder/version.rb +1 -1
- data/specs/binding_changes.rb +9 -5
- data/specs/bool_var.rb +8 -12
- data/specs/branch.rb +85 -19
- data/specs/constraints/arithmetic.rb +99 -71
- data/specs/constraints/bool_enum.rb +26 -18
- data/specs/constraints/boolean.rb +53 -49
- data/specs/constraints/cardinality.rb +33 -26
- data/specs/constraints/channel.rb +77 -6
- data/specs/constraints/connection.rb +352 -0
- data/specs/constraints/constraints.rb +10 -1
- data/specs/constraints/count.rb +79 -39
- data/specs/constraints/distinct.rb +128 -9
- data/specs/constraints/element.rb +26 -19
- data/specs/constraints/equality.rb +2 -1
- data/specs/constraints/int_domain.rb +19 -12
- data/specs/constraints/int_relation.rb +12 -6
- data/specs/constraints/linear.rb +30 -30
- data/specs/constraints/reification_sugar.rb +8 -4
- data/specs/constraints/set_domain.rb +24 -18
- data/specs/constraints/set_relation.rb +38 -23
- data/specs/constraints/sort.rb +12 -10
- data/specs/enum_wrapper.rb +9 -3
- data/specs/int_var.rb +8 -4
- data/specs/logging.rb +24 -0
- data/specs/model.rb +25 -7
- data/specs/search.rb +41 -1
- data/specs/set_var.rb +36 -7
- data/specs/spec_helper.rb +3 -10
- data/vendor/rust/rust/templates/FunctionDefinition.rusttpl +1 -1
- metadata +12 -3
- 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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
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
|
-
|
50
|
-
|
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
|
-
|
90
|
-
|
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
|
-
|
140
|
-
:strength)
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
-
|
28
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
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.
|
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
|