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