Need a ruby math genius - potential ruby bug.

T

Todd S.

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"
 
T

Todd S.

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.
 
A

Andrew Dudzik

------=_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.

I should have included the results...

with the script run as is here is the output:

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

Matthew Moss

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 /=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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,008
Latest member
HaroldDark

Latest Threads

Top