A better way to write this function?

Here is my attempt at Newton’s second law in Ruby:

#c is the drag coefficent
#m is mass
#t is total time elapsed
#dt is time increment
#vi is the starting velocity

G = 9.8

def velocity(c, m, t, dt, vi)
vel = []
t += dt
steps = t/dt

steps.times do
v = vi
vi = v + ( G - c/m*v) * dt
vel << v
end
return vel
end

Is there a better way to do write this function? It seems pretty
straight forward to me so I guess that might be a sign that it is fine
the way it is…?

What if I needed more speed but still wanted it in Ruby?

Thank you!

On 28.05.2010 17:20, Jason L. wrote:

return vel
end

Just a slight improvement

def velocity(c, m, t, dt, vi)
vel = []

(t/dt + 1).times do
vel << vi
vi += ( G - c/m*v) * dt
end

vel
end

You could also do

def velocity(c, m, t, dt, vi)
(t/dt + 1).times do
yield vi
vi += ( G - c/m*v) * dt
end
end

and use it with a block.

Kind regards

robert

On 5/28/10, Jason L. [email protected] wrote:

def velocity(c, m, t, dt, vi)
end

Is there a better way to do write this function? It seems pretty
straight forward to me so I guess that might be a sign that it is fine
the way it is…?

What if I needed more speed but still wanted it in Ruby?

A couple small speedups. c/m never changes while the loop is running,
so compute it once when the loop starts, then reuse the value. And you
could replace the times loop by a while loop for another small
speedup… but this is never going to be fast in ruby. If you want
fast, use C.

Jason L. wrote:

What if I needed more speed but still wanted it in Ruby?

Not quite pure ruby, but if it’s acceptable to have a C backend, then
you can use redshift. On my computer, if I create 20K of the objects
below, they run (concurrently) at about “real” time, i.e. 1 sec process
time per 1 sec simulation time. Also, it is more accurate than your
integration algorithm, since it uses a higher-order Runge-Kutta
integrator. Plus, there are other features suitable for
discrete/continuous multi-agent simulation (algebraic equations,
discrete state transitions, event synchronization, queues with
pattern-matching like Erlang, dataflow ports like Simulink, link
variables, delay flows, differentiation, interactive shell and
debuggers, …).

If you want to try this, unpack this tarball:

http://path.berkeley.edu/~vjoel/redshift/redshift-1.3.14.tgz

and put the lib dir on your RUBYLIB. Some of the examples expect gnuplot
to be installed, but otherwise there are no deps.

However, you must be able to build extensions, i.e, have a working C
compiler that is binary compatible with your ruby (gcc, msvc, and
solaris work for me), so that redshift can translate the equations into
C and compile them into ruby extensions. If native gems build on your
system, you should be ok. Note that the first time you run a particular
redshift program, there is a delay while this build happens (the build
goes into a tmp dir under the current dir).


require ‘redshift’

class Thing < RedShift::Component
continuous :v, :x
constant :m, :c, :G # per-instance constants

flow do
# “differential” means Runge-Kutta 4th order integration; if
# you replace that word with “euler”, it uses forward Euler
# integration, which gives exactly the same results as the
# original poster’s example. Usually, RK4 is significantly
# more accurate than Euler.
differential " v’ = G - c/m * v "

 # just for fun, let's keep track of distance traveled
 differential " x' = v "

end
end

world = RedShift::World.new
world.time_step = 0.1

thing = world.create Thing
thing.m = 10
thing.c = 0.01
thing.v = 0
thing.x = 0
thing.G = 9.8

p thing
world.evolve 5.0 do
p thing
end

END

Output:

<<Thing 0>: Enter; G = 9.8, c = 0.01, m = 10.0; v = 0.0, x = 0.0>
<<Thing 0>: Enter; G = 9.8, c = 0.01, m = 10.0; v = 0.979951001633293, x
= 0.0489983667075>
<<Thing 0>: Enter; G = 9.8, c = 0.01, m = 10.0; v = 1.95980401306601, x
= 0.195986933986642>


<<Thing 0>: Enter; G = 9.8, c = 0.01, m = 10.0; v = 47.9025429248678, x
= 117.457075132208>
<<Thing 0>: Enter; G = 9.8, c = 0.01, m = 10.0; v = 48.8777039117133, x
= 122.296088286671>

Jason L. wrote:

Joel VanderWerf wrote:

snip

Thank you Joel.

is this redshift you introduced to me the same thing as

RedShift download | SourceForge.net

Yes, that’s the same thing (but there is no formal release on that
site).

On Fri, May 28, 2010 at 4:20 PM, Jason L. <
[email protected]> wrote:

Here is my attempt at Newton’s second law in Ruby:

What if I needed more speed but still wanted it in Ruby?

Using C for the “fast” bits has been suggested.

As an alternative to C for speed, I’ve found that using JRuby with parts
of
the code as Java functions works well.

Perhaps I ought to add that I’ve never tried compiling a C program and
integrating it with Ruby, because of my lack of knowledge of the finer -
and
not so finer! - points of C compilation and integration with Ruby. But
I’ve
found writing and compiling parts of the code that need to be fast
fairly
easy in Java, and integration of Java with JRuby is also fairly
straightforward.

Joel VanderWerf wrote:

snip

Thank you Joel.

is this redshift you introduced to me the same thing as

??

Joel VanderWerf wrote:

Jason L. wrote:

is this redshift you introduced to me the same thing as

RedShift download | SourceForge.net

Yes, that’s the same thing (but there is no formal release on that site).

It’s a gem now:

gem install redshift

unknown wrote:

Duby code:
…snip

I’m sorry, can you tell me what Duby is? I tried looking it up and found
nothing. I’m familiar with JRuby but not Duby.

Thank you.

On Wed, Jun 2, 2010 at 11:44 PM, Colin B.
[email protected] wrote:

As an alternative to C for speed, I’ve found that using JRuby with parts of
the code as Java functions works well.

I took a quick shot w/ duby-inline.

Host:
2.6.32-22-generic #33-Ubuntu SMP Wed Apr 28 13:27:30 UTC 2010 i686
GNU/Linux
Intel(R) Pentium(R) M processor 1.60GHz

Benchmark block:
100000.times{ velocity(0.5, 21.6, 600, 10, 0.0) }

“Original” code (see note below):

ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux]
user system total real
18.210000 0.040000 18.250000 ( 21.596111)

jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (OpenJDK
Client VM 1.6.0_18) [i386-java]
user system total real
4.525000 0.000000 4.525000 ( 4.525000)

jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (OpenJDK
Server VM 1.6.0_18) [i386-java]
user system total real
3.651000 0.000000 3.651000 ( 3.651000)

Duby code:

jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (OpenJDK
Client VM 1.6.0_18) [i386-java]
0.415000 0.000000 0.415000 ( 0.415000)

jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (OpenJDK
Server VM 1.6.0_18) [i386-java]
0.208000 0.000000 0.208000 ( 0.208000)

I had to cheat a little bit and removed the array and the block to
make it Duby-compatible:

G = 9.8

class Newton
def rb_velocity(c, m, t, dt, vi)
t += dt
steps = t/dt

i = 0
while i < steps
  v = vi
  vi = v + ( G - c/m*v) * dt
  i += 1
end

vi

end
end

And, the duby version:

require ‘inline’
require ‘duby_inline’

G = 9.8

class Newton
inline :Duby do |builder|
builder.duby "
def db_velocity(c:double, m:double, t:int, dt:int, vi:double)
t += dt
steps = t/dt

    i = 0
    while i < steps
      v = vi
      vi = v + ( #{G} - c/m*v) * dt
      i += 1
    end

    vi
  end
"

end
end

On Mon, Jun 7, 2010 at 3:44 PM, Jason L.
[email protected] wrote:

unknown wrote:

Duby code:
…snip

I’m sorry, can you tell me what Duby is? I tried looking it up and found
nothing. I’m familiar with JRuby but not Duby.

Thank you.

Duby is a Ruby-like static-typed language with local type inference
and not many bells and whistles. The primary motivation was to have a
Ruby-like language to implement parts of JRuby. Duby looks and feels
mostly like Ruby but compiles to tight JVM bytecode on par with what
you’d get from compiled Java.

def fib(a => :fixnum)
if a < 2
a
else
fib(a - 1) + fib(a - 2)
end
end

puts fib(45)

unknown wrote:

Duby is a Ruby-like static-typed language…

Thank you for the links. Duby is really fast. thanks.

[email protected] wrote:

I took a quick shot w/ duby-inline.

ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux]
user system total real
18.210000 0.040000 18.250000 ( 21.596111)

jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (OpenJDK
Server VM 1.6.0_18) [i386-java]
0.208000 0.000000 0.208000 ( 0.208000)

That is a very impressive speed-up!

I see a more modest improvement going from the same original code (on
different hardware of course) to a version written in redshift. Of
course, redshift is using a more accurate integration algorithm (with 4
substeps instead of 1). Plus there are costs for the availability of
other features that are not being used in this example (e.g., a layer of
function pointers so that variables can switch to different equations
when a discrete state change occurs).

$ ruby bench-rb.rb
user system total real
evolve 6s 0.710000 0.160000 0.870000 ( 0.881276)
evolve 600s 15.320000 1.010000 16.330000 ( 16.366811)

$ ruby bench-rs.rb
user system total real
evolve 6s 0.020000 0.000000 0.020000 ( 0.025213)
evolve 600s 1.170000 0.000000 1.170000 ( 1.178752)

$ ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]

Here are the sources:

$ cat bench-rb.rb
G = 9.8

def velocity(c, m, t, dt, vi)
t += dt
steps = t/dt

i = 0
while i < steps
v = vi
vi = v + ( G - c/m*v) * dt
i += 1
end

vi
end

require ‘benchmark’
Benchmark.bm(12) do |b|
b.report(“evolve 6s”) do
100000.times do
velocity(0.5, 21.6, 6.0, 10.0, 0.0)
# Note: I changed the t and dt to floats for a more direct
# comparison with redshift, which uses floats for time values
end
end
b.report(“evolve 600s”) do
100000.times do
velocity(0.5, 21.6, 600.0, 10.0, 0.0)
end
end
end

$ cat bench-rs.rb
require ‘redshift’

class Thing < RedShift::Component
continuous :v
constant :m, :c, :G

flow do
differential " v’ = G - c/m * v "
end
end

def make_world(n)
RedShift::World.new do |world|
world.time_step = 10.0
n.times do
world.create Thing do |thing|
thing.m = 21.6
thing.c = 0.5
thing.v = 0
thing.G = 9.8
end
end
end
end

require ‘benchmark’
Benchmark.bm(12) do |b|
n = 1000
reps = 100

Note: instead of 100000 sequential runs, this benchmark is doing

100 sequential runs of 1000 parallel instances of the same

integration problem, which is more typical of the conditions

redshift is optimized for

world = make_world(n)
b.report(“evolve 6s”) do
reps.times do
world.evolve 6.0
end
end

world = make_world(n)
b.report(“evolve 600s”) do
reps.times do
world.evolve 600.0
end
end
end

Jason L. wrote:

G = 9.8

def velocity(c, m, t, dt, vi)
vel = []
t += dt
steps = t/dt

steps.times do
v = vi
vi = v + ( G - c/m*v) * dt
vel << v
end
return vel
end

Is there a better way to do write this function?

You can make it a one-liner using range and map:

def velocity(c, m, t, dt, v)
[v] + (0…t/dt).map { v += (G - c/m*v) * dt }
end

This version also works with float values of t and dt, which yours
rejects because there is no Float#times.