graph-function 0.1.5 → 0.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 421167ce11bda065293d3e2d411366c005f6dac8
4
- data.tar.gz: 611fa94c29782e68e69a9c40e643caa7ab4fb9be
3
+ metadata.gz: b8156ac336aa21fcec58bb48b1a126a56858ef0a
4
+ data.tar.gz: d3cac5ba48aa74d4454e93c0155713543052a33a
5
5
  SHA512:
6
- metadata.gz: ee22e9f5c505376d33b136582a4afd7ab3a5f0d8a2902a80e18372fcf8f82623f8a11648b8642dad3b0cdaceaa1c1ee2c5160e16394c03a1946ae5f275dd3eb1
7
- data.tar.gz: daa1821d691bc300454e8e683f0f2667f2392938db475ac2a6b6057383e98870a8d572b58010c6267fab0da94b2780993cab1f59a610601cd8c7929375bde4a4
6
+ metadata.gz: b3eee5a95f3524c98b31e2e563ab2d59ce3c525ce7a2ca95e0d21a0cecc42259122bd2b23be6365c741f6534a636d6722b9bb716db56b66d5e7c3258d518bdfe
7
+ data.tar.gz: 8dd2d7be6cbbdaf401772dbbe293aa78a97952bf63da5ef69b1a382ded1f232a4d4e0b5143123b8adfcff1d68eec602c58eb327788be73ab5299586ba9ff0133
data/README.md CHANGED
@@ -22,10 +22,6 @@ This gem's goal is to make it easy to compare the [asymptotic performance](https
22
22
 
23
23
  When I work on katas and exercises I found I often wanted to compare my implementations. After doing so a half dozen times I noticed some patterns, and figured it'd be valuable to capture those into an easier API to work with. While working on a kata I like the immediacy of replotting back on x11, but because of gnuplot's structure it is just as easy to get images or html canvas graphs.
24
24
 
25
- ## Disclaimer
26
-
27
- Because of the current implementation details: Ruby methods which operate on `self` **will not work**, and there is a negligible constant slow down on all functions tested by `Comparison` because of the use of `send(:func)`. The latter won't corrupt comparisons, but means you don't want to use this gem to benchmark functions individually **except** through `Graph::Function::Only`.
28
-
29
25
  ## Installation
30
26
 
31
27
  Because this gem depends on `gnuplot` and `xquartz`, we need to follow their [prereq steps](https://github.com/rdp/ruby_gnuplot#pre-requisites-and-installation):
@@ -64,6 +60,10 @@ Graph::Function.as_gif
64
60
  Graph::Function::IntsComparison.of(method(:sort), method(:bubble_sort))
65
61
  ```
66
62
 
63
+ Produces:
64
+
65
+ ![comparing ints](examples/comparing_ints.gif)
66
+
67
67
  ### Setup
68
68
 
69
69
  To set up, you only need the following:
@@ -73,9 +73,11 @@ require 'graph/function'
73
73
  Graph::Function.as_x11
74
74
  ```
75
75
 
76
- If you don't want to output to [x11](https://www.xquartz.org/), just set `config.terminal` to a different option. Two convenience methods exist for `gif` and `canvas`, you just need to provide them with a file to output to:
76
+ If you don't want to output to [x11](https://www.xquartz.org/), just set `config.terminal` to a different option. Two convenience methods exist for `gif` and `canvas`:
77
77
 
78
78
  ```ruby
79
+ # by default file will be set to name of the executing file and dumped in its dir
80
+ # or you can set file yourself like so:
79
81
  Graph::Function.as_gif(File.expand_path('../comparing_ints.gif', __FILE__))
80
82
  Graph::Function.as_canvas(File.expand_path('../comparing_ints.html', __FILE__))
81
83
  ```
@@ -87,10 +89,13 @@ Graph::Function.configure do |config|
87
89
  config.terminal = 'dumb'
88
90
  config.output = File.expand_path('../your_graph_name.txt', __FILE__)
89
91
  config.step = (0..10_000).step(1000).to_a # default value
92
+ config.trials = 1
90
93
  end
91
94
  ```
92
95
 
93
- In configuration, you can also control the "step" size of `x` in the plot. Its default value is `(0..10_000).step(1000).to_a` (`[0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]`) but you can make it as fine or rough grained as you need up to any size.
96
+ In configuration, you can control the "step" size of `x` in the plot. Its default value is `(0..10_000).step(1000).to_a` (`[0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]`) but you can make it as fine or rough grained as you need up to any size.
97
+
98
+ You can also set a number of trials over which to average execution times.
94
99
 
95
100
  ### Graphing
96
101
 
@@ -104,9 +109,24 @@ Graph::Function::IntsComparison.of(c.method(:function_name_one), c.method(:funct
104
109
 
105
110
  ![comparison](spec/graph/two_func.gif)
106
111
 
107
- For more complex use cases, you'll be creating a `Graph::Function::Comparison` (or `Graph::Function::Only` if you want to graph a single function) with some generator of data, and executing `#of` with `Method` objects that operate on the same parameter types<sup id="a1">[1](#f1)</sup>. (Note because `IntsComparison` *does not need a generator*, `.of` is a class method instead.)
112
+ For more complex use cases, you'll be creating a `Graph::Function::Comparison` with some generator of data, and executing `#of` with `Method` objects or `Proc`s that operate on the same parameter types<sup id="a1">[1](#f1)</sup>. (Note because `IntsComparison` *does not need a generator*, `.of` is a class method instead.)
113
+
114
+ ### Generators
115
+
116
+ To generate values of the type needed by your function, you can write
117
+ a generator in Ruby or use the provided dependency
118
+ [Rantly](https://github.com/hayeah/rantly).
119
+
120
+ Here's an example of a simple Ruby generator, it's just a `Proc` parameterized on `size`:
108
121
 
109
- To generate values of the type needed by your function, use the provided dependency [Rantly](https://github.com/hayeah/rantly). There's great documentation on generating many different kinds of data in their documentation, but here's an example of comparing two functions that take `Hash{String => Integer}`:
122
+ ```ruby
123
+ tiny_int_generator = proc {|size| Array.new(size) { rand(-9...9) } }
124
+ comparison = Graph::Function::Comparison.new(tiny_int_generator)
125
+ ```
126
+
127
+ For Rantly usage, there's great documentation on generating many different kinds of data in
128
+ their documentation, but here's an example of comparing two functions that
129
+ take `Hash{String => Integer}`:
110
130
 
111
131
  ```ruby
112
132
  # you must put it in a proc taking size so Graph::Function can increase it
@@ -124,8 +144,7 @@ If you want to make use of more "real" fake data, [Faker](https://github.com/sty
124
144
  ```ruby
125
145
  # again, we need to parameterize our generator with size
126
146
  faker_generator = proc {|size| Rantly(size) { call(Proc.new { Faker::Date.backward(14) }) }
127
- # using Only here, but anything that takes a generator can take one with Faker
128
- graph = Graph::Function::Only.new(faker_generator)
147
+ graph = Graph::Function::Comparison.new(faker_generator)
129
148
  graph.of(method(:custom_types))
130
149
  # => will output an xquartz graph
131
150
  ```
@@ -136,6 +155,18 @@ The only downside here is that you can't parameterize `Faker`, but you could use
136
155
 
137
156
  Check out the [spec file](spec/graph/function_spec.rb) to see all of these or see [examples](examples/).
138
157
 
158
+ ### Functions that use `self`
159
+
160
+ For graphing functions that operate on `self`, such as `String#upcase`, you must provide a `Method` or `Proc` that wraps the method call. For instance:
161
+
162
+ ```ruby
163
+ generator = proc {|size| Rantly { sized(size) { string } } }
164
+ # wrap the call to upcase
165
+ test_upcase = proc {|s| s.upcase }
166
+ graph = Graph::Function::Comparison.new(generator)
167
+ graph.of(test_upcase)
168
+ ```
169
+
139
170
  ## Development
140
171
 
141
172
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Binary file
@@ -168,7 +168,7 @@ L(5759,301);
168
168
  L(540,301);
169
169
  ctx.closePath();
170
170
  ctx.stroke();
171
- TR(90,2041,270,10.0,"Center","execution time");
171
+ TR(90,2041,270,10.0,"Center","execution time (seconds)");
172
172
  T(3149,3980,10.0,"Center","input size");
173
173
  T(3149,201,10.0,"Center","Sort vs BubbleSort");
174
174
  if (typeof(gnuplot.hide_gp_plot_1) == "undefined"|| !gnuplot.hide_gp_plot_1) {
@@ -223,29 +223,29 @@ ctx.beginPath();
223
223
  M(5179,589);
224
224
  L(5599,589);
225
225
  M(540,3680);
226
- L(1062,3650);
227
- L(1584,3558);
228
- L(2106,3406);
229
- L(2628,3187);
230
- L(3150,2903);
231
- L(3671,2555);
232
- L(4193,2170);
233
- L(4715,1722);
234
- L(5237,1168);
235
- L(5759,453);
226
+ L(1062,3648);
227
+ L(1584,3554);
228
+ L(2106,3390);
229
+ L(2628,3172);
230
+ L(3150,2885);
231
+ L(3671,2518);
232
+ L(4193,2108);
233
+ L(4715,1616);
234
+ L(5237,1078);
235
+ L(5759,439);
236
236
  ctx.stroke();
237
237
  ctx.closePath();
238
238
  Pt(1,540,3680,60.0);
239
- Pt(1,1062,3650,60.0);
240
- Pt(1,1584,3558,60.0);
241
- Pt(1,2106,3406,60.0);
242
- Pt(1,2628,3187,60.0);
243
- Pt(1,3150,2903,60.0);
244
- Pt(1,3671,2555,60.0);
245
- Pt(1,4193,2170,60.0);
246
- Pt(1,4715,1722,60.0);
247
- Pt(1,5237,1168,60.0);
248
- Pt(1,5759,453,60.0);
239
+ Pt(1,1062,3648,60.0);
240
+ Pt(1,1584,3554,60.0);
241
+ Pt(1,2106,3390,60.0);
242
+ Pt(1,2628,3172,60.0);
243
+ Pt(1,3150,2885,60.0);
244
+ Pt(1,3671,2518,60.0);
245
+ Pt(1,4193,2108,60.0);
246
+ Pt(1,4715,1616,60.0);
247
+ Pt(1,5237,1078,60.0);
248
+ Pt(1,5759,439,60.0);
249
249
  Pt(1,5389,589,60.0);
250
250
  } // End gp_plot_2
251
251
  ctx.lineWidth = 2;
@@ -6,7 +6,7 @@ require 'graph/function/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'graph-function'
8
8
  spec.version = Graph::Function::VERSION
9
- spec.authors = ['Alex Moore-Niemi']
9
+ spec.authors = ['Alex Moore-Niemi', 'Cora Johnson-Roberson']
10
10
  spec.email = ['moore.niemi@gmail.com']
11
11
 
12
12
  spec.summary = %q{Graph function performance.}
@@ -9,38 +9,61 @@ module Graph
9
9
  @data_generator = generator
10
10
  end
11
11
 
12
- def of(*methods)
13
- fail unless methods.all? {|m| m.is_a?(Method) }
12
+ def of(*functions)
13
+ fail unless functions.all? {|f| f.respond_to?(:call) }
14
14
 
15
- methods.each do |m|
16
- self.class.send(:define_method, m.name, proc(&m))
17
- end
15
+ results = {}
18
16
 
19
17
  Gnuplot.open do |gp|
20
18
  Gnuplot::Plot.new(gp) do |plot|
21
-
22
- plot.title "#{title = methods.map {|m| camel_title(m.name) }.join(', ') }"
23
- set_up(plot)
19
+ title = functions_to_title(functions)
20
+ plot.title title
21
+ set_up(plot)
24
22
 
25
23
  x = Graph::Function.configuration.step
24
+ trials = Graph::Function.configuration.trials
26
25
  pb = ProgressBar.create(title: title, total: x.size)
27
26
 
28
- methods.each do |m|
27
+ functions.each do |f|
29
28
  pb.reset
29
+
30
+ name = fname(f)
31
+ results[name] = {}
32
+
30
33
  y = x.collect do |v|
31
34
  pb.increment
32
35
  data = data_generator.call(v)
33
- # FIXME can i get ride of the cost of `send`?
34
- Benchmark.measure { self.send(m.name, data) }.real
36
+ current_trials = (1..trials).collect do |_|
37
+ Benchmark.measure { f.call(data) }.real
38
+ end
39
+ results[name][v] = current_trials
40
+ current_trials.reduce(0.0, :+) / trials
35
41
  end
36
42
 
37
43
  plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
38
44
  ds.with = "linespoints"
39
- ds.title = "#{escape_underscores(m.name)}"
45
+ ds.title = "#{escape_underscores(name)}"
40
46
  end
41
47
  end
42
48
  end
43
49
  end
50
+ results
51
+ end
52
+
53
+ private
54
+ def fname(function)
55
+ function.respond_to?(:name) ? function.name : extract_filename(function.to_s)
56
+ end
57
+
58
+ def functions_to_title(functions)
59
+ case functions.size
60
+ when 1
61
+ "#{camel_title(fname(functions[0]))}"
62
+ when 2
63
+ "#{camel_title(fname(functions[0]))} vs #{camel_title(fname(functions[1]))}"
64
+ else
65
+ "#{functions.map {|f| camel_title(fname(f)) }.join(', ') }"
66
+ end
44
67
  end
45
68
  end
46
69
  end
@@ -1,48 +1,13 @@
1
1
  module Graph
2
2
  module Function
3
- class IntsComparison
4
- extend ReformatString
5
- extend PlotConfig
6
-
7
- def self.of(method_one, method_two)
8
- fail unless method_one.is_a?(Method) && method_two.is_a?(Method)
9
-
10
- Gnuplot.open do |gp|
11
- Gnuplot::Plot.new(gp) do |plot|
12
- self.class.send(:define_method, :a, proc(&method_one))
13
- self.class.send(:define_method, :b, proc(&method_two))
14
-
15
- plot.title (title = "#{camel_title(method_one.name)} vs #{camel_title(method_two.name)}")
16
- set_up(plot)
17
-
18
- x = Graph::Function.configuration.step
19
- pb = ProgressBar.create(title: title, total: x.size)
20
-
21
- y = x.collect do |v|
22
- pb.increment
23
- array = (0..v - 1).to_a.shuffle
24
- Benchmark.measure { a(array) }.real
25
- end
26
-
27
- plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
28
- ds.with = "linespoints"
29
- ds.title = "#{escape_underscores(method_one.name)}"
30
- end
31
-
32
- pb.reset
33
-
34
- z = x.collect do |v|
35
- pb.increment
36
- array = (0..v - 1).to_a.shuffle
37
- Benchmark.measure { b(array) }.real
38
- end
3
+ class IntsComparison < Comparison
4
+ def initialize
5
+ @data_generator = Proc.new {|v| (-v/2 + 1 .. v/2).to_a.shuffle }
6
+ end
39
7
 
40
- plot.data << Gnuplot::DataSet.new( [x, z] ) do |ds|
41
- ds.with = "linespoints"
42
- ds.title = "#{escape_underscores(method_two.name)}"
43
- end
44
- end
45
- end
8
+ def self.of(*methods)
9
+ comparison = self.new
10
+ comparison.of(*methods)
46
11
  end
47
12
  end
48
13
  end
@@ -2,7 +2,7 @@ module Graph
2
2
  module Function
3
3
  module PlotConfig
4
4
  def set_up(plot)
5
- plot.ylabel 'execution time (seconds)'
5
+ plot.ylabel (Graph::Function.configuration.trials == 1 ? 'execution time (seconds)' : 'average execution time (seconds)')
6
6
  plot.xlabel 'input size'
7
7
  plot.terminal (t = Graph::Function.configuration.terminal)
8
8
  plot.output Graph::Function.configuration.output unless t == 'x11'
@@ -7,6 +7,10 @@ module Graph
7
7
  def camel_title(s)
8
8
  s.to_s.split('_').collect(&:capitalize).join
9
9
  end
10
+ def extract_filename(s)
11
+ matches = /([^\/]+:\d+)\>$/.match(s)
12
+ !matches.nil? ? matches[1] : s
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -1,5 +1,5 @@
1
1
  module Graph
2
2
  module Function
3
- VERSION = '0.1.5'
3
+ VERSION = '0.1.7'
4
4
  end
5
5
  end
@@ -7,7 +7,6 @@ require 'ruby-progressbar'
7
7
  require 'graph/function/version'
8
8
  require 'graph/function/reformat_string'
9
9
  require 'graph/function/plot_config'
10
- require 'graph/function/only'
11
10
  require 'graph/function/comparison'
12
11
  require 'graph/function/ints_comparison'
13
12
 
@@ -25,14 +24,18 @@ module Graph
25
24
 
26
25
  singleton_class.send(:alias_method, :as_x11, :configure)
27
26
 
28
- def self.as_gif(file = File.expand_path("../#{$0.slice(0..-4)}.gif", __FILE__))
27
+ def self.file_location(ext)
28
+ File.new("#{$0.slice(0..-4)}.#{ext}", 'w').path
29
+ end
30
+
31
+ def self.as_gif(file = file_location('gif'))
29
32
  self.configure do |config|
30
33
  config.terminal = 'gif'
31
34
  config.output = file
32
35
  end
33
36
  end
34
37
 
35
- def self.as_canvas(file = File.expand_path("../#{$0.slice(0..-4)}.html", __FILE__))
38
+ def self.as_canvas(file = file_location('html'))
36
39
  self.configure do |config|
37
40
  config.terminal = 'canvas'
38
41
  config.output = file
@@ -42,6 +45,7 @@ module Graph
42
45
  class Configuration
43
46
  attr_accessor :terminal, :output
44
47
  attr_accessor :step
48
+ attr_accessor :trials
45
49
 
46
50
  # defaults
47
51
  # see https://github.com/rdp/ruby_gnuplot/blob/master/examples/output_image_file.rb
@@ -50,6 +54,7 @@ module Graph
50
54
  @terminal = 'x11'
51
55
  @output = '.'
52
56
  @step = (0..10_000).step(1000).to_a
57
+ @trials = 1
53
58
  end
54
59
  end
55
60
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graph-function
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Moore-Niemi
8
+ - Cora Johnson-Roberson
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2016-10-02 00:00:00.000000000 Z
12
+ date: 2016-10-04 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: gnuplot
@@ -162,7 +163,6 @@ files:
162
163
  - lib/graph/function.rb
163
164
  - lib/graph/function/comparison.rb
164
165
  - lib/graph/function/ints_comparison.rb
165
- - lib/graph/function/only.rb
166
166
  - lib/graph/function/plot_config.rb
167
167
  - lib/graph/function/reformat_string.rb
168
168
  - lib/graph/function/version.rb
@@ -1,41 +0,0 @@
1
- module Graph
2
- module Function
3
- class Only
4
- include ReformatString
5
- include PlotConfig
6
- attr_accessor :data_generator
7
-
8
- def initialize(generator)
9
- @data_generator = generator
10
- end
11
-
12
- def of(method_obj)
13
- fail unless method_obj.is_a?(Method)
14
-
15
- self.class.send(:define_method, :a, proc(&method_obj))
16
-
17
- Gnuplot.open do |gp|
18
- Gnuplot::Plot.new(gp) do |plot|
19
-
20
- plot.title "#{title = camel_title(method_obj.name)}"
21
- set_up(plot)
22
-
23
- x = Graph::Function.configuration.step
24
- pb = ProgressBar.create(title: title, total: x.size)
25
-
26
- y = x.collect do |v|
27
- pb.increment
28
- data = data_generator.call(v)
29
- Benchmark.measure { a(data) }.real
30
- end
31
-
32
- plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
33
- ds.with = "linespoints"
34
- ds.title = "#{escape_underscores(method_obj.name)}"
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
41
- end