How to make homework more fun, MATLAB to Ruby
I wanted to liven up my ElectroMagnetism homework this past week which required us to do some MATLAB. Unfortunately, I don't have MATLAB because it's not OS and I didn't want to go to the school to use a computer, so I did some monkey patching to make my Ruby code look like MATLAB. Now I can just copy in the MATLAB code, make a few changes and run it. Only a few changes necessary at this point to convert the MATLAB to Ruby:
[1 2 3] -> [1, 2, 3] # arrays
x^2 -> x**2 # exponents
% -> # # comments
Update: ^
has lesser precedence than **
which is why I stuck with **
. Comment if you know how to change operator precedence. I couldn't find much.
Here is the file I require:
#matlab_helper.rb
$VERBOSE = nil # surpress constant redefinition warnings
include Math
def norm(vector)
sqrt vector.map{ |x| x**2 }.inject(0, &:+)
end
class Array
def +(op)
self.zip(op).map { |a, b| a + b }
end
def -(op)
self.zip(op).map { |a, b| a - b }
end
def /(op)
self.map { |a| a / op }
end
end
class Float
alias_method :mult, :*
def *(op)
if op.is_a? Array
op.map { |a| self * a }
else
mult op
end
end
end
class Fixnum
alias_method :mult, :*
def *(op)
if op.is_a? Array
op.map { |a| self * a }
else
mult op
end
end
end
def pi
PI
end
And my converted MATLAB homework:
#hw.rb
require './matlab_helper'
# clc #clear the command line
# clear #remove all previous variables
Q1 = 8e-9 #charges on Q1
Q2 = 8e-9 #charges on Q2
pL = 2e-9 #charge density of the line
Epsilon_o = 8.8419e-12 #Permitivity of free space
P = [2, 3, 4] #coordinates of observation point
A = [0, 0, 1] #coordinates of Q1
B = [0, 0, -1] #coordinates of Q2
C = [2, 0, 0] #coordinates of the center of the line charge
Number_of_L_Steps = 100000 #the steps of L
##the following routine calculates the electric fields at the
##observation point generated by the point charges
R1 = P - A #the vector pointing from Q1 to the observation point
R2 = P - B #the vector pointing from Q2 to the observation point
R1Mag = norm(R1) #the magnitude of R1
R2Mag = norm(R2) #the magnitude of R1
E1 = Q1 / (4 * pi * Epsilon_o * R1Mag**3) * R1 #the electric field generated by Q1
E2 = Q2 / (4 * pi * Epsilon_o * R2Mag**3) * R2 #the electric field generated by Q2
##the following routine calculates the electric field at the
##observation point generated by the line charge
d = norm(P - C) #the distance from the observation point to the center of the line
length = 100 * d #the length of the line
dL_V = length / Number_of_L_Steps * [1, 0, 0] #vector of a segment
dL = norm(dL_V) #length of a segment
EL = [0, 0, 0] #initialize the electric field generated by EL
C_segment = C - (Number_of_L_Steps / 2 * dL_V - dL_V / 2) #the center of the first segment
for i in (1..Number_of_L_Steps)
R = P - C_segment #the vector seen from the center of the first segment to the observation point
RMag = norm(R) #the magnitude of the vector R
EL = EL + dL * pL / (4 * pi * Epsilon_o * RMag**3) * R #get contibution from each segment
C_segment = C_segment + dL_V #the center of the i-th segment
end
E = E1 + E2 + EL # the electric field at P
puts "E = #{E}"
# $ ruby hw.rb
# E = [2.010238790127288, 7.334514610377015, 9.388970095358529]
It runs in about 2 seconds with 100,000 steps, but it's Ruby!
Note: I only included the operators necessary to complete my homework, so there are quite a few combinations left out.
Written by Kelton
Related protips
7 Responses
Ruby has the ^ operator too, so you can cross that off the list of necessary changes.
Yeah, I wanted to add that as an alias that for **
, however ^
has a lesser operator precedence than **
. So if you want to use ^
, you have to wrap it in parens just about everywhere it is used. I figured that it would just be better to use **
so that you wouldn't have that somewhat confusing problem.
That is, unless anyone knows how to change the precedence of an operator.
You can swap their definitions and their precedences might swap too :O
I just checked, they seem to. Try this:
>> 5 ** 3 ^ 5
#=> 120
class Fixnum
alias_method :"__**", :"**"
alias_method :"**", :"^"
alias_method :"^", :"__**"
end
>> 5 ** 3 ^ 5
#=> 7776
Check out Octave: it's an open-source Matlab clone.
They keep the same precedence when you do that. You just traded the underlying method.
>> 2 * 2 ** 3 # ** has higher precedence
=> 16
>> 2 * 2 ^ 3
=> 7
>> 2 * (2 ^ 3) # ^ has lower precedence
=> 2
class Fixnum
alias_method :pow, :**
alias_method :**, :^
alias_method :^, :pow
end
>> 2 * 2 ** 3 # ** still has higher precedence
=> 2
>> (2 * 2) ** 3
=> 7
>> 2 * 2 ^ 3 # ^ still have lower precedence
=> 64
>> 2 * (2 ^ 3)
=> 16
Still don't know how (or if) you can change precedence of an operator
Thanks. I was just fooling around with this, but that will actually help me out when I get sick of writing MATLAB functions in Ruby or when performance is intolerable.
Ruby doesn't have to be slow for scientific computation. It's possible to generate C code on the fly and keep ruby as the DSL and utility language. If you are working with continuous systems (ODEs) or hybrid systems (ODEs plus discrete state transitions and dynamic dataflow networks), then you might be interested in RedShift. For example, here's a simulation of a simple thermostat controller: https://github.com/vjoel/redshift/blob/master/examples/thermostat.rb. (I am the author of RedShift.)