Skip to content

corajr/graph-function

 
 
                        .-.      / \        _
            ^^         /   \    /^./\__   _/ \
          _        .--'\/\_ \__/.      \ /    \  ^^  ___
         / \_    _/ ^      \/  __  :'   /\/\  /\  __/   \
        /    \  /    .'   _/  /  \   ^ /    \/  \/ .`'\_/\
       /\/\  /\/ :' __  ^/  ^/    `--./.'  ^  `-.\ _    _:\ _
      /    \/  \  _/  \-' __/.' ^ _   \_   .'\   _/ \ .  __/ \
    /\  .-   `. \/     \ / -.   _/ \ -. `_/   \ /    `._/  ^  \
   /  `-.__ ^   / .-'.--'    . /    `--./ .-'  `-.  `-. `.  -  `.
 @/        `.  / /      `-.   /  .-'   / .   .'   \    \  \  .-  \%
 @(88%@)@%% @)&@&(88&@.-_=_-=_-=_-=_-=_.8@% &@&&8(8%@%8)(8@%8 8%@)%
 @88:::&(&8&&8::JGS:&`.~-_~~-~~_~-~_~-~~=.'@(&%::::%@8&8)::&#@8::::
 `::::::8%@@%:::::@%&8:`.=~~-.~~-.~~=..~'8::::::::&@8:::::&8::::::'
  `::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::'

Graph::Function

This gem's goal is to make it easy to compare the asymptotic performance of two or more functions via graphing.

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.

Disclaimer

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.

Installation

Because this gem depends on gnuplot and xquartz, we need to follow their prereq steps:

# these will vary by your system, mine is mac
brew install Caskroom/cask/xquartz
brew install gnuplot --with-x11
# verify you have x11
xpdyinfo | grep version

Now we're set. Add this line to your application's Gemfile:

gem 'graph-function'

And then execute:

$ bundle

Or install it yourself as:

$ gem install graph-function

Usage

TL;DR

From the comparing ints example:

require 'graph/function'
Graph::Function.as_gif
Graph::Function::IntsComparison.of(method(:sort), method(:bubble_sort))

Produces:

comparing ints

Setup

To set up, you only need the following:

require 'graph/function'
Graph::Function.as_x11

If you don't want to output to x11, just set config.terminal to a different option. Two convenience methods exist for gif and canvas:

# by default file will be set to name of the executing file and dumped in its dir
# or you can set file yourself like so:
Graph::Function.as_gif(File.expand_path('../comparing_ints.gif', __FILE__))
Graph::Function.as_canvas(File.expand_path('../comparing_ints.html', __FILE__))

You can use anything else gnuplot respects as a terminal, even outputting to just txt!

Graph::Function.configure do |config|
  config.terminal = 'dumb'
  config.output = File.expand_path('../your_graph_name.txt', __FILE__)
  config.step = (0..10_000).step(1000).to_a # default value
end

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.

Graphing

The simplest usage (suitable for a large class of exercises, in my experience) is if you're comparing two functions that take a single argument of Array[Int] type:

c = YourClass.new # this class has #function_name_one & #function_name_two
Graph::Function::IntsComparison.of(c.method(:function_name_one), c.method(:function_name_two))
# => will output an xquartz graph

comparison

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 types1. (Note because IntsComparison does not need a generator, .of is a class method instead.)

Generators

To generate values of the type needed by your function, you can write a generator in Ruby or use the provided dependency Rantly.

Here's an example of a simple Ruby generator, it's just a Proc parameterized on size:

tiny_int_generator = proc {|size| Array.new(size) { rand(-9...9) } }
comparison = Graph::Function::Comparison.new(tiny_int_generator)

For Rantly usage, 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}:

# you must put it in a proc taking size so Graph::Function can increase it
generator = proc {|size| Rantly { dict(size) { [string, integer] } }
dict_comparison = Graph::Function::Comparison.new(generator)
# Comparison can take any number of Methods, but for now, 2
dict_comparison.of(method(:hash_func_one), method(:hash_func_two))
# => will output an xquartz graph

comparison

If you want to make use of more "real" fake data, Faker is also included, and can be used like so in your generators:

# again, we need to parameterize our generator with size
faker_generator = proc {|size| Rantly(size) { call(Proc.new { Faker::Date.backward(14) }) }
# using Only here, but anything that takes a generator can take one with Faker
graph = Graph::Function::Only.new(faker_generator)
graph.of(method(:custom_types))
# => will output an xquartz graph

faker

The only downside here is that you can't parameterize Faker, but you could use random generators to mix it up. Using the above example, graph-function won't pass anything into the faker_generator but the size, so if we want the value to change, we could use Faker::Date.backward(proc { rand(10) }.call).

Check out the spec file to see all of these or see examples.

Development

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.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/mooreniemi/graph-function. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Footnotes

1 Why are we constrained to testing the same parameter types? The intent of this library is to graph implementations. Changing parameter types suggests a change in the behavior of the function. That doesn't make for a very productive comparison.

About

📈 graphing your ruby functions

Resources

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
LICENSE.txt

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Ruby 98.7%
  • Shell 1.3%