gecoder 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|