Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Truffle] Error adding Complex to Fixnum #2805

Closed
bjfish opened this issue Apr 4, 2015 · 13 comments
Closed

[Truffle] Error adding Complex to Fixnum #2805

bjfish opened this issue Apr 4, 2015 · 13 comments
Milestone

Comments

@bjfish
Copy link
Contributor

bjfish commented Apr 4, 2015

Adding Fixnum to Complex works but the reverse doesn't.

$ ~/Documents/jruby/bin/jruby -X+T -e 'puts 1 + Complex(1, 2)'
-e:1:in `+': Truffle doesn't have a case for the org.jruby.truffle.nodes.core.FixnumNodesFactory$AddNodeFactory$AddUninitializedNode node with values of type  java.lang.Integer=1 Complex(org.jruby.truffle.runtime.core.RubyBasicObject) (TypeError)
    from -e:1:in `<main>'

$  ~/Documents/jruby/bin/jruby -X+T -e 'puts Complex(1, 2) + 1'
2+2i

Do we need to add a RubyComplex class to add a specialization for this? Or how would you resolve this?

This is used in matrix stdlib.

@chrisseaton chrisseaton added this to the truffle-dev milestone Apr 4, 2015
@chrisseaton
Copy link
Contributor

This is more complicated that with other types as Complex doesn't exist as far as the Java code knows. We solved this with Rational by implementing Fixnum + Rational as Rational + Fixnum - which I think for some reason is in fact the correct thing to do.

@Specialization(guards = "isRational(arguments[1])")
public Object add(VirtualFrame frame, int a, RubyBasicObject b) {
if (rationalAdd == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
rationalAdd = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}
return rationalAdd.call(frame, b, "+", null, a);
}

Where that isn't possible, like divide, we do convertToRational(Fixnum) + Rational.

@Specialization(guards = "isRational(arguments[1])")
public Object div(VirtualFrame frame, int a, RubyBasicObject b) {
if (rationalConvertNode == null) {
CompilerDirectives.transferToInterpreter();
rationalConvertNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
rationalDivNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}
final Object aRational = rationalConvertNode.call(frame, getContext().getCoreLibrary().getRationalClass(), "convert", null, a, 1);
return rationalDivNode.call(frame, aRational, "/", null, b);
}

@bjfish
Copy link
Contributor Author

bjfish commented Apr 4, 2015

@chrisseaton I can try to implement this if you haven't done so already

@chrisseaton
Copy link
Contributor

Yes please

@chrisseaton
Copy link
Contributor

It's probably much much easier now we have ruby() actually.

@bjfish bjfish closed this as completed in 704cc35 Apr 5, 2015
@bjfish
Copy link
Contributor Author

bjfish commented Apr 5, 2015

@chrisseaton Can you recommend how to add a Specialization like this for a non-core class? e.g. Matrix

How would the guard look? Would the specialization go in FIxnumNodes even though this is non-core?

I need a Fixnum#/ and I think this can be implemented in terms of Fixnum#* fixnum * matrix.inverse

@chrisseaton
Copy link
Contributor

I don't see why Fixnum#/ should have to know about a non-core class? In MRI the C code doesn't know about Matrix (I don't think it does), so why should our implementation?

I think you might find that through coercion, Fixnum / Matrix actually becomes Matrix / Matrix (or maybe something called Scala / Matrix), and it's not due to special casing of Matrix in Fixnum#/ - take a look in the Rubinius kernel at Numeric#redo_coerced and #math_coerce, and then Matrix#coerce.

We could do with a solid understanding of this - resist the temptation to special case for Matrix - I think there is a general solution in here somewhere.

@eregon @nirvdrum do you know anything about how this works?

@bjfish
Copy link
Contributor Author

bjfish commented Apr 5, 2015

@chrisseaton The spec that is failing is call the coerce_spec (library/matrix) so I think you are on the right track but I don't know where the coercion happens.

1 / Matrix[[0,1],[-1,0]]

@chrisseaton
Copy link
Contributor

Rubinius does

  def divide(o)
    Rubinius.primitive :fixnum_div
    redo_coerced :/, o
  end
  alias_method :/, :divide

Maybe look at adding a specialisation to Fixnum#/ that does redo_coerced :/, o.

@eregon
Copy link
Member

eregon commented Apr 5, 2015

Yes, this is just the coerce mechanism, actually even Rational and Complex do not need a special case, just Float and Integer (Fixnum and Bignum) since they don't coerce well between each other.
So there is Matrix#coerce, which returns a Scalar (a class defined by Matrix) and then Fixnum should just do the redo_coerced on it so 3 + matrix becomes Scalar.new(3) + matrix thanks to matrix.coerce(3).

@eregon
Copy link
Member

eregon commented Apr 5, 2015

I think it's even preferable to not have Rational/Complex specializations unless we need it, since the class check is imprecise and there is a better mechanism already defined by the normal semantics.

@bjfish
Copy link
Contributor Author

bjfish commented Apr 5, 2015

@chrisseaton Added redo_coerced to / here: dce0852 . Let me know if Rational/Complex should be removed.

@chrisseaton
Copy link
Contributor

Can you try removing them and see what happens? You need to try running any of the benchmarks as well (do bench debug to get the command, add --loop to the end and let it run for a while) as the Rubinius implementation of Time.now.to_f uses Rational and that gets pulled into what gets compiled for the benchmarks. If that seems ok and the specs pass it's fine to remove.

@eregon
Copy link
Member

eregon commented Apr 6, 2015

We don't use Time anymore I think, but the monotonic time. Also, if Process::CLOCK_MONOTONIC fails, Time#-(Time) is used which should have the proper optimization to not go by rational (converting time to float could additionally lose precision).

@enebo enebo added this to the Non-Release milestone Dec 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants