Skip to content

Conversation

@GarkGarcia
Copy link
Contributor

@GarkGarcia GarkGarcia commented Aug 25, 2020

This is a follow up to #801, an implementation of Wolfram's Root. The implementation is pretty simple, it's a wrapper for SymPy's CRootOf.

If SymPy can represent the CRootOf instance with radicals then Root resolves to the radical representation:

>> Root[x ^ 2 - 1, 1]
 = -1

Otherwise, Root resolves to itself:

>> Root[x ^ 5 + 2 x + 1, 1]
 = Root[x ^ 5 + 2 x + 1, 1]

I think a got everything working, but I was not able to test my implementation. I'm not sure how testing works in this project.

Relevant Links:

@GarkGarcia
Copy link
Contributor Author

@weakit @sn6uv @bnjones I'd like to get your input on this.

@GarkGarcia
Copy link
Contributor Author

@sn6uv @bnjones It would be great if I could make it so that Root[f, i] gets converted to CRootOf(f, i) when the to_sympy method is called. I'm not sure how to do that though.

@weakit
Copy link
Contributor

weakit commented Aug 26, 2020

@weakit @sn6uv @bnjones I'd like to get your input on this.

I'm not really familiar with how mathics works, and I'm not that good at working with big projects. I can help with bugs, but that's the most I think I'll be able to do. You'll have to ask the maintainers.

@wolfv
Copy link
Member

wolfv commented Aug 26, 2020

Hi @GarkGarcia cool!
The tests in mathics are in the documentation ;)

@GarkGarcia
Copy link
Contributor Author

@wolfv It looks like all tests are passing, but for some reason the Root function isn't processed at all. I thought adding a new builtin was as simple as creating a Builtin subclass and implementing apply, is there something else I need to do? Could you help me figure this out?

@GarkGarcia
Copy link
Contributor Author

Solve[eq, x] is now working fine, but Root[f, i] isn't processed with the Root class for some reason. For example, Root[x, 1] yields Root[x, 1] instead of 0. I was able to verified that Root.apply doesn't get called by placing a random print call at the beginning of Root.apply (the text never gets print).

@GarkGarcia
Copy link
Contributor Author

GarkGarcia commented Aug 26, 2020

Solve[eq, x] is now working fine, but Root[f, i] isn't processed with the Root class for some reason. For example, Root[x, 1] yields Root[x, 1] instead of 0. I was able to verified that Root.apply doesn't get called by placing a random print call at the beginning of Root.apply (the text never gets print).

Those are the only tests that fail (besides a bunch of unrelated stuff).

@wolfv
Copy link
Member

wolfv commented Aug 26, 2020

is it possible that another version of root is implemented somewhere else? Did you CTRL+F for it?

@GarkGarcia
Copy link
Contributor Author

Did you CTRL+F for it?

I haven't. Just tried pressing Ctrl+F in the command line interface and nothing happened. Is this what I'm supposed to do?

@wolfv
Copy link
Member

wolfv commented Aug 26, 2020

ah sorry, I meant to search the entire source code for "Root" (CTRL+F on linux is the shortcut to "search-in-page").

@GarkGarcia
Copy link
Contributor Author

ah sorry, I meant to search the entire source code for "Root" (CTRL+F on linux is the shortcut to "search-in-page").

This is the output of searching for "Root" with grep

$ grep -E "Root" mathics/**.py
mathics/builtin/arithmetic.py:class CubeRoot(Builtin):
mathics/builtin/arithmetic.py:    <dt>'CubeRoot[$n$]'
mathics/builtin/arithmetic.py:    >> CubeRoot[16]
mathics/builtin/arithmetic.py:    #> CubeRoot[-5]
mathics/builtin/arithmetic.py:    #> CubeRoot[-510000]
mathics/builtin/arithmetic.py:    #> CubeRoot[-5.1]
mathics/builtin/arithmetic.py:    #> CubeRoot[b]
mathics/builtin/arithmetic.py:    #> CubeRoot[-0.5]
mathics/builtin/arithmetic.py:    #> CubeRoot[3 + 4 I]
mathics/builtin/arithmetic.py:        'CubeRoot[n_?NumericQ]': 'If[n > 0, Power[n, Divide[1, 3]], Times[-1, Power[Times[-1, n], Divide[1, 3]]]]',
mathics/builtin/arithmetic.py:        'CubeRoot[n_]': 'Power[n, Divide[1, 3]]',
mathics/builtin/arithmetic.py:        'CubeRoot[n_Complex]'
mathics/builtin/arithmetic.py:        evaluation.message('CubeRoot', 'preal', n)
mathics/builtin/calculus.py:     = RootSum[625 #1 ^ 4 + 125 #1 ^ 3 + 25 #1 ^ 2 + 5 #1 + 1&, Log[x + 5 #1] #1&] + Log[1 + x] / 5
mathics/builtin/calculus.py:class Root(Builtin):
mathics/builtin/calculus.py:    <dt>'Root[$f$, $i$]'
mathics/builtin/calculus.py:    >> Root[-1 + x ^ 2, 1]
mathics/builtin/calculus.py:    >> Root[-1 + x ^ 2, 2]
mathics/builtin/calculus.py:    Roots that can't be represented by radicals:
mathics/builtin/calculus.py:    >> Root[1 + 2 x + x ^ 5, 2]
mathics/builtin/calculus.py:     = Root[1 + 2 x + x ^ 5, 2]
mathics/builtin/calculus.py:        'Root[f, i]'
mathics/builtin/calculus.py:            r = sympy.CRootOf(f.to_sympy(), i.to_sympy() - 1)
mathics/builtin/calculus.py:            evaluation.message('Root', 'nuni', f)
mathics/builtin/calculus.py:            evaluation.message('Root', 'nint', i)
mathics/builtin/calculus.py:            evaluation.message('Root', 'iidx', i)
mathics/builtin/calculus.py:class FindRoot(Builtin):
mathics/builtin/calculus.py:    <dt>'FindRoot[$f$, {$x$, $x0$}]'
mathics/builtin/calculus.py:    <dt>'FindRoot[$lhs$ == $rhs$, {$x$, $x0$}]'
mathics/builtin/calculus.py:    'FindRoot' uses Newton\'s method, so the function of interest should have a first derivative.
mathics/builtin/calculus.py:    >> FindRoot[Cos[x], {x, 1}]
mathics/builtin/calculus.py:    >> FindRoot[Sin[x] + Exp[x],{x, 0}]
mathics/builtin/calculus.py:    >> FindRoot[Sin[x] + Exp[x] == Pi,{x, 0}]
mathics/builtin/calculus.py:    'FindRoot' has attribute 'HoldAll' and effectively uses 'Block' to localize $x$.
mathics/builtin/calculus.py:    >> FindRoot[Tan[x] + Sin[x] == Pi, {x, 1}]
mathics/builtin/calculus.py:    'FindRoot' stops after 100 iterations:
mathics/builtin/calculus.py:    >> FindRoot[x^2 + x + 1, {x, 1}]
mathics/builtin/calculus.py:    >> FindRoot[x ^ 2 + x + 1, {x, -I}]
mathics/builtin/calculus.py:    >> FindRoot[f[x] == 0, {x, 0}]
mathics/builtin/calculus.py:     = FindRoot[f[x] - 0, {x, 0}]
mathics/builtin/calculus.py:    >> FindRoot[Sin[x] == x, {x, 0}]
mathics/builtin/calculus.py:     = FindRoot[Sin[x] - x, {x, 0}]
mathics/builtin/calculus.py:    #> FindRoot[2.5==x,{x,0}]
mathics/builtin/calculus.py:        'FindRoot[lhs_ == rhs_, {x_, xs_}]': 'FindRoot[lhs - rhs, {x, xs}]',
mathics/builtin/calculus.py:        'FindRoot[f_, {x_, x0_}]'
mathics/builtin/calculus.py:            evaluation.message('FindRoot', 'snum', x0)
mathics/builtin/calculus.py:            evaluation.message('FindRoot', 'sym', x, 2)
mathics/builtin/calculus.py:                evaluation.message('FindRoot', 'dsing', x, x0)
mathics/builtin/calculus.py:                evaluation.message('FindRoot', 'nnum', x, x0)
mathics/builtin/calculus.py:            evaluation.message('FindRoot', 'maxiter')
mathics/builtin/files.py:class RootDirectory(Predefined):
mathics/builtin/files.py:    <dt>'$RootDirectory'
mathics/builtin/files.py:    >> $RootDirectory
mathics/builtin/files.py:    name = '$RootDirectory'
mathics/builtin/files.py:    #> FileNameDepth[$RootDirectory]
mathics/core/convert.py:    elif isinstance(expr, sympy.RootSum):
mathics/core/convert.py:        return Expression('RootSum', from_sympy(expr.poly),
mathics/core/convert.py:    elif isinstance(expr, sympy.CRootOf):
mathics/core/convert.py:        return Expression('Root', from_sympy(e), i + 1)

In other words, Root isn't defined anywhere else.

@GarkGarcia
Copy link
Contributor Author

GarkGarcia commented Aug 26, 2020

Just found out what the error was. As one might expect, it's one of those silly errors that are a pain to debug.

Apparently the way Mathics patterns are generated from builtin classes is via the docstring of their apply method implementations. The docstring for Root.apply was 'Root[f, i]' instead of 'Root[f_, i_]'.

@GarkGarcia
Copy link
Contributor Author

Apparently there's only one test that isn't passing (which has nothing to do with this PR). The only thing left to address is that fact that Wolfram's Root uses that slot thing (and I don't know what it is) and our Root uses regular unbound variables.

  • Wolfram: Root[#1 ^ 5 + 2 #1 + 1 &, 2]
  • This implementation: Root[x ^ 5 + 2 x + 1, 2]

I don't know how much of slot is implemented in Mathics and how could I convert a regular (univariate) equation to this format.

@GarkGarcia
Copy link
Contributor Author

@weakit Perhaps you could help me understand what slot is all about.

@suhr
Copy link
Contributor

suhr commented Aug 27, 2020

@weakit
Copy link
Contributor

weakit commented Aug 27, 2020

@weakit Perhaps you could help me understand what slot is all about.

Slot is used to represent the arguments of a function.

So say you have a function f(x, y) = x² + y + 1,
In Mathematica, you could represent the function as (#1^2 + #2 + 1)&, where #1 and #2 are the first and second arguments.

if f is #1^2 + #2 + 1
f[x, y] would work out to x^2 + y + 2

Root represents the function x^5 + 2 x + 1 as #^5 + 2 # + 1.

I remember that mathics did have Slot[], but I don't remember if it worked properly. Try looking for it.

This is the way I understand it. It may be a bit off (?) so make sure to go through the documentation once.

Try going through these:
https://reference.wolfram.com/language/howto/WorkWithPureFunctions.html
https://reference.wolfram.com/language/ref/Function.html

@GarkGarcia
Copy link
Contributor Author

GarkGarcia commented Aug 27, 2020

@weakit Perhaps you could help me understand what slot is all about.

Slot is used to represent the arguments of a function.

So say you have a function f(x, y) = x² + y + 1,
In Mathematica, you could represent the function as (#1^2 + #2 + 1)&, where #1 and #2 are the first and second arguments.

if f is #1^2 + #2 + 1
f[x, y] would work out to x^2 + y + 2

Root represents the function x^5 + 2 x + 1 as #^5 + 2 # + 1.

I remember that mathics did have Slot[], but I don't remember if it worked properly. Try looking for it.

This is the way I understand it. It may be a bit off (?) so make sure to go through the documentation once.

Try going through these:
https://reference.wolfram.com/language/howto/WorkWithPureFunctions.html
https://reference.wolfram.com/language/ref/Function.html

I see. @wolfv How do I appropriately convert between a Function and a sympy.PurePoly?

@suhr
Copy link
Contributor

suhr commented Aug 27, 2020

The most straightforward way it to encode #1 as a variable with name _1, and so on.

@GarkGarcia
Copy link
Contributor Author

GarkGarcia commented Aug 27, 2020

The most straightforward way it to encode #1 as a variable with name _1, and so on.

Seems a bit hacky but it works well enough 😋️

Alright, I've made it so that Root takes a proper function (with slots) as it's first argument. If the implementation passes the tests we're all done in here.

@suhr
Copy link
Contributor

suhr commented Aug 27, 2020

You need to fix the tests though.

@GarkGarcia
Copy link
Contributor Author

@suhr The (#1 ...) & idea didn't work out, it caused the tests to fail because the expected results where not format in the way Mathics prints them (although both expressions represent the same value).

@GarkGarcia
Copy link
Contributor Author

GarkGarcia commented Aug 27, 2020

It looks like all relevant tests are passing. @wolfv @suhr Can we merge the PR?

@GarkGarcia GarkGarcia requested a review from wolfv August 27, 2020 21:15
@wolfv wolfv merged commit 909052e into mathics:master Aug 28, 2020
@wolfv
Copy link
Member

wolfv commented Aug 28, 2020

great! thanks!!

@GarkGarcia GarkGarcia deleted the root-implementation branch September 12, 2020 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants