gecoder 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +21 -0
- data/README +9 -1
- data/ext/missing.cpp +1 -1
- data/ext/vararray.cpp +4 -0
- data/ext/vararray.h +2 -1
- data/lib/gecoder/bindings/bindings.rb +55 -5
- data/lib/gecoder/interface.rb +1 -0
- data/lib/gecoder/interface/binding_changes.rb +183 -81
- data/lib/gecoder/interface/branch.rb +1 -1
- data/lib/gecoder/interface/constraints.rb +121 -5
- data/lib/gecoder/interface/constraints/bool/boolean.rb +160 -0
- data/lib/gecoder/interface/constraints/bool_var_constraints.rb +23 -0
- data/lib/gecoder/interface/constraints/int/linear.rb +258 -0
- data/lib/gecoder/interface/constraints/int_enum/distinct.rb +66 -0
- data/lib/gecoder/interface/constraints/int_enum_constraints.rb +31 -0
- data/lib/gecoder/interface/constraints/int_var_constraints.rb +22 -0
- data/lib/gecoder/interface/constraints/reifiable_constraints.rb +57 -0
- data/lib/gecoder/interface/enum_wrapper.rb +39 -33
- data/lib/gecoder/interface/model.rb +44 -64
- data/lib/gecoder/interface/search.rb +40 -3
- data/lib/gecoder/interface/variables.rb +62 -0
- metadata +19 -8
- data/lib/gecoder/interface/constraints/distinct.rb +0 -15
- data/lib/gecoder/interface/constraints/linear.rb +0 -158
- data/lib/gecoder/interface/constraints/relation.rb +0 -76
@@ -0,0 +1,66 @@
|
|
1
|
+
module Gecode
|
2
|
+
module IntEnumMethods
|
3
|
+
# Specifies offsets to be used with a distinct constraint. The offsets can
|
4
|
+
# be specified one by one or as an array of offsets.
|
5
|
+
def with_offsets(*offsets)
|
6
|
+
if offsets.kind_of? Enumerable
|
7
|
+
offsets = *offsets
|
8
|
+
end
|
9
|
+
params = {:lhs => self, :offsets => offsets}
|
10
|
+
return Gecode::Constraints::IntEnum::Distinct::OffsetExpression.new(
|
11
|
+
@model, params)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Gecode::Constraints::IntEnum
|
17
|
+
class Expression
|
18
|
+
# Posts a distinct constraint on the variables in the enum.
|
19
|
+
def distinct(options = {})
|
20
|
+
if @params[:negate]
|
21
|
+
# The best we could implement it as from here would be a bunch of
|
22
|
+
# reified pairwise equality constraints.
|
23
|
+
raise Gecode::MissingConstraintError, 'A negated distinct is not ' +
|
24
|
+
'implemented.'
|
25
|
+
end
|
26
|
+
|
27
|
+
@model.add_constraint Distinct::DistinctConstraint.new(@model,
|
28
|
+
@params.update(Gecode::Constraints::OptionUtil.decode_options(options)))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# A module that gathers the classes and modules used in distinct constraints.
|
33
|
+
module Distinct
|
34
|
+
# Describes an expression started with an int var enum following with
|
35
|
+
# #with_offsets .
|
36
|
+
class OffsetExpression < Gecode::Constraints::IntEnum::Expression
|
37
|
+
include Gecode::Constraints::LeftHandSideMethods
|
38
|
+
|
39
|
+
def initialize(model, params)
|
40
|
+
@model = model
|
41
|
+
@params = params
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Produces an expression with offsets for the lhs module.
|
47
|
+
def expression(params)
|
48
|
+
params.update(@params)
|
49
|
+
Gecode::Constraints::IntEnum::Expression.new(@model, params)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Describes a distinct constraint (optionally with offsets).
|
54
|
+
class DistinctConstraint < Gecode::Constraints::Constraint
|
55
|
+
def post
|
56
|
+
# Bind lhs.
|
57
|
+
@params[:lhs] = @params[:lhs].to_int_var_array
|
58
|
+
|
59
|
+
# Fetch the parameters to Gecode.
|
60
|
+
params = @params.values_at(:offsets, :lhs, :strength)
|
61
|
+
params.delete_if{ |x| x.nil? }
|
62
|
+
Gecode::Raw::distinct(@model.active_space, *params)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gecode
|
2
|
+
module IntEnumMethods
|
3
|
+
include Gecode::Constraints::LeftHandSideMethods
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# Produces an expression for the lhs module.
|
8
|
+
def expression(params)
|
9
|
+
params.update(:lhs => self)
|
10
|
+
Constraints::IntEnum::Expression.new(@model, params)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# A module containing constraints that have enumerations of integer
|
15
|
+
# variables as left hand side.
|
16
|
+
module Constraints::IntEnum
|
17
|
+
# Expressions with int enums as left hand sides.
|
18
|
+
class Expression < Gecode::Constraints::Expression
|
19
|
+
# Raises TypeError unless the left hand side is an int enum.
|
20
|
+
def initialize(model, params)
|
21
|
+
super
|
22
|
+
|
23
|
+
unless params[:lhs].respond_to? :to_int_var_array
|
24
|
+
raise TypeError, 'Must have int enum as left hand side.'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'gecoder/interface/constraints/int_enum/distinct'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Gecode
|
2
|
+
class FreeIntVar
|
3
|
+
include Gecode::Constraints::LeftHandSideMethods
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# Produces an expression for the lhs module.
|
8
|
+
def expression(params)
|
9
|
+
params.update(:lhs => self)
|
10
|
+
Constraints::Int::Expression.new(@model, params)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# A module containing constraints that have int variables as left hand side
|
15
|
+
# (but not enumerations).
|
16
|
+
module Constraints::Int
|
17
|
+
class Expression < Gecode::Constraints::Expression
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'gecoder/interface/constraints/int/linear'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Gecode::Constraints
|
2
|
+
# Base class for all reifiable constraints.
|
3
|
+
class ReifiableConstraint < Constraint
|
4
|
+
# Gets the reification variable of the constraint, nil if none exists.
|
5
|
+
def reification_var
|
6
|
+
@params[:reif]
|
7
|
+
end
|
8
|
+
|
9
|
+
# Sets the reification variable of the constraint, nil if none should be
|
10
|
+
# used.
|
11
|
+
def reification_var=(new_var)
|
12
|
+
@params[:reif] = new_var
|
13
|
+
end
|
14
|
+
|
15
|
+
# Produces a disjunction of two reifiable constraints, producing a new
|
16
|
+
# reifiable constraint.
|
17
|
+
def |(constraint)
|
18
|
+
with_reification_variables(constraint) do |b1, b2|
|
19
|
+
# Create the disjunction constraint.
|
20
|
+
(b1 | b2).must_be.true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Produces a conjunction of two reifiable constraints, producing a new
|
25
|
+
# reifiable constraint.
|
26
|
+
def &(constraint)
|
27
|
+
with_reification_variables(constraint) do |b1, b2|
|
28
|
+
# Create the conjunction constraint.
|
29
|
+
(b1 & b2).must_be.true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Yields two boolean variables to the specified block. The first one is
|
36
|
+
# self's reification variable and the second one is the reification variable
|
37
|
+
# of the specified constraint. Reuses reification variables if possible,
|
38
|
+
# otherwise creates new ones.
|
39
|
+
def with_reification_variables(constraint, &block)
|
40
|
+
raise TypeError unless constraint.kind_of? ReifiableConstraint
|
41
|
+
|
42
|
+
# Set up the reification variables, using existing variables if they
|
43
|
+
# exist.
|
44
|
+
con1_holds = self.reification_var
|
45
|
+
con2_holds = constraint.reification_var
|
46
|
+
if con1_holds.nil?
|
47
|
+
con1_holds = @model.bool_var
|
48
|
+
self.reification_var = con1_holds
|
49
|
+
end
|
50
|
+
if con2_holds.nil?
|
51
|
+
con2_holds = @model.bool_var
|
52
|
+
constraint.reification_var = con2_holds
|
53
|
+
end
|
54
|
+
yield(con1_holds, con2_holds)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -8,32 +8,41 @@ module Gecode
|
|
8
8
|
unless enum.kind_of? Enumerable
|
9
9
|
raise TypeError, 'Only enumerables can be wrapped.'
|
10
10
|
end
|
11
|
+
if enum.empty?
|
12
|
+
raise ArgumentError, 'Enumerable must not be empty.'
|
13
|
+
end
|
11
14
|
|
12
|
-
|
13
|
-
|
15
|
+
if enum.first.kind_of? FreeIntVar
|
16
|
+
class <<enum
|
17
|
+
include Gecode::IntEnumMethods
|
18
|
+
end
|
19
|
+
elsif enum.first.kind_of? FreeBoolVar
|
20
|
+
class <<enum
|
21
|
+
include Gecode::BoolEnumMethods
|
22
|
+
end
|
23
|
+
else
|
24
|
+
raise TypeError, "Enumerable doesn't contain variables #{enum.inspect}."
|
14
25
|
end
|
26
|
+
|
15
27
|
enum.model = self
|
16
28
|
return enum
|
17
29
|
end
|
18
30
|
end
|
19
31
|
|
32
|
+
# A module containing the methods needed by enumerations containing variables.
|
33
|
+
module EnumMethods
|
34
|
+
attr_accessor :model
|
35
|
+
# Gets the current space of the model the array is connected to.
|
36
|
+
def active_space
|
37
|
+
@model.active_space
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
20
41
|
# A module containing the methods needed by enumerations containing int
|
21
42
|
# variables. Requires that it's included in an enumerable.
|
22
|
-
module
|
23
|
-
|
24
|
-
|
25
|
-
IntVarEnumConstraintExpression.new(active_space, to_int_var_array)
|
26
|
-
end
|
27
|
-
alias_method :must_be, :must
|
28
|
-
|
29
|
-
# Specifies that the negation of a constraint must hold for the integer
|
30
|
-
# variable.
|
31
|
-
def must_not
|
32
|
-
IntVarEnumConstraintExpression.new(active_space, to_int_var_array,
|
33
|
-
true)
|
34
|
-
end
|
35
|
-
alias_method :must_not_be, :must_not
|
36
|
-
|
43
|
+
module IntEnumMethods
|
44
|
+
include EnumMethods
|
45
|
+
|
37
46
|
# Returns an int variable array with all the bound variables.
|
38
47
|
def to_int_var_array
|
39
48
|
elements = to_a
|
@@ -41,24 +50,21 @@ module Gecode
|
|
41
50
|
elements.each_with_index{ |var, index| arr[index] = var.bind }
|
42
51
|
return arr
|
43
52
|
end
|
44
|
-
|
45
|
-
attr_accessor :model
|
46
|
-
# Gets the current space of the model the array is connected to.
|
47
|
-
def active_space
|
48
|
-
@model.active_space
|
49
|
-
end
|
53
|
+
alias_method :to_var_array, :to_int_var_array
|
50
54
|
end
|
51
55
|
|
52
|
-
#
|
53
|
-
# variables
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
#
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
# A module containing the methods needed by enumerations containing boolean
|
57
|
+
# variables. Requires that it's included in an enumerable.
|
58
|
+
module BoolEnumMethods
|
59
|
+
include EnumMethods
|
60
|
+
|
61
|
+
# Returns a bool variable array with all the bound variables.
|
62
|
+
def to_bool_var_array
|
63
|
+
elements = to_a
|
64
|
+
arr = Gecode::Raw::BoolVarArray.new(active_space, elements.size)
|
65
|
+
elements.each_with_index{ |var, index| arr[index] = var.bind }
|
66
|
+
return arr
|
62
67
|
end
|
68
|
+
alias_method :to_var_array, :to_bool_var_array
|
63
69
|
end
|
64
70
|
end
|
@@ -1,21 +1,9 @@
|
|
1
1
|
module Gecode
|
2
|
-
# Model is the base class that all models must inherit from.
|
3
|
-
# constructor must be called.
|
2
|
+
# Model is the base class that all models must inherit from.
|
4
3
|
class Model
|
5
|
-
|
6
|
-
|
7
|
-
# initialization. The model binds the int variables to the current space
|
8
|
-
# upon use.
|
4
|
+
attr :constraints
|
5
|
+
protected :constraints
|
9
6
|
|
10
|
-
# The base from which searches are made.
|
11
|
-
attr :base_space
|
12
|
-
# The currently active space (the one which variables refer to).
|
13
|
-
attr :active_space
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
@active_space = @base_space = Gecode::Raw::Space.new
|
17
|
-
end
|
18
|
-
|
19
7
|
# Creates a new integer variable with the specified domain. The domain can
|
20
8
|
# either be a range or a number of elements.
|
21
9
|
def int_var(*domain_args)
|
@@ -38,6 +26,44 @@ module Gecode
|
|
38
26
|
return wrap_enum(variables)
|
39
27
|
end
|
40
28
|
|
29
|
+
# Creates a new boolean variable.
|
30
|
+
def bool_var(*domain_args)
|
31
|
+
index = active_space.new_bool_vars.first
|
32
|
+
FreeBoolVar.new(self, index)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Creates an array containing the specified number of boolean variables.
|
36
|
+
def bool_var_array(count)
|
37
|
+
variables = []
|
38
|
+
active_space.new_bool_vars(count).each do |index|
|
39
|
+
variables << FreeBoolVar.new(self, index)
|
40
|
+
end
|
41
|
+
return wrap_enum(variables)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Retrieves the currently active space (the one which variables refer to).
|
45
|
+
def active_space
|
46
|
+
@active_space ||= base_space
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieves the base from which searches are made.
|
50
|
+
def base_space
|
51
|
+
@base_space ||= Gecode::Raw::Space.new
|
52
|
+
end
|
53
|
+
|
54
|
+
# Adds the specified constraint to the model. Returns the newly added
|
55
|
+
# constraint.
|
56
|
+
def add_constraint(constraint)
|
57
|
+
constraints << constraint
|
58
|
+
return constraint
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def constraints
|
64
|
+
@constraints ||= []
|
65
|
+
end
|
66
|
+
|
41
67
|
private
|
42
68
|
|
43
69
|
# Returns the range of the specified domain arguments, which can either be
|
@@ -52,9 +78,11 @@ module Gecode
|
|
52
78
|
max = domain_args.max
|
53
79
|
else
|
54
80
|
element = domain_args.first
|
55
|
-
if element.respond_to?
|
81
|
+
if element.respond_to?(:begin) and element.respond_to?(:end) and
|
82
|
+
element.respond_to?(:exclude_end?)
|
56
83
|
min = element.begin
|
57
84
|
max = element.end
|
85
|
+
max -= 1 if element.exclude_end?
|
58
86
|
else
|
59
87
|
min = max = element
|
60
88
|
end
|
@@ -79,52 +107,4 @@ module Gecode
|
|
79
107
|
return var
|
80
108
|
end
|
81
109
|
end
|
82
|
-
|
83
|
-
# An IntVar that is bound to a model, but not to a particular space.
|
84
|
-
class FreeIntVar
|
85
|
-
attr_accessor :model
|
86
|
-
|
87
|
-
# Creates an int variable with the specified index.
|
88
|
-
def initialize(model, index)
|
89
|
-
@model = model
|
90
|
-
@index = index
|
91
|
-
@bound_space = @bound_var = nil
|
92
|
-
end
|
93
|
-
|
94
|
-
# Delegate methods we can't handle to the bound int variable if possible.
|
95
|
-
def method_missing(name, *args)
|
96
|
-
if Gecode::Raw::IntVar.instance_methods.include? name.to_s
|
97
|
-
bind.send(name, *args)
|
98
|
-
else
|
99
|
-
super
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# Binds the int variable to the currently active space of the model,
|
104
|
-
# returning the bound int variable.
|
105
|
-
def bind
|
106
|
-
space = active_space
|
107
|
-
unless @bound_space == space
|
108
|
-
# We have not bound the variable to this space, so we do it now.
|
109
|
-
@bound = space.int_var(@index)
|
110
|
-
@bound_space = space
|
111
|
-
end
|
112
|
-
return @bound
|
113
|
-
end
|
114
|
-
|
115
|
-
def inspect
|
116
|
-
if assigned?
|
117
|
-
"#<FreeIntVar range: val.to_s>"
|
118
|
-
else
|
119
|
-
"#<FreeIntVar range: #{min}..#{max}>"
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
private
|
124
|
-
|
125
|
-
# Returns the space that the int variable should bind to when needed.
|
126
|
-
def active_space
|
127
|
-
@model.active_space
|
128
|
-
end
|
129
|
-
end
|
130
110
|
end
|
@@ -12,12 +12,49 @@ module Gecode
|
|
12
12
|
# to that solution. Returns the model if a solution was found, nil
|
13
13
|
# otherwise.
|
14
14
|
def solve!
|
15
|
-
|
16
|
-
dfs = Gecode::Raw::DFS.new(@base_space, COPY_DIST, ADAPTATION_DIST, stop)
|
17
|
-
space = dfs.next
|
15
|
+
space = dfs_engine.next
|
18
16
|
return nil if space.nil?
|
19
17
|
@active_space = space
|
20
18
|
return self
|
21
19
|
end
|
20
|
+
|
21
|
+
# Returns to the original state, before any search was made (but propagation
|
22
|
+
# might have been performed). Returns the reset model.
|
23
|
+
def reset!
|
24
|
+
@active_space = base_space
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Yields the first solution (if any) to the block. If no solution is found
|
29
|
+
# then the block is not used. Returns the result of the block (nil in case
|
30
|
+
# the block wasn't run).
|
31
|
+
def solution(&block)
|
32
|
+
solution = self.solve!
|
33
|
+
res = yield solution unless solution.nil?
|
34
|
+
self.reset!
|
35
|
+
return res
|
36
|
+
end
|
37
|
+
|
38
|
+
# Yields each solution that the model has.
|
39
|
+
def each_solution(&block)
|
40
|
+
dfs = dfs_engine
|
41
|
+
while not (@active_space = dfs.next).nil?
|
42
|
+
yield self
|
43
|
+
end
|
44
|
+
self.reset!
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Creates an DFS engine for the search, executing any unexecuted
|
50
|
+
# constraints first.
|
51
|
+
def dfs_engine
|
52
|
+
# Execute constraints.
|
53
|
+
constraints.each{ |con| con.post }
|
54
|
+
constraints.clear # Empty the queue.
|
55
|
+
|
56
|
+
stop = Gecode::Raw::Search::Stop.new
|
57
|
+
Gecode::Raw::DFS.new(active_space, COPY_DIST, ADAPTATION_DIST, stop)
|
58
|
+
end
|
22
59
|
end
|
23
60
|
end
|