cassowary-ruby 0.5.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/lib/strength.rb ADDED
@@ -0,0 +1,31 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class Strength
5
+ attr_accessor :name, :symbolic_weight
6
+
7
+ def initialize(name = nil, symbolic_weight = nil)
8
+ self.name = name
9
+ self.symbolic_weight = symbolic_weight
10
+ end
11
+
12
+ def required?
13
+ self == RequiredStrength
14
+ end
15
+
16
+ def inspect
17
+ "#{name}"
18
+ end
19
+
20
+ def each
21
+ [RequiredStrength, StrongStrength, MediumStrength, WeakStrength].each do |str|
22
+ yield str
23
+ end
24
+ end
25
+
26
+ RequiredStrength = new "required"
27
+ StrongStrength = new "strong", SymbolicWeight.new([1.0])
28
+ MediumStrength = new "medium", SymbolicWeight.new([0.0, 1.0])
29
+ WeakStrength = new "weak", SymbolicWeight.new([0.0, 0.0, 1.0])
30
+ end
31
+ end
@@ -0,0 +1,115 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class SymbolicWeight
5
+ include Enumerable
6
+ include Comparable
7
+
8
+ StrengthLevels = 3
9
+
10
+ def initialize(levels = {})
11
+ @levels = [0.0] * StrengthLevels
12
+ case levels
13
+ when Hash
14
+ levels.each_pair do |k, v|
15
+ self[k] = v
16
+ end
17
+ when Array
18
+ levels.each_with_index do |e, idx|
19
+ self[idx] = e
20
+ end
21
+ else
22
+ raise InternalError
23
+ end
24
+ end
25
+
26
+ def each(*args, &block)
27
+ @levels.each(*args, &block)
28
+ end
29
+
30
+ def [](idx)
31
+ @levels[idx]
32
+ end
33
+
34
+ def []=(idx, value)
35
+ @levels[idx] = value
36
+ end
37
+
38
+ def *(n)
39
+ raise InternalError unless n.is_a? Numeric
40
+ result = SymbolicWeight.new
41
+ each_with_index do |e, idx|
42
+ result[idx] = e * n
43
+ end
44
+ result
45
+ end
46
+
47
+ def /(n)
48
+ raise InternalError unless n.is_a? Numeric
49
+ result = SymbolicWeight.new
50
+ each_with_index do |e, idx|
51
+ result[idx] = e / n
52
+ end
53
+ result
54
+ end
55
+
56
+ def +(n)
57
+ raise InternalError unless n.is_a? SymbolicWeight
58
+ result = SymbolicWeight.new
59
+ each_with_index do |e, idx|
60
+ result[idx] = e + n[idx]
61
+ end
62
+ result
63
+ end
64
+
65
+ def -(n)
66
+ raise InternalError unless n.is_a? SymbolicWeight
67
+ result = SymbolicWeight.new
68
+ each_with_index do |e, idx|
69
+ result[idx] = e - n[idx]
70
+ end
71
+ result
72
+ end
73
+
74
+ def <=>(other)
75
+ raise InternalError unless other.is_a? SymbolicWeight
76
+ each_with_index do |e, idx|
77
+ return -1 if e < other[idx]
78
+ return 1 if e > other[idx]
79
+ end
80
+ 0
81
+ end
82
+
83
+ def cl_approx(s)
84
+ raise InternalError unless s.is_a? SymbolicWeight
85
+ each_with_index do |e, idx|
86
+ return false unless e.cl_approx(s[idx])
87
+ end
88
+ true
89
+ end
90
+
91
+ def cl_approx_zero
92
+ cl_approx Zero
93
+ end
94
+
95
+ def definitely_negative
96
+ epsilon = SimplexSolver::Epsilon
97
+ nepsilon = 0.0 - epsilon
98
+ each do |e|
99
+ return true if e < nepsilon
100
+ return false if e > epsilon
101
+ end
102
+ false
103
+ end
104
+
105
+ def symbolic_weight?
106
+ true
107
+ end
108
+
109
+ def inspect
110
+ "[" + @levels.join(",") + "]"
111
+ end
112
+
113
+ Zero = new([0.0] * StrengthLevels)
114
+ end
115
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ module Equalities
5
+ def cn_equal(expr, strength = Strength::RequiredStrength, weight = 1.0)
6
+ cn_equality(LinearEquation, self - expr, strength, weight)
7
+ end
8
+
9
+ def cn_geq(expr, strength = Strength::RequiredStrength, weight = 1.0)
10
+ cn_equality(LinearInequality, self - expr, strength, weight)
11
+ end
12
+
13
+ def cn_leq(expr, strength = Strength::RequiredStrength, weight = 1.0)
14
+ expr = expr.as_linear_expression if expr.is_a?(Numeric)
15
+ cn_equality(LinearInequality, expr - self, strength, weight)
16
+ end
17
+
18
+ private
19
+ def cn_equality(klass, expr, strength, weight)
20
+ cn = klass.new
21
+ cn.expression = expr
22
+ cn.strength = strength
23
+ cn.weight = weight
24
+ cn
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class AbstractVariable
5
+ attr_accessor :name
6
+
7
+ def initialize(hash = {})
8
+ self.name = hash[:name]
9
+ end
10
+
11
+ def dummy?
12
+ false
13
+ end
14
+
15
+ def external?
16
+ raise NotImplementedError, "my subclass should have implemented #external?"
17
+ end
18
+
19
+ def pivotable?
20
+ raise NotImplementedError, "my subclass should have implemented #pivotable?"
21
+ end
22
+
23
+ def restricted?
24
+ raise NotImplementedError, "my subclass should have implemented #restricted?"
25
+ end
26
+
27
+ def inspect
28
+ if name
29
+ "#{name}"
30
+ else
31
+ "<CV#0x#{object_id.to_s(16)}>"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class DummyVariable < AbstractVariable
5
+ def dummy?
6
+ true
7
+ end
8
+
9
+ def external?
10
+ false
11
+ end
12
+
13
+ def pivotable?
14
+ false
15
+ end
16
+
17
+ def restricted?
18
+ true
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class ObjectiveVariable < AbstractVariable
5
+ def external?
6
+ false
7
+ end
8
+
9
+ def pivotable?
10
+ false
11
+ end
12
+
13
+ def restricted?
14
+ false
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class SlackVariable < AbstractVariable
5
+ def external?
6
+ false
7
+ end
8
+
9
+ def pivotable?
10
+ true
11
+ end
12
+
13
+ def restricted?
14
+ true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class Variable < AbstractVariable
5
+ include Equalities
6
+
7
+ attr_accessor :value
8
+
9
+ def initialize(hash)
10
+ super
11
+ self.value = hash[:value]
12
+ end
13
+
14
+ def *(expr)
15
+ self.as_linear_expression * expr
16
+ end
17
+
18
+ def +(expr)
19
+ self.as_linear_expression + expr
20
+ end
21
+
22
+ def -(expr)
23
+ self.as_linear_expression - expr
24
+ end
25
+
26
+ def /(expr)
27
+ self.as_linear_expression / expr
28
+ end
29
+
30
+ def as_linear_expression
31
+ expr = LinearExpression.new
32
+ expr.terms[self] = 1.0
33
+ expr
34
+ end
35
+
36
+ def external?
37
+ true
38
+ end
39
+
40
+ def pivotable?
41
+ false
42
+ end
43
+
44
+ def restricted?
45
+ false
46
+ end
47
+
48
+ def -@
49
+ -1.0.as_linear_expression * self
50
+ end
51
+
52
+ def inspect
53
+ "#{super}[#{value.inspect}]"
54
+ end
55
+ end
56
+ end
data/lib/variables.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "variables/abstract_variable"
2
+ require "variables/dummy_variable"
3
+ require "variables/objective_variable"
4
+ require "variables/slack_variable"
5
+ require "variables/variable"
@@ -0,0 +1,22 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class AbstractMethodsTest < Test::Unit::TestCase
4
+ def test_constraint
5
+ assert_raise NotImplementedError do
6
+ Cassowary::Constraint.new.expression
7
+ end
8
+ end
9
+
10
+ def test_abstract_variable
11
+ var = Cassowary::AbstractVariable.new
12
+ assert_raise NotImplementedError do
13
+ var.external?
14
+ end
15
+ assert_raise NotImplementedError do
16
+ var.pivotable?
17
+ end
18
+ assert_raise NotImplementedError do
19
+ var.restricted?
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,184 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class CassowaryTests < Test::Unit::TestCase
4
+ include Cassowary
5
+
6
+ def test_add_delete1
7
+ x = Variable.new(name: 'x')
8
+ solver = SimplexSolver.new
9
+ solver.add_constraint x.cn_equal(100.0, Strength::WeakStrength)
10
+ c10 = x.cn_leq 10.0
11
+ c20 = x.cn_leq 20.0
12
+ solver.add_constraint c10
13
+ solver.add_constraint c20
14
+ assert x.value.cl_approx(10.0)
15
+
16
+ solver.remove_constraint c10
17
+ assert x.value.cl_approx(20.0)
18
+
19
+ solver.remove_constraint c20
20
+ assert x.value.cl_approx(100.0)
21
+
22
+ c10again = x.cn_leq 10.0
23
+ solver.add_constraint c10
24
+ solver.add_constraint c10again
25
+ assert x.value.cl_approx(10.0)
26
+
27
+ solver.remove_constraint c10
28
+ assert x.value.cl_approx(10.0)
29
+
30
+ solver.remove_constraint c10again
31
+ assert x.value.cl_approx(100.0)
32
+ end
33
+
34
+ def test_add_delete2
35
+ x = Variable.new name: 'x'
36
+ y = Variable.new name: 'y'
37
+
38
+ solver = SimplexSolver.new
39
+ solver.add_constraint x.cn_equal(100.0, Strength::WeakStrength)
40
+ solver.add_constraint y.cn_equal(120.0, Strength::StrongStrength)
41
+
42
+ c10 = x.cn_leq(10.0)
43
+ c20 = x.cn_leq(20.0)
44
+ solver.add_constraint c10
45
+ solver.add_constraint c20
46
+ assert x.value.cl_approx(10.0)
47
+ assert y.value.cl_approx(120.0)
48
+
49
+ solver.remove_constraint c10
50
+ assert x.value.cl_approx 20.0
51
+ assert y.value.cl_approx 120.0
52
+
53
+ cxy = (x * 2).cn_equal y
54
+ solver.add_constraint cxy
55
+ assert x.value.cl_approx 20
56
+ assert y.value.cl_approx 40
57
+
58
+ solver.remove_constraint c20
59
+ assert x.value.cl_approx 60
60
+ assert y.value.cl_approx 120
61
+
62
+ solver.remove_constraint cxy
63
+ assert x.value.cl_approx 100
64
+ assert y.value.cl_approx 120
65
+
66
+ cxy2 = (x * 1.as_linear_expression).cn_equal(1000)
67
+ solver.add_constraint cxy2
68
+ assert x.value.cl_approx 1000
69
+ end
70
+
71
+ def test_add_delete3
72
+ x = Variable.new name: 'x'
73
+ solver = SimplexSolver.new
74
+ c1 = x.cn_equal 100, Strength::WeakStrength, 5
75
+ c2 = x.cn_equal 200, Strength::WeakStrength
76
+
77
+ solver.add_constraint c1
78
+ solver.add_constraint c2
79
+ assert x.value.cl_approx 100
80
+
81
+ solver.remove_constraint c1
82
+ assert x.value.cl_approx 200
83
+ end
84
+
85
+ def test_inconsistent1
86
+ x = Variable.new name: 'x'
87
+ solver = SimplexSolver.new
88
+ solver.add_constraint x.cn_equal 10
89
+ assert_raise RequiredFailure do
90
+ solver.add_constraint x.cn_equal 5
91
+ end
92
+ end
93
+
94
+ def test_inconsistent2
95
+ x = Variable.new name: 'x'
96
+ solver = SimplexSolver.new
97
+ solver.add_constraint x.cn_geq 10
98
+ assert_raise RequiredFailure do
99
+ solver.add_constraint x.cn_leq 5
100
+ end
101
+ end
102
+
103
+ def test_stay1
104
+ x = Variable.new name: 'x', value: 20
105
+ solver = SimplexSolver.new
106
+
107
+ solver.add_stay x, Strength::WeakStrength
108
+ assert x.value.cl_approx 20
109
+ end
110
+
111
+ def test_two_solutions
112
+ x = Variable.new name: 'x'
113
+ y = Variable.new name: 'y'
114
+
115
+ solver = SimplexSolver.new
116
+ solver.add_constraint x.cn_leq y
117
+ solver.add_constraint y.cn_equal x + 3
118
+ solver.add_constraint x.cn_equal 10, Strength::WeakStrength
119
+ solver.add_constraint y.cn_equal 10, Strength::WeakStrength
120
+
121
+ assert(x.value.cl_approx(10) && y.value.cl_approx(13) ||
122
+ x.value.cl_approx(7) && y.value.cl_approx(10))
123
+ end
124
+
125
+ def test_weighted1
126
+ x = Variable.new name: 'x'
127
+ solver = SimplexSolver.new
128
+
129
+ c15 = x.cn_equal 15, Strength::WeakStrength
130
+ c20 = x.cn_equal 20, Strength::WeakStrength, 2
131
+
132
+ solver.add_constraint c15
133
+ assert x.value.cl_approx 15
134
+
135
+ solver.add_constraint c20
136
+ assert x.value.cl_approx 20
137
+
138
+ solver.remove_constraint c20
139
+ assert x.value.cl_approx 15
140
+ end
141
+
142
+ def test_edit1
143
+ x = Variable.new name: 'x', value: 20
144
+ y = Variable.new name: 'y', value: 30
145
+
146
+ solver = SimplexSolver.new
147
+ solver.add_stay x, Strength::WeakStrength
148
+ solver.add_constraint x.cn_geq 10
149
+ solver.add_constraint x.cn_leq 100
150
+ solver.add_constraint x.cn_equal y * 2
151
+ assert x.value.cl_approx 20
152
+ assert y.value.cl_approx 10
153
+
154
+ solver.add_edit_var y, Strength::StrongStrength
155
+ solver.begin_edit
156
+ solver.suggest_value y, 35
157
+ solver.resolve
158
+ assert x.value.cl_approx 70
159
+ assert y.value.cl_approx 35
160
+
161
+ solver.suggest_value y, 80
162
+ solver.resolve
163
+ assert x.value.cl_approx 100
164
+ assert y.value.cl_approx 50
165
+
166
+ solver.suggest_value y, 25
167
+ solver.resolve
168
+ assert x.value.cl_approx 50
169
+ assert y.value.cl_approx 25
170
+
171
+ solver.end_edit
172
+ assert x.value.cl_approx 50
173
+ assert y.value.cl_approx 25
174
+
175
+ solver.add_edit_var x, Strength::StrongStrength
176
+ solver.begin_edit
177
+ solver.suggest_value x, 44.0
178
+ solver.resolve
179
+ assert x.value.cl_approx 44
180
+ assert y.value.cl_approx 22
181
+
182
+ solver.end_edit
183
+ end
184
+ end
data/test/test_ext.rb ADDED
@@ -0,0 +1,38 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class ExtTests < Test::Unit::TestCase
4
+ def test_float_approx_zero
5
+ assert !1.1.cl_approx_zero
6
+ assert 0.0.cl_approx_zero
7
+ assert 0.1e-8.cl_approx_zero
8
+ end
9
+
10
+ def test_float_approx
11
+ assert 1.1.cl_approx 1.1
12
+ end
13
+
14
+ def test_float_negative
15
+ assert -1.1.definitely_negative
16
+ assert -0.1e-5.definitely_negative
17
+ assert ! -0.1e-8.definitely_negative
18
+ end
19
+
20
+ def test_numeric_as_linear_expression
21
+ expr = 1.as_linear_expression
22
+ assert expr.terms.empty?
23
+ assert expr.constant == 1.to_f
24
+
25
+ expr = 1.1.as_linear_expression
26
+ assert expr.terms.empty?
27
+ assert expr.constant == 1.1
28
+ end
29
+
30
+ def test_object_approx
31
+ assert "foo".cl_approx "foo"
32
+ assert ! Object.new.cl_approx(Object.new)
33
+ end
34
+
35
+ def test_object_symbolic_weight
36
+ assert !Object.new.symbolic_weight?
37
+ end
38
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+
3
+ if ENV["coverage"]
4
+ require "simplecov"
5
+ SimpleCov.start do
6
+ add_filter "/test/"
7
+ minimum_coverage 90
8
+ end
9
+ end
10
+
11
+ require "test/unit"
12
+ require "cassowary"
@@ -0,0 +1,33 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class VariablesTests < Test::Unit::TestCase
4
+ def test_operations
5
+ x = Cassowary::Variable.new name: 'x', value: 20
6
+ expr = x / 10
7
+ assert expr.constant == 0
8
+ assert expr.terms[x] == 0.1
9
+
10
+ expr = x * 10
11
+ assert expr.terms[x] == 10
12
+
13
+ expr = x - 10
14
+ assert expr.constant == -10
15
+
16
+ expr = x + 10
17
+ assert expr.constant == 10
18
+
19
+ expr = -x
20
+ assert expr.terms[x] == -1
21
+ end
22
+
23
+ def test_inspect
24
+ x = Cassowary::Variable.new name: 'x', value: 21.1
25
+ assert_equal "x[21.1]", x.inspect
26
+
27
+ x = Cassowary::Variable.new name: 'x'
28
+ assert_equal "x[nil]", x.inspect
29
+
30
+ x = Cassowary::SlackVariable.new
31
+ assert_equal "<CV#0x" + x.object_id.to_s(16) + ">", x.inspect
32
+ end
33
+ end