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 CHANGED
@@ -1 +1 @@
1
- 0.7.1
1
+ 0.7.2
data/data/tsp/pac10.txt CHANGED
@@ -1,55 +1,68 @@
1
- % Driving distances (in minutes) between Pac-10 stadiums
2
-
3
- Seattle Pullman 320
4
- Seattle Corvallis 317
5
- Seattle Eugene 338
6
- Seattle Berkeley 863
7
- Seattle Stanford 914
8
- Seattle Pasadena 1190
9
- Seattle Los_Angeles 1190
10
- Seattle Tempe 1620
11
- Seattle Tucson 1800
12
-
13
- Pullman Corvallis 470
14
- Pullman Eugene 492
15
- Pullman Berkeley 1070
16
- Pullman Stanford 1123
17
- Pullman Pasadena 1437
18
- Pullman Los_Angeles 1437
19
- Pullman Tempe 1560
20
- Pullman Tucson 1680
21
-
22
- Corvallis Eugene 55
23
- Corvallis Berkeley 572
24
- Corvallis Stanford 624
25
- Corvallis Pasadena 898
26
- Corvallis Los_Angeles 899
27
- Corvallis Tempe 1268
28
- Corvallis Tucson 1379
29
-
30
- Eugene Berkeley 526
31
- Eugene Stanford 578
32
- Eugene Pasadena 852
33
- Eugene Los_Angeles 852
34
- Eugene Tempe 1222
35
- Eugene Tucson 1332
36
-
37
- Berkeley Stanford 58
38
- Berkeley Pasadena 369
39
- Berkeley Los_Angeles 371
40
- Berkeley Tempe 740
41
- Berkeley Tucson 857
42
-
43
- Stanford Pasadena 370
44
- Stanford Los_Angeles 371
45
- Stanford Tempe 740
46
- Stanford Tucson 858
47
-
48
- Pasadena Los_Angeles 16
49
- Pasadena Tempe 373
50
- Pasadena Tucson 483
51
-
52
- Los_Angeles Tempe 375
53
- Los_Angeles Tucson 486
54
-
55
- Tempe Tucson 122
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
- % [[443, 493], [496, 981], [539, 666], [617, 337], [698, 224], [750, 450], [850, 850], [987, 237]]
2
- % a.each { |p| Canvas.circle(p[0]/3, p[1]/3, 3, :fill => 'darkblue') }
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 = 1)
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
- r0, v0 = random_vectors(mr/2, i, big)
437
- res << Body.new(mm*100/big, r0, v0)
438
- # write_tuple( :view, [i, 10, "#0080ff"] )
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
- # write_tuple( :view, [i+big, 5, "#0080ff"] )
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 == 2 && args[1].class == Fixnum
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 "max height: #{@@drawing.options[:hmax]}"
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
- @@drawing.options[:dashcount] = (@@drawing.options[:dashcount] + 1) % @@drawing.options[:dash]
666
- if @@drawing.options[:dashcount] == 0
667
- @@drawing.options[:pendown] = @@drawing.options[:pendown].nil? ? :track : nil
668
- end
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
- =begin rdoc
43
- The lines in the input file passed to the constructor come from a
44
- program that uses Google Maps to look up dirving times, so there are
45
- 3 distance fields: days, hours, minutes. The input record format is
46
- city 1, city 2, days, hours, minutes, all separated by tabs. Each
47
- city is assigned a one-letter key.
48
- =end
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 or array of points"
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
- next if line.chomp.length == 0
116
- next if line[0] == ?%
144
+ line.chomp!
145
+ next if line.length == 0
117
146
  rec = line.split
118
- i = getKey(rec[0])
119
- j = getKey(rec[1])
120
- dist = rec[2].to_i
121
- assign(i,j,dist)
122
- assign(j,i,dist)
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..@key-1).each do |i|
129
- (i+1..@key-1).each do |j|
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 - 1
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
- for j in i...a.length
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.1
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-23 00:00:00 -08:00
12
+ date: 2010-02-26 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15