compsci 0.1.1.1 → 0.2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +74 -5
- data/Rakefile +20 -9
- data/VERSION +1 -1
- data/compsci.gemspec +1 -0
- data/examples/binary_search_tree.rb +16 -0
- data/examples/heap.rb +0 -42
- data/examples/heap_push.rb +46 -0
- data/examples/tree.rb +2 -1
- data/examples/{binary_tree.rb → tree_push.rb} +3 -2
- data/lib/compsci/binary_search_tree.rb +86 -0
- data/lib/compsci/fibonacci.rb +1 -9
- data/lib/compsci/fit.rb +34 -14
- data/lib/compsci/names.rb +3 -4
- data/lib/compsci/node.rb +66 -19
- data/lib/compsci/simplex.rb +173 -0
- data/lib/compsci/simplex/parse.rb +125 -0
- data/lib/compsci/tree.rb +14 -1
- data/test/bench/complete_tree.rb +59 -0
- data/test/bench/fibonacci.rb +0 -4
- data/test/bench/simplex.rb +141 -0
- data/test/bench/tree.rb +20 -15
- data/test/binary_search_tree.rb +106 -0
- data/test/fit.rb +5 -11
- data/test/node.rb +55 -4
- data/test/simplex.rb +291 -0
- data/test/simplex_parse.rb +94 -0
- data/test/tree.rb +33 -9
- metadata +27 -3
data/test/simplex.rb
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'compsci/simplex'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
class SimplexTest < Minitest::Test
|
7
|
+
def test_2x2
|
8
|
+
result = Simplex.new(
|
9
|
+
[1, 1],
|
10
|
+
[
|
11
|
+
[ 2, 1],
|
12
|
+
[ 1, 2],
|
13
|
+
],
|
14
|
+
[4, 3]
|
15
|
+
).solution
|
16
|
+
assert_equal [Rational(5, 3), Rational(2, 3)], result
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_2x2_b
|
20
|
+
result = Simplex.new(
|
21
|
+
[3, 4],
|
22
|
+
[
|
23
|
+
[ 1, 1],
|
24
|
+
[ 2, 1],
|
25
|
+
],
|
26
|
+
[4, 5]
|
27
|
+
).solution
|
28
|
+
assert_equal [0, 4], result
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_2x2_c
|
32
|
+
result = Simplex.new(
|
33
|
+
[2, -1],
|
34
|
+
[
|
35
|
+
[ 1, 2],
|
36
|
+
[ 3, 2],
|
37
|
+
],
|
38
|
+
[6, 12]
|
39
|
+
).solution
|
40
|
+
assert_equal [4, 0], result
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_3x3_a
|
44
|
+
result = Simplex.new(
|
45
|
+
[60, 90, 300],
|
46
|
+
[
|
47
|
+
[1, 1, 1],
|
48
|
+
[1, 3, 0],
|
49
|
+
[2, 0, 1]
|
50
|
+
],
|
51
|
+
[600, 600, 900]
|
52
|
+
).solution
|
53
|
+
assert_equal [0, 0, 600], result
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_3x3_b
|
57
|
+
result = Simplex.new(
|
58
|
+
[70, 210, 140],
|
59
|
+
[
|
60
|
+
[ 1, 1, 1],
|
61
|
+
[ 5, 4, 4],
|
62
|
+
[40, 20, 30]
|
63
|
+
],
|
64
|
+
[100, 480, 3200]
|
65
|
+
).solution
|
66
|
+
assert_equal [0, 100, 0], result
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_3x3_c
|
70
|
+
result = Simplex.new(
|
71
|
+
[2, -1, 2],
|
72
|
+
[
|
73
|
+
[ 2, 1, 0],
|
74
|
+
[ 1, 2, -2],
|
75
|
+
[ 0, 1, 2]
|
76
|
+
],
|
77
|
+
[10, 20, 5]
|
78
|
+
).solution
|
79
|
+
assert_equal [5, 0, Rational(5, 2)], result
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_3x3_d
|
83
|
+
result = Simplex.new(
|
84
|
+
[11, 16, 15],
|
85
|
+
[
|
86
|
+
[ 1, 2, Rational(3, 2)],
|
87
|
+
[Rational(2, 3), Rational(2, 3), 1],
|
88
|
+
[Rational(1, 2), Rational(1, 3), Rational(1, 2)]
|
89
|
+
],
|
90
|
+
[12_000, 4_600, 2_400]
|
91
|
+
).solution
|
92
|
+
assert_equal [600, 5_100, 800], result
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_3x3_e
|
96
|
+
simplex = Simplex.new(
|
97
|
+
[5, 4, 3],
|
98
|
+
[
|
99
|
+
[2, 3, 1],
|
100
|
+
[4, 1, 2],
|
101
|
+
[3, 4, 2]
|
102
|
+
],
|
103
|
+
[5, 11, 8]
|
104
|
+
)
|
105
|
+
assert_equal [2, 0, 1], simplex.solution
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_3x3_f
|
109
|
+
simplex = Simplex.new(
|
110
|
+
[3, 2, -4],
|
111
|
+
[
|
112
|
+
[1, 4, 0],
|
113
|
+
[2, 4,-2],
|
114
|
+
[1, 1,-2]
|
115
|
+
],
|
116
|
+
[5, 6, 2]
|
117
|
+
)
|
118
|
+
assert_equal [4, 0, 1], simplex.solution
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_3x3_g
|
122
|
+
simplex = Simplex.new(
|
123
|
+
[2, -1, 8],
|
124
|
+
[
|
125
|
+
[2, -4, 6],
|
126
|
+
[-1, 3, 4],
|
127
|
+
[0, 0, 2]
|
128
|
+
],
|
129
|
+
[3, 2, 1]
|
130
|
+
)
|
131
|
+
assert_equal [Rational(17, 2), Rational(7,2), 0], simplex.solution
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_3x4
|
135
|
+
result = Simplex.new(
|
136
|
+
[100_000, 40_000, 18_000],
|
137
|
+
[
|
138
|
+
[20, 6, 3],
|
139
|
+
[ 0, 1, 0],
|
140
|
+
[-1,-1, 1],
|
141
|
+
[-9, 1, 1]
|
142
|
+
],
|
143
|
+
[182, 10, 0, 0]
|
144
|
+
).solution
|
145
|
+
assert_equal [4, 10, 14], result
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_4x4
|
149
|
+
result = Simplex.new(
|
150
|
+
[1, 2, 1, 2],
|
151
|
+
[
|
152
|
+
[1, 0, 1, 0],
|
153
|
+
[0, 1, 0, 1],
|
154
|
+
[1, 1, 0, 0],
|
155
|
+
[0, 0, 1, 1]
|
156
|
+
],
|
157
|
+
[1, 4, 2, 2]
|
158
|
+
).solution
|
159
|
+
assert_equal [0, 2, 0, 2], result
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_cycle
|
163
|
+
result = Simplex.new(
|
164
|
+
[10, -57, -9, -24],
|
165
|
+
[
|
166
|
+
[0.5, -5.5, -2.5, 9],
|
167
|
+
[0.5, -1.5, -0.5, 1],
|
168
|
+
[ 1, 0, 0, 0]
|
169
|
+
],
|
170
|
+
[0, 0, 1]
|
171
|
+
).solution
|
172
|
+
assert_equal [1, 0, 1, 0], result
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_cycle2
|
176
|
+
simplex = Simplex.new(
|
177
|
+
[2, 3, -1, -12],
|
178
|
+
[
|
179
|
+
[-2, -9, 1, 9],
|
180
|
+
[Rational(1, 3), 1, Rational(-1, 3), -2],
|
181
|
+
],
|
182
|
+
[0, 0]
|
183
|
+
)
|
184
|
+
assert_raises Simplex::UnboundedProblem do
|
185
|
+
simplex.solution
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_error_mismatched_dimensions
|
190
|
+
assert_raises ArgumentError do
|
191
|
+
Simplex.new(
|
192
|
+
[10, -57, -9],
|
193
|
+
[
|
194
|
+
[0.5, -5.5, -2.5, 9],
|
195
|
+
[0.5, -1.5, -0.5, 1],
|
196
|
+
[ 1, 0, 0, 0]
|
197
|
+
],
|
198
|
+
[0, 0, 1]
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
assert_raises ArgumentError do
|
203
|
+
Simplex.new(
|
204
|
+
[10, -57, -9, 2],
|
205
|
+
[
|
206
|
+
[0.5, -5.5, 9, 4],
|
207
|
+
[0.5, -1.5, 1],
|
208
|
+
[ 1, 0, 0]
|
209
|
+
],
|
210
|
+
[0, 0, 1]
|
211
|
+
)
|
212
|
+
end
|
213
|
+
|
214
|
+
assert_raises ArgumentError do
|
215
|
+
Simplex.new(
|
216
|
+
[10, -57, -9, 2],
|
217
|
+
[
|
218
|
+
[0.5, -5.5, 9, 4],
|
219
|
+
[0.5, -1.5, 1, 5],
|
220
|
+
[ 1, 0, 0, 5]
|
221
|
+
],
|
222
|
+
[0, 1]
|
223
|
+
)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_manual_iteration
|
228
|
+
simplex = Simplex.new(
|
229
|
+
[10, -57, -9, -24],
|
230
|
+
[
|
231
|
+
[0.5, -5.5, -2.5, 9],
|
232
|
+
[0.5, -1.5, -0.5, 1],
|
233
|
+
[ 1, 0, 0, 0]
|
234
|
+
],
|
235
|
+
[0, 0, 1]
|
236
|
+
)
|
237
|
+
while simplex.can_improve?
|
238
|
+
assert simplex.formatted_tableau.is_a?(String)
|
239
|
+
simplex.pivot
|
240
|
+
end
|
241
|
+
result = simplex.solution
|
242
|
+
assert_equal [1, 0, 1, 0], result
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_cup_factory
|
246
|
+
result = Simplex.new(
|
247
|
+
[25, 20],
|
248
|
+
[
|
249
|
+
[20, 12],
|
250
|
+
[1, 1]
|
251
|
+
],
|
252
|
+
[1800, 8*15]
|
253
|
+
)
|
254
|
+
assert_equal [45, 75], result.solution
|
255
|
+
end
|
256
|
+
|
257
|
+
#def test_infeasible1
|
258
|
+
# simplex = Simplex.new(
|
259
|
+
# [2, -1],
|
260
|
+
# [
|
261
|
+
# [1, -1],
|
262
|
+
# [-1, 1]
|
263
|
+
# ],
|
264
|
+
# [1, -2]
|
265
|
+
# )
|
266
|
+
# while simplex.can_improve?
|
267
|
+
# puts
|
268
|
+
# puts simplex.formatted_tableau
|
269
|
+
# simplex.pivot
|
270
|
+
# end
|
271
|
+
# p :done
|
272
|
+
# puts
|
273
|
+
# puts simplex.formatted_tableau
|
274
|
+
|
275
|
+
#end
|
276
|
+
|
277
|
+
def test_unbounded
|
278
|
+
simplex = Simplex.new(
|
279
|
+
[1, 1, 1],
|
280
|
+
[
|
281
|
+
[3, 1, -2],
|
282
|
+
[4, 3, 0]
|
283
|
+
],
|
284
|
+
[5, 7]
|
285
|
+
)
|
286
|
+
assert_raises Simplex::UnboundedProblem do
|
287
|
+
simplex.solution
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'compsci/simplex/parse'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
include CompSci
|
5
|
+
|
6
|
+
describe Simplex::Parse do
|
7
|
+
P = Simplex::Parse
|
8
|
+
|
9
|
+
describe "Parse.term" do
|
10
|
+
it "must parse valid terms" do
|
11
|
+
{ "-1.2A" => [-1.2, :A],
|
12
|
+
"99x" => [99.0, :x],
|
13
|
+
"z" => [ 1.0, :z],
|
14
|
+
"-b" => [-1.0, :b] }.each { |valid, expected|
|
15
|
+
P.term(valid).must_equal expected
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "must reject invalid terms" do
|
20
|
+
["3xy", "24/7x", "x17", "2*x"].each { |invalid|
|
21
|
+
proc { P.term(invalid) }.must_raise RuntimeError
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "Parse.expression" do
|
27
|
+
it "must parse valid expressions" do
|
28
|
+
{ "x + y" => { x: 1.0, y: 1.0 },
|
29
|
+
"2x - 5y" => { x: 2.0, y: -5.0 },
|
30
|
+
"-2x - 3y + -42.7z" => { x: -2.0, y: -3.0, z: -42.7 },
|
31
|
+
" -5y + -x " => { y: -5.0, x: -1.0 },
|
32
|
+
"a - -b" => { a: 1.0, b: 1.0 },
|
33
|
+
"a A b" => { a: 1.0, :A => 1.0, b: 1.0 },
|
34
|
+
}.each { |valid, expected|
|
35
|
+
P.expression(valid).must_equal expected
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
it "must reject invalid expressions" do
|
40
|
+
["a2 + b2 = c2",
|
41
|
+
"x + xy",
|
42
|
+
"x * 2"].each { |invalid|
|
43
|
+
proc { P.expression(invalid) }.must_raise P::Error
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "Parse.tokenize" do
|
49
|
+
it "ignores leading or trailing whitespace" do
|
50
|
+
P.tokenize(" 5x + 2.9y ").must_equal ["5x", "+", "2.9y"]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "ignores multiple spaces" do
|
54
|
+
P.tokenize("5x + 2.9y").must_equal ["5x", "+", "2.9y"]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "Parse.inequality" do
|
59
|
+
it "must parse valid inequalities" do
|
60
|
+
{ "x + y <= 4" => [{ x: 1.0, y: 1.0 }, 4.0],
|
61
|
+
"0.94a - 22.1b <= -14.67" => [{ a: 0.94, b: -22.1 }, -14.67],
|
62
|
+
"x <= 0" => [{ x: 1.0 }, 0],
|
63
|
+
}.each { |valid, expected|
|
64
|
+
P.inequality(valid).must_equal expected
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
it "must reject invalid inequalities" do
|
69
|
+
["x + y >= 4",
|
70
|
+
"0.94a - 22.1b <= -14.67c",
|
71
|
+
"x < 0",
|
72
|
+
].each { |invalid|
|
73
|
+
proc { P.inequality(invalid) }.must_raise P::Error
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "Simplex.maximize" do
|
80
|
+
it "must problem stuff" do
|
81
|
+
prob = Simplex.problem(maximize: 'x + y',
|
82
|
+
constraints: ['2x + y <= 4',
|
83
|
+
'x + 2y <= 3'])
|
84
|
+
sol = prob.solution
|
85
|
+
sol.must_equal [Rational(5, 3), Rational(2, 3)]
|
86
|
+
end
|
87
|
+
|
88
|
+
it "must maximize stuff" do
|
89
|
+
Simplex.maximize('x + y',
|
90
|
+
'2x + y <= 4',
|
91
|
+
'x + 2y <= 3').must_equal [Rational(5, 3),
|
92
|
+
Rational(2, 3)]
|
93
|
+
end
|
94
|
+
end
|
data/test/tree.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'compsci/node'
|
1
2
|
require 'compsci/tree'
|
2
3
|
require 'minitest/autorun'
|
3
4
|
|
@@ -5,7 +6,7 @@ include CompSci
|
|
5
6
|
|
6
7
|
describe Tree do
|
7
8
|
before do
|
8
|
-
@tree = Tree.new(
|
9
|
+
@tree = Tree.new(FlexNode, 42)
|
9
10
|
@vals = Array.new(99) { rand 99 }
|
10
11
|
end
|
11
12
|
|
@@ -16,7 +17,7 @@ describe Tree do
|
|
16
17
|
|
17
18
|
it "does depth_first search" do
|
18
19
|
vals = (0..30).to_a
|
19
|
-
tree = Tree.new(
|
20
|
+
tree = Tree.new(FlexNode, vals.shift)
|
20
21
|
tree.root.new_child vals.shift
|
21
22
|
tree.root.new_child vals.shift
|
22
23
|
tree.root.children.each { |c|
|
@@ -40,7 +41,7 @@ describe Tree do
|
|
40
41
|
|
41
42
|
it "does breadth_first search" do
|
42
43
|
vals = (0..30).to_a
|
43
|
-
tree = Tree.new(
|
44
|
+
tree = Tree.new(FlexNode, vals.shift)
|
44
45
|
tree.root.new_child vals.shift
|
45
46
|
tree.root.new_child vals.shift
|
46
47
|
tree.root.children.each { |c|
|
@@ -64,9 +65,32 @@ describe Tree do
|
|
64
65
|
end
|
65
66
|
|
66
67
|
describe NaryTree do
|
67
|
-
|
68
|
+
it "must power_of?" do
|
69
|
+
powers = {}
|
70
|
+
basemax = 12
|
71
|
+
expmax = 10
|
72
|
+
2.upto(basemax) { |base|
|
73
|
+
0.upto(expmax) { |exp|
|
74
|
+
powers[base] ||= []
|
75
|
+
powers[base] << base**exp
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
# 12k assertions below!
|
80
|
+
2.upto(basemax) { |base|
|
81
|
+
1.upto(2**expmax) { |num|
|
82
|
+
if powers[base].include?(num)
|
83
|
+
NaryTree.power_of?(num, base).must_equal true
|
84
|
+
else
|
85
|
+
NaryTree.power_of?(num, base).must_equal false
|
86
|
+
end
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "with FlexNode" do
|
68
92
|
before do
|
69
|
-
@tree = NaryTree.new(
|
93
|
+
@tree = NaryTree.new(FlexNode, 42, child_slots: 3)
|
70
94
|
end
|
71
95
|
|
72
96
|
it "must have an open parent" do
|
@@ -82,9 +106,9 @@ describe NaryTree do
|
|
82
106
|
end
|
83
107
|
end
|
84
108
|
|
85
|
-
describe "with
|
109
|
+
describe "with ChildFlexNode" do
|
86
110
|
before do
|
87
|
-
@tree = NaryTree.new(
|
111
|
+
@tree = NaryTree.new(ChildFlexNode, 42, child_slots: 4)
|
88
112
|
end
|
89
113
|
|
90
114
|
it "must have an open parent" do
|
@@ -103,7 +127,7 @@ end
|
|
103
127
|
|
104
128
|
describe "BinaryTree" do
|
105
129
|
before do
|
106
|
-
@tree = BinaryTree.new(
|
130
|
+
@tree = BinaryTree.new(FlexNode, 42)
|
107
131
|
end
|
108
132
|
|
109
133
|
it "must have 2 child_slots" do
|
@@ -121,7 +145,7 @@ describe "BinaryTree" do
|
|
121
145
|
|
122
146
|
describe "searching" do
|
123
147
|
before do
|
124
|
-
@tree = NaryTree.new(
|
148
|
+
@tree = NaryTree.new(FlexNode, 42, child_slots: 2)
|
125
149
|
99.times { |i| @tree.push i }
|
126
150
|
end
|
127
151
|
|