ConstraintSolver 0.1

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.
Files changed (155) hide show
  1. data/bin/ConstraintSolver +24 -0
  2. data/doc/classes/Array.html +209 -0
  3. data/doc/classes/ConstraintSolver.html +242 -0
  4. data/doc/classes/ConstraintSolver/AbstractConstraint.html +317 -0
  5. data/doc/classes/ConstraintSolver/AllDifferentConstraint.html +451 -0
  6. data/doc/classes/ConstraintSolver/AllDifferentConstraintTest.html +397 -0
  7. data/doc/classes/ConstraintSolver/BinaryConstraint.html +483 -0
  8. data/doc/classes/ConstraintSolver/BinaryConstraintTest.html +367 -0
  9. data/doc/classes/ConstraintSolver/BinaryRelation.html +276 -0
  10. data/doc/classes/ConstraintSolver/BinaryRelationTest.html +194 -0
  11. data/doc/classes/ConstraintSolver/ConstraintList.html +208 -0
  12. data/doc/classes/ConstraintSolver/ConstraintListTest.html +252 -0
  13. data/doc/classes/ConstraintSolver/ConstraintSolver.html +353 -0
  14. data/doc/classes/ConstraintSolver/ConstraintSolverTest.html +403 -0
  15. data/doc/classes/ConstraintSolver/Domain.html +522 -0
  16. data/doc/classes/ConstraintSolver/DomainTest.html +356 -0
  17. data/doc/classes/ConstraintSolver/DomainWipeoutException.html +158 -0
  18. data/doc/classes/ConstraintSolver/Problem.html +239 -0
  19. data/doc/classes/ConstraintSolver/ProblemTest.html +227 -0
  20. data/doc/classes/ConstraintSolver/Solution.html +342 -0
  21. data/doc/classes/ConstraintSolver/SolutionTest.html +250 -0
  22. data/doc/classes/ConstraintSolver/UndoStackEmptyException.html +158 -0
  23. data/doc/classes/ConstraintSolver/Variable.html +418 -0
  24. data/doc/classes/ConstraintSolver/VariableTest.html +284 -0
  25. data/doc/classes/ExtensionsTest.html +233 -0
  26. data/doc/classes/Fixnum.html +153 -0
  27. data/doc/created.rid +1 -0
  28. data/doc/dot/f_0.dot +38 -0
  29. data/doc/dot/f_0.png +0 -0
  30. data/doc/dot/f_1.dot +392 -0
  31. data/doc/dot/f_1.png +0 -0
  32. data/doc/dot/f_10.dot +392 -0
  33. data/doc/dot/f_10.png +0 -0
  34. data/doc/dot/f_11.dot +38 -0
  35. data/doc/dot/f_11.png +0 -0
  36. data/doc/dot/f_12.dot +392 -0
  37. data/doc/dot/f_12.png +0 -0
  38. data/doc/dot/f_13.dot +392 -0
  39. data/doc/dot/f_13.png +0 -0
  40. data/doc/dot/f_14.dot +392 -0
  41. data/doc/dot/f_14.png +0 -0
  42. data/doc/dot/f_15.dot +392 -0
  43. data/doc/dot/f_15.png +0 -0
  44. data/doc/dot/f_16.dot +392 -0
  45. data/doc/dot/f_16.png +0 -0
  46. data/doc/dot/f_17.dot +392 -0
  47. data/doc/dot/f_17.png +0 -0
  48. data/doc/dot/f_18.dot +392 -0
  49. data/doc/dot/f_18.png +0 -0
  50. data/doc/dot/f_19.dot +392 -0
  51. data/doc/dot/f_19.png +0 -0
  52. data/doc/dot/f_2.dot +392 -0
  53. data/doc/dot/f_2.png +0 -0
  54. data/doc/dot/f_3.dot +392 -0
  55. data/doc/dot/f_3.png +0 -0
  56. data/doc/dot/f_4.dot +392 -0
  57. data/doc/dot/f_4.png +0 -0
  58. data/doc/dot/f_5.dot +392 -0
  59. data/doc/dot/f_5.png +0 -0
  60. data/doc/dot/f_6.dot +14 -0
  61. data/doc/dot/f_6.png +0 -0
  62. data/doc/dot/f_7.dot +392 -0
  63. data/doc/dot/f_7.png +0 -0
  64. data/doc/dot/f_8.dot +392 -0
  65. data/doc/dot/f_8.png +0 -0
  66. data/doc/dot/f_9.dot +392 -0
  67. data/doc/dot/f_9.png +0 -0
  68. data/doc/dot/m_10_0.dot +392 -0
  69. data/doc/dot/m_10_0.png +0 -0
  70. data/doc/dot/m_12_0.dot +392 -0
  71. data/doc/dot/m_12_0.png +0 -0
  72. data/doc/dot/m_13_0.dot +392 -0
  73. data/doc/dot/m_13_0.png +0 -0
  74. data/doc/dot/m_14_0.dot +392 -0
  75. data/doc/dot/m_14_0.png +0 -0
  76. data/doc/dot/m_15_0.dot +392 -0
  77. data/doc/dot/m_15_0.png +0 -0
  78. data/doc/dot/m_16_0.dot +392 -0
  79. data/doc/dot/m_16_0.png +0 -0
  80. data/doc/dot/m_17_0.dot +392 -0
  81. data/doc/dot/m_17_0.png +0 -0
  82. data/doc/dot/m_18_0.dot +392 -0
  83. data/doc/dot/m_18_0.png +0 -0
  84. data/doc/dot/m_19_0.dot +392 -0
  85. data/doc/dot/m_19_0.png +0 -0
  86. data/doc/dot/m_1_0.dot +392 -0
  87. data/doc/dot/m_1_0.png +0 -0
  88. data/doc/dot/m_2_0.dot +392 -0
  89. data/doc/dot/m_2_0.png +0 -0
  90. data/doc/dot/m_3_0.dot +392 -0
  91. data/doc/dot/m_3_0.png +0 -0
  92. data/doc/dot/m_4_0.dot +392 -0
  93. data/doc/dot/m_4_0.png +0 -0
  94. data/doc/dot/m_5_0.dot +392 -0
  95. data/doc/dot/m_5_0.png +0 -0
  96. data/doc/dot/m_7_0.dot +392 -0
  97. data/doc/dot/m_7_0.png +0 -0
  98. data/doc/dot/m_8_0.dot +392 -0
  99. data/doc/dot/m_8_0.png +0 -0
  100. data/doc/dot/m_9_0.dot +392 -0
  101. data/doc/dot/m_9_0.png +0 -0
  102. data/doc/files/lib/AbstractConstraint_rb.html +148 -0
  103. data/doc/files/lib/AllDifferentConstraint_rb.html +156 -0
  104. data/doc/files/lib/BinaryConstraint_rb.html +155 -0
  105. data/doc/files/lib/ConstraintList_rb.html +148 -0
  106. data/doc/files/lib/ConstraintSolver_rb.html +162 -0
  107. data/doc/files/lib/Domain_rb.html +155 -0
  108. data/doc/files/lib/Problem_rb.html +148 -0
  109. data/doc/files/lib/Solution_rb.html +148 -0
  110. data/doc/files/lib/Variable_rb.html +148 -0
  111. data/doc/files/lib/extensions_rb.html +108 -0
  112. data/doc/files/test/AllDifferentConstraintTest_rb.html +158 -0
  113. data/doc/files/test/BinaryConstraintTest_rb.html +158 -0
  114. data/doc/files/test/ConstraintListTest_rb.html +160 -0
  115. data/doc/files/test/ConstraintSolverTest_rb.html +164 -0
  116. data/doc/files/test/DomainTest_rb.html +156 -0
  117. data/doc/files/test/ProblemTest_rb.html +160 -0
  118. data/doc/files/test/SolutionTest_rb.html +159 -0
  119. data/doc/files/test/TestSuite_rb.html +113 -0
  120. data/doc/files/test/VariableTest_rb.html +157 -0
  121. data/doc/files/test/extensionsTest_rb.html +118 -0
  122. data/doc/fr_class_index.html +51 -0
  123. data/doc/fr_file_index.html +46 -0
  124. data/doc/fr_method_index.html +133 -0
  125. data/doc/index.html +24 -0
  126. data/examples/example.rb +7 -0
  127. data/examples/queens.rb +13 -0
  128. data/examples/soft.rb +14 -0
  129. data/lib/AbstractConstraint.rb +45 -0
  130. data/lib/AllDifferentConstraint.rb +160 -0
  131. data/lib/BinaryConstraint.rb +187 -0
  132. data/lib/ConstraintList.rb +31 -0
  133. data/lib/ConstraintSolver.rb +213 -0
  134. data/lib/Domain.rb +100 -0
  135. data/lib/GraphUtils.rb +293 -0
  136. data/lib/OneOfEqualsConstraint.rb +81 -0
  137. data/lib/Problem.rb +30 -0
  138. data/lib/Solution.rb +56 -0
  139. data/lib/TupleConstraint.rb +111 -0
  140. data/lib/Variable.rb +74 -0
  141. data/lib/extensions.rb +55 -0
  142. data/test/AllDifferentConstraintTest.rb +140 -0
  143. data/test/BinaryConstraintTest.rb +108 -0
  144. data/test/ConstraintListTest.rb +41 -0
  145. data/test/ConstraintSolverTest.rb +274 -0
  146. data/test/DomainTest.rb +83 -0
  147. data/test/GraphUtilsTest.rb +83 -0
  148. data/test/OneOfEqualsConstraintTest.rb +82 -0
  149. data/test/ProblemTest.rb +35 -0
  150. data/test/SolutionTest.rb +35 -0
  151. data/test/TestSuite.rb +10 -0
  152. data/test/TupleConstraintTest.rb +151 -0
  153. data/test/VariableTest.rb +47 -0
  154. data/test/extensionsTest.rb +57 -0
  155. metadata +212 -0
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'AbstractConstraint'
4
+ require 'extensions'
5
+
6
+ module ConstraintSolver
7
+ # Represents a one-of-equals constraint.
8
+ class OneOfEqualsConstraint < AbstractConstraint
9
+ attr_reader :variables, :value, :violationCost
10
+ # Initialises a new one-of-equals constraint.
11
+ # The arguments are the list of variables involved in the constraint and
12
+ # the value at least one of the variables should be equal to.
13
+ # Optionally, a cost for violating the constraint can be specified.
14
+ def initialize(variables, value, violationCost=1)
15
+ if variables.empty?
16
+ raise ArgumentError, "List of variables must not be empty!"
17
+ end
18
+ @variables = variables
19
+ @value = value
20
+ @violationCost = violationCost
21
+ end
22
+
23
+ def holds?
24
+ if not allAssigned? or not @variables.find { |var| var.value == @value }.nil?
25
+ return true
26
+ else
27
+ return false
28
+ end
29
+ end
30
+
31
+ def allAssigned?
32
+ @variables.each { |var|
33
+ return false if not var.assigned?
34
+ }
35
+ return true
36
+ end
37
+
38
+ def include?(variable)
39
+ @variables.include?(variable)
40
+ end
41
+
42
+ def to_s
43
+ @variables.collect { |var| var.name }.join(" != ")
44
+ end
45
+
46
+ def to_s_full
47
+ @variables.collect { |var| var.to_s }.join(" != ")
48
+ end
49
+
50
+ def ==(constraint)
51
+ return false unless constraint.kind_of?(OneOfEqualsConstraint)
52
+ (@variables == constraint.variables) and (@value == constraint.value) and
53
+ (@violationCost == constraint.violationCost)
54
+ end
55
+
56
+ def each
57
+ @variables.each { |var|
58
+ yield var
59
+ }
60
+ end
61
+
62
+ def revise
63
+ checks = 0
64
+ revisedVariables = []
65
+ wipeout = false
66
+ if @variables.find_all { |var| var.assigned? and not var.value == @value }.size == @variables.size - 1
67
+ unassigned = @variables.find { |var| not var.assigned? }
68
+ pruneList = unassigned.domain.values - [ @value ]
69
+ if not pruneList.empty?
70
+ begin
71
+ unassigned.domain.prune(pruneList)
72
+ rescue DomainWipeoutException
73
+ wipeout = true
74
+ end
75
+ revisedVariables << unassigned
76
+ end
77
+ end
78
+ return revisedVariables, checks, wipeout
79
+ end
80
+ end
81
+ end
data/lib/Problem.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module ConstraintSolver
4
+ # Representation of a constraint satisfaction problem.
5
+ class Problem
6
+ attr_reader :variables, :constraints, :meritMap, :firstSolution, :maxViolation
7
+ # Creates a new instances of a constraint satisfaction problem.
8
+ # The constructor takes the lists of variables and constraints as parameters.
9
+ # Optionally, a map that maps domain elements to their merit,
10
+ # a boolean indicating whether to sop solving after a solution has
11
+ # been found, and a number designating the maximum cost for constraint
12
+ # violation that can be accepted can be specified.
13
+ def initialize(variables, constraints, meritMap={}, firstSolution=false, maxViolation=0)
14
+ if not variables.kind_of?(Array)
15
+ variables = [ variables ]
16
+ end
17
+ if not constraints.kind_of?(ConstraintList)
18
+ constraints = ConstraintList.new([ constraints ])
19
+ end
20
+ if variables.empty? or constraints.empty?
21
+ raise ArgumentError, "Variables and constraints must not be empty!"
22
+ end
23
+ @variables = variables
24
+ @constraints = constraints
25
+ @meritMap = meritMap
26
+ @firstSolution = firstSolution
27
+ @maxViolation = maxViolation
28
+ end
29
+ end
30
+ end
data/lib/Solution.rb ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module ConstraintSolver
4
+ # This class represents a solution to a constraint satisfaction problem.
5
+ class Solution
6
+ attr_reader :variables, :merit, :violation
7
+ # Initialises a new solution with a list of variables. The variables
8
+ # must all have values assigned to them.
9
+ # Optionally, a map to map domain values of variables to their merit and
10
+ # a number designating the cost of violating constraints in the solution can
11
+ # be specified.
12
+ def initialize(variables, meritMap={}, violation=0)
13
+ if not variables.kind_of?(Array)
14
+ variables = Array.new([ variables ])
15
+ end
16
+ if variables.inject(true) { |res,var| res & var.assigned? }
17
+ @variables = Array.new
18
+ variables.each { |variable|
19
+ @variables << variable.dup
20
+ }
21
+ else
22
+ raise ArgumentError, "All variables must have values assigned to them!"
23
+ end
24
+ @merit = 0
25
+ @variables.each { |var|
26
+ @merit += meritMap[var.value] ? var.merit * meritMap[var.value] : var.merit
27
+ }
28
+ @violation = violation
29
+ @merit -= @violation
30
+ end
31
+
32
+ def each
33
+ @variables.each { |variable|
34
+ yield variable
35
+ }
36
+ end
37
+
38
+ def <=>(solution)
39
+ @value <=> solution.value
40
+ end
41
+
42
+ def ==(solution)
43
+ return false unless solution.kind_of?(Solution)
44
+ @variables == solution.variables and @merit == solution.merit and @violation == solution.violation
45
+ end
46
+
47
+ def to_s
48
+ s = (@variables.collect { |variable|
49
+ variable.to_s
50
+ }).join(", ")
51
+ s += ", merit " + @merit.to_s unless @merit.nil?
52
+ s += ", violation " + @violation.to_s unless @violation.nil?
53
+ return s
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'AbstractConstraint'
4
+ require 'extensions'
5
+
6
+ module ConstraintSolver
7
+ # Represents a constraint which is specified by the allowed or disallowed
8
+ # tuples of arbitrary arity.
9
+ class TupleConstraint < AbstractConstraint
10
+ attr_reader :variables, :tuples, :allowedTuples, :violationCost
11
+ # Initialises a new tuple constraint.
12
+ # The first argument is the list of variables involved in the
13
+ # constraint, the second argument is an array of tuples. Each element of
14
+ # the array is an array with exactly as many elements as the array of
15
+ # variables. The third argument specifies whether the array of tuples
16
+ # lists the allowed or disallowed tuples. Default is allowed tuples.
17
+ # Optionally, a cost for violating the constraint can be specified.
18
+ def initialize(variables, tuples, allowedTuples=true, violationCost=1)
19
+ @variables = variables
20
+ unless tuples.inject(true) { |bool,tuple| bool & (tuple.size == variables.size) }
21
+ raise ArgumentError, "All tuples must have " + variables.size.to_s + " elements!"
22
+ end
23
+ @tuples = tuples
24
+ @violationCost = violationCost
25
+ @allowedTuples = allowedTuples
26
+ end
27
+
28
+ # Checks whether the assignments to the variables are allowed tuples.
29
+ def holds?
30
+ tups = @tuples
31
+ @variables.each_with_index { |var,i|
32
+ next unless var.assigned?
33
+ tups = tups.find_all { |tup| tup[i] == var.value }
34
+ if @allowedTuples
35
+ return false if tups.empty?
36
+ end
37
+ }
38
+ if not @allowedTuples and allAssigned? and tuples.include?(@variables.collect { |var| var.value })
39
+ return false
40
+ end
41
+ return true
42
+ end
43
+
44
+ def allAssigned?
45
+ @variables.each { |var|
46
+ return false if not var.assigned?
47
+ }
48
+ return true
49
+ end
50
+
51
+ def include?(variable)
52
+ @variables.include?(variable)
53
+ end
54
+
55
+ def to_s
56
+ @variables.collect { |var| var.name }.join(", ") + ": " + tuples.join(", ")
57
+ end
58
+
59
+ def to_s_full
60
+ @variables.collect { |var| var.to_s }.join(", ") + ": " + tuples.join(", ")
61
+ end
62
+
63
+ def ==(constraint)
64
+ return false unless constraint.kind_of?(TupleConstraint)
65
+ (@variables == constraint.variables) and (@tuples == constraint.tuples) and
66
+ (@allowedTuples == constraint.allowedTuples) and (@violationCost == constraint.violationCost)
67
+ end
68
+
69
+ def each
70
+ @variables.each { |var|
71
+ yield var
72
+ }
73
+ end
74
+
75
+ def revise
76
+ revisedVariables = []
77
+ checks = 0
78
+ wipeout = false
79
+ assignedIndices, unassignedIndices = (0..(@variables.size - 1)).partition { |i| @variables[i].assigned? }
80
+ pruneTuples = (@allowedTuples ? @tuples : [])
81
+ assignedIndices.each { |i|
82
+ if @allowedTuples
83
+ pruneTuples = pruneTuples.find_all { |tup| tup[i] == @variables[i].value }
84
+ else
85
+ (@tuples - pruneTuples).each { |tup|
86
+ if @variables[i].value == tup[i]
87
+ pruneTuples << tup
88
+ end
89
+ }
90
+ end
91
+ }
92
+ unassignedIndices.each { |i|
93
+ var = @variables[i]
94
+ pruneList = pruneTuples.collect { |tup| tup[i] }.to_set
95
+ if @allowedTuples
96
+ pruneList = var.domain.values - pruneList
97
+ end
98
+ if var.domain.include_any?(pruneList)
99
+ begin
100
+ var.domain.prune(pruneList)
101
+ rescue DomainWipeoutException
102
+ wipeout = true
103
+ end
104
+ revisedVariables << var
105
+ break if wipeout
106
+ end
107
+ }
108
+ return revisedVariables, checks, wipeout
109
+ end
110
+ end
111
+ end
data/lib/Variable.rb ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module ConstraintSolver
4
+ # This class represents a variable in a constraint satisfaction problem.
5
+ class Variable
6
+ attr_reader :name, :domain, :value, :merit
7
+ # Initialises a new variable. The name of the variable and at least one
8
+ # of domain or value are required.
9
+ # If domain is nil and a value is given, a new Domain is constructed
10
+ # with only the value specified in it.
11
+ # The fourth argument is the merit of the variable.
12
+ def initialize(name, domain=nil, value=nil, merit=0)
13
+ if not name.kind_of?(String)
14
+ raise ArgumentError, "The name of the variable has to be specified."
15
+ end
16
+ if domain.nil? and value.nil?
17
+ raise ArgumentError, "At least one of domain or value are required."
18
+ end
19
+ @name = name
20
+ @value = value
21
+ if not domain.nil? and not domain.kind_of?(Domain)
22
+ domain = Domain.new([ domain ].to_set);
23
+ end
24
+ if value.nil?
25
+ @domain = domain.nil? ? Domain.new : domain
26
+ else
27
+ if domain.nil?
28
+ @domain = Domain.new([ value ].to_set)
29
+ else
30
+ if domain.include?(value)
31
+ @domain = domain
32
+ else
33
+ raise ArgumentError, "Value " + value.to_s + " not in domain " + domain.to_s + "!"
34
+ end
35
+ end
36
+ end
37
+ @merit = merit
38
+ end
39
+
40
+ # Assigns a new value to the variable.
41
+ # The new value has to be in the domain of the variable.
42
+ def value=(value)
43
+ if value.nil?
44
+ return
45
+ end
46
+ if @domain.include?(value)
47
+ @value = value
48
+ else
49
+ raise ArgumentError, "The domain of " + @name.to_s + " (" + @domain.to_s +
50
+ ") does not contain " + value.to_s + "!"
51
+ end
52
+ end
53
+
54
+ def values
55
+ domain.values
56
+ end
57
+
58
+ def to_s
59
+ @name.to_s + " = " + @value.to_s + " \\in " + @domain.to_s
60
+ end
61
+
62
+ def ==(variable)
63
+ variable.kind_of?(Variable) and (@name == variable.name) and (@value == variable.value)
64
+ end
65
+
66
+ def assigned?
67
+ not @value.nil?
68
+ end
69
+
70
+ def reset
71
+ @value = nil
72
+ end
73
+ end
74
+ end
data/lib/extensions.rb ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/ruby
2
+
3
+ class Array
4
+ def rest
5
+ retval = self.clone
6
+ retval.shift
7
+ return retval
8
+ end
9
+
10
+ def foldLeft(function)
11
+ if self.size == 1
12
+ return self.first
13
+ else
14
+ return function.call(self.first, self.rest.foldLeft(function))
15
+ end
16
+ end
17
+
18
+ def eachAfter(element, &block)
19
+ self[((self.index(element) or -1) + 1)..-1].each { |x|
20
+ yield x
21
+ }
22
+ end
23
+
24
+ def eachStartWith(element, &block)
25
+ self[(self.index(element) or 0)..-1].each { |x|
26
+ yield x
27
+ }
28
+ end
29
+ end
30
+
31
+ class Fixnum
32
+ def not_equal?(num)
33
+ self != num
34
+ end
35
+ end
36
+
37
+ class Set
38
+ def include_any?(enum)
39
+ enum.each { |e|
40
+ if self.include?(e)
41
+ return true
42
+ end
43
+ }
44
+ return false
45
+ end
46
+ end
47
+
48
+ class Hash
49
+ def include_hash?(hash)
50
+ hash.each { |k,v|
51
+ return false unless self[k] == v
52
+ }
53
+ return true
54
+ end
55
+ end
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
4
+
5
+ require 'test/unit'
6
+ require 'AllDifferentConstraint'
7
+ require 'Variable'
8
+ require 'Domain'
9
+
10
+ module ConstraintSolver
11
+ class AllDifferentConstraintTest < Test::Unit::TestCase
12
+ def setup
13
+ @x = Variable.new("x", Domain.new([ 1, 2, 3 ].to_set))
14
+ @y = Variable.new("y", Domain.new([ 1, 2, 3 ].to_set))
15
+ @z = Variable.new("z", Domain.new([ 1, 2, 3 ].to_set))
16
+ @constraint = AllDifferentConstraint.new([ @x, @y, @z ])
17
+ end
18
+
19
+ def testConstructor
20
+ assert_raise(ArgumentError) { AllDifferentConstraint.new }
21
+ assert_raise(ArgumentError) { AllDifferentConstraint.new([]) }
22
+ assert_raise(ArgumentError) { AllDifferentConstraint.new([ @x ]) }
23
+ assert_nothing_raised { AllDifferentConstraint.new([ @x, @y, @z ]) }
24
+ assert_nothing_raised { AllDifferentConstraint.new([ @x, @y, @z ], 0) }
25
+ end
26
+
27
+ def testAssigned
28
+ assert_equal(false, @constraint.allAssigned?)
29
+ @y.value = 1
30
+ @z.value = 1
31
+ assert_equal(false, @constraint.allAssigned?)
32
+ @x.value = 1
33
+ assert_equal(true, @constraint.allAssigned?)
34
+ @x.reset
35
+ @y.reset
36
+ @z.reset
37
+ end
38
+
39
+ def testInclude
40
+ assert_equal(true, @constraint.include?(@x))
41
+ assert_equal(true, @constraint.include?(@y))
42
+ assert_equal(true, @constraint.include?(@z))
43
+ assert_equal(false, @constraint.include?(Variable.new("foobar", 1)))
44
+ end
45
+
46
+ def testEquals
47
+ assert_not_equal(AllDifferentConstraint.new([ @x, @y ]), @constraint)
48
+ assert_equal(AllDifferentConstraint.new([ @x, @y, @z ]), @constraint)
49
+ end
50
+
51
+ def testHolds
52
+ @x.value = 1
53
+ @y.value = 2
54
+ @z.value = 2
55
+ assert_equal(false, @constraint.holds?)
56
+ @z.value = 3
57
+ assert_equal(true, @constraint.holds?)
58
+ @constraint.variables.each { |var| var.reset }
59
+ end
60
+
61
+ def testRevise_decomposed_local
62
+ assert_equal([ [], 0, false ], @constraint.revise_decomposed_local)
63
+ @x.value = 1
64
+ assert_equal([ [ @y, @z ], 0, false ], @constraint.revise_decomposed_local)
65
+ assert_equal([ 2, 3 ].to_set, @y.values)
66
+ assert_equal([ 2, 3 ].to_set, @z.values)
67
+ @x.reset
68
+
69
+ x = Variable.new("x", Domain.new([ 1, 3 ].to_set))
70
+ y = Variable.new("y", Domain.new([ 2, 3 ].to_set))
71
+ z = Variable.new("z", Domain.new([ 2, 3 ].to_set))
72
+ constraint = AllDifferentConstraint.new([ x, y, z ])
73
+ assert_equal([ [], 0, false ], constraint.revise_decomposed_local)
74
+ assert_equal([ 1, 3 ].to_set, x.values)
75
+ assert_equal([ 2, 3 ].to_set, y.values)
76
+ assert_equal([ 2, 3 ].to_set, z.values)
77
+
78
+ a = Variable.new("a", Domain.new([ 1 ].to_set))
79
+ b = Variable.new("b", Domain.new([ 1, 2 ].to_set))
80
+ c = Variable.new("c", Domain.new([ 1, 2, 3 ].to_set))
81
+ constraint = AllDifferentConstraint.new([ a, b, c ])
82
+ assert_equal([ [ b, c, c ], 0, false ], constraint.revise_decomposed_local)
83
+ assert_equal([ 1 ].to_set, a.values)
84
+ assert_equal([ 2 ].to_set, b.values)
85
+ assert_equal([ 3 ].to_set, c.values)
86
+ end
87
+
88
+ def testDomainWipeout_decomposed_local
89
+ x = Variable.new("x", Domain.new([ 1, 2, 3 ].to_set))
90
+ y = Variable.new("y", Domain.new([ 1, 2, 3 ].to_set))
91
+ z = Variable.new("z", Domain.new([ 1, 2, 3 ].to_set))
92
+ a = Variable.new("a", Domain.new([ 1, 2, 3 ].to_set))
93
+ constraint = AllDifferentConstraint.new([ x, y, z, a ])
94
+ a.value = 1
95
+ assert_equal([ [ x, y, z ], 0, true ], constraint.revise_decomposed_local)
96
+ end
97
+
98
+ def testRevise_hyperarc
99
+ x = Variable.new("x", Domain.new([ 1, 3 ].to_set))
100
+ y = Variable.new("y", Domain.new([ 2, 3 ].to_set))
101
+ z = Variable.new("z", Domain.new([ 2, 3 ].to_set))
102
+ constraint = AllDifferentConstraint.new([ x, y, z ])
103
+ assert_equal([ [ x ], 0, false ], constraint.revise_hyperarc)
104
+ assert_equal([ 1 ].to_set, x.values)
105
+ assert_equal([ 2, 3 ].to_set, y.values)
106
+ assert_equal([ 2, 3 ].to_set, z.values)
107
+
108
+ a = Variable.new("a", Domain.new([ 1 ].to_set))
109
+ b = Variable.new("b", Domain.new([ 1, 2 ].to_set))
110
+ c = Variable.new("c", Domain.new([ 1, 2, 3 ].to_set))
111
+ constraint = AllDifferentConstraint.new([ a, b, c ])
112
+ revisedVariables, checks, wipeout = constraint.revise_hyperarc
113
+ assert_equal(false, wipeout)
114
+ assert_equal(Set.new, [ b, c ].to_set - revisedVariables.to_set)
115
+ assert_equal(0, checks)
116
+ assert_equal([ 1 ].to_set, a.values)
117
+ assert_equal([ 2 ].to_set, b.values)
118
+ assert_equal([ 3 ].to_set, c.values)
119
+
120
+ assert_equal([ [], 0, false ], constraint.revise_hyperarc)
121
+
122
+ x = Variable.new("x", Domain.new([ 1, 3 ].to_set))
123
+ y = Variable.new("y", Domain.new([ 2, 3 ].to_set))
124
+ z = Variable.new("z", Domain.new([ 2, 3, 4 ].to_set))
125
+ constraint = AllDifferentConstraint.new([ x, y, z ])
126
+ assert_equal([ [], 0, false ], constraint.revise_hyperarc)
127
+ assert_equal([ 1, 3 ].to_set, x.values)
128
+ assert_equal([ 2, 3 ].to_set, y.values)
129
+ assert_equal([ 2, 3, 4 ].to_set, z.values)
130
+ end
131
+
132
+ def testDomainWipeout_hyperarc
133
+ x = Variable.new("x", Domain.new([ 1, 2 ].to_set))
134
+ y = Variable.new("y", Domain.new([ 1, 2 ].to_set))
135
+ z = Variable.new("z", Domain.new([ 1, 2 ].to_set))
136
+ constraint = AllDifferentConstraint.new([ x, y, z ])
137
+ assert_equal([ [], 0, true ], constraint.revise_hyperarc)
138
+ end
139
+ end
140
+ end