Velocity Reviews > Ruby > Need a ruby math genius - potential ruby bug.

# Need a ruby math genius - potential ruby bug.

Todd S.
Guest
Posts: n/a

 01-26-2006
Please forgive the longish post, I've tried to boil down a bug to its
minimum and my hair is now nearly entirely pulled out.

The below code should be executable and requires only the "matrix.rb"
library that comes with a standard ruby install (At least I think it
does).

Under certain conditions, if I add a veriable to the script and never
use it the result of the procedure is changed. I cannot determin why.

Code:
```#!/usr/bin/env ruby
#=Synopsis
#Executable Script for debugging potential ruby floating point and/or
matrix error
require "matrix"

#Expand the Vector class from Matrix.rb to include:
#  normalize, sum, delta, dot, cross, effectively_equal
#Defaults to a triplet but can contain an n-deminsional vector
#  Currently most of these functions only operate on a 3-deminsional
vector.

class Vector
attr :elements, true

def normalize()
divisor = 0
@elements.each do |elem|
divisor += elem*elem
end

if divisor != 0
divisor = Math.sqrt(divisor);
i = 0
@elements.each do |elem|
@elements[i] /= divisor
i += 1
end
end
end

def Vector.normalize(v)
divisor = 0
v.elements.each do |elem|
divisor += elem*elem
end

if divisor != 0
divisor = Math.sqrt(divisor);
i = 0
v.elements.each do |elem|
v.elements[i] /= divisor
i += 1
end
end
v
end

def Vector.sum(u, v)
newVector = Vector[0,0,0]
i=0
u.elements.each do |uElem|
newVector.elements[i] = uElem.to_f + v.elements[i].to_f
i += 1
end
newVector
end

def Vector.delta(u, v)
newVector = Vector[0,0,0]
i=0
# puts "U: #{u} V: #{v}"
u.elements.each do |uElem|
newVector.elements[i] = uElem.to_f - v.elements[i].to_f
i += 1
end
newVector
end

def Vector.dot(u, v)
value = 0
i=0
u.elements.each do |uElem|
value += uElem.to_f * v.elements[i].to_f
i += 1
end
value
end

def Vector.cross(u, v)
n = Vector[0,0,0]
n.elements[0] = u.elements[1].to_f * v.elements[2].to_f -
u.elements[2].to_f * v.elements[1].to_f
n.elements[1] = u.elements[2].to_f * v.elements[0].to_f -
u.elements[0].to_f * v.elements[2].to_f
n.elements[2] = u.elements[0].to_f * v.elements[1].to_f -
u.elements[1].to_f * v.elements[0].to_f
n
end

def Vector.effectively_equal(u, v, epsilon = 0.000001)
i = 0
eq = TRUE
u.elements.each do |uElem|
if uElem.to_f - v.elements[i].to_f > epsilon
eq = FALSE
end
break if not eq
i += 1
end
eq
end

end # Vector class expansion

# Note, This error occurs when delta of curVert and prevVert have an
axis of zero.
#       and have a resonably high percision.
curVert = Vector[0.015525, -0.284744, 0.532219]
prevVert = Vector[-0.007794, -0.283622, 0.532219]
nextVert = Vector[0.017021, -0.256189, 0.52561]

puts "-----------"
puts "prevVert \t#{prevVert}"
puts "curVert \t#{curVert}"
puts "nextVert \t#{nextVert}"
a = Vector.delta(prevVert,curVert)
puts "a: \t\t#{a}"
b0 = Vector.delta(nextVert,curVert)
puts "b0: \t\t#{b0}"
l = Vector.cross(a,b0)
puts "l: \t\t#{l}"
l = Vector.normalize(l)
puts "l normalized: \t#{l}"
a = Vector.normalize(a)
puts "a normalized: \t#{a}"
b1 = Vector.cross(l,a)
puts "b1: \t\t#{b1}"

# With b2 uncommented, even though it is not used in any function,
# it changes the output of mInverse by a factor of 2
# - Comment out the two lines below and rerun to see the change in
mInverse
#--------------v------------
b2 = Vector.normalize(b1)
puts "b2: \t\t#{b2}"
#--------------^------------

m = Matrix[l.elements,b1.elements,a.elements]
mInverse = m.inverse
puts "matrix:   \t#{m}"
puts "mInverse: \t#{mInverse}\n\n"```
--
Posted via http://www.ruby-forum.com/.

Todd S.
Guest
Posts: n/a

 01-26-2006
I should have included the results...

with the script run as is here is the output:

-----------
prevVert Vector[-0.007794, -0.283622, 0.532219]
curVert Vector[0.015525, -0.284744, 0.532219]
nextVert Vector[0.017021, -0.256189, 0.52561]
a: Vector[-0.023319, 0.00112200000000001, 0.0]
b0: Vector[0.001496, 0.028555, -0.00660899999999998]
l: Vector[-7.41529800000005e-06,
-0.000154115270999999, -0.000667552557]
l normalized: Vector[-0.0108228549457685, -0.224935966560048,
-0.97431343866259]
a normalized: Vector[-0.99884446633489, 0.0480596719939859, 0.0]
b1: Vector[0.0468251842814566, 0.973187586683846,
-0.225196188336926]
b2: Vector[0.0468251842814566, 0.973187586683846,
-0.225196188336926]
matrix: Matrix[[-0.0108228549457685, -0.224935966560048,
-0.97431343866259], [0.0468251842814566, 0.973187586683846,
-0.225196188336926], [-0.99884446633489, 0.0480596719939859, 0.0]]
mInverse: Matrix[[-128.0, -32.0, -0.99884446633489], [0.0, 2.0,
0.0480596719939859], [-0.97431343866259, -0.225196188336926,
-1.20157827529705e-18]]

With the variable "b2" commented out this is the ouput. Note the change
in mInverse...

-----------
prevVert Vector[-0.007794, -0.283622, 0.532219]
curVert Vector[0.015525, -0.284744, 0.532219]
nextVert Vector[0.017021, -0.256189, 0.52561]
a: Vector[-0.023319, 0.00112200000000001, 0.0]
b0: Vector[0.001496, 0.028555, -0.00660899999999998]
l: Vector[-7.41529800000005e-06,
-0.000154115270999999, -0.000667552557]
l normalized: Vector[-0.0108228549457685, -0.224935966560048,
-0.97431343866259]
a normalized: Vector[-0.99884446633489, 0.0480596719939859, 0.0]
b1: Vector[0.0468251842814566, 0.973187586683846,
-0.225196188336926]
matrix: Matrix[[-0.0108228549457685, -0.224935966560048,
-0.97431343866259], [0.0468251842814566, 0.973187586683846,
-0.225196188336926], [-0.99884446633489, 0.0480596719939859, 0.0]]
mInverse: Matrix[[-64.0, -16.0, -0.99884446633489], [0.0, 1.0,
0.0480596719939859], [-0.97431343866259, -0.225196188336926,
-2.40315655059409e-18]]

incidentally, I believe both of these results are incorrect.

--
Posted via http://www.ruby-forum.com/.

Andrew Dudzik
Guest
Posts: n/a

 01-26-2006
------=_Part_465_7687169.1138311364578
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

For starters, Vector#normalize(v) both changes v and returns it--you should
make a copy. Certainly doesn't explain the behavior, at least not in my
mind, but you should try it.

I'll give a shot at a more detailed analysis, but at the moment I can't get
the code to compile. It would help if you could post a .rb file.

On 1/26/06, Todd S. <(E-Mail Removed)> wrote:
>
> I should have included the results...
>
> with the script run as is here is the output:
>

------=_Part_465_7687169.1138311364578--

Matthew Moss
Guest
Posts: n/a

 01-26-2006
I think (offhand, didn't actually run your code) your problem is not
math but pass-by-reference vs. pass-by-value issues. Look at your
Vector.normalize function:

> def Vector.normalize(v)
> divisor =3D 0
> v.elements.each do |elem|
> divisor +=3D elem*elem
> end
>
> if divisor !=3D 0
> divisor =3D Math.sqrt(divisor);
> i =3D 0
> v.elements.each do |elem|
> v.elements[i] /=3D divisor
> i +=3D 1
> end
> end
> v
> end

v is not a copy of the parameter passed in but a reference to it. And
you are changing the elements of v directly.

> l =3D Vector.normalize(l)
> a =3D Vector.normalize(a)

In these two cases here, you don't notice the destructive behaviour,
since you assign the result to the parameter. But here:

> b2 =3D Vector.normalize(b1)

You are assigning the result to a different variable, but you've
actually changed b1 as well because it's not pass-by-value.

I would write a Vector normalize like such:

class Vector
def normalize
div =3D inner_product(self)
if div.zero?
self
else
self * (1 / Math.sqrt(div))
end
end

def Vector.normalize(v)
v.normalize
end
end