rubylabs 0.7.1 → 0.7.2
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/VERSION +1 -1
- data/data/tsp/pac10.txt +68 -55
- data/data/tsp/test.txt +9 -2
- data/lib/rubylabs.rb +4 -0
- data/lib/spherelab.rb +27 -16
- data/lib/tsplab.rb +174 -29
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.2
|
data/data/tsp/pac10.txt
CHANGED
@@ -1,55 +1,68 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
1
|
+
:map Pac-10 conference
|
2
|
+
|
3
|
+
91 92 uw
|
4
|
+
145 113 wsu
|
5
|
+
61 148 osu
|
6
|
+
84 161 uo
|
7
|
+
78 256 ucb
|
8
|
+
60 278 lsju
|
9
|
+
116 310 ucla
|
10
|
+
100 328 usc
|
11
|
+
193 337 asu
|
12
|
+
197 369 ua
|
13
|
+
|
14
|
+
:matrix driving distances (in minutes) between stadiums
|
15
|
+
|
16
|
+
uw wsu 320
|
17
|
+
uw osu 317
|
18
|
+
uw uo 338
|
19
|
+
uw ucb 863
|
20
|
+
uw lsju 914
|
21
|
+
uw ucla 1190
|
22
|
+
uw usc 1190
|
23
|
+
uw asu 1620
|
24
|
+
uw ua 1800
|
25
|
+
|
26
|
+
wsu osu 470
|
27
|
+
wsu uo 492
|
28
|
+
wsu ucb 1070
|
29
|
+
wsu lsju 1123
|
30
|
+
wsu ucla 1437
|
31
|
+
wsu usc 1437
|
32
|
+
wsu asu 1560
|
33
|
+
wsu ua 1680
|
34
|
+
|
35
|
+
osu uo 55
|
36
|
+
osu ucb 572
|
37
|
+
osu lsju 624
|
38
|
+
osu ucla 898
|
39
|
+
osu usc 899
|
40
|
+
osu asu 1268
|
41
|
+
osu ua 1379
|
42
|
+
|
43
|
+
uo ucb 526
|
44
|
+
uo lsju 578
|
45
|
+
uo ucla 852
|
46
|
+
uo usc 852
|
47
|
+
uo asu 1222
|
48
|
+
uo ua 1332
|
49
|
+
|
50
|
+
ucb lsju 58
|
51
|
+
ucb ucla 369
|
52
|
+
ucb usc 371
|
53
|
+
ucb asu 740
|
54
|
+
ucb ua 857
|
55
|
+
|
56
|
+
lsju ucla 370
|
57
|
+
lsju usc 371
|
58
|
+
lsju asu 740
|
59
|
+
lsju ua 858
|
60
|
+
|
61
|
+
ucla usc 16
|
62
|
+
ucla asu 373
|
63
|
+
ucla ua 483
|
64
|
+
|
65
|
+
usc asu 375
|
66
|
+
usc ua 486
|
67
|
+
|
68
|
+
asu ua 122
|
data/data/tsp/test.txt
CHANGED
@@ -1,2 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
:map eight random points
|
2
|
+
443 493
|
3
|
+
496 981
|
4
|
+
539 666
|
5
|
+
617 337
|
6
|
+
698 224
|
7
|
+
750 450
|
8
|
+
850 850
|
9
|
+
987 237
|
data/lib/rubylabs.rb
CHANGED
@@ -528,6 +528,10 @@ Similar to TestArray, but draws random words from a file.
|
|
528
528
|
@@tkroot
|
529
529
|
end
|
530
530
|
|
531
|
+
def Canvas.create(*args)
|
532
|
+
@@canvas.send(:create, *args)
|
533
|
+
end
|
534
|
+
|
531
535
|
# Idea for the future, abandoned for now: allow applications to define a coordinate
|
532
536
|
# system, e.g. cartesian with the origin in the middle of the canvas, and map coords
|
533
537
|
# pass to line, rectangle, etc from user-specified coordinates to Tk coordinates.
|
data/lib/spherelab.rb
CHANGED
@@ -31,6 +31,7 @@ module SphereLab
|
|
31
31
|
:mxmin => 100,
|
32
32
|
:mymin => 50,
|
33
33
|
:hmax => 100,
|
34
|
+
:dash => 1,
|
34
35
|
}
|
35
36
|
|
36
37
|
@@robotOptions = {
|
@@ -427,23 +428,27 @@ module SphereLab
|
|
427
428
|
res = Vector.new(-r * cos)
|
428
429
|
end
|
429
430
|
|
430
|
-
def random_bodies(n, big
|
431
|
+
def random_bodies(n, big)
|
432
|
+
big = 1 if big.nil?
|
431
433
|
res = []
|
432
434
|
mm = 1e12 # average mass
|
433
435
|
mr = 150 # average distance from origin
|
434
|
-
# write_tuple( :scale, [ 1.0 ] )
|
435
436
|
big.times do |i|
|
436
|
-
|
437
|
-
|
438
|
-
|
437
|
+
r, v = random_vectors(mr/2, i, big)
|
438
|
+
b = Body.new(mm*100/big, r, v)
|
439
|
+
b.name = "b#{i}"
|
440
|
+
b.color = '#0080ff'
|
441
|
+
b.size = 10
|
442
|
+
res << b
|
439
443
|
end
|
440
444
|
(n-big).times do |i|
|
441
445
|
r, v = random_vectors(mr, i, n-big)
|
442
446
|
b = Body.new(mm, r, v)
|
443
|
-
|
447
|
+
b.name = "b#{i+big}"
|
448
|
+
b.color = '#0080ff'
|
449
|
+
b.size = 5
|
444
450
|
res << b
|
445
451
|
end
|
446
|
-
res.each_with_index { |b,i| b.name = "b#{i}"; b.color = '#0080ff'; b.size = 5 }
|
447
452
|
return res
|
448
453
|
end
|
449
454
|
|
@@ -458,8 +463,8 @@ module SphereLab
|
|
458
463
|
begin
|
459
464
|
raise "usage: make_system(id)" unless args.length > 0
|
460
465
|
if args[0] == :random
|
461
|
-
raise "usage: make_system(:random, n)" unless args.length
|
462
|
-
return random_bodies(args[1])
|
466
|
+
raise "usage: make_system(:random, n, m)" unless args.length >= 2 && args[1].class == Fixnum
|
467
|
+
return random_bodies(args[1], args[2])
|
463
468
|
end
|
464
469
|
filename = args[0]
|
465
470
|
if filename.class == Symbol
|
@@ -608,7 +613,6 @@ module SphereLab
|
|
608
613
|
|
609
614
|
def view_melon(blist, userOptions = {})
|
610
615
|
options = @@droppingOptions.merge(userOptions)
|
611
|
-
options[:dash] = 1
|
612
616
|
options[:dashcount] = 0
|
613
617
|
options[:pendown] = :track
|
614
618
|
edge = options[:canvasSize]
|
@@ -636,8 +640,8 @@ module SphereLab
|
|
636
640
|
def position_melon(blist, height)
|
637
641
|
melon = blist[0]
|
638
642
|
if @@drawing
|
639
|
-
if height > @@drawing.options[:hmax]
|
640
|
-
puts "
|
643
|
+
if height < 0 || height > @@drawing.options[:hmax]
|
644
|
+
puts "Height must be between 0 and #{@@drawing.options[:hmax]} meters"
|
641
645
|
return false
|
642
646
|
end
|
643
647
|
melon.prevy = @@drawing.ground
|
@@ -652,6 +656,11 @@ module SphereLab
|
|
652
656
|
melon.position.y += height
|
653
657
|
end
|
654
658
|
melon.velocity.y = 0.0
|
659
|
+
class <<melon
|
660
|
+
def height
|
661
|
+
return position.y - prevy
|
662
|
+
end
|
663
|
+
end
|
655
664
|
return height
|
656
665
|
end
|
657
666
|
|
@@ -662,10 +671,12 @@ module SphereLab
|
|
662
671
|
return "splat" if melon.prevy.nil? || my < melon.prevy
|
663
672
|
step_system(blist, dt)
|
664
673
|
if @@drawing
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
674
|
+
if @@drawing.options[:dash] > 0
|
675
|
+
@@drawing.options[:dashcount] = (@@drawing.options[:dashcount] + 1) % @@drawing.options[:dash]
|
676
|
+
if @@drawing.options[:dashcount] == 0
|
677
|
+
@@drawing.options[:pendown] = @@drawing.options[:pendown].nil? ? :track : nil
|
678
|
+
end
|
679
|
+
end
|
669
680
|
dx = (melon.position.x - mx) * @@drawing.scale
|
670
681
|
dy = (my - melon.position.y) * @@drawing.scale
|
671
682
|
Canvas.move(melon.graphic, dx, dy, @@drawing.options[:pendown])
|
data/lib/tsplab.rb
CHANGED
@@ -10,6 +10,17 @@
|
|
10
10
|
TODO test: selection only, no mutations
|
11
11
|
TODO test: random draws only, no selection
|
12
12
|
TODO plot space: get all 180K costs, order lexicographically, plot (smoothed)
|
13
|
+
|
14
|
+
TODO hide mutate, cross methods -- but accept optional params in call to reproduce constructor so students can control effects to see how method works
|
15
|
+
TODO (idea is that genes are fixed when critter made, don't change after that)
|
16
|
+
=end
|
17
|
+
|
18
|
+
=begin
|
19
|
+
TODO clean up interface
|
20
|
+
todo two constructors (mutate, cross) instead of one
|
21
|
+
todo distances are floats, not ints
|
22
|
+
todo wider histogram, up to 100 bins 3 pixels wide
|
23
|
+
todo line color = blue if bin width 4 or less
|
13
24
|
=end
|
14
25
|
|
15
26
|
module RubyLabs
|
@@ -17,6 +28,8 @@ module RubyLabs
|
|
17
28
|
module TSPLab
|
18
29
|
|
19
30
|
require 'set'
|
31
|
+
|
32
|
+
MapView = Struct.new(:cities, :nodes, :links, :histogram, :options)
|
20
33
|
|
21
34
|
=begin rdoc
|
22
35
|
|
@@ -35,25 +48,21 @@ minutes.
|
|
35
48
|
# values() returns an array of city names
|
36
49
|
# [x] returns the name of the city with key x
|
37
50
|
# distance(x,y) returns the distance between cities with keys x and y
|
38
|
-
|
51
|
+
# coords(x) returns x, y coordinates of city
|
39
52
|
|
40
53
|
class CityList
|
54
|
+
|
55
|
+
attr_reader :cities, :matrix
|
41
56
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def initialize(matrix)
|
51
|
-
if matrix.class == String || matrix.class == Symbol
|
52
|
-
read_file(matrix)
|
53
|
-
elsif matrix.class == Array
|
54
|
-
make_map(matrix)
|
57
|
+
def initialize(arg)
|
58
|
+
if arg.class == String || arg.class == Symbol
|
59
|
+
read_file(arg)
|
60
|
+
elsif arg.class == Array
|
61
|
+
make_map(arg)
|
62
|
+
elsif arg.class == Fixnum
|
63
|
+
make_random_map(arg)
|
55
64
|
else
|
56
|
-
raise "CityList: parameter must be a file name
|
65
|
+
raise "CityList: parameter must be a file name, array of points, or an integer"
|
57
66
|
end
|
58
67
|
end
|
59
68
|
|
@@ -70,6 +79,20 @@ city is assigned a one-letter key.
|
|
70
79
|
end
|
71
80
|
return true
|
72
81
|
end
|
82
|
+
|
83
|
+
def print_matrix
|
84
|
+
# colarray =
|
85
|
+
puts " " + (?B..(@key-1)).map { |x| x.chr }.join(" ")
|
86
|
+
(?A..(@key-2)).each_with_index do |i, n|
|
87
|
+
s = sprintf "%2s: ", i.chr
|
88
|
+
s += " " * n
|
89
|
+
(i+1..@key-1).each do |j|
|
90
|
+
s += sprintf " %3d", @matrix[i][j]
|
91
|
+
end
|
92
|
+
puts s
|
93
|
+
end
|
94
|
+
return true
|
95
|
+
end
|
73
96
|
|
74
97
|
def size()
|
75
98
|
return @key - ?A
|
@@ -96,6 +119,10 @@ city is assigned a one-letter key.
|
|
96
119
|
return nil unless y >= ?A && y < @key
|
97
120
|
return @matrix[x][y]
|
98
121
|
end
|
122
|
+
|
123
|
+
def coords(name)
|
124
|
+
return @coords[name]
|
125
|
+
end
|
99
126
|
|
100
127
|
# private methods -- return the key for a city (make a new one if necessary),
|
101
128
|
# get a time value from an input line, save a distance
|
@@ -110,23 +137,32 @@ city is assigned a one-letter key.
|
|
110
137
|
@key = ?A
|
111
138
|
@cities = Hash.new # initially a map from name string to one-letter key
|
112
139
|
@matrix = Hash.new
|
113
|
-
|
140
|
+
@coords = Hash.new
|
141
|
+
readingLocations = true
|
142
|
+
|
114
143
|
File.open(matrixfilename).each do |line|
|
115
|
-
|
116
|
-
next if line
|
144
|
+
line.chomp!
|
145
|
+
next if line.length == 0
|
117
146
|
rec = line.split
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
147
|
+
if rec[0][0] == ?:
|
148
|
+
readingLocations = false if rec[0] == ":matrix"
|
149
|
+
# tbd -- deal with other directives
|
150
|
+
elsif readingLocations
|
151
|
+
@coords[getKey(rec[2]).chr] = [rec[0].to_i, rec[1].to_i]
|
152
|
+
else
|
153
|
+
i = getKey(rec[0])
|
154
|
+
j = getKey(rec[1])
|
155
|
+
dist = rec[2].to_i
|
156
|
+
assign(i,j,dist)
|
157
|
+
assign(j,i,dist)
|
158
|
+
end
|
123
159
|
end
|
124
|
-
|
125
|
-
@cities = @cities.invert # from now on a map from key to name string
|
160
|
+
|
161
|
+
@cities = @cities.invert # from now on @cities is a map from key to name string
|
126
162
|
|
127
163
|
errs = 0
|
128
|
-
(?A
|
129
|
-
(i+1
|
164
|
+
(?A...@key).each do |i|
|
165
|
+
(i+1...@key).each do |j|
|
130
166
|
unless @matrix[i][j] && @matrix[j][i]
|
131
167
|
errs += 1
|
132
168
|
puts "CityList.new: missing distance between #{@cities[i]} and #{@cities[j]}"
|
@@ -137,14 +173,38 @@ city is assigned a one-letter key.
|
|
137
173
|
end
|
138
174
|
|
139
175
|
def make_map(a)
|
140
|
-
@key = ?A + a.length
|
176
|
+
@key = ?A + a.length
|
141
177
|
@cities = Hash.new
|
142
178
|
@matrix = Hash.new
|
179
|
+
@coords = Hash.new
|
143
180
|
for i in 0...a.length
|
144
|
-
|
181
|
+
chi = ?A + i
|
182
|
+
@cities[chi] = chi.chr
|
183
|
+
for j in (i+1)...a.length
|
184
|
+
x = a[i][0] - a[j][0]
|
185
|
+
y = a[i][1] - a[j][1]
|
186
|
+
d = Math.sqrt(x**2 + y**2).round
|
187
|
+
chj = ?A + j
|
188
|
+
assign(chi,chj,d)
|
189
|
+
assign(chj,chi,d)
|
145
190
|
end
|
191
|
+
@coords[chi.chr] = a[i]
|
146
192
|
end
|
147
193
|
end
|
194
|
+
|
195
|
+
def make_random_map(n)
|
196
|
+
h = Hash.new
|
197
|
+
a = Array.new
|
198
|
+
while h.size < n
|
199
|
+
h[rand(400)] = 1
|
200
|
+
end
|
201
|
+
h.keys.each do |k|
|
202
|
+
x = k / 20
|
203
|
+
y = k % 20
|
204
|
+
a << [x*20 + rand(5) + 50, y*20 + rand(5) + 50]
|
205
|
+
end
|
206
|
+
make_map(a)
|
207
|
+
end
|
148
208
|
|
149
209
|
def getKey(s)
|
150
210
|
if @cities[s] == nil
|
@@ -218,6 +278,7 @@ inherited from a parent, and incremented whenever a mutation or crossover is app
|
|
218
278
|
if y.nil? || y == :trace
|
219
279
|
i = rand(x.path.length)
|
220
280
|
j = (i+1) % x.path.length
|
281
|
+
# j = rand(x.path.length)
|
221
282
|
trace = :trace if y == :trace # handle calls like reproduce(x, :trace)
|
222
283
|
x.clone.mutate(i, j, trace)
|
223
284
|
else
|
@@ -389,6 +450,11 @@ inherited from a parent, and incremented whenever a mutation or crossover is app
|
|
389
450
|
puts "maxstatic: #{maxstatic}"
|
390
451
|
puts "pcross: #{param[:pcross]}"
|
391
452
|
end
|
453
|
+
|
454
|
+
if @@drawing
|
455
|
+
view_tour(best)
|
456
|
+
make_histogram(p)
|
457
|
+
end
|
392
458
|
|
393
459
|
while ngen < maxgen && nstatic < maxstatic
|
394
460
|
puts "best tour: #{p[0]}" if param[:verbose]
|
@@ -396,9 +462,11 @@ inherited from a parent, and incremented whenever a mutation or crossover is app
|
|
396
462
|
if p[0].cost < best.cost
|
397
463
|
best = p[0]
|
398
464
|
nstatic = 0
|
465
|
+
view_tour(best) if @@drawing
|
399
466
|
else
|
400
467
|
nstatic += 1
|
401
468
|
end
|
469
|
+
update_histogram(p) if @@drawing
|
402
470
|
ngen += 1
|
403
471
|
end
|
404
472
|
|
@@ -441,10 +509,87 @@ inherited from a parent, and incremented whenever a mutation or crossover is app
|
|
441
509
|
puts "Copy of #{matrix} saved in #{outfilename}"
|
442
510
|
end
|
443
511
|
|
512
|
+
=begin rdoc
|
513
|
+
Methods for displaying a map and a tour
|
514
|
+
=end
|
515
|
+
|
516
|
+
def view_map(m, userOptions = {} )
|
517
|
+
options = @@mapOptions.merge(userOptions)
|
518
|
+
Canvas.init(700, 500, "TSPLab")
|
519
|
+
links = Array.new
|
520
|
+
nodes = Array.new
|
521
|
+
m.keys.each do |loc|
|
522
|
+
x, y = m.coords(loc)
|
523
|
+
nodes << Canvas.circle( x, y, options[:dotRadius], :fill => options[:dotColor] )
|
524
|
+
end
|
525
|
+
@@drawing = MapView.new(m, nodes, links, Array.new, options)
|
526
|
+
Canvas.sync
|
527
|
+
return true
|
528
|
+
end
|
529
|
+
|
530
|
+
def view_tour(t, userOptions = {} )
|
531
|
+
map = @@drawing.cities
|
532
|
+
@@drawing.links.each { |x| x.delete }
|
533
|
+
@@drawing.links.clear
|
534
|
+
x0, y0 = map.coords(t.path[-1].chr)
|
535
|
+
for i in 0...t.path.length
|
536
|
+
x1, y1 = map.coords(t.path[i].chr)
|
537
|
+
@@drawing.links << Canvas.line(x0, y0, x1, y1)
|
538
|
+
x0, y0 = x1, y1
|
539
|
+
end
|
540
|
+
@@drawing.nodes.each { |x| x.raise }
|
541
|
+
sleep 0.1
|
542
|
+
end
|
543
|
+
|
544
|
+
# todo -- get drawing params from options
|
545
|
+
# todo -- make_histogram and update_histogram are private
|
546
|
+
# todo -- check to see if new histogram has same number of bins as old one -- throw some out or make some new ones
|
547
|
+
# todo -- scale w to fit number of tours, min size = 1
|
548
|
+
# todo -- max bins 100 -- if pop size over that just show first 100 tours
|
549
|
+
# todo! color bin red if new tour comes from crossover
|
550
|
+
|
551
|
+
def make_histogram(pop)
|
552
|
+
if @@drawing.histogram.length > 0
|
553
|
+
update_histogram(pop)
|
554
|
+
else
|
555
|
+
x = 520
|
556
|
+
y = 100
|
557
|
+
ymax = 200.0
|
558
|
+
w = 5
|
559
|
+
scale = ymax / pop[-1].cost
|
560
|
+
pop[0..29].each do |t|
|
561
|
+
h = t.cost*scale
|
562
|
+
@@drawing.histogram << Canvas.rectangle(x, y+ymax-h, x+w, y+ymax, :fill => 'darkblue' )
|
563
|
+
x += w
|
564
|
+
end
|
565
|
+
@@drawing.options[:scale] = scale
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
def update_histogram(pop)
|
570
|
+
y = 100
|
571
|
+
ymax = 200.0
|
572
|
+
scale = @@drawing.options[:scale]
|
573
|
+
@@drawing.histogram.each_with_index do |box, i|
|
574
|
+
h = pop[i].cost * scale
|
575
|
+
x0, y0, x1, y1 = box.coords
|
576
|
+
box.coords = x0, y+ymax-h, x1, y1
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
444
580
|
# Values accessible to all the methods in the module
|
445
581
|
|
446
582
|
@@dataDirectory = File.join(File.dirname(__FILE__), '..', 'data', 'tsp')
|
447
583
|
|
584
|
+
@@mapOptions = {
|
585
|
+
:dotColor => '#cccccc',
|
586
|
+
:dotRadius => 5.0,
|
587
|
+
}
|
588
|
+
|
589
|
+
@@tourOptions = {
|
590
|
+
|
591
|
+
}
|
592
|
+
|
448
593
|
end # module TSPLab
|
449
594
|
|
450
595
|
end # module RubyLabs
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubylabs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- conery
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-26 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|