L i pdZddlZddlZddlmZddlmZddlm Z m Z m Z m Z m Z mZddlmZmZdd lmZdd lmZdd lmZd Zej4d ZdZdZddZddddej> ej>fddddddf dZ dZ!dZ"dZ#GddZ$ej> ej>fddfdZ%y)z'Routines for numerical differentiation.N)norm)LinearOperator)issparse isspmatrixfind csc_array csr_array csr_matrix) group_dense group_sparse)array_namespace) MapWrapper)array_api_extrac|dk(rtj|t}nA|dk(r1tj|}tj|t}n t dtj |tj k(|tjk(zr||fS||z}|j}||z } ||z } |dk(ry||z} | |k| |kDz} tj|tj| | k} || | zxxdzcc<| | k\| z}| ||z ||<| | k| z}| | |z ||<||fS|dk(r| |k\| |k\z}| | k\|z}tj||d| |z|z ||<d||<| | k|z}tj||d| |z|z  ||<d||<tj| | |z }|tj||kz}||||<d||<||fS) aAdjust final difference scheme to the presence of bounds. Parameters ---------- x0 : ndarray, shape (n,) Point at which we wish to estimate derivative. h : ndarray, shape (n,) Desired absolute finite difference steps. num_steps : int Number of `h` steps in one direction required to implement finite difference scheme. For example, 2 means that we need to evaluate f(x0 + 2 * h) or f(x0 - 2 * h) scheme : {'1-sided', '2-sided'} Whether steps in one or both directions are required. In other words '1-sided' applies to forward and backward schemes, '2-sided' applies to center schemes. lb : ndarray, shape (n,) Lower bounds on independent variables. ub : ndarray, shape (n,) Upper bounds on independent variables. Returns ------- h_adjusted : ndarray, shape (n,) Adjusted absolute step sizes. Step size decreases only if a sign flip or switching to one-sided scheme doesn't allow to take a full step. use_one_sided : ndarray of bool, shape (n,) Whether to switch to one-sided scheme. Informative only for ``scheme='2-sided'``. 1-sideddtype2-sidedz(`scheme` must be '1-sided' or '2-sided'.?TF) np ones_likeboolabs zeros_like ValueErrorallinfcopymaximumminimum)x0h num_stepsschemelbub use_one_sidedh_total h_adjusted lower_dist upper_distxviolatedfittingforwardbackwardcentralmin_distadjusted_centrals ]/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/scipy/optimize/_numdiff.py_adjust_scheme_to_boundsr8sX> Qd3 9  FF1I at4 CDD vvrbffW}rvv./-)mGJbJbJ  LFq2v&&&/RZZ J%GG8g%&",&+x7(1I= 7+x7 *8 44y@ 8& } $$% 9 (Z7-BC+x7 jj gJj11I=? 7!% g+x7 " hKz(33i?!A A 8"& h::j*5 A$Hz(:h(FG'/0@'A #$*/ &' } $$c4tjtjj}d}tj|tj r@tj|j}tj |j}d}tj|tj rEtj |j}|r$|krtj|j}|dvr|dzS|dvr|dzStd)a Calculates relative EPS step to use for a given data type and numdiff step method. Progressively smaller steps are used for larger floating point types. Parameters ---------- f0_dtype: np.dtype dtype of function evaluation x0_dtype: np.dtype dtype of parameter vector method: {'2-point', '3-point', 'cs'} Returns ------- EPS: float relative step size. May be np.float16, np.float32, np.float64 Notes ----- The default relative step will be np.float64. However, if x0 or f0 are smaller floating point types (np.float16, np.float32), then the smallest floating point type is chosen. FT)2-pointcsr)3-pointgUUUUUU?zBUnknown step method, should be one of {'2-point', '3-point', 'cs'}) rfinfofloat64eps issubdtypeinexactritemsize RuntimeError)x0_dtypef0_dtypemethodEPSx0_is_fp x0_itemsize f0_itemsizes r7_eps_for_methodrL]s< ((2::  " "CH }}Xrzz*hhx $$hhx(11  }}Xrzz*hhx(11  k1((8$((C ""Cx ; Sz:; ;r9c |dk\jtdzdz }t|j|j|}|1||zt j dt j |z}|S||zt j |z}||z|z }t j|dk(||zt j dt j |z|}|S)az Computes an absolute step from a relative step for finite difference calculation. Parameters ---------- rel_step: None or array-like Relative step for the finite difference calculation x0 : np.ndarray Parameter vector f0 : np.ndarray or scalar method : {'2-point', '3-point', 'cs'} Returns ------- h : float The absolute step size Notes ----- `h` will always be np.float64. However, if `x0` or `f0` are smaller floating point dtypes (e.g. np.float32), then the absolute step size will be calculated from the smallest floating point size. rrr ?)astypefloatrLrrr"rwhere)rel_stepr$f0rGsign_x0rstepabs_stepdxs r7_compute_absolute_steprXs6Qwu%)A-G BHHbhh 7E7?RZZRVVBZ%@@ Og%r 2H}"88B!G!GObjjbffRj.II$& Or9cd|D\}}|jdk(r tj||j}|jdk(r tj||j}||fS)aa Prepares new-style bounds from a two-tuple specifying the lower and upper limits for values in x0. If a value is not bound then the lower/upper bound will be expected to be -np.inf/np.inf. Examples -------- >>> _prepare_bounds([(0, 1, 2), (1, 2, np.inf)], [0.5, 1.5, 2.5]) (array([0., 1., 2.]), array([ 1., 2., inf])) c3RK|]}tj|t!yw)rN)rasarrayrP).0bs r7 z"_prepare_bounds..s 9Qbjj%(( 9s%'r)ndimrresizeshape)boundsr$r(r)s r7_prepare_boundsrcsY:& 9FB ww!| YYr288 $ ww!| YYr288 $ r6Mr9ct|r t|}n7tj|}|dk7j tj }|j dk7r td|j\}}|tj|r1tjj|}|j|}n0tj|}|j|fk7r td|dd|f}t|r#t|||j|j }n t#|||}|j%||<|S)aGroup columns of a 2-D matrix for sparse finite differencing [1]_. Two columns are in the same group if in each row at least one of them has zero. A greedy sequential algorithm is used to construct groups. Parameters ---------- A : array_like or sparse array, shape (m, n) Matrix of which to group columns. order : int, iterable of int with shape (n,) or None Permutation array which defines the order of columns enumeration. If int or None, a random permutation is used with `order` used as a random seed. Default is 0, that is use a random permutation but guarantee repeatability. Returns ------- groups : ndarray of int, shape (n,) Contains values from 0 to n_groups-1, where n_groups is the number of found groups. Each value ``groups[i]`` is an index of a group to which ith column assigned. The procedure was helpful only if n_groups is significantly less than n. References ---------- .. [1] A. Curtis, M. J. D. Powell, and J. Reid, "On the estimation of sparse Jacobian matrices", Journal of the Institute of Mathematics and its Applications, 13 (1974), pp. 117-120. rrz`A` must be 2-dimensional.Nz`order` has incorrect shape.)rr r atleast_2drOint32r_rraisscalarrandom RandomState permutationr[rindicesindptrr r!)Aordermnrnggroupss r7 group_columnsrss<{ aL MM!  !VOOBHH %vv{566 77DAq } E*ii##E*" 5! ;;1$ ;< < !U( A{aAIIqxx8Q1%KKMF5M Mr9r=Fc t|dvrtd|dddi} t|}tj|j |d|}|j }|j |jdr |j}|j||}|jdkDr td t||\}}|j|jk7s|j|jk7r td |r[tjtj|r(tjtj|s td | i} t||| | }d x}}| ||}d}n/tj |}|jdkDr td tj"||k||kDzr td|r7|!t%|j|j|}t'|||||\}}n|t)||||}n|d k\jt*dzdz }|}||z|z }tj,|d k(t%|j|j||ztj.dtj0|z|}|dk(rt3||dd||\}}n |dk(rt3||dd||\}}n|dk(rd}| xst4} t7| 5}|t9||||||\}}nt;|st=|dk(r|\}}n |}t?|}t;|r|jA}ntjB|}tj |}tE|||||||| \}}ddd| r||z }|| d<| fSS#1swYxYw)uZ*Compute finite difference approximation of the derivatives of a vector-valued function. If a function maps from R^n to R^m, its derivatives form m-by-n matrix called the Jacobian, where an element (i, j) is a partial derivative of f[i] with respect to x[j]. Parameters ---------- fun : callable Function of which to estimate the derivatives. The argument x passed to this function is ndarray of shape (n,) (never a scalar even if n=1). It must return 1-D array_like of shape (m,) or a scalar. x0 : array_like of shape (n,) or float Point at which to estimate the derivatives. Float will be converted to a 1-D array. method : {'3-point', '2-point', 'cs'}, optional Finite difference method to use: - '2-point' - use the first order accuracy forward or backward difference. - '3-point' - use central difference in interior points and the second order accuracy forward or backward difference near the boundary. - 'cs' - use a complex-step finite difference scheme. This assumes that the user function is real-valued and can be analytically continued to the complex plane. Otherwise, produces bogus results. rel_step : None or array_like, optional Relative step size to use. If None (default) the absolute step size is computed as ``h = rel_step * sign(x0) * max(1, abs(x0))``, with `rel_step` being selected automatically, see Notes. Otherwise ``h = rel_step * sign(x0) * abs(x0)``. For ``method='3-point'`` the sign of `h` is ignored. The calculated step size is possibly adjusted to fit into the bounds. abs_step : array_like, optional Absolute step size to use, possibly adjusted to fit into the bounds. For ``method='3-point'`` the sign of `abs_step` is ignored. By default relative steps are used, only if ``abs_step is not None`` are absolute steps used. f0 : None or array_like, optional If not None it is assumed to be equal to ``fun(x0)``, in this case the ``fun(x0)`` is not called. Default is None. bounds : tuple of array_like, optional Lower and upper bounds on independent variables. Defaults to no bounds. Each bound must match the size of `x0` or be a scalar, in the latter case the bound will be the same for all variables. Use it to limit the range of function evaluation. Bounds checking is not implemented when `as_linear_operator` is True. sparsity : {None, array_like, sparse array, 2-tuple}, optional Defines a sparsity structure of the Jacobian matrix. If the Jacobian matrix is known to have only few non-zero elements in each row, then it's possible to estimate its several columns by a single function evaluation [3]_. To perform such economic computations two ingredients are required: * structure : array_like or sparse array of shape (m, n). A zero element means that a corresponding element of the Jacobian identically equals to zero. * groups : array_like of shape (n,). A column grouping for a given sparsity structure, use `group_columns` to obtain it. A single array or a sparse array is interpreted as a sparsity structure, and groups are computed inside the function. A tuple is interpreted as (structure, groups). If None (default), a standard dense differencing will be used. Note, that sparse differencing makes sense only for large Jacobian matrices where each row contains few non-zero elements. as_linear_operator : bool, optional When True the function returns an `scipy.sparse.linalg.LinearOperator`. Otherwise it returns a dense array or a sparse array depending on `sparsity`. The linear operator provides an efficient way of computing ``J.dot(p)`` for any vector ``p`` of shape (n,), but does not allow direct access to individual elements of the matrix. By default `as_linear_operator` is False. args, kwargs : tuple and dict, optional Additional arguments passed to `fun`. Both empty by default. The calling signature is ``fun(x, *args, **kwargs)``. full_output : bool, optional If True then the function also returns a dictionary with extra information about the calculation. workers : int or map-like callable, optional Supply a map-like callable, such as `multiprocessing.Pool.map` for evaluating the population in parallel. This evaluation is carried out as ``workers(fun, iterable)``. Alternatively, if `workers` is an int the task is subdivided into `workers` sections and the fun evaluated in parallel (uses `multiprocessing.Pool `). Supply -1 to use all available CPU cores. It is recommended that a map-like be used instead of int, as repeated calls to `approx_derivative` will incur large overhead from setting up new processes. Returns ------- J : {ndarray, sparse array, LinearOperator} Finite difference approximation of the Jacobian matrix. If `as_linear_operator` is True returns a LinearOperator with shape (m, n). Otherwise it returns a dense array or sparse array depending on how `sparsity` is defined. If `sparsity` is None then a ndarray with shape (m, n) is returned. If `sparsity` is not None returns a csr_array or csr_matrix with shape (m, n) following the array/matrix type of the incoming structure. For sparse arrays and linear operators it is always returned as a 2-D structure. For ndarrays, if m=1 it is returned as a 1-D gradient array with shape (n,). info_dict : dict Dictionary containing extra information about the calculation. The keys include: - `nfev`, number of function evaluations. If `as_linear_operator` is True then `fun` is expected to track the number of evaluations itself. This is because multiple calls may be made to the linear operator which are not trackable here. See Also -------- check_derivative : Check correctness of a function computing derivatives. Notes ----- If `rel_step` is not provided, it assigned as ``EPS**(1/s)``, where EPS is determined from the smallest floating point dtype of `x0` or `fun(x0)`, ``np.finfo(x0.dtype).eps``, s=2 for '2-point' method and s=3 for '3-point' method. Such relative step approximately minimizes a sum of truncation and round-off errors, see [1]_. Relative steps are used by default. However, absolute steps are used when ``abs_step is not None``. If any of the absolute or relative steps produces an indistinguishable difference from the original `x0`, ``(x0 + dx) - x0 == 0``, then a automatic step size is substituted for that particular entry. A finite difference scheme for '3-point' method is selected automatically. The well-known central difference scheme is used for points sufficiently far from the boundary, and 3-point forward or backward scheme is used for points near the boundary. Both schemes have the second-order accuracy in terms of Taylor expansion. Refer to [2]_ for the formulas of 3-point forward and backward difference schemes. For dense differencing when m=1 Jacobian is returned with a shape (n,), on the other hand when n=1 Jacobian is returned with a shape (m, 1). Our motivation is the following: a) It handles a case of gradient computation (m=1) in a conventional way. b) It clearly separates these two different cases. b) In all cases np.atleast_2d can be called to get 2-D Jacobian with correct dimensions. References ---------- .. [1] W. H. Press et. al. "Numerical Recipes. The Art of Scientific Computing. 3rd edition", sec. 5.7. .. [2] A. Curtis, M. J. D. Powell, and J. Reid, "On the estimation of sparse Jacobian matrices", Journal of the Institute of Mathematics and its Applications, 13 (1974), pp. 117-120. .. [3] B. Fornberg, "Generation of Finite Difference Formulas on Arbitrarily Spaced Grids", Mathematics of Computation 51, 1988. Examples -------- >>> import numpy as np >>> from scipy.optimize._numdiff import approx_derivative >>> >>> def f(x, c1, c2): ... return np.array([x[0] * np.sin(c1 * x[1]), ... x[0] * np.cos(c2 * x[1])]) ... >>> x0 = np.array([1.0, 0.5 * np.pi]) >>> approx_derivative(f, x0, args=(1, 2)) array([[ 1., 0.], [-1., 0.]]) Bounds can be used to limit the region of function evaluation. In the example below we compute left and right derivative at point 1.0. >>> def g(x): ... return x**2 if x >= 1 else x ... >>> x0 = 1.0 >>> approx_derivative(g, x0, bounds=(-np.inf, 1.0)) array([ 1.]) >>> approx_derivative(g, x0, bounds=(1.0, np.inf)) array([ 2.]) We can also parallelize the derivative calculation using the workers keyword. >>> from multiprocessing import Pool >>> import time >>> def fun2(x): # import from an external file for use with multiprocessing ... time.sleep(0.002) ... return rosen(x) >>> rng = np.random.default_rng() >>> x0 = rng.uniform(high=10, size=(2000,)) >>> f0 = rosen(x0) >>> %timeit approx_derivative(fun2, x0, f0=f0) # may vary 10.5 s ± 5.91 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) >>> elapsed = [] >>> with Pool() as workers: ... for i in range(10): ... t = time.perf_counter() ... approx_derivative(fun2, x0, workers=workers.map, f0=f0) ... et = time.perf_counter() ... elapsed.append(et - t) >>> np.mean(elapsed) # may vary np.float64(1.442545195999901) Create a map-like vectorized version. `x` is a generator, so first of all a 2-D array, `xx`, is reconstituted. Here `xx` has shape `(Y, N)` where `Y` is the number of function evaluations to perform and `N` is the dimensionality of the objective function. The underlying objective function is `rosen`, which requires `xx` to have shape `(N, Y)`, so a transpose is required. >>> def fun(f, x, *args, **kwds): ... xx = np.r_[[xs for xs in x]] ... return f(xx.T) >>> %timeit approx_derivative(fun2, x0, workers=fun, f0=f0) # may vary 91.8 ms ± 755 μs per loop (mean ± std. dev. of 7 runs, 10 loops each) )r;r=r<zUnknown method 'z'. nfevNr )r_xp real floatingz#`x0` must have at most 1 dimension.z,Inconsistent shapes between bounds and `x0`.z7Bounds not supported when `as_linear_operator` is True.rz&`f0` passed has more than 1 dimension.z `x0` violates bound constraints.rrNr;rr=rr<F)#rrxpx atleast_ndr[r?isdtyperrOr_rcrarrisinf _Fun_Wrapper atleast_1danyrL_linear_operator_differencerXrPrQr"rr8mapr_dense_differencerlenrstocscre_sparse_difference)funr$rGrRrVrSrbsparsityas_linear_operatorargskwargs full_outputworkers info_dictrw_x_dtyper(r) fun_wrappedrv_nfevJ_r%rTrWr*mf structurerrs r7approx_derivativersF11+F83788I  B  2Q2 6B ZZF zz"((O, 2v B ww{>?? VR (FB xx288rxx2883GHH266"((2,#7')vvbhhrl';9: :~sBf5KD5 z _ ]]2  77Q;EF F vvrBw27#$;<<  &rxx6BH*;+-xA1  &xR@AQw&&u-1A5GA6R-Bq(288VD !#%::c266":#>?A Y 7Aq)R - A} y 7Aq)R - A} t^!M.S   AB,["b!)6)+-5 )c(mq.@(0%Iv (I*84FI& ) 1I " i 8Iv.-k2r1-:I-3VRA5# A*   &)|5 A As 4B N..N7cjj}|dk(r fd}n'|dk(r fd}n|dk(r fd}n tdt|f|dfS) Nr;ctj|tj|rtjSt |z }||zz}|z }||z SNr array_equalrzerosr) prWr/dfrSrr%ror$s r7matvecz+_linear_operator_difference..matvecrsX~~aq!12xx{"T!WBRT AQ"B7Nr9r=ctj|tj|rtj Sdzt |z } |dz |zz } |dz |zz}|}|}||z }||z SNrr) rrWx1x2f1f2rrr%ror$s r7rz+_linear_operator_difference..matvec|s~~aq!12xx{"1tAwBr!tQhBr!tQhBRBRBbB7Nr9r<ctj|tj|rtjSt |z }||zdzz}|}|j }||z SN?)rrrrrimag) rrWr/rrrr%ror$s r7rz+_linear_operator_difference..matvecsa~~aq!12xx{"T!WBRT#X AQBB7Nr9Never be here.r)sizerDr)rr$rSr%rGrprros```` @r7rrlsr A A    9    4  +,, 1a&& )1 ,,r9c ,|j}|jtj|f}d} |dk(rfd} ||| ||} tD cgc]} || || z|| z } } | Dcgc]}||z  }}t || Dcgc] \}}||z  }}}| t |z } n|dk(rd}t ||||||} ||||}t} t}t|D]\} }t|}t|}t| }t| }|r8| j|| || z |jd|zd|zz|z l| j|| || z |j||z t || Dcgc] \}}||z  }}}| dt |zz } nh|d k(rXfd }t |||||} t | |Dcgc]\}}|j|z }}}| t |z } n td t|D] \} }||| < |d k(rtj|}|j| fScc} wcc}wcc}}wcc}}wcc}}w) Nrr;c3|KtD])}tj|}||||z||<|+ywr)rangerr!)r$r%irrps r7 x_generator2z'_dense_difference..x_generator2sC1X WWR[1! 1 s9<r=c3Kt|D]u\}}tj|}tj|}|r ||||z||<||d||zz||<n||||z ||<||||z||<||wywr) enumeraterr!)r$r%r*r one_sidedrrs r7 x_generator3z'_dense_difference..x_generator3s )- 8  9WWR[WWR[qEAaDLBqEqEAadFNBqEqEAaDLBqEqEAaDLBqE sBBgrr<c3KtD]0}|jtd}||xx||dzz cc<|2yw)NTr!r)rrOcomplex)r$r%rxcrps r7x_generator_csz)_dense_difference..x_generator_cssG1X YYwTY211# sAArr )rremptyrzipriterlistrnextappendrrDravelT)rr$rSr%r*rGrro J_transposedrvrf_evalsrrWf_evalrdelfdelxdf_dxrgenrlurrrhivrps @r7rrs A A88QF#L D  #|B23.3Ah 7r!uqt|r!u$ 7 7(/ 0ffrk 0 0/22r{;t;; E  9  wsLQ $FGH2q-0 V V%m4 #LAyS AS AgBgB !A$A,' $)a"f,r12 !A$1+& "r'" #032r{;t;; CJ 4  wsN2q$9:;,/O<&"b2<< E +,,% 1 Q Avxx - >>4 u8 0;F<=sI:8 I?J.J Jc #$|j} j} g} g} g} tjdz$d}$fd##fd}#fd}#fd}|dk(rt|||}|}n@|dk(rt|||}|}n|d k(rt|||}#D]}tj|\}t |dd|f\}}}||}|dk(r"t z }t |z }|dz }n|dk(rt }t |}|z}|z}tj| }|||z ||<||||z ||<t }t |} |d z }|}!tj| }||!}"d ||"zd ||"zz| |"z ||"<||!}"| |"||"z ||"<n2|d k(r"t }|dz }|j}|z}n td | j|| j|| j||||z tj| } tj| } tj| } t|rt| | | ff| | f|fSt| | | ff| | f|fS)Nr rc3^KtD]}tj|ywr)rrequal)grouprrn_groupss r7 e_generatorz'_sparse_difference..e_generators+8_ *E((5&) ) *s*-c3FK}|D]}|z}|z}|ywrrt)e_geneh_vecr/rr%r$s r7rz(_sparse_difference..x_generator2s4  AEEU AG s!c3 K}|D]}}|z} j} j} |z}||xx||z cc<||xxd||zz cc< |z}||xx||zcc<||xx||z cc<||ywrr) rrrrrmask_1mask_2rr%r*r$s r7rz(_sparse_difference..x_generator3s  AEEBB"Q&F vJ%- 'J vJ!eFm+ +J#^a'F vJ%- 'J vJ%- 'JHH sB Bc3HK}|D]}|z}|dzzywrrt)rrrrr%r$s r7rz*_sparse_difference..x_generator_css5  #AEEus{" " #s"r;r=r<rrr)ra)rrmaxrnonzerorrrrrrrhstackrr r )%rr$rSr%r*rrrrGrrorp row_indices col_indices fractionsrvrrrrxsrcolsrjrrWrrrrrrrmaskrowsrrs% ` `` ` @@r7rrs A AKKIvvf~!H D* "#wsLN34 ^ 9 wsLN34 ^ 4wsN$456 ]2( 1 yD)*1a G Y bBBg#B AID y bBbB"Q&F#^a'F!BFbj0BvJFbj0BvJgBgB AID #D!BT7DBtH}q2d8|3bh>BtHdU8D$x"T(*BtH t^gB AIDBQB-. . 11AA'e2(h))K(K))K(K )$I)9{K&@A!QPRVVV i+{!;>> import numpy as np >>> from scipy.optimize._numdiff import check_derivative >>> >>> >>> def f(x, c1, c2): ... return np.array([x[0] * np.sin(c1 * x[1]), ... x[0] * np.cos(c2 * x[1])]) ... >>> def jac(x, c1, c2): ... return np.array([ ... [np.sin(c1 * x[1]), c1 * x[0] * np.cos(c1 * x[1])], ... [np.cos(c2 * x[1]), -c2 * x[0] * np.sin(c2 * x[1])] ... ]) ... >>> >>> x0 = np.array([1.0, 0.5 * np.pi]) >>> check_derivative(f, jac, x0, args=(1, 2)) 2.4492935982947064e-16 )rbrrrr )rbrr) rrr rrr[rrrr") rjacr$rbrr J_to_testJ_diffabs_errrr abs_err_data J_diff_datas r7check_derivativervsz~B(((I "36I(,V=i( f$!']1ljj1.446 vvbff\*jjBFF;$789: :#36(,V=&&V+,vvg 1bffVn ==>>r9)r)&__doc__ functoolsnumpyr numpy.linalgrscipy.sparse.linalgrsparserrrr r r _group_columnsr rscipy._lib._array_apirscipy._lib._utilr scipy._librryr8 lru_cacherLrXrcrsr rrrrr}rrtr9r7rs-.QQ51'-L%^ 2;2;j.b*:z'0$w&7$).R"'Sl (-VP frRj.-/FF7BFF*;" M?r9