JRuby: Stochastic Simulation with SSJ

I needed to work on a simulation project that involved working with the SSJ java library.
If I haven’t mentioned before I love Ruby (the programming language) and Java, though a tool in my toolkit, does not evoke the same feeling.

So my first reaction was I can use JRuby to benefit from the existing java library and beauty of Ruby.
The SSJ library comes with a comprehensive sets of examples and documentation. I started exploring that.

Fortunately at RubyConf 2008 with some help from Charles Nutter I was able to install jruby on my laptop, getting jruby installed and running was not trivial. I wish somebody can do a one click installer for jruby same as they did for Ruby and Rails.

I downloaded SSJ and unzipped the downloaded file containing jar files into C:\Program Files\Java\jdk1.6.0_02\lib\ext directory. Added the CLASSPATH variable in my windows environment set to this directory.

I tried the following example from examples.pdf

import umontreal.iro.lecuyer.rng.*;
import umontreal.iro.lecuyer.stat.*;
public class Collision {
    int k; // Number of locations.
    int m; // Number of items.
    double lambda; // Theoretical expectation of C (asymptotic).
    boolean[] used; // Locations already used.
    public Collision (int k, int m) {
        this.k = k;
        this.m = m;
        lambda = (double) m * m / (2.0 * k);
        used = new boolean[k];
    }
    // Generates and returns the number of collisions.
    public int generateC (RandomStream stream) {
        int C = 0;
        for (int i = 0; i < k; i++) used&#91;i&#93; = false;
        for (int j = 0; j < m; j++) {
            int loc = stream.nextInt (0, k-1);
            if (used&#91;loc&#93;) C++;
            else used&#91;loc&#93; = true;
        }
        return C;
    }
    // Performs n indep. runs using stream and collects statistics in statC.
    public void simulateRuns (int n, RandomStream stream, Tally statC) {
        statC.init();
        for (int i=0; i<n; i++) statC.add (generateC (stream));
        statC.setConfidenceIntervalStudent();
        System.out.println (statC.report (0.95, 3));
        System.out.println (" Theoretical mean: " + lambda);
    }
    public static void main (String&#91;&#93; args) {
        Tally statC = new Tally ("Statistics on collisions");
        Collision col = new Collision (10000, 500);
        col.simulateRuns (100000, new MRG32k3a(), statC);
    }
}
&#91;/sourcecode&#93;

I compiled and ran the Java code above to ensure I was getting the same results using the following steps:
<pre>
javac -cp "C:\Program Files\Java\jdk1.6.0_02\lib\ext\ssj.jar" Collision.java
java -cp "C:\Program Files\Java\jdk1.6.0_02\lib\ext\ssj.jar;C:\code\ruby\jruby" Collision
</pre>

As you can notice my code was in the C:\code\ruby\jruby directory and I was in this directory when I ran the above program.

Here is the same code example in jruby code:

require 'java'
require 'ssj.jar'

import 'umontreal.iro.lecuyer.rng.RandomStream'
import 'umontreal.iro.lecuyer.stat.Tally'
import 'umontreal.iro.lecuyer.rng.MRG32k3a'

class Collision
  def initialize(k, m)
    @k = k
    @m = m
    @lambda = m * m / (2.0 * k)
    @used = Array.new(k, false)
  end

  def generate_c(stream)
    c = 0
    @k.times { |i| @used[i] = false }
    @m.times do |j|
      loc = stream.nextInt(0, @k-1)
      if @used[loc]
        c += 1
      else
        @used[loc] = true
      end
    end
    return c
  end

  def simulate_runs(n, stream, stat_c)
   stat_c.init
   n.times { stat_c.add(generate_c(stream)) }
   stat_c.setConfidenceIntervalStudent()
   puts stat_c.report(0.95, 3)
   puts " Theoretical mean: #{@lambda} "
  end

  def self.run
    stat_c = Tally.new("Statistics on collision")
    col = Collision.new(10000,500)
    col.simulate_runs(100000, MRG32k3a.new, stat_c)
  end

end

Collision.run

The jruby code runs comparatively slower (my feeling is 5 times slower but I will measure and add it later). I am using the latest available jruby 1.1.5 at the time of this post.

Update: The JRuby version takes around 13 minutes while the equivalent Java code above takes 17 seconds. This seems to be unacceptable performance, I am not sure if this is specific to SSJ or other libraries too. If I understand correctly, JRuby should be running inside the JVM and when I call SSJ library that is the same Java code that runs so I don’t understand why it is so slow unless I have used some technique that slows down my JRuby version.

One Response