compsci 0.1.1.1 → 0.2.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.
- 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
|
|