
    \hB                         S r SSKJr  SSKJr  SSKJr  SSKJr  SSKJ	r	  SSK
Jr  \R                  4S jr\R                  4S	 jrSS jrS
SSSS.S jrg)am  
Finite difference weights
=========================

This module implements an algorithm for efficient generation of finite
difference weights for ordinary differentials of functions for
derivatives from 0 (interpolation) up to arbitrary order.

The core algorithm is provided in the finite difference weight generating
function (``finite_diff_weights``), and two convenience functions are provided
for:

- estimating a derivative (or interpolate) directly from a series of points
    is also provided (``apply_finite_diff``).
- differentiating by using finite difference approximations
    (``differentiate_finite``).

    )
Derivative)S)Subs)preorder_traversal)sympy_deprecation_warning)iterablec                 .   [        U 5      n U R                  (       d  [        S5      eU S:  a  [        S5      e[        U 5      U :w  a  [        S5      eU n[	        U5      S-
  n[        US-   5       VVVs/ s HA  n[        US-   5       VVs/ s H"  n[        US-   5       Vs/ s H  nSPM     snPM$     snnPMC     nnnn[         R                  US   S   S'   [         R                  n	[        SUS-   5       GH  n[         R                  n
[        U5       H  nX   X   -
  nX-  n
Xc::  a  SX   US-
     U'   [        [        Xc5      S-   5       HF  nX   U-
  X   US-
     U   -  XXUS-
     US-
     U   -  -
  X   U   U'   X   U   U==   U-  ss'   MH     M     [        [        Xc5      S-   5       HB  nX-  XXUS-
     US-
     US-
     -  XS-
     U-
  X   US-
     US-
     -  -
  -  X   U   U'   MD     U
n	GM     U$ s  snf s  snnf s  snnnf )a  
Calculates the finite difference weights for an arbitrarily spaced
one-dimensional grid (``x_list``) for derivatives at ``x0`` of order
0, 1, ..., up to ``order`` using a recursive formula. Order of accuracy
is at least ``len(x_list) - order``, if ``x_list`` is defined correctly.

Parameters
==========

order: int
    Up to what derivative order weights should be calculated.
    0 corresponds to interpolation.
x_list: sequence
    Sequence of (unique) values for the independent variable.
    It is useful (but not necessary) to order ``x_list`` from
    nearest to furthest from ``x0``; see examples below.
x0: Number or Symbol
    Root or value of the independent variable for which the finite
    difference weights should be generated. Default is ``S.One``.

Returns
=======

list
    A list of sublists, each corresponding to coefficients for
    increasing derivative order, and each containing lists of
    coefficients for increasing subsets of x_list.

Examples
========

>>> from sympy import finite_diff_weights, S
>>> res = finite_diff_weights(1, [-S(1)/2, S(1)/2, S(3)/2, S(5)/2], 0)
>>> res
[[[1, 0, 0, 0],
  [1/2, 1/2, 0, 0],
  [3/8, 3/4, -1/8, 0],
  [5/16, 15/16, -5/16, 1/16]],
 [[0, 0, 0, 0],
  [-1, 1, 0, 0],
  [-1, 1, 0, 0],
  [-23/24, 7/8, 1/8, -1/24]]]
>>> res[0][-1]  # FD weights for 0th derivative, using full x_list
[5/16, 15/16, -5/16, 1/16]
>>> res[1][-1]  # FD weights for 1st derivative
[-23/24, 7/8, 1/8, -1/24]
>>> res[1][-2]  # FD weights for 1st derivative, using x_list[:-1]
[-1, 1, 0, 0]
>>> res[1][-1][0]  # FD weight for 1st deriv. for x_list[0]
-23/24
>>> res[1][-1][1]  # FD weight for 1st deriv. for x_list[1], etc.
7/8

Each sublist contains the most accurate formula at the end.
Note, that in the above example ``res[1][1]`` is the same as ``res[1][2]``.
Since res[1][2] has an order of accuracy of
``len(x_list[:3]) - order = 3 - 1 = 2``, the same is true for ``res[1][1]``!

>>> res = finite_diff_weights(1, [S(0), S(1), -S(1), S(2), -S(2)], 0)[1]
>>> res
[[0, 0, 0, 0, 0],
 [-1, 1, 0, 0, 0],
 [0, 1/2, -1/2, 0, 0],
 [-1/2, 1, -1/3, -1/6, 0],
 [0, 2/3, -2/3, -1/12, 1/12]]
>>> res[0]  # no approximation possible, using x_list[0] only
[0, 0, 0, 0, 0]
>>> res[1]  # classic forward step approximation
[-1, 1, 0, 0, 0]
>>> res[2]  # classic centered approximation
[0, 1/2, -1/2, 0, 0]
>>> res[3:]  # higher order approximations
[[-1/2, 1, -1/3, -1/6, 0], [0, 2/3, -2/3, -1/12, 1/12]]

Let us compare this to a differently defined ``x_list``. Pay attention to
``foo[i][k]`` corresponding to the gridpoint defined by ``x_list[k]``.

>>> foo = finite_diff_weights(1, [-S(2), -S(1), S(0), S(1), S(2)], 0)[1]
>>> foo
[[0, 0, 0, 0, 0],
 [-1, 1, 0, 0, 0],
 [1/2, -2, 3/2, 0, 0],
 [1/6, -1, 1/2, 1/3, 0],
 [1/12, -2/3, 0, 2/3, -1/12]]
>>> foo[1]  # not the same and of lower accuracy as res[1]!
[-1, 1, 0, 0, 0]
>>> foo[2]  # classic double backward step approximation
[1/2, -2, 3/2, 0, 0]
>>> foo[4]  # the same as res[4]
[1/12, -2/3, 0, 2/3, -1/12]

Note that, unless you plan on using approximations based on subsets of
``x_list``, the order of gridpoints does not matter.

The capability to generate weights at arbitrary points can be
used e.g. to minimize Runge's phenomenon by using Chebyshev nodes:

>>> from sympy import cos, symbols, pi, simplify
>>> N, (h, x) = 4, symbols('h x')
>>> x_list = [x+h*cos(i*pi/(N)) for i in range(N,-1,-1)] # chebyshev nodes
>>> print(x_list)
[-h + x, -sqrt(2)*h/2 + x, x, sqrt(2)*h/2 + x, h + x]
>>> mycoeffs = finite_diff_weights(1, x_list, 0)[1][4]
>>> [simplify(c) for c in  mycoeffs] #doctest: +NORMALIZE_WHITESPACE
[(h**3/2 + h**2*x - 3*h*x**2 - 4*x**3)/h**4,
(-sqrt(2)*h**3 - 4*h**2*x + 3*sqrt(2)*h*x**2 + 8*x**3)/h**4,
(6*h**2*x - 8*x**3)/h**4,
(sqrt(2)*h**3 - 4*h**2*x - 3*sqrt(2)*h*x**2 + 8*x**3)/h**4,
(-h**3/2 + h**2*x + 3*h*x**2 - 4*x**3)/h**4]

Notes
=====

If weights for a finite difference approximation of 3rd order
derivative is wanted, weights for 0th, 1st and 2nd order are
calculated "for free", so are formulae using subsets of ``x_list``.
This is something one can take advantage of to save computational cost.
Be aware that one should define ``x_list`` from nearest to furthest from
``x0``. If not, subsets of ``x_list`` will yield poorer approximations,
which might not grand an order of accuracy of ``len(x_list) - order``.

See also
========

sympy.calculus.finite_diff.apply_finite_diff

References
==========

.. [1] Generation of Finite Difference Formulas on Arbitrarily Spaced
        Grids, Bengt Fornberg; Mathematics of computation; 51; 184;
        (1988); 699-706; doi:10.1090/S0025-5718-1988-0935077-0

zCannot handle symbolic order.r   z"Negative derivative order illegal.zNon-integer order illegal   )r   	is_number
ValueErrorintlenrangeOnemin)orderx_listx0MNmnnudeltac1c2c3s               R/var/www/auris/envauris/lib/python3.13/site-packages/sympy/calculus/finite_diff.pyfinite_diff_weightsr      s3   P eHE??899qy=>>
5zU455AFaA!*Q 16ac
;
151:&:Rq:&
; 
 UUE!HQKN	
B1ac]UU(BVZ'BBv$%1b!3q9Q;'#)9R<!A#r1B"BAaCj1ob))#*BB2% (  s1y{#AUAAaCj1oac&:$:%+aC[^UXac]1Q35G$G%H IEHQKN $   L' '; s*   5HH
"H.H
5HH

Hc                     [        U5      S-
  n[        U5      [        U5      :w  a  [        S5      e[        XU5      nSn[        [        U5      5       H  nXeU    U   U   X'   -  -  nM     U$ )a  
Calculates the finite difference approximation of
the derivative of requested order at ``x0`` from points
provided in ``x_list`` and ``y_list``.

Parameters
==========

order: int
    order of derivative to approximate. 0 corresponds to interpolation.
x_list: sequence
    Sequence of (unique) values for the independent variable.
y_list: sequence
    The function value at corresponding values for the independent
    variable in x_list.
x0: Number or Symbol
    At what value of the independent variable the derivative should be
    evaluated. Defaults to 0.

Returns
=======

sympy.core.add.Add or sympy.core.numbers.Number
    The finite difference expression approximating the requested
    derivative order at ``x0``.

Examples
========

>>> from sympy import apply_finite_diff
>>> cube = lambda arg: (1.0*arg)**3
>>> xlist = range(-3,3+1)
>>> apply_finite_diff(2, xlist, map(cube, xlist), 2) - 12 # doctest: +SKIP
-3.55271367880050e-15

we see that the example above only contain rounding errors.
apply_finite_diff can also be used on more abstract objects:

>>> from sympy import IndexedBase, Idx
>>> x, y = map(IndexedBase, 'xy')
>>> i = Idx('i')
>>> x_list, y_list = zip(*[(x[i+j], y[i+j]) for j in range(-1,2)])
>>> apply_finite_diff(1, x_list, y_list, x[i])
((x[i + 1] - x[i])/(-x[i - 1] + x[i]) - 1)*y[i]/(x[i + 1] - x[i]) -
(x[i + 1] - x[i])*y[i - 1]/((x[i + 1] - x[i - 1])*(-x[i - 1] + x[i])) +
(-x[i - 1] + x[i])*y[i + 1]/((x[i + 1] - x[i - 1])*(x[i + 1] - x[i]))

Notes
=====

Order = 0 corresponds to interpolation.
Only supply so many points you think makes sense
to around x0 when extracting the derivative (the function
need to be well behaved within that region). Also beware
of Runge's phenomenon.

See also
========

sympy.calculus.finite_diff.finite_diff_weights

References
==========

Fortran 90 implementation with Python interface for numerics: finitediff_

.. _finitediff: https://github.com/bjodah/finitediff

r
   z&x_list and y_list not equal in length.r   )r   r   r   r   )r   r   y_listr   r   r   
derivativer   s           r   apply_finite_diffr#      st    V 	FaA
6{c&k!ABBr2EJCK El1ob)&*44
 !    r
   Nc                 4   U R                   (       a  OUU R                  (       a  U $ U R                  " U R                   Vs/ s H  n[	        XAX#5      PM     sn40 U R
                  D6$ Uc+  SnU R                   H  nXVL a  M	  [	        XX&5      n UnM     U $ U R                  R                  U5      nUc  Un[        U5      (       d  [        USS5      (       a   X1R                  ;   a  UR                  X25      nUS-  S:X  a+  [        U* S-  US-  S-   5       Vs/ s H	  oX-  -   PM     nnO2[        U* US-   S5       Vs/ s H  oU[        U5      -  S-  -   PM     nnUS/n	[        U R                  5       H)  nXc:X  a  M
  XU R                  R                  U5      /-  n	M+     [        U5      US-   :  a  [        SU-  5      e[!        XqU V
s/ s H)  n
[#        U R$                  R                  X:05      /U	Q76 PM+     sn
U5      $ s  snf s  snf s  snf s  sn
f )a	  
Returns an approximation of a derivative of a function in
the form of a finite difference formula. The expression is a
weighted sum of the function at a number of discrete values of
(one of) the independent variable(s).

Parameters
==========

derivative: a Derivative instance

points: sequence or coefficient, optional
    If sequence: discrete values (length >= order+1) of the
    independent variable used for generating the finite
    difference weights.
    If it is a coefficient, it will be used as the step-size
    for generating an equidistant sequence of length order+1
    centered around ``x0``. default: 1 (step-size 1)

x0: number or Symbol, optional
    the value of the independent variable (``wrt``) at which the
    derivative is to be approximated. Default: same as ``wrt``.

wrt: Symbol, optional
    "with respect to" the variable for which the (partial)
    derivative is to be approximated for. If not provided it
    is required that the Derivative is ordinary. Default: ``None``.

Examples
========

>>> from sympy import symbols, Function, exp, sqrt, Symbol
>>> from sympy.calculus.finite_diff import _as_finite_diff
>>> x, h = symbols('x h')
>>> f = Function('f')
>>> _as_finite_diff(f(x).diff(x))
-f(x - 1/2) + f(x + 1/2)

The default step size and number of points are 1 and ``order + 1``
respectively. We can change the step size by passing a symbol
as a parameter:

>>> _as_finite_diff(f(x).diff(x), h)
-f(-h/2 + x)/h + f(h/2 + x)/h

We can also specify the discretized values to be used in a sequence:

>>> _as_finite_diff(f(x).diff(x), [x, x+h, x+2*h])
-3*f(x)/(2*h) + 2*f(h + x)/h - f(2*h + x)/(2*h)

The algorithm is not restricted to use equidistant spacing, nor
do we need to make the approximation around ``x0``, but we can get
an expression estimating the derivative at an offset:

>>> e, sq2 = exp(1), sqrt(2)
>>> xl = [x-h, x+h, x+e*h]
>>> _as_finite_diff(f(x).diff(x, 1), xl, x+h*sq2)
2*h*((h + sqrt(2)*h)/(2*h) - (-sqrt(2)*h + h)/(2*h))*f(E*h + x)/((-h + E*h)*(h + E*h)) +
(-(-sqrt(2)*h + h)/(2*h) - (-sqrt(2)*h + E*h)/(2*h))*f(-h + x)/(h + E*h) +
(-(h + sqrt(2)*h)/(2*h) + (-sqrt(2)*h + E*h)/(2*h))*f(h + x)/(-h + E*h)

Partial derivatives are also supported:

>>> y = Symbol('y')
>>> d2fdxdy=f(x,y).diff(x,y)
>>> _as_finite_diff(d2fdxdy, wrt=x)
-Derivative(f(x - 1/2, y), y) + Derivative(f(x + 1/2, y), y)

See also
========

sympy.calculus.finite_diff.apply_finite_diff
sympy.calculus.finite_diff.finite_diff_weights

Nis_FunctionF   r   r
   zToo few points for order %d)is_Derivativeis_Atomfromiterargs_as_finite_diffassumptions0	variablescountr   getattrsubsr   r   setr   r   r#   r   expr)r"   pointsr   wrtaroldvr   iothersxs              r   r,   r,     sC   X 			""! 79_R1!=$.$;$;= 	= {%%Ax(RCJC	 &
   &&s+E	zF6=%00SKK5G[[)F 19> vqy%(Q,797 )*68m7  9F
 vuqy!464 ./6!A$;q=(4  61XF%%&8j**00344 ' 6{U1W6>??U- 	:??''1;F;- K!096-s    HH?H0HF)r4   r   r5   evaluatec                (  ^^^ [        S [        [        U 5      5       5       5      (       a  SnU R                  " USU06nU(       a$  [	        SSSS9  UR                  S UUU4S	 j5      $ UR                  TTTS
9nUR                  S U4S j5      $ )a  Differentiate expr and replace Derivatives with finite differences.

Parameters
==========

expr : expression
\*symbols : differentiate with respect to symbols
points: sequence, coefficient or undefined function, optional
    see ``Derivative.as_finite_difference``
x0: number or Symbol, optional
    see ``Derivative.as_finite_difference``
wrt: Symbol, optional
    see ``Derivative.as_finite_difference``

Examples
========

>>> from sympy import sin, Function, differentiate_finite
>>> from sympy.abc import x, y, h
>>> f, g = Function('f'), Function('g')
>>> differentiate_finite(f(x)*g(x), x, points=[x-h, x+h])
-f(-h + x)*g(-h + x)/(2*h) + f(h + x)*g(h + x)/(2*h)

``differentiate_finite`` works on any expression, including the expressions
with embedded derivatives:

>>> differentiate_finite(f(x) + sin(x), x, 2)
-2*f(x) + f(x - 1) + f(x + 1) - 2*sin(x) + sin(x - 1) + sin(x + 1)
>>> differentiate_finite(f(x, y), x, y)
f(x - 1/2, y - 1/2) - f(x - 1/2, y + 1/2) - f(x + 1/2, y - 1/2) + f(x + 1/2, y + 1/2)
>>> differentiate_finite(f(x)*g(x).diff(x), x)
(-g(x) + g(x + 1))*f(x + 1/2) - (g(x) - g(x - 1))*f(x - 1/2)

To make finite difference with non-constant discretization step use
undefined functions:

>>> dx = Function('dx')
>>> differentiate_finite(f(x)*g(x).diff(x), points=dx(x))
-(-g(x - dx(x)/2 - dx(x - dx(x)/2)/2)/dx(x - dx(x)/2) +
g(x - dx(x)/2 + dx(x - dx(x)/2)/2)/dx(x - dx(x)/2))*f(x - dx(x)/2)/dx(x) +
(-g(x + dx(x)/2 - dx(x + dx(x)/2)/2)/dx(x + dx(x)/2) +
g(x + dx(x)/2 + dx(x + dx(x)/2)/2)/dx(x + dx(x)/2))*f(x + dx(x)/2)/dx(x)

c              3   8   #    U  H  oR                   v   M     g 7fNr(   ).0terms     r   	<genexpr>'differentiate_finite.<locals>.<genexpr>  s     
I*H$*Hs   Fr<   a  
        The evaluate flag to differentiate_finite() is deprecated.

        evaluate=True expands the intermediate derivatives before computing
        differences, but this usually not what you want, as it does not
        satisfy the product rule.
        z1.5z(deprecated-differentiate_finite-evaluate)deprecated_since_versionactive_deprecations_targetc                     U R                   $ r?   r@   args    r   <lambda>&differentiate_finite.<locals>.<lambda>  s
    ))r$   c                 &   > U R                  TTTS9$ )Nr4   r   r5   )as_finite_difference)rI   r4   r5   r   s    r   rJ   rK     s    00230Or$   rM   c                 "    [        U [        5      $ r?   )
isinstancer   rH   s    r   rJ   rK     s    
3-r$   c                 n   > U R                   R                  TU R                  S   U R                  S   S9$ )Nr   rM   )r3   rN   pointr.   )rI   r4   s    r   rJ   rK     s2    55!ciila8H 6 Jr$   )anylistr   diffr   replacerN   )r3   r4   r   r5   r<   symbolsDexprDFexprs    ```    r   differentiate_finiterZ     s    \ 
I$/A$/G*H
IIIIIw22E! # &+(R		
 }})OQ 	Q ++6bc+J~~-JK 	Kr$   )r
   NN)__doc__sympy.core.functionr   sympy.core.singletonr   r   sympy.core.traversalr   sympy.utilities.exceptionsr   sympy.utilities.iterablesr   r   r   Zeror#   r,   rZ    r$   r   <module>rc      sW   & + " $ 3 @ . +,%% dN 12 Tnyz !"duEKr$   