mathics-core icon indicating copy to clipboard operation
mathics-core copied to clipboard

Improve SphericalBessel? rules for reach faster its fixed point

Open mmatera opened this issue 4 years ago • 6 comments

Certain expressions like SphericalBesselJ are implemented as native WL rules. Depending of how we write these rules, we need several steps before reaching a fixed point in the evaluation. For example,

SphericalBesselJ[n_,x_]

is internally defined as

SphericalBesselJ[n_, z_]->Sqrt[Pi / 2] / Sqrt[z] BesselJ[n + 0.5, z]

but, after evaluation, we get

Sqrt[Pi] Sqrt[2] BesselJ[0.5 + n_, x_] / (2 Sqrt[x_])

The output of TraceEvaluation[SphericalBesselJ[n, z]] is detailed below. However, it is more or less evident that a lot of computation could be saved by defining a more suitable rule. Notice also that such a better rule could depend on the context (for example, if BesselJ is called with numeric arguments, the best rule could be different.

TraceEvaluation[SphericalBesselJ[n,x]]

produces

    Evaluating: System`SphericalBesselJ[Global`n, Global`x]
      Evaluating: System`SphericalBesselJ
      Evaluating: Global`n
      Evaluating: Global`x
    -> System`Times[System`Sqrt[System`Times[System`Pi, System`Power[2, -1]]], System`Power[System`Sqrt[Global`x], -1], System`BesselJ[System`Plus[Global`n, 0.5], Global`x]]
      Evaluating: System`Times
      Evaluating: System`Sqrt[System`Times[System`Pi, System`Power[2, -1]]]
        Evaluating: System`Sqrt
        Evaluating: System`Times[System`Pi, System`Power[2, -1]]
          Evaluating: System`Times
          Evaluating: System`Pi
          Evaluating: System`Power[2, -1]
            Evaluating: System`Power
          -> 1/2
      -> System`Power[System`Times[1/2, System`Pi], System`Times[1, System`Power[2, -1]]]
        Evaluating: System`Power
        Evaluating: System`Times[1/2, System`Pi]
        Evaluating: System`Times[1, System`Power[2, -1]]
          Evaluating: System`Times
          Evaluating: System`Power[2, -1]
            Evaluating: System`Power
          -> 1/2
        -> 1/2
        Evaluating: System`Power[System`Pi, 1/2]
          Evaluating: System`Power
          Evaluating: System`Pi
          Evaluating: System`Pi
        Evaluating: System`Power[2, 1/2]
          Evaluating: System`Power
      -> System`Times[1/2, System`Power[System`Pi, 1/2], System`Power[2, 1/2]]
        Evaluating: System`Times
        Evaluating: System`Power[System`Pi, 1/2]
        Evaluating: System`Power[2, 1/2]
      Evaluating: System`Power[System`Sqrt[Global`x], -1]
        Evaluating: System`Power
        Evaluating: System`Sqrt[Global`x]
          Evaluating: System`Sqrt
          Evaluating: Global`x
        -> System`Power[Global`x, System`Times[1, System`Power[2, -1]]]
          Evaluating: System`Power
          Evaluating: Global`x
          Evaluating: System`Times[1, System`Power[2, -1]]
            Evaluating: System`Times
            Evaluating: System`Power[2, -1]
              Evaluating: System`Power
            -> 1/2
          -> 1/2
          Evaluating: Global`x
        Evaluating: Global`x
      -> System`Power[Global`x, -1/2]
        Evaluating: System`Power
        Evaluating: Global`x
        Evaluating: Global`x
      Evaluating: System`BesselJ[System`Plus[Global`n, 0.5], Global`x]
        Evaluating: System`BesselJ
        Evaluating: System`Plus[Global`n, 0.5]
          Evaluating: System`Plus
          Evaluating: Global`n
          Evaluating: System`N[Global`n, 15]
            Evaluating: System`N
            Evaluating: Global`n
            Evaluating: System`$MinPrecision
            -> 0
            Evaluating: System`N[0]
              Evaluating: System`N
            -> System`N[0, System`MachinePrecision]
              Evaluating: System`N
              Evaluating: System`MachinePrecision
            -> 0.0
            Evaluating: System`$MaxPrecision
            -> System`Infinity
            Evaluating: System`Infinity
            -> System`DirectedInfinity[1]
            Evaluating: System`DirectedInfinity[1]
              Evaluating: System`DirectedInfinity
              Evaluating: System`Unequal[System`N[System`Abs[1]], 1]
                Evaluating: System`Unequal
                Evaluating: System`N[System`Abs[1]]
                  Evaluating: System`N
                  Evaluating: System`Abs[1]
                    Evaluating: System`Abs
                  -> 1
                -> System`N[1, System`MachinePrecision]
                  Evaluating: System`N
                  Evaluating: System`MachinePrecision
                -> 1.0
                Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]][1.0]
                  Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]]
                    Evaluating: System`Function
                -> System`Not[System`ExactNumberQ[1.0]]
                  Evaluating: System`Not
                  Evaluating: System`ExactNumberQ[1.0]
                    Evaluating: System`ExactNumberQ
                  -> System`False
                    Evaluating: System`False
                -> System`True
                  Evaluating: System`True
                Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]][1]
                  Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]]
                    Evaluating: System`Function
                -> System`Not[System`ExactNumberQ[1]]
                  Evaluating: System`Not
                  Evaluating: System`ExactNumberQ[1]
                    Evaluating: System`ExactNumberQ
                  -> System`True
                    Evaluating: System`True
                -> System`False
                  Evaluating: System`False
                Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]][1.0]
                  Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]]
                    Evaluating: System`Function
                -> System`Not[System`ExactNumberQ[1.0]]
                  Evaluating: System`Not
                  Evaluating: System`ExactNumberQ[1.0]
                    Evaluating: System`ExactNumberQ
                  -> System`False
                    Evaluating: System`False
                -> System`True
                  Evaluating: System`True
                Evaluating: System`ExactNumberQ[1.0]
                  Evaluating: System`ExactNumberQ
                -> System`False
                  Evaluating: System`False
                Evaluating: System`ExactNumberQ[1]
                  Evaluating: System`ExactNumberQ
                -> System`True
                  Evaluating: System`True
                Evaluating: System`$MaxExtraPrecision
              -> System`False
                Evaluating: System`False
            Evaluating: System`N[15]
              Evaluating: System`N
            -> System`N[15, System`MachinePrecision]
              Evaluating: System`N
              Evaluating: System`MachinePrecision
            -> 15.0
          -> Global`n
            Evaluating: Global`n
        Evaluating: Global`x
        Evaluating: System`Plus[0.5, Global`n]
          Evaluating: System`Plus
          Evaluating: Global`n
          Evaluating: System`N[Global`n, 15]
            Evaluating: System`N
            Evaluating: Global`n
            Evaluating: System`$MinPrecision
            -> 0
            Evaluating: System`N[0]
              Evaluating: System`N
            -> System`N[0, System`MachinePrecision]
              Evaluating: System`N
              Evaluating: System`MachinePrecision
            -> 0.0
            Evaluating: System`$MaxPrecision
            -> System`Infinity
            Evaluating: System`Infinity
            -> System`DirectedInfinity[1]
            Evaluating: System`DirectedInfinity[1]
              Evaluating: System`DirectedInfinity
              Evaluating: System`Unequal[System`N[System`Abs[1]], 1]
                Evaluating: System`Unequal
                Evaluating: System`N[System`Abs[1]]
                  Evaluating: System`N
                  Evaluating: System`Abs[1]
                    Evaluating: System`Abs
                  -> 1
                -> System`N[1, System`MachinePrecision]
                  Evaluating: System`N
                  Evaluating: System`MachinePrecision
                -> 1.0
                Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]][1.0]
                  Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]]
                    Evaluating: System`Function
                -> System`Not[System`ExactNumberQ[1.0]]
                  Evaluating: System`Not
                  Evaluating: System`ExactNumberQ[1.0]
                    Evaluating: System`ExactNumberQ
                  -> System`False
                    Evaluating: System`False
                -> System`True
                  Evaluating: System`True
                Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]][1]
                  Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]]
                    Evaluating: System`Function
                -> System`Not[System`ExactNumberQ[1]]
                  Evaluating: System`Not
                  Evaluating: System`ExactNumberQ[1]
                    Evaluating: System`ExactNumberQ
                  -> System`True
                    Evaluating: System`True
                -> System`False
                  Evaluating: System`False
                Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]][1.0]
                  Evaluating: System`Function[System`Not[System`ExactNumberQ[System`Slot[1]]]]
                    Evaluating: System`Function
                -> System`Not[System`ExactNumberQ[1.0]]
                  Evaluating: System`Not
                  Evaluating: System`ExactNumberQ[1.0]
                    Evaluating: System`ExactNumberQ
                  -> System`False
                    Evaluating: System`False
                -> System`True
                  Evaluating: System`True
                Evaluating: System`ExactNumberQ[1.0]
                  Evaluating: System`ExactNumberQ
                -> System`False
                  Evaluating: System`False
                Evaluating: System`ExactNumberQ[1]
                  Evaluating: System`ExactNumberQ
                -> System`True
                  Evaluating: System`True
                Evaluating: System`$MaxExtraPrecision
              -> System`False
                Evaluating: System`False
            Evaluating: System`N[15]
              Evaluating: System`N
            -> System`N[15, System`MachinePrecision]
              Evaluating: System`N
              Evaluating: System`MachinePrecision
            -> 15.0
          -> Global`n
            Evaluating: Global`n
        Evaluating: Global`x
Sqrt[Pi] Sqrt[2] BesselJ[0.5 + n, x] / (2 Sqrt[x])

mmatera avatar Dec 06 '21 12:12 mmatera

Part of the key in approaching this would be a better and more precise definition of "Certain Expressions".

It seems that one reason WL has attributes associated with functions like "commutative" or "associative" and "listable" is to that this kind of thing can be made use of these in more efficient evaluation.

So we might consider what class of attributes would make this kind of thing automatic.

rocky avatar Dec 06 '21 14:12 rocky

@mmatera It would be helpful to show the same sequence after you add a transformation rule that improves this. The one where the N[] are distributed inside.

rocky avatar Dec 06 '21 14:12 rocky

@mmatera It would be helpful if the same sequence after you add a transformation rule.

Do you mean, create an issue and afterward, a PR?

Regarding the other part, I can think about a way to collect and list exhaustively all the WL native rules in Builtin that are not in their final form.

mmatera avatar Dec 06 '21 19:12 mmatera

@mmatera It would be helpful if the same sequence after you add a transformation rule.

Do you mean, create an issue and afterward, a PR?

I meant as clarification of this issue. In order to address it in a general way, one would need to understand it better. This means characterizing the this more precisely than "Certain expressions..." and more generally than a specific trace of a specific expression.

rocky avatar Dec 06 '21 20:12 rocky

Let's add the examples that you asked @rocky. First, what happens if we redefine SphericalBesselJ using the fixed point:

In[1]:= MySphericalBesselJ[n_,x_]:=Sqrt[Pi] Sqrt[2] BesselJ[1/2 + n, x] / (2 Sqrt[x]);
In[2]:= TraceEvaluation[MySphericalBesselJ[n,x]]

This is the output:

    Evaluating: Global`MySphericalBesselJ[Global`n, Global`x]
      Evaluating: Global`MySphericalBesselJ
      Evaluating: Global`n
      Evaluating: Global`x
    -> System`Times[System`Sqrt[System`Pi], System`Sqrt[2], System`BesselJ[System`Plus[System`Rational[1, 2], Global`n], Global`x], System`Power[System`Times[2, System`Sqrt[Global`x]], -1]]
      Evaluating: System`Times
      Evaluating: System`Sqrt[System`Pi]
        Evaluating: System`Sqrt
        Evaluating: System`Pi
      -> System`Power[System`Pi, System`Times[1, System`Power[2, -1]]]
        Evaluating: System`Power
        Evaluating: System`Pi
        Evaluating: System`Times[1, System`Power[2, -1]]
          Evaluating: System`Times
          Evaluating: System`Power[2, -1]
            Evaluating: System`Power
          -> 1/2
        -> 1/2
        Evaluating: System`Pi
      Evaluating: System`Sqrt[2]
        Evaluating: System`Sqrt
      -> System`Power[2, System`Times[1, System`Power[2, -1]]]
        Evaluating: System`Power
        Evaluating: System`Times[1, System`Power[2, -1]]
          Evaluating: System`Times
          Evaluating: System`Power[2, -1]
            Evaluating: System`Power
          -> 1/2
        -> 1/2
      Evaluating: System`BesselJ[System`Plus[System`Rational[1, 2], Global`n], Global`x]
        Evaluating: System`BesselJ
        Evaluating: System`Plus[System`Rational[1, 2], Global`n]
          Evaluating: System`Plus
          Evaluating: System`Rational[1, 2]
            Evaluating: System`Rational
          -> 1/2
          Evaluating: Global`n
        Evaluating: Global`x
        Evaluating: System`Plus[1/2, Global`n]
          Evaluating: System`Plus
          Evaluating: Global`n
        Evaluating: Global`x
      Evaluating: System`Power[System`Times[2, System`Sqrt[Global`x]], -1]
        Evaluating: System`Power
        Evaluating: System`Times[2, System`Sqrt[Global`x]]
          Evaluating: System`Times
          Evaluating: System`Sqrt[Global`x]
            Evaluating: System`Sqrt
            Evaluating: Global`x
          -> System`Power[Global`x, System`Times[1, System`Power[2, -1]]]
            Evaluating: System`Power
            Evaluating: Global`x
            Evaluating: System`Times[1, System`Power[2, -1]]
              Evaluating: System`Times
              Evaluating: System`Power[2, -1]
                Evaluating: System`Power
              -> 1/2
            -> 1/2
            Evaluating: Global`x
        Evaluating: System`Power[Global`x, -1/2]
          Evaluating: System`Power
          Evaluating: Global`x
          Evaluating: Global`x
      -> System`Times[1/2, System`Power[Global`x, -1/2]]
        Evaluating: System`Times
        Evaluating: System`Power[Global`x, -1/2]
Out[2]= Sqrt[Pi] Sqrt[2] BesselJ[1 / 2 + n, x] / (2 Sqrt[x])

which is much shorter, but still takes a "trivial" step of evaluation.

mmatera avatar Dec 07 '21 03:12 mmatera

For N[SphericalBesselJ[1,1/2]], a better rule could be N[SphericalBesselJ[n_?NumberQ,x_?NumberQ],prec_]:=Sqrt[N[Pi,prec_]/ 2] N[BesselJ[n +Rational[1,2], z]/ Sqrt[z],prec] that would avoid some reevaluations. In any case, it would be still much faster to implement the rule directly in Python.

mmatera avatar Dec 07 '21 10:12 mmatera