
    (phF                         S SK JrJr  S SKJr   " S S5      r " S S\5      r " S S\5      r " S	 S
\5      rS r	SS jr
S rg)    )array_namespacexp_size)cached_propertyc                   ,    \ rS rSrSrSS jrSS jrSrg)Rule   a  
Base class for numerical integration algorithms (cubatures).

Finds an estimate for the integral of ``f`` over the region described by two arrays
``a`` and ``b`` via `estimate`, and find an estimate for the error of this
approximation via `estimate_error`.

If a subclass does not implement its own `estimate_error`, then it will use a
default error estimate based on the difference between the estimate over the whole
region and the sum of estimates over that region divided into ``2^ndim`` subregions.

See Also
--------
FixedRule

Examples
--------
In the following, a custom rule is created which uses 3D Genz-Malik cubature for
the estimate of the integral, and the difference between this estimate and a less
accurate estimate using 5-node Gauss-Legendre quadrature as an estimate for the
error.

>>> import numpy as np
>>> from scipy.integrate import cubature
>>> from scipy.integrate._rules import (
...     Rule, ProductNestedFixed, GenzMalikCubature, GaussLegendreQuadrature
... )
>>> def f(x, r, alphas):
...     # f(x) = cos(2*pi*r + alpha @ x)
...     # Need to allow r and alphas to be arbitrary shape
...     npoints, ndim = x.shape[0], x.shape[-1]
...     alphas_reshaped = alphas[np.newaxis, :]
...     x_reshaped = x.reshape(npoints, *([1]*(len(alphas.shape) - 1)), ndim)
...     return np.cos(2*np.pi*r + np.sum(alphas_reshaped * x_reshaped, axis=-1))
>>> genz = GenzMalikCubature(ndim=3)
>>> gauss = GaussKronrodQuadrature(npoints=21)
>>> # Gauss-Kronrod is 1D, so we find the 3D product rule:
>>> gauss_3d = ProductNestedFixed([gauss, gauss, gauss])
>>> class CustomRule(Rule):
...     def estimate(self, f, a, b, args=()):
...         return genz.estimate(f, a, b, args)
...     def estimate_error(self, f, a, b, args=()):
...         return np.abs(
...             genz.estimate(f, a, b, args)
...             - gauss_3d.estimate(f, a, b, args)
...         )
>>> rng = np.random.default_rng()
>>> res = cubature(
...     f=f,
...     a=np.array([0, 0, 0]),
...     b=np.array([1, 1, 1]),
...     rule=CustomRule(),
...     args=(rng.random((2,)), rng.random((3, 2, 3)))
... )
>>> res.estimate
 array([[-0.95179502,  0.12444608],
        [-0.96247411,  0.60866385],
        [-0.97360014,  0.25515587]])
 c                     [         e)a  
Calculate estimate of integral of `f` in rectangular region described by
corners `a` and ``b``.

Parameters
----------
f : callable
    Function to integrate. `f` must have the signature::
        f(x : ndarray, \*args) -> ndarray

    `f` should accept arrays ``x`` of shape::
        (npoints, ndim)

    and output arrays of shape::
        (npoints, output_dim_1, ..., output_dim_n)

    In this case, `estimate` will return arrays of shape::
        (output_dim_1, ..., output_dim_n)
a, b : ndarray
    Lower and upper limits of integration as rank-1 arrays specifying the left
    and right endpoints of the intervals being integrated over. Infinite limits
    are currently not supported.
args : tuple, optional
    Additional positional args passed to ``f``, if any.

Returns
-------
est : ndarray
    Result of estimation. If `f` returns arrays of shape ``(npoints,
    output_dim_1, ..., output_dim_n)``, then `est` will be of shape
    ``(output_dim_1, ..., output_dim_n)``.
NotImplementedError)selffabargss        O/var/www/html/venv/lib/python3.13/site-packages/scipy/integrate/_rules/_base.pyestimateRule.estimateC   s    B "!    c                     U R                  XX45      nSn[        X#5       H  u  pxX`R                  XX5      -  nM     U R                  R                  XV-
  5      $ )a  
Estimate the error of the approximation for the integral of `f` in rectangular
region described by corners `a` and `b`.

If a subclass does not override this method, then a default error estimator is
used. This estimates the error as ``|est - refined_est|`` where ``est`` is
``estimate(f, a, b)`` and ``refined_est`` is the sum of
``estimate(f, a_k, b_k)`` where ``a_k, b_k`` are the coordinates of each
subregion of the region described by ``a`` and ``b``. In the 1D case, this
is equivalent to comparing the integral over an entire interval ``[a, b]`` to
the sum of the integrals over the left and right subintervals, ``[a, (a+b)/2]``
and ``[(a+b)/2, b]``.

Parameters
----------
f : callable
    Function to estimate error for. `f` must have the signature::
        f(x : ndarray, \*args) -> ndarray

    `f` should accept arrays `x` of shape::
        (npoints, ndim)

    and output arrays of shape::
        (npoints, output_dim_1, ..., output_dim_n)

    In this case, `estimate` will return arrays of shape::
        (output_dim_1, ..., output_dim_n)
a, b : ndarray
    Lower and upper limits of integration as rank-1 arrays specifying the left
    and right endpoints of the intervals being integrated over. Infinite limits
    are currently not supported.
args : tuple, optional
    Additional positional args passed to `f`, if any.

Returns
-------
err_est : ndarray
    Result of error estimation. If `f` returns arrays of shape
    ``(npoints, output_dim_1, ..., output_dim_n)``, then `est` will be
    of shape ``(output_dim_1, ..., output_dim_n)``.
r   )r   _split_subregionxpabs)	r   r   r   r   r   estrefined_esta_kb_ks	            r   estimate_errorRule.estimate_errorf   sV    V mmA!*(.HC==;;K / ww{{3,--r   Nr	   )__name__
__module____qualname____firstlineno____doc__r   r   __static_attributes__r	   r   r   r   r      s    :x!"F1.r   r   c                   8    \ rS rSrSrS r\S 5       rSS jrSr	g)		FixedRule   a^  
A rule implemented as the weighted sum of function evaluations at fixed nodes.

Attributes
----------
nodes_and_weights : (ndarray, ndarray)
    A tuple ``(nodes, weights)`` of nodes at which to evaluate ``f`` and the
    corresponding weights. ``nodes`` should be of shape ``(num_nodes,)`` for 1D
    cubature rules (quadratures) and more generally for N-D cubature rules, it
    should be of shape ``(num_nodes, ndim)``. ``weights`` should be of shape
    ``(num_nodes,)``. The nodes and weights should be for integrals over
    :math:`[-1, 1]^n`.

See Also
--------
GaussLegendreQuadrature, GaussKronrodQuadrature, GenzMalikCubature

Examples
--------

Implementing Simpson's 1/3 rule:

>>> import numpy as np
>>> from scipy.integrate._rules import FixedRule
>>> class SimpsonsQuad(FixedRule):
...     @property
...     def nodes_and_weights(self):
...         nodes = np.array([-1, 0, 1])
...         weights = np.array([1/3, 4/3, 1/3])
...         return (nodes, weights)
>>> rule = SimpsonsQuad()
>>> rule.estimate(
...     f=lambda x: x**2,
...     a=np.array([0]),
...     b=np.array([1]),
... )
 [0.3333333]
c                     S U l         g Nr   r   s    r   __init__FixedRule.__init__   s	    r   c                     [         er+   r   r-   s    r   nodes_and_weightsFixedRule.nodes_and_weights   s    !!r   c           	          U R                   u  pVU R                  c  [        U5      U l        [        XX5XdU R                  5      $ )am  
Calculate estimate of integral of `f` in rectangular region described by
corners `a` and `b` as ``sum(weights * f(nodes))``.

Nodes and weights will automatically be adjusted from calculating integrals over
:math:`[-1, 1]^n` to :math:`[a, b]^n`.

Parameters
----------
f : callable
    Function to integrate. `f` must have the signature::
        f(x : ndarray, \*args) -> ndarray

    `f` should accept arrays `x` of shape::
        (npoints, ndim)

    and output arrays of shape::
        (npoints, output_dim_1, ..., output_dim_n)

    In this case, `estimate` will return arrays of shape::
        (output_dim_1, ..., output_dim_n)
a, b : ndarray
    Lower and upper limits of integration as rank-1 arrays specifying the left
    and right endpoints of the intervals being integrated over. Infinite limits
    are currently not supported.
args : tuple, optional
    Additional positional args passed to `f`, if any.

Returns
-------
est : ndarray
    Result of estimation. If `f` returns arrays of shape ``(npoints,
    output_dim_1, ..., output_dim_n)``, then `est` will be of shape
    ``(output_dim_1, ..., output_dim_n)``.
)r1   r   r   _apply_fixed_rule)r   r   r   r   r   nodesweightss          r   r   FixedRule.estimate   s<    H //77?%e,DG qHHr   r,   Nr    )
r!   r"   r#   r$   r%   r.   propertyr1   r   r&   r	   r   r   r(   r(      s'    %N " ")Ir   r(   c                   H    \ rS rSrSrS r\S 5       r\S 5       rS	S jr	Sr
g)
NestedFixedRule   ab  
A cubature rule with error estimate given by the difference between two underlying
fixed rules.

If constructed as ``NestedFixedRule(higher, lower)``, this will use::

    estimate(f, a, b) := higher.estimate(f, a, b)
    estimate_error(f, a, b) := \|higher.estimate(f, a, b) - lower.estimate(f, a, b)|

(where the absolute value is taken elementwise).

Attributes
----------
higher : Rule
    Higher accuracy rule.

lower : Rule
    Lower accuracy rule.

See Also
--------
GaussKronrodQuadrature

Examples
--------

>>> from scipy.integrate import cubature
>>> from scipy.integrate._rules import (
...     GaussLegendreQuadrature, NestedFixedRule, ProductNestedFixed
... )
>>> higher = GaussLegendreQuadrature(10)
>>> lower = GaussLegendreQuadrature(5)
>>> rule = NestedFixedRule(
...     higher,
...     lower
... )
>>> rule_2d = ProductNestedFixed([rule, rule])
c                 *    Xl         X l        S U l        g r+   higherlowerr   )r   r>   r?   s      r   r.   NestedFixedRule.__init__  s    
r   c                 T    U R                   b  U R                   R                  $ [        er+   )r>   r1   r   r-   s    r   r1   !NestedFixedRule.nodes_and_weights"  s"    ;;";;000%%r   c                 T    U R                   b  U R                   R                  $ [        er+   )r?   r1   r   r-   s    r   lower_nodes_and_weights'NestedFixedRule.lower_nodes_and_weights)  s"    ::!::///%%r   c                 D   U R                   u  pVU R                  u  pxU R                  c  [        U5      U l        U R                  R	                  XW/SS9n	U R                  R	                  Xh* /SS9n
U R                  R                  [        XX9XU R                  5      5      $ )a  
Estimate the error of the approximation for the integral of `f` in rectangular
region described by corners `a` and `b`.

Parameters
----------
f : callable
    Function to estimate error for. `f` must have the signature::
        f(x : ndarray, \*args) -> ndarray

    `f` should accept arrays `x` of shape::
        (npoints, ndim)

    and output arrays of shape::
        (npoints, output_dim_1, ..., output_dim_n)

    In this case, `estimate` will return arrays of shape::
        (output_dim_1, ..., output_dim_n)
a, b : ndarray
    Lower and upper limits of integration as rank-1 arrays specifying the left
    and right endpoints of the intervals being integrated over. Infinite limits
    are currently not supported.
args : tuple, optional
    Additional positional args passed to `f`, if any.

Returns
-------
err_est : ndarray
    Result of error estimation. If `f` returns arrays of shape
    ``(npoints, output_dim_1, ..., output_dim_n)``, then `est` will be
    of shape ``(output_dim_1, ..., output_dim_n)``.
r   axis)r1   rD   r   r   concatr   r4   )r   r   r   r   r   r5   r6   lower_nodeslower_weightserror_nodeserror_weightss              r   r   NestedFixedRule.estimate_error0  s    D //%)%A%A"77?%e,DGggnne%9nB'@qIww{{aAMQ
 	
r   r=   Nr    )r!   r"   r#   r$   r%   r.   r8   r1   rD   r   r&   r	   r   r   r:   r:      s:    %N
 & & & &-
r   r:   c                   >    \ rS rSrSrS r\S 5       r\S 5       rSr	g)ProductNestedFixedi`  a  
Find the n-dimensional cubature rule constructed from the Cartesian product of 1-D
`NestedFixedRule` quadrature rules.

Given a list of N 1-dimensional quadrature rules which support error estimation
using NestedFixedRule, this will find the N-dimensional cubature rule obtained by
taking the Cartesian product of their nodes, and estimating the error by taking the
difference with a lower-accuracy N-dimensional cubature rule obtained using the
``.lower_nodes_and_weights`` rule in each of the base 1-dimensional rules.

Parameters
----------
base_rules : list of NestedFixedRule
    List of base 1-dimensional `NestedFixedRule` quadrature rules.

Attributes
----------
base_rules : list of NestedFixedRule
    List of base 1-dimensional `NestedFixedRule` qudarature rules.

Examples
--------

Evaluate a 2D integral by taking the product of two 1D rules:

>>> import numpy as np
>>> from scipy.integrate import cubature
>>> from scipy.integrate._rules import (
...  ProductNestedFixed, GaussKronrodQuadrature
... )
>>> def f(x):
...     # f(x) = cos(x_1) + cos(x_2)
...     return np.sum(np.cos(x), axis=-1)
>>> rule = ProductNestedFixed(
...     [GaussKronrodQuadrature(15), GaussKronrodQuadrature(15)]
... ) # Use 15-point Gauss-Kronrod, which implements NestedFixedRule
>>> a, b = np.array([0, 0]), np.array([1, 1])
>>> rule.estimate(f, a, b) # True value 2*sin(1), approximately 1.6829
 np.float64(1.682941969615793)
>>> rule.estimate_error(f, a, b)
 np.float64(2.220446049250313e-16)
c                 p    U H#  n[        U[        5      (       a  M  [        S5      e   Xl        S U l        g )Nz<base rules for product need to be instance ofNestedFixedRule)
isinstancer:   
ValueError
base_rulesr   )r   rT   rules      r   r.   ProductNestedFixed.__init__  s9    DdO44  "3 4 4 
 %r   c           	      N   [        U R                   Vs/ s H  oR                  S   PM     sn5      nU R                  c  [	        U5      U l        U R                  R                  [        U R                   Vs/ s H  oR                  S   PM     sn5      SS9nX#4$ s  snf s  snf Nr      rG   )_cartesian_productrT   r1   r   r   prod)r   rU   r5   r6   s       r   r1   $ProductNestedFixed.nodes_and_weights  s    "37??C?4##A&?C
 77?%e,DG'',,7;Gt''*G 	  
 ~ D H   B8B"c           	      N   [        U R                   Vs/ s H  oR                  S   PM     sn5      nU R                  c  [	        U5      U l        U R                  R                  [        U R                   Vs/ s H  oR                  S   PM     sn5      SS9nX#4$ s  snf s  snf rX   )r[   rT   rD   r   r   r\   )r   cubaturer5   r6   s       r   rD   *ProductNestedFixed.lower_nodes_and_weights  s    "AEQX--a0Q
 77?%e,DG'',,EI__U_11!4_U 	  
 ~ R Vr^   )rT   r   N)
r!   r"   r#   r$   r%   r.   r   r1   rD   r&   r	   r   r   rP   rP   `  s5    )V  "  r   rP   c                     [        U 6 nUR                  " U SS06nUR                  UR                  USS9S[	        U 5      45      nU$ )NindexingijrZ   rG   )r   meshgridreshapestacklen)arraysr   	arrays_ixresults       r   r[   r[     sJ    	&	!BV3d3IZZ4r3v;6GHFMr   Nc              #     #    [        X5      nUc  X-   S-  n[        U R                  S   5       Vs/ s H  oBR                  X   X4   /5      PM     nn[        UR                  S   5       Vs/ s H  oBR                  X4   X   /5      PM     nn[	        U5      n[	        U5      n[        UR                  S   5       H  nXtS4   XS4   4v   M     gs  snf s  snf 7f)z
Given the coordinates of a region like a=[0, 0] and b=[1, 1], yield the coordinates
of all subregions, which in this case would be::

    ([0, 0], [1/2, 1/2]),
    ([0, 1/2], [1/2, 1]),
    ([1/2, 0], [1, 1/2]),
    ([1/2, 1/2], [1, 1])
N   r   .)r   rangeshapeasarrayr[   )	r   r   r   split_atileftrighta_subb_subs	            r   r   r     s      
	BEQ;5:1771:5FG5FJJhk*+5FDG6;AGGAJ6GH6GZZad+,6GEHt$Eu%E5;;q>"sFmUc6]** # HHs   0C CC. CACc                    UR                   nUR                  X75      nUR                  XG5      nUR                  S:X  a	  US S 2S 4   nUR                  S   n[	        U5      n	[	        U5      n
X:w  d  X:w  a  [        SU SU	 SU
 35      eX!-
  nUS-   US-  -  U-   nUR                  XS9SU-  -  nXM-  nU " U/UQ76 nUR                  US/S/UR                  S-
  -  Q75      nUR                  UU-  S	US
9nU$ )NrY   rZ   z@rule and function are of incompatible dimension, nodes havendim z,, while limit of integration has ndima_ndim=z	, b_ndim=g      ?)dtyperm   r   )rH   rx   )	rx   astypendimro   r   rS   r\   rf   sum)r   r   r   
orig_nodesorig_weightsr   r   result_dtype	rule_ndima_ndimb_ndimlengthsr5   weight_scale_factorr6   f_nodesweights_reshapedr   s                     r   r4   r4     s9   77L:4J99\8L !4(
  $IQZFQZFi1 !!* ,##)()F8= > 	> eG !^#.2E ''''>IM0GooGzz'B+L1#9I2J+LM
 &&!G+!<&
HCJr   r+   )scipy._lib._array_apir   r   	functoolsr   r   r(   r:   rP   r[   r   r4   r	   r   r   <module>r      sV    : %Q. Q.hXI XIvh
i h
VW Wt+2*r   