gecoder 0.3.0 → 0.4.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 +17 -2
- data/README +7 -1
- data/Rakefile +4 -0
- data/lib/gecoder/interface/constraints/bool/boolean.rb +1 -4
- data/lib/gecoder/interface/constraints/int/arithmetic.rb +77 -0
- data/lib/gecoder/interface/constraints/int/domain.rb +50 -0
- data/lib/gecoder/interface/constraints/int/linear.rb +12 -44
- data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +72 -0
- data/lib/gecoder/interface/constraints/int_enum/channel.rb +32 -0
- data/lib/gecoder/interface/constraints/int_enum/count.rb +90 -0
- data/lib/gecoder/interface/constraints/int_enum/distinct.rb +3 -8
- data/lib/gecoder/interface/constraints/int_enum/element.rb +75 -0
- data/lib/gecoder/interface/constraints/int_enum/equality.rb +31 -0
- data/lib/gecoder/interface/constraints/int_enum/sort.rb +104 -0
- data/lib/gecoder/interface/constraints/int_enum_constraints.rb +6 -0
- data/lib/gecoder/interface/constraints/int_var_constraints.rb +2 -0
- data/lib/gecoder/interface/constraints/reifiable_constraints.rb +21 -0
- data/lib/gecoder/interface/constraints.rb +57 -6
- data/lib/gecoder/interface/enum_matrix.rb +64 -0
- data/lib/gecoder/interface/enum_wrapper.rb +33 -5
- data/lib/gecoder/interface/model.rb +36 -6
- data/lib/gecoder/interface.rb +1 -0
- data/lib/gecoder/version.rb +4 -0
- data/lib/gecoder.rb +1 -0
- data/specs/binding_changes.rb +72 -0
- data/specs/bool_var.rb +20 -0
- data/specs/branch.rb +104 -0
- data/specs/constraints/arithmetic.rb +227 -0
- data/specs/constraints/boolean.rb +132 -0
- data/specs/constraints/channel.rb +55 -0
- data/specs/constraints/constraint_helper.rb +48 -0
- data/specs/constraints/constraints.rb +28 -0
- data/specs/constraints/count.rb +99 -0
- data/specs/constraints/distinct.rb +99 -0
- data/specs/constraints/domain.rb +56 -0
- data/specs/constraints/element.rb +128 -0
- data/specs/constraints/equality.rb +30 -0
- data/specs/constraints/linear.rb +166 -0
- data/specs/constraints/reification_sugar.rb +92 -0
- data/specs/constraints/relation.rb +72 -0
- data/specs/constraints/sort.rb +173 -0
- data/specs/enum_matrix.rb +43 -0
- data/specs/enum_wrapper.rb +100 -0
- data/specs/int_var.rb +108 -0
- data/specs/model.rb +84 -0
- data/specs/search.rb +157 -0
- data/specs/spec_helper.rb +63 -0
- data/specs/tmp +135 -0
- data/tasks/all_tasks.rb +1 -0
- data/tasks/distribution.rake +64 -0
- data/tasks/rcov.rake +17 -0
- data/tasks/specs.rake +16 -0
- data/tasks/svn.rake +11 -0
- data/tasks/website.rake +58 -0
- data/vendor/rust/include/rust_conversions.hh +1 -2
- metadata +53 -2
@@ -0,0 +1,104 @@
|
|
1
|
+
module Gecode::Constraints::IntEnum
|
2
|
+
class Expression
|
3
|
+
# Initiates a sort constraint. Beyond the common options the sort constraint
|
4
|
+
# can also take the following options:
|
5
|
+
#
|
6
|
+
# [:as] Defines a target (must be an int variable enumerable) that will
|
7
|
+
# hold the sorted version of the original enumerable. The original
|
8
|
+
# enumerable will not be affected (i.e. will not necessarily be
|
9
|
+
# sorted)
|
10
|
+
# [:order] Sets an int variable enumerable that should be used to store the
|
11
|
+
# order of the original enum's variables when sorted. The original
|
12
|
+
# enumerable will not be affected (i.e. will not necessarily be
|
13
|
+
# sorted)
|
14
|
+
#
|
15
|
+
# If neither of those options are specified then the original enumerable
|
16
|
+
# will be constrained to be sorted (otherwise not). Sort constraints with
|
17
|
+
# options do not allow negation.
|
18
|
+
def sorted(options = {})
|
19
|
+
# Extract and check options.
|
20
|
+
target = options.delete(:as)
|
21
|
+
order = options.delete(:order)
|
22
|
+
unless target.nil? or target.respond_to? :to_int_var_array
|
23
|
+
raise TypeError, 'Expected int var enum as :as, got ' +
|
24
|
+
"#{target.class}."
|
25
|
+
end
|
26
|
+
unless order.nil? or order.respond_to? :to_int_var_array
|
27
|
+
raise TypeError, 'Expected int var enum as :order, got ' +
|
28
|
+
"#{order.class}."
|
29
|
+
end
|
30
|
+
|
31
|
+
# Extract standard options and convert to constraint.
|
32
|
+
@params.update(Gecode::Constraints::Util.decode_options(options))
|
33
|
+
if target.nil? and order.nil?
|
34
|
+
@model.add_constraint Sort::SortConstraint.new(@model, @params)
|
35
|
+
else
|
36
|
+
# Do not allow negation.
|
37
|
+
if @params[:negate]
|
38
|
+
raise Gecode::MissingConstraintError, 'A negated sort with options ' +
|
39
|
+
'is not implemented.'
|
40
|
+
end
|
41
|
+
|
42
|
+
@params.update(:target => target, :order => order)
|
43
|
+
@model.add_constraint Sort::SortConstraintWithOptions.new(@model,
|
44
|
+
@params)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# A module that gathers the classes and modules used in sort constraints.
|
50
|
+
module Sort
|
51
|
+
# Describes a sort constraint with target and order.
|
52
|
+
class SortConstraintWithOptions < Gecode::Constraints::Constraint
|
53
|
+
def post
|
54
|
+
if @params[:target].nil?
|
55
|
+
# We must have a target.
|
56
|
+
lhs = @params[:lhs]
|
57
|
+
@params[:target] = @model.int_var_array(lhs.size, lhs.domain_range)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Prepare the parameters.
|
61
|
+
params = @params.values_at(:lhs, :target, :order, :strength).map do |param|
|
62
|
+
if param.respond_to? :to_int_var_array
|
63
|
+
param.to_int_var_array
|
64
|
+
else
|
65
|
+
param
|
66
|
+
end
|
67
|
+
end.delete_if{ |param| param.nil? }
|
68
|
+
# Post the constraint.
|
69
|
+
Gecode::Raw::sortedness(@model.active_space, *params)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Describes a sort constraint.
|
74
|
+
class SortConstraint < Gecode::Constraints::ReifiableConstraint
|
75
|
+
def post
|
76
|
+
lhs, strength, reif_var = @params.values_at(:lhs, :strength, :reif)
|
77
|
+
using_reification = !reif_var.nil?
|
78
|
+
|
79
|
+
# We translate the constraint into n-1 relation constraints.
|
80
|
+
options = {:strength =>
|
81
|
+
Gecode::Constraints::Util::PROPAGATION_STRENGTHS.invert[strength]}
|
82
|
+
if using_reification
|
83
|
+
reification_variables = @model.bool_var_array(lhs.size - 1)
|
84
|
+
end
|
85
|
+
(lhs.size - 1).times do |i|
|
86
|
+
first, second = lhs[i, 2]
|
87
|
+
rel_options = options.clone
|
88
|
+
if using_reification
|
89
|
+
# Reify each relation constraint and then bind them all together.
|
90
|
+
rel_options[:reify] = reification_variables[i]
|
91
|
+
end
|
92
|
+
first.must_be.less_than_or_equal_to(second, rel_options)
|
93
|
+
end
|
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)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
negate_using_reification
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -29,3 +29,9 @@ module Gecode
|
|
29
29
|
end
|
30
30
|
|
31
31
|
require 'gecoder/interface/constraints/int_enum/distinct'
|
32
|
+
require 'gecoder/interface/constraints/int_enum/equality'
|
33
|
+
require 'gecoder/interface/constraints/int_enum/channel'
|
34
|
+
require 'gecoder/interface/constraints/int_enum/element'
|
35
|
+
require 'gecoder/interface/constraints/int_enum/count'
|
36
|
+
require 'gecoder/interface/constraints/int_enum/sort'
|
37
|
+
require 'gecoder/interface/constraints/int_enum/arithmetic'
|
@@ -53,5 +53,26 @@ module Gecode::Constraints
|
|
53
53
|
end
|
54
54
|
yield(con1_holds, con2_holds)
|
55
55
|
end
|
56
|
+
|
57
|
+
# If called the negation of the constraint will be handled using the
|
58
|
+
# reification variable. This means that the post method (which has to be
|
59
|
+
# defined prior to calling this method) doesn't have to bother about
|
60
|
+
# negation.
|
61
|
+
def self.negate_using_reification
|
62
|
+
class_eval do
|
63
|
+
alias_method :post_without_negation, :post
|
64
|
+
|
65
|
+
def post
|
66
|
+
if @params[:negate]
|
67
|
+
if @params[:reif].nil?
|
68
|
+
# Create a reification variable if none exists.
|
69
|
+
@params[:reif] = @model.bool_var
|
70
|
+
end
|
71
|
+
@params[:reif].must_be.false
|
72
|
+
end
|
73
|
+
post_without_negation
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
56
77
|
end
|
57
78
|
end
|
@@ -50,6 +50,35 @@ module Gecode
|
|
50
50
|
@model = model
|
51
51
|
@params = params
|
52
52
|
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Creates aliases for any defined comparison methods.
|
57
|
+
def self.alias_comparison_methods
|
58
|
+
Gecode::Constraints::Util::COMPARISON_ALIASES.each_pair do |orig, aliases|
|
59
|
+
if instance_methods.include?(orig.to_s)
|
60
|
+
aliases.each do |name|
|
61
|
+
alias_method(name, orig)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Describes a constraint expression that has yet to be completed. I.e. a
|
69
|
+
# form of must has not yet been called, but some method has been called to
|
70
|
+
# initiate the expression. An example is distinct with offsets:
|
71
|
+
#
|
72
|
+
# enum.with_offsets(0..n).must_be.distinct
|
73
|
+
#
|
74
|
+
# The call of with_offsets initiates the constraint as a stub, even though
|
75
|
+
# must has not yet been called.
|
76
|
+
class ExpressionStub
|
77
|
+
# Constructs a new expression with the specified parameters.
|
78
|
+
def initialize(model, params)
|
79
|
+
@model = model
|
80
|
+
@params = params
|
81
|
+
end
|
53
82
|
end
|
54
83
|
|
55
84
|
# Base class for all constraints.
|
@@ -68,11 +97,8 @@ module Gecode
|
|
68
97
|
end
|
69
98
|
end
|
70
99
|
|
71
|
-
# A module that provides some utility-methods for
|
72
|
-
|
73
|
-
module OptionUtil
|
74
|
-
private
|
75
|
-
|
100
|
+
# A module that provides some utility-methods for constraints.
|
101
|
+
module Util
|
76
102
|
# Maps the name used in options to the value used in Gecode for
|
77
103
|
# propagation strengths.
|
78
104
|
PROPAGATION_STRENGTHS = {
|
@@ -82,7 +108,32 @@ module Gecode
|
|
82
108
|
:domain => Gecode::Raw::ICL_DOM
|
83
109
|
}
|
84
110
|
|
85
|
-
|
111
|
+
# Maps the names of the methods to the corresponding integer relation
|
112
|
+
# type in Gecode.
|
113
|
+
RELATION_TYPES = {
|
114
|
+
:== => Gecode::Raw::IRT_EQ,
|
115
|
+
:<= => Gecode::Raw::IRT_LQ,
|
116
|
+
:< => Gecode::Raw::IRT_LE,
|
117
|
+
:>= => Gecode::Raw::IRT_GQ,
|
118
|
+
:> => Gecode::Raw::IRT_GR }
|
119
|
+
# The same as above, but negated.
|
120
|
+
NEGATED_RELATION_TYPES = {
|
121
|
+
:== => Gecode::Raw::IRT_NQ,
|
122
|
+
:<= => Gecode::Raw::IRT_GR,
|
123
|
+
:< => Gecode::Raw::IRT_GQ,
|
124
|
+
:>= => Gecode::Raw::IRT_LE,
|
125
|
+
:> => Gecode::Raw::IRT_LQ
|
126
|
+
}
|
127
|
+
|
128
|
+
# Various method aliases for comparison methods. Maps the original
|
129
|
+
# (symbol) name to an array of aliases.
|
130
|
+
COMPARISON_ALIASES = {
|
131
|
+
:== => [:equal, :equal_to],
|
132
|
+
:> => [:greater, :greater_than],
|
133
|
+
:>= => [:greater_or_equal, :greater_than_or_equal_to],
|
134
|
+
:< => [:less, :less_than],
|
135
|
+
:<= => [:less_or_equal, :less_than_or_equal_to]
|
136
|
+
}
|
86
137
|
|
87
138
|
module_function
|
88
139
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
|
3
|
+
module Gecode::Util
|
4
|
+
# Methods that make a matrix an enumerable.
|
5
|
+
module MatrixEnumMethods
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Iterates over every element in the matrix.
|
9
|
+
def each(&block)
|
10
|
+
row_size.times do |i|
|
11
|
+
column_size.times do |j|
|
12
|
+
yield self[i,j]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Extends Matrix so that it's an enumerable.
|
19
|
+
class EnumMatrix < Matrix
|
20
|
+
include MatrixEnumMethods
|
21
|
+
|
22
|
+
def row(i)
|
23
|
+
wrap_if_wrapped make_vector_enumerable(super)
|
24
|
+
end
|
25
|
+
|
26
|
+
def column(i)
|
27
|
+
wrap_if_wrapped make_vector_enumerable(super)
|
28
|
+
end
|
29
|
+
|
30
|
+
def minor(*args)
|
31
|
+
matrix = super
|
32
|
+
class <<matrix
|
33
|
+
include MatrixEnumMethods
|
34
|
+
end
|
35
|
+
return wrap_if_wrapped(matrix)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Makes the specified vector enumerable.
|
41
|
+
def make_vector_enumerable(vector)
|
42
|
+
class <<vector
|
43
|
+
include Enumerable
|
44
|
+
|
45
|
+
# Iterates over every element in the matrix.
|
46
|
+
def each(&block)
|
47
|
+
size.times do |i|
|
48
|
+
yield self[i]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
return vector
|
53
|
+
end
|
54
|
+
|
55
|
+
# Wraps the specified enumerable if the matrix itself is already wrapped.
|
56
|
+
def wrap_if_wrapped(enum)
|
57
|
+
if respond_to? :model
|
58
|
+
model.wrap_enum(enum)
|
59
|
+
else
|
60
|
+
enum
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,25 +1,28 @@
|
|
1
1
|
module Gecode
|
2
2
|
class Model
|
3
|
-
private
|
4
|
-
|
5
3
|
# Wraps a custom enumerable so that constraints can be specified using it.
|
6
4
|
# The argument is altered and returned.
|
7
5
|
def wrap_enum(enum)
|
8
6
|
unless enum.kind_of? Enumerable
|
9
7
|
raise TypeError, 'Only enumerables can be wrapped.'
|
10
8
|
end
|
11
|
-
|
9
|
+
elements = enum.to_a
|
10
|
+
if elements.empty?
|
12
11
|
raise ArgumentError, 'Enumerable must not be empty.'
|
13
12
|
end
|
14
13
|
|
15
|
-
if
|
14
|
+
if elements.map{ |var| var.kind_of? FreeIntVar }.all?
|
16
15
|
class <<enum
|
17
16
|
include Gecode::IntEnumMethods
|
18
17
|
end
|
19
|
-
elsif
|
18
|
+
elsif elements.map{ |var| var.kind_of? FreeBoolVar }.all?
|
20
19
|
class <<enum
|
21
20
|
include Gecode::BoolEnumMethods
|
22
21
|
end
|
22
|
+
elsif elements.map{ |var| var.kind_of? Fixnum }.all?
|
23
|
+
class <<enum
|
24
|
+
include Gecode::FixnumEnumMethods
|
25
|
+
end
|
23
26
|
else
|
24
27
|
raise TypeError, "Enumerable doesn't contain variables #{enum.inspect}."
|
25
28
|
end
|
@@ -51,6 +54,19 @@ module Gecode
|
|
51
54
|
return arr
|
52
55
|
end
|
53
56
|
alias_method :to_var_array, :to_int_var_array
|
57
|
+
|
58
|
+
# Returns the smallest range that contains the domains of all integer
|
59
|
+
# variables involved.
|
60
|
+
def domain_range
|
61
|
+
inject(nil) do |range, var|
|
62
|
+
next var.min..var.max if range.nil?
|
63
|
+
min = var.min
|
64
|
+
max = var.max
|
65
|
+
range = min..range.last if min < range.first
|
66
|
+
range = range.first..max if max > range.last
|
67
|
+
range
|
68
|
+
end
|
69
|
+
end
|
54
70
|
end
|
55
71
|
|
56
72
|
# A module containing the methods needed by enumerations containing boolean
|
@@ -67,4 +83,16 @@ module Gecode
|
|
67
83
|
end
|
68
84
|
alias_method :to_var_array, :to_bool_var_array
|
69
85
|
end
|
86
|
+
|
87
|
+
# A module containing the methods needed by enumerations containing fixnums.
|
88
|
+
# Requires that it's included in an enumerable.
|
89
|
+
module FixnumEnumMethods
|
90
|
+
include EnumMethods
|
91
|
+
|
92
|
+
# Returns the smallest range that contains the domains of all integer
|
93
|
+
# variables involved.
|
94
|
+
def domain_range
|
95
|
+
min..max
|
96
|
+
end
|
97
|
+
end
|
70
98
|
end
|
@@ -26,6 +26,24 @@ module Gecode
|
|
26
26
|
return wrap_enum(variables)
|
27
27
|
end
|
28
28
|
|
29
|
+
# Creates a matrix containing the specified number rows and columns of
|
30
|
+
# integer variables with the specified domain. The domain can either be a
|
31
|
+
# range or a number of elements.
|
32
|
+
def int_var_matrix(row_count, col_count, *domain_args)
|
33
|
+
# TODO: Maybe the custom domain should be specified as an array instead?
|
34
|
+
|
35
|
+
range = domain_range(*domain_args)
|
36
|
+
indices = active_space.new_int_vars(range.begin, range.end,
|
37
|
+
row_count*col_count)
|
38
|
+
rows = []
|
39
|
+
row_count.times do |i|
|
40
|
+
rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
|
41
|
+
construct_int_var(index, *domain_args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
return wrap_enum(Util::EnumMatrix.rows(rows, false))
|
45
|
+
end
|
46
|
+
|
29
47
|
# Creates a new boolean variable.
|
30
48
|
def bool_var(*domain_args)
|
31
49
|
index = active_space.new_bool_vars.first
|
@@ -41,6 +59,19 @@ module Gecode
|
|
41
59
|
return wrap_enum(variables)
|
42
60
|
end
|
43
61
|
|
62
|
+
# Creates a matrix containing the specified number rows and columns of
|
63
|
+
# boolean variables.
|
64
|
+
def bool_var_matrix(row_count, col_count)
|
65
|
+
indices = active_space.new_bool_vars(row_count*col_count)
|
66
|
+
rows = []
|
67
|
+
row_count.times do |i|
|
68
|
+
rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
|
69
|
+
FreeBoolVar.new(self, index)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
return wrap_enum(Util::EnumMatrix.rows(rows, false))
|
73
|
+
end
|
74
|
+
|
44
75
|
# Retrieves the currently active space (the one which variables refer to).
|
45
76
|
def active_space
|
46
77
|
@active_space ||= base_space
|
@@ -97,12 +128,11 @@ module Gecode
|
|
97
128
|
|
98
129
|
if domain_args.size > 1
|
99
130
|
# Place an additional domain constraint on the variable with the
|
100
|
-
# arguments as domain.
|
101
|
-
#
|
102
|
-
#
|
103
|
-
|
104
|
-
|
105
|
-
Gecode::Raw::ICL_DEF)
|
131
|
+
# arguments as domain. We post it directly since there's no reason not
|
132
|
+
# to and the user might otherwise get unexpected domains when inspecting
|
133
|
+
# the variable before solving.
|
134
|
+
constraint = var.must_be.in domain_args
|
135
|
+
@constraints.delete(constraint).post
|
106
136
|
end
|
107
137
|
return var
|
108
138
|
end
|
data/lib/gecoder/interface.rb
CHANGED
data/lib/gecoder.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe 'Space', :shared => true do
|
4
|
+
it 'should give different indices when creating int variables' do
|
5
|
+
@space.new_int_vars(0, 17).should_not equal(@space.new_int_vars(0, 17))
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should give different indices when creating bool variables' do
|
9
|
+
@space.new_bool_vars().should_not equal(@space.new_bool_vars())
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should give different indices when creating multiple int variables' do
|
13
|
+
@space.new_int_vars(0, 17, 17).uniq.size.should equal(17)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should give different indices when creating multiple bool variables' do
|
17
|
+
@space.new_bool_vars(17).uniq.size.should equal(17)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should not return nil for created int variables' do
|
21
|
+
@space.new_int_vars(0, 17, 4).each do |i|
|
22
|
+
@space.int_var(i).should_not be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should not return nil for created int variables' do
|
27
|
+
@space.new_bool_vars(4).each do |i|
|
28
|
+
@space.bool_var(i).should_not be_nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return nil when requesting int variables with negative indices' do
|
33
|
+
@space.int_var(-1).should be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should return nil when requesting bool variables with negative indices' do
|
37
|
+
@space.bool_var(-1).should be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Gecode::Raw::Space, ' (new)' do
|
42
|
+
before do
|
43
|
+
@space = Gecode::Raw::Space.new
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return nil when requesting int variables' do
|
47
|
+
@space.int_var(0).should be_nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return nil when requesting bool variables' do
|
51
|
+
@space.bool_var(0).should be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it_should_behave_like 'Space'
|
55
|
+
end
|
56
|
+
|
57
|
+
describe Gecode::Raw::Space, ' (with items)' do
|
58
|
+
before do
|
59
|
+
@space = Gecode::Raw::Space.new
|
60
|
+
@first = @space.new_int_vars(1, 4).first
|
61
|
+
@second = @space.new_int_vars(-5, 5).first
|
62
|
+
end
|
63
|
+
|
64
|
+
it_should_behave_like 'Space'
|
65
|
+
|
66
|
+
it 'should give int variables with the correct domains' do
|
67
|
+
@space.int_var(@first).min.should equal(1)
|
68
|
+
@space.int_var(@first).max.should equal(4)
|
69
|
+
@space.int_var(@second).min.should equal(-5)
|
70
|
+
@space.int_var(@second).max.should equal(5)
|
71
|
+
end
|
72
|
+
end
|
data/specs/bool_var.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe 'non-empty bool variable', :shared => true do
|
4
|
+
it 'should give a NoMethodError when calling a method that doesn\'t exist' do
|
5
|
+
lambda{ @var.this_method_does_not_exists }.should raise_error(NoMethodError)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Gecode::FreeBoolVar, '(not assigned)' do
|
10
|
+
before do
|
11
|
+
model = Gecode::Model.new
|
12
|
+
@var = model.bool_var
|
13
|
+
end
|
14
|
+
|
15
|
+
it_should_behave_like 'non-empty bool variable'
|
16
|
+
|
17
|
+
it 'should not be assigned' do
|
18
|
+
@var.should_not be_assigned
|
19
|
+
end
|
20
|
+
end
|
data/specs/branch.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class BranchSampleProblem < Gecode::Model
|
4
|
+
attr :vars
|
5
|
+
attr :bools
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@vars = int_var_array(2, 0..3)
|
9
|
+
@bools = bool_var_array(2)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Gecode::Model, ' (branch)' do
|
14
|
+
before do
|
15
|
+
@model = BranchSampleProblem.new
|
16
|
+
@vars = @model.vars
|
17
|
+
@bools = @model.bools
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should pass the variables given in int arrays' do
|
21
|
+
Gecode::Raw.should_receive(:branch).once.and_return{ |s, vars, x, y| vars }
|
22
|
+
int_var_array = @model.branch_on @vars
|
23
|
+
int_var_array.size.should equal(2)
|
24
|
+
2.times do |i|
|
25
|
+
int_var_array.at(i).should have_domain(0..3)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should pass the variables given in bool arrays' do
|
30
|
+
Gecode::Raw.should_receive(:branch).once.and_return{ |s, vars, x, y| vars }
|
31
|
+
bool_var_array = @model.branch_on @bools
|
32
|
+
bool_var_array.size.should equal(2)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should default to :none and :min' do
|
36
|
+
Gecode::Raw.should_receive(:branch).once.with(@model.active_space,
|
37
|
+
anything, Gecode::Raw::BVAR_NONE, Gecode::Raw::BVAL_MIN)
|
38
|
+
@model.branch_on @vars
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should ensure that branched int variables are assigned in a solution' do
|
42
|
+
@model.branch_on @vars
|
43
|
+
@model.solve!.vars.each{ |var| var.should be_assigned }
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should ensure that branched bool variables are assigned in a solution' do
|
47
|
+
@model.branch_on @bools
|
48
|
+
@model.solve!.bools.each{ |var| var.should be_assigned }
|
49
|
+
end
|
50
|
+
|
51
|
+
supported_var_selectors = {
|
52
|
+
:none => Gecode::Raw::BVAR_NONE,
|
53
|
+
:smallest_min => Gecode::Raw::BVAR_MIN_MIN,
|
54
|
+
:largest_min => Gecode::Raw::BVAR_MIN_MAX,
|
55
|
+
:smallest_max => Gecode::Raw::BVAR_MAX_MIN,
|
56
|
+
:largest_max => Gecode::Raw::BVAR_MAX_MAX,
|
57
|
+
:smallest_size => Gecode::Raw::BVAR_SIZE_MIN,
|
58
|
+
:largest_size => Gecode::Raw::BVAR_SIZE_MAX,
|
59
|
+
:smallest_degree => Gecode::Raw::BVAR_DEGREE_MIN,
|
60
|
+
:largest_degree => Gecode::Raw::BVAR_DEGREE_MAX,
|
61
|
+
:smallest_min_regret => Gecode::Raw::BVAR_REGRET_MIN_MIN,
|
62
|
+
:largest_min_regret => Gecode::Raw::BVAR_REGRET_MIN_MAX,
|
63
|
+
:smallest_max_regret => Gecode::Raw::BVAR_REGRET_MAX_MIN,
|
64
|
+
:largest_max_regret => Gecode::Raw::BVAR_REGRET_MAX_MAX
|
65
|
+
}.each_pair do |name, gecode_const|
|
66
|
+
it "should support #{name} as variable selection strategy" do
|
67
|
+
Gecode::Raw.should_receive(:branch).once.with(@model.active_space,
|
68
|
+
anything, gecode_const, an_instance_of(Numeric))
|
69
|
+
@model.branch_on @vars, :variable => name
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
supported_val_selectors = {
|
74
|
+
:min => Gecode::Raw::BVAL_MIN,
|
75
|
+
:med => Gecode::Raw::BVAL_MED,
|
76
|
+
:max => Gecode::Raw::BVAL_MAX,
|
77
|
+
:split_min => Gecode::Raw::BVAL_SPLIT_MIN,
|
78
|
+
:split_max => Gecode::Raw::BVAL_SPLIT_MAX
|
79
|
+
}.each_pair do |name, gecode_const|
|
80
|
+
it "should support #{name} as value selection strategy" do
|
81
|
+
Gecode::Raw.should_receive(:branch).once.with(@model.active_space,
|
82
|
+
anything, an_instance_of(Numeric), gecode_const)
|
83
|
+
@model.branch_on @vars, :value => name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should raise errors for unrecognized var selection strategies' do
|
88
|
+
lambda do
|
89
|
+
@model.branch_on @vars, :variable => :foo
|
90
|
+
end.should raise_error(ArgumentError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should raise errors for unrecognized val selection strategies' do
|
94
|
+
lambda do
|
95
|
+
@model.branch_on @vars, :value => :foo
|
96
|
+
end.should raise_error(ArgumentError)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should raise errors for unrecognized options' do
|
100
|
+
lambda do
|
101
|
+
@model.branch_on @vars, :foo => 5
|
102
|
+
end.should raise_error(ArgumentError)
|
103
|
+
end
|
104
|
+
end
|