Need a ruby math genius - potential ruby bug.

Discussion in 'Ruby' started by Todd S., Jan 26, 2006.

  1. Todd S.

    Todd S. Guest

    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., Jan 26, 2006
    #1
    1. Advertising

  2. Todd S.

    Todd S. Guest

    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/.
     
    Todd S., Jan 26, 2006
    #2
    1. Advertising

  3. ------=_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. <> wrote:
    >
    > I should have included the results...
    >
    > with the script run as is here is the output:
    >


    ------=_Part_465_7687169.1138311364578--
     
    Andrew Dudzik, Jan 26, 2006
    #3
  4. Todd S.

    Matthew Moss Guest

    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
     
    Matthew Moss, Jan 26, 2006
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Toby A Inkster
    Replies:
    0
    Views:
    411
    Toby A Inkster
    Dec 11, 2003
  2. GHUM
    Replies:
    6
    Views:
    352
  3. Replies:
    0
    Views:
    108
  4. Giles Bowkett
    Replies:
    16
    Views:
    213
    Giles Bowkett
    Jun 18, 2007
  5. VK
    Replies:
    15
    Views:
    1,218
    Dr J R Stockton
    May 2, 2010
Loading...

Share This Page