Need a ruby math genius - potential ruby bug

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.

#!/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"

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.

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 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:

    v.elements[i] /= divisor
    i += 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 = Vector.normalize(l)
a = 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 = 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 = inner_product(self)
if div.zero?
self
else
self * (1 / Math.sqrt(div))
end
end

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

Brilliant! Thankyou Very much. I completely forgot about the variable
refrence operations with ruby. I’m still very new with my ruby code.
Not quite through the whole book yet.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs