Forum: Ruby-core [Bug #1708] require 'complex' Causes Unexpected Behaviour

Posted by Run Paint Run Run (Guest)
on 2009-06-30 21:40
(Received via mailing list)
Bug #1708: require 'complex' Causes Unexpected Behaviour
http://redmine.ruby-lang.org/issues/show/1708

Author: Run Paint Run Run
Status: Open, Priority: Normal
Category: lib
ruby -v: ruby 1.9.2dev (2009-06-27 trunk 23871) [i686-linux]

1.9 has Complex in core, yet on 1.8 you had to explicitly require it 
with "require 'complex'". However, 1.9 also has a 'complex.rb' in its 
lib directory which in turn requires 'cmath.rb'. 'cmath.rb' redefines 
quite a few Math methods. This is particularly confusing because you 
would expect "require 'complex'" to be a no-op on 1.9, whereas it 
actually causes Math methods to fail in mysterious ways.

For example, where Math used to raise TypeErrors it now raises 
NoMethodErrors. This happens for all of the overridden methods.

    >> Math.atan(nil)
    TypeError: can't convert nil into Float
  from (irb):1:in `atan'
  from (irb):1
  from /usr/local/bin/irb:12:in `<main>'
    >> require 'complex'
    => true
    >> Math.atan(nil)
    NoMethodError: undefined method `real?' for nil:NilClass
  from /usr/local/lib/ruby/1.9.1/cmath.rb:145:in `atan'
  from (irb):3
  from /usr/local/bin/irb:12:in `<main>'

As Complex is in core Math methods should work with complex numbers by 
default. This half-in-, half-out-, approach is confusing. If the current 
behavior is intentional, it needs to be clarified in the Math and 
Complex documentation.
Posted by tadayoshi funaba (Guest)
on 2009-07-03 13:02
(Received via mailing list)
Issue #1708 has been updated by tadayoshi funaba.


not a bug

----------------------------------------
http://redmine.ruby-lang.org/issues/show/1708
Posted by Yukihiro Matsumoto (Guest)
on 2009-07-03 18:18
(Received via mailing list)
Hi,

In message "Re: [ruby-core:24126] [Bug #1708] require 'complex' Causes 
Unexpected Behaviour"
    on Fri, 3 Jul 2009 20:01:54 +0900, tadayoshi funaba 
<redmine@ruby-lang.org> writes:

|not a bug

Let me clarify.  As Complex numbers made built-in in 1.9, Tadayoshi
asked me to make Math functions work well with complex numbers, but I
did want to keep semantics taken libm functions.  So he separated
those functions into cmath.rb.  And required it from complex.rb to
make 1.8 compatibility.

So options are:

  * make those functions complex numbers aware from the beginning.
    lose libm semantics and compatibility a bit.
  * keep them as they are.  they are at least compatible with 1.8.
  * stop requiring cmath.rb from complex.rb.  lose compatibility.
    keep libm semantics.
  * something else.

Opinion?

              matz.
Posted by Run Paint Run Run (Guest)
on 2009-07-03 19:11
(Received via mailing list)
Issue #1708 has been updated by Run Paint Run Run.


Thank you for the explanation, matz. :-)

When I heard that Complex had been moved to core I assumed that _all_ 
Complex functionality had been translated, include the Math extensions. 
I imagine that this was a common reaction.

It would surely be optimal for the Math methods to seamlessly support 
Complex numbers. Let's take Math.cos for example. Passing it a Complex 
number without 'cmath':

    >> Math.cos(Complex(1,2))
    RangeError: can't convert 1+2i into Float
  from (irb):79:in `to_f'
  from (irb):79:in `cos'
  from (irb):79
  from /usr/local/bin/irb:12:in `<main>'

Passing it a Complex number with 'cmath':

    >> Math.cos(Complex(1,2)).inspect
    => "(2.0327230070196656-3.0518977991517997i)"

In this case, what would be the harm of including 'cmath' by default? 
Performance isn't affected for the common cases (non-Complex numbers), 
yet Complex arguments "do the right thing". Indeed, the majority of the 
methods in 'cmath' appear to be only act differently from the default if 
their argument is Complex (and, in some cases, positive, too). What is 
the benefit to the user of having a method raise an exception when it 
could produce the right answer? Or, what do we lose by integrating 
'cmath'?

Unless there are serious incompatibilities or performance concerns, it 
seems more robust and well-rounded if Complex numbers work as one would 
expect.
----------------------------------------
http://redmine.ruby-lang.org/issues/show/1708
Posted by Yukihiro Matsumoto (Guest)
on 2009-07-03 19:48
(Received via mailing list)
Hi,

In message "Re: [ruby-core:24129] [Bug #1708] require 'complex' Causes 
Unexpected Behaviour"
    on Sat, 4 Jul 2009 02:10:15 +0900, Run Paint Run Run 
<redmine@ruby-lang.org> writes:

|When I heard that Complex had been moved to core I assumed that _all_ Complex functionality had been translated, include the Math extensions. I imagine that this was a common reaction.

According to the fail-early principle, unexpected situation should be
told (by raising an error) as early as possible.  When the program do
not expect complex number calculation (or negative argument to sqrt),
it should cause an error, rather than continuing calculation with
complex numbers.  Requiring 'complex' (or 'cmath') gives a chance to a
program to declare it is expecting complex number calculation.

Probably I think this way mostly because I have never had a chance to
use complex numbers excepct for testing the implementation.  Those who
use complex numbers often might feel differently.

Of course I understand what you claim.  It's a matter of trade-offs.
Tadayoshi would feel happy, I think, if you can persuade me.

              matz.
Posted by Run Paint Run Run (Guest)
on 2009-07-03 22:07
(Received via mailing list)
Issue #1708 has been updated by Run Paint Run Run.


> According to the fail-early principle, unexpected situation should be
> told (by raising an error) as early as possible.

I'd always considered that to apply to calculations that would 
ultimately fail, the logic being that you should complain about 
arguments that would cause failure up front, rather than attempting to 
perform an expensive calculation with them and then failing. In this 
case, we don't need to fail. We can give the right answer.

Let's use Math.sqrt(-2) for an example. Without 'cmath' the programmer's 
only option would be to rescue Errno::EDOM. With 'cmath' they could 
achieve the same effect by inspecting the result's #real? method, or 
they could perform mathematical operations on the result. With Complex 
implementing so many Numeric methods, it's perfectly possible for their 
code to continue to work with complex results. The difference being that 
now exceptions are much rarer. Integration of 'cmath' gives the 
programmer more control. Put another way, one can perform many more 
operations with a Complex object than he can with an Exception object.

Further, Math.sqrt(-2) raising an exception seems dishonest. The answer 
is perfectly knowable and calculable; we'd raise an exception not 
because the input was exceptional but because, presumably, we wanted to 
protect the programmer from calling a method on the result that was 
allowed for reals, but disallowed for non-reals. That is, we'd want to 
protect the programmer from saying:

    Math.sqrt(n) > LIMIT  # Raises a NoMethodError for :> if .sqrt 
returns a Complex

Or:
    side = Math.sqrt(area_of_square)
    # ....
    cost = side * cost_per_side   # cost may be a complex number...

Note that a similar argument could be levied against methods that 
occasionally returned Floats, too. A programmer who blindly calls #odd? 
on the result will have the same shock as if he blindly called a Fixnum 
method on a result which was occasionally complex.

Lastly, if we're considering programmers who may not expect to receive 
complex results from Math methods, we must also consider those who, 
knowing that Complex is in core, have the contrary expectation... ;-)

And now I'll be quiet to give other parties a chance to participate. ;-)
----------------------------------------
http://redmine.ruby-lang.org/issues/show/1708
Posted by tadayoshi funaba (Guest)
on 2009-07-04 05:57
(Received via mailing list)
Issue #1708 has been updated by tadayoshi funaba.


ruby is a great believer in unix/c.

Math.acosh(-1) # EDOM
(-8) ** 0.5 #=> NaN
1 / 2 #=> 0

and

require 'cmath'
CMath.acosh(-1) #=> (0.0+3.141592653589793i)
Complex(-8) ** 0.5 #=> (1.7318549141438708e-16+2.82842712474619i)
Float(1) / 2 #=> 0.5
Rational(1) / 2 #=> (1/2)

it's too hard.

in scheme:

(acosh -1) ;=> 0.0+3.141592653589793i
(expt -8 0.5) ;=> 1.7318549141438708e-16+2.82842712474619i
(/ 1 2) ;=> 1/2

it's so simple.

i think that introducing complex number means extending the domain of 
ruby's numeric system.
we should provide proper results in the domain.
we should not restrict any general operations.

we can always use "div" instead of "/" for integer division.
and we can also provide FMath.sqrt as a restricted version, if you want.

of course, this is an option.
i'm looking forward to 2.0.
----------------------------------------
http://redmine.ruby-lang.org/issues/show/1708
Posted by Yuki Sonoda (Guest)
on 2009-07-13 13:48
(Received via mailing list)
Issue #1708 has been updated by Yuki Sonoda.

Status changed from Open to Rejected
Assigned to set to tadayoshi funaba

Was the issue rejected? right?
----------------------------------------
http://redmine.ruby-lang.org/issues/show/1708
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.