diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..10f4b39e48e2d6c0b042582ca65f572bde6ba575 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__init__.py @@ -0,0 +1,103 @@ +""" +========================================================= +Legacy discrete Fourier transforms (:mod:`scipy.fftpack`) +========================================================= + +.. legacy:: + + New code should use :mod:`scipy.fft`. + +Fast Fourier Transforms (FFTs) +============================== + +.. autosummary:: + :toctree: generated/ + + fft - Fast (discrete) Fourier Transform (FFT) + ifft - Inverse FFT + fft2 - 2-D FFT + ifft2 - 2-D inverse FFT + fftn - N-D FFT + ifftn - N-D inverse FFT + rfft - FFT of strictly real-valued sequence + irfft - Inverse of rfft + dct - Discrete cosine transform + idct - Inverse discrete cosine transform + dctn - N-D Discrete cosine transform + idctn - N-D Inverse discrete cosine transform + dst - Discrete sine transform + idst - Inverse discrete sine transform + dstn - N-D Discrete sine transform + idstn - N-D Inverse discrete sine transform + +Differential and pseudo-differential operators +============================================== + +.. autosummary:: + :toctree: generated/ + + diff - Differentiation and integration of periodic sequences + tilbert - Tilbert transform: cs_diff(x,h,h) + itilbert - Inverse Tilbert transform: sc_diff(x,h,h) + hilbert - Hilbert transform: cs_diff(x,inf,inf) + ihilbert - Inverse Hilbert transform: sc_diff(x,inf,inf) + cs_diff - cosh/sinh pseudo-derivative of periodic sequences + sc_diff - sinh/cosh pseudo-derivative of periodic sequences + ss_diff - sinh/sinh pseudo-derivative of periodic sequences + cc_diff - cosh/cosh pseudo-derivative of periodic sequences + shift - Shift periodic sequences + +Helper functions +================ + +.. autosummary:: + :toctree: generated/ + + fftshift - Shift the zero-frequency component to the center of the spectrum + ifftshift - The inverse of `fftshift` + fftfreq - Return the Discrete Fourier Transform sample frequencies + rfftfreq - DFT sample frequencies (for usage with rfft, irfft) + next_fast_len - Find the optimal length to zero-pad an FFT for speed + +Note that ``fftshift``, ``ifftshift`` and ``fftfreq`` are numpy functions +exposed by ``fftpack``; importing them from ``numpy`` should be preferred. + +Convolutions (:mod:`scipy.fftpack.convolve`) +============================================ + +.. module:: scipy.fftpack.convolve + +.. autosummary:: + :toctree: generated/ + + convolve + convolve_z + init_convolution_kernel + destroy_convolve_cache + +""" + + +__all__ = ['fft','ifft','fftn','ifftn','rfft','irfft', + 'fft2','ifft2', + 'diff', + 'tilbert','itilbert','hilbert','ihilbert', + 'sc_diff','cs_diff','cc_diff','ss_diff', + 'shift', + 'fftfreq', 'rfftfreq', + 'fftshift', 'ifftshift', + 'next_fast_len', + 'dct', 'idct', 'dst', 'idst', 'dctn', 'idctn', 'dstn', 'idstn' + ] + +from ._basic import * +from ._pseudo_diffs import * +from ._helper import * +from ._realtransforms import * + +# Deprecated namespaces, to be removed in v2.0.0 +from . import basic, helper, pseudo_diffs, realtransforms + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4570fc01084d932821b0a5d489202cd5f1e169d Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_basic.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_basic.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d500fb67baf1ddaeb330c8e06a294f11e8db4bec Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_basic.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_helper.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_helper.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e65344132d9f9c656f97175fe5b2c3f99adee995 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_helper.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_pseudo_diffs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_pseudo_diffs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e13dbf90eaf85bf85cefe54abe8bc18a3f56b61 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_pseudo_diffs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_realtransforms.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_realtransforms.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..629751b166c6a781d6022ba42c10b3ab530b6399 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/_realtransforms.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/basic.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/basic.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4cf76c0722888ea48ad7ed1bc29a24a65bcc760 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/basic.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/helper.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/helper.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70b5e736365ea9ada2b28865d65f930068af265f Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/helper.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/pseudo_diffs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/pseudo_diffs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..580c58470b051f540cbb8982ebb66b223070c0ce Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/pseudo_diffs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/realtransforms.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/realtransforms.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c9164dcf5ddccec471dad16d668c9e47694b5f0 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/__pycache__/realtransforms.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_basic.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..59c85ae4b364464a66489ef221f7f7ac45624694 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_basic.py @@ -0,0 +1,428 @@ +""" +Discrete Fourier Transforms - _basic.py +""" +# Created by Pearu Peterson, August,September 2002 +__all__ = ['fft','ifft','fftn','ifftn','rfft','irfft', + 'fft2','ifft2'] + +from scipy.fft import _pocketfft +from ._helper import _good_shape + + +def fft(x, n=None, axis=-1, overwrite_x=False): + """ + Return discrete Fourier transform of real or complex sequence. + + The returned complex array contains ``y(0), y(1),..., y(n-1)``, where + + ``y(j) = (x * exp(-2*pi*sqrt(-1)*j*np.arange(n)/n)).sum()``. + + Parameters + ---------- + x : array_like + Array to Fourier transform. + n : int, optional + Length of the Fourier transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the fft's are computed; the default is over the + last axis (i.e., ``axis=-1``). + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + z : complex ndarray + with the elements:: + + [y(0),y(1),..,y(n/2),y(1-n/2),...,y(-1)] if n is even + [y(0),y(1),..,y((n-1)/2),y(-(n-1)/2),...,y(-1)] if n is odd + + where:: + + y(j) = sum[k=0..n-1] x[k] * exp(-sqrt(-1)*j*k* 2*pi/n), j = 0..n-1 + + See Also + -------- + ifft : Inverse FFT + rfft : FFT of a real sequence + + Notes + ----- + The packing of the result is "standard": If ``A = fft(a, n)``, then + ``A[0]`` contains the zero-frequency term, ``A[1:n/2]`` contains the + positive-frequency terms, and ``A[n/2:]`` contains the negative-frequency + terms, in order of decreasingly negative frequency. So ,for an 8-point + transform, the frequencies of the result are [0, 1, 2, 3, -4, -3, -2, -1]. + To rearrange the fft output so that the zero-frequency component is + centered, like [-4, -3, -2, -1, 0, 1, 2, 3], use `fftshift`. + + Both single and double precision routines are implemented. Half precision + inputs will be converted to single precision. Non-floating-point inputs + will be converted to double precision. Long-double precision inputs are + not supported. + + This function is most efficient when `n` is a power of two, and least + efficient when `n` is prime. + + Note that if ``x`` is real-valued, then ``A[j] == A[n-j].conjugate()``. + If ``x`` is real-valued and ``n`` is even, then ``A[n/2]`` is real. + + If the data type of `x` is real, a "real FFT" algorithm is automatically + used, which roughly halves the computation time. To increase efficiency + a little further, use `rfft`, which does the same calculation, but only + outputs half of the symmetrical spectrum. If the data is both real and + symmetrical, the `dct` can again double the efficiency by generating + half of the spectrum from half of the signal. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import fft, ifft + >>> x = np.arange(5) + >>> np.allclose(fft(ifft(x)), x, atol=1e-15) # within numerical accuracy. + True + + """ + return _pocketfft.fft(x, n, axis, None, overwrite_x) + + +def ifft(x, n=None, axis=-1, overwrite_x=False): + """ + Return discrete inverse Fourier transform of real or complex sequence. + + The returned complex array contains ``y(0), y(1),..., y(n-1)``, where + + ``y(j) = (x * exp(2*pi*sqrt(-1)*j*np.arange(n)/n)).mean()``. + + Parameters + ---------- + x : array_like + Transformed data to invert. + n : int, optional + Length of the inverse Fourier transform. If ``n < x.shape[axis]``, + `x` is truncated. If ``n > x.shape[axis]``, `x` is zero-padded. + The default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the ifft's are computed; the default is over the + last axis (i.e., ``axis=-1``). + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + ifft : ndarray of floats + The inverse discrete Fourier transform. + + See Also + -------- + fft : Forward FFT + + Notes + ----- + Both single and double precision routines are implemented. Half precision + inputs will be converted to single precision. Non-floating-point inputs + will be converted to double precision. Long-double precision inputs are + not supported. + + This function is most efficient when `n` is a power of two, and least + efficient when `n` is prime. + + If the data type of `x` is real, a "real IFFT" algorithm is automatically + used, which roughly halves the computation time. + + Examples + -------- + >>> from scipy.fftpack import fft, ifft + >>> import numpy as np + >>> x = np.arange(5) + >>> np.allclose(ifft(fft(x)), x, atol=1e-15) # within numerical accuracy. + True + + """ + return _pocketfft.ifft(x, n, axis, None, overwrite_x) + + +def rfft(x, n=None, axis=-1, overwrite_x=False): + """ + Discrete Fourier transform of a real sequence. + + Parameters + ---------- + x : array_like, real-valued + The data to transform. + n : int, optional + Defines the length of the Fourier transform. If `n` is not specified + (the default) then ``n = x.shape[axis]``. If ``n < x.shape[axis]``, + `x` is truncated, if ``n > x.shape[axis]``, `x` is zero-padded. + axis : int, optional + The axis along which the transform is applied. The default is the + last axis. + overwrite_x : bool, optional + If set to true, the contents of `x` can be overwritten. Default is + False. + + Returns + ------- + z : real ndarray + The returned real array contains:: + + [y(0),Re(y(1)),Im(y(1)),...,Re(y(n/2))] if n is even + [y(0),Re(y(1)),Im(y(1)),...,Re(y(n/2)),Im(y(n/2))] if n is odd + + where:: + + y(j) = sum[k=0..n-1] x[k] * exp(-sqrt(-1)*j*k*2*pi/n) + j = 0..n-1 + + See Also + -------- + fft, irfft, scipy.fft.rfft + + Notes + ----- + Within numerical accuracy, ``y == rfft(irfft(y))``. + + Both single and double precision routines are implemented. Half precision + inputs will be converted to single precision. Non-floating-point inputs + will be converted to double precision. Long-double precision inputs are + not supported. + + To get an output with a complex datatype, consider using the newer + function `scipy.fft.rfft`. + + Examples + -------- + >>> from scipy.fftpack import fft, rfft + >>> a = [9, -9, 1, 3] + >>> fft(a) + array([ 4. +0.j, 8.+12.j, 16. +0.j, 8.-12.j]) + >>> rfft(a) + array([ 4., 8., 12., 16.]) + + """ + return _pocketfft.rfft_fftpack(x, n, axis, None, overwrite_x) + + +def irfft(x, n=None, axis=-1, overwrite_x=False): + """ + Return inverse discrete Fourier transform of real sequence x. + + The contents of `x` are interpreted as the output of the `rfft` + function. + + Parameters + ---------- + x : array_like + Transformed data to invert. + n : int, optional + Length of the inverse Fourier transform. + If n < x.shape[axis], x is truncated. + If n > x.shape[axis], x is zero-padded. + The default results in n = x.shape[axis]. + axis : int, optional + Axis along which the ifft's are computed; the default is over + the last axis (i.e., axis=-1). + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + irfft : ndarray of floats + The inverse discrete Fourier transform. + + See Also + -------- + rfft, ifft, scipy.fft.irfft + + Notes + ----- + The returned real array contains:: + + [y(0),y(1),...,y(n-1)] + + where for n is even:: + + y(j) = 1/n (sum[k=1..n/2-1] (x[2*k-1]+sqrt(-1)*x[2*k]) + * exp(sqrt(-1)*j*k* 2*pi/n) + + c.c. + x[0] + (-1)**(j) x[n-1]) + + and for n is odd:: + + y(j) = 1/n (sum[k=1..(n-1)/2] (x[2*k-1]+sqrt(-1)*x[2*k]) + * exp(sqrt(-1)*j*k* 2*pi/n) + + c.c. + x[0]) + + c.c. denotes complex conjugate of preceding expression. + + For details on input parameters, see `rfft`. + + To process (conjugate-symmetric) frequency-domain data with a complex + datatype, consider using the newer function `scipy.fft.irfft`. + + Examples + -------- + >>> from scipy.fftpack import rfft, irfft + >>> a = [1.0, 2.0, 3.0, 4.0, 5.0] + >>> irfft(a) + array([ 2.6 , -3.16405192, 1.24398433, -1.14955713, 1.46962473]) + >>> irfft(rfft(a)) + array([1., 2., 3., 4., 5.]) + + """ + return _pocketfft.irfft_fftpack(x, n, axis, None, overwrite_x) + + +def fftn(x, shape=None, axes=None, overwrite_x=False): + """ + Return multidimensional discrete Fourier transform. + + The returned array contains:: + + y[j_1,..,j_d] = sum[k_1=0..n_1-1, ..., k_d=0..n_d-1] + x[k_1,..,k_d] * prod[i=1..d] exp(-sqrt(-1)*2*pi/n_i * j_i * k_i) + + where d = len(x.shape) and n = x.shape. + + Parameters + ---------- + x : array_like + The (N-D) array to transform. + shape : int or array_like of ints or None, optional + The shape of the result. If both `shape` and `axes` (see below) are + None, `shape` is ``x.shape``; if `shape` is None but `axes` is + not None, then `shape` is ``numpy.take(x.shape, axes, axis=0)``. + If ``shape[i] > x.shape[i]``, the ith dimension is padded with zeros. + If ``shape[i] < x.shape[i]``, the ith dimension is truncated to + length ``shape[i]``. + If any element of `shape` is -1, the size of the corresponding + dimension of `x` is used. + axes : int or array_like of ints or None, optional + The axes of `x` (`y` if `shape` is not None) along which the + transform is applied. + The default is over all axes. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed. Default is False. + + Returns + ------- + y : complex-valued N-D NumPy array + The (N-D) DFT of the input array. + + See Also + -------- + ifftn + + Notes + ----- + If ``x`` is real-valued, then + ``y[..., j_i, ...] == y[..., n_i-j_i, ...].conjugate()``. + + Both single and double precision routines are implemented. Half precision + inputs will be converted to single precision. Non-floating-point inputs + will be converted to double precision. Long-double precision inputs are + not supported. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import fftn, ifftn + >>> y = (-np.arange(16), 8 - np.arange(16), np.arange(16)) + >>> np.allclose(y, fftn(ifftn(y))) + True + + """ + shape = _good_shape(x, shape, axes) + return _pocketfft.fftn(x, shape, axes, None, overwrite_x) + + +def ifftn(x, shape=None, axes=None, overwrite_x=False): + """ + Return inverse multidimensional discrete Fourier transform. + + The sequence can be of an arbitrary type. + + The returned array contains:: + + y[j_1,..,j_d] = 1/p * sum[k_1=0..n_1-1, ..., k_d=0..n_d-1] + x[k_1,..,k_d] * prod[i=1..d] exp(sqrt(-1)*2*pi/n_i * j_i * k_i) + + where ``d = len(x.shape)``, ``n = x.shape``, and ``p = prod[i=1..d] n_i``. + + For description of parameters see `fftn`. + + See Also + -------- + fftn : for detailed information. + + Examples + -------- + >>> from scipy.fftpack import fftn, ifftn + >>> import numpy as np + >>> y = (-np.arange(16), 8 - np.arange(16), np.arange(16)) + >>> np.allclose(y, ifftn(fftn(y))) + True + + """ + shape = _good_shape(x, shape, axes) + return _pocketfft.ifftn(x, shape, axes, None, overwrite_x) + + +def fft2(x, shape=None, axes=(-2,-1), overwrite_x=False): + """ + 2-D discrete Fourier transform. + + Return the 2-D discrete Fourier transform of the 2-D argument + `x`. + + See Also + -------- + fftn : for detailed information. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import fft2, ifft2 + >>> y = np.mgrid[:5, :5][0] + >>> y + array([[0, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + [2, 2, 2, 2, 2], + [3, 3, 3, 3, 3], + [4, 4, 4, 4, 4]]) + >>> np.allclose(y, ifft2(fft2(y))) + True + """ + return fftn(x,shape,axes,overwrite_x) + + +def ifft2(x, shape=None, axes=(-2,-1), overwrite_x=False): + """ + 2-D discrete inverse Fourier transform of real or complex sequence. + + Return inverse 2-D discrete Fourier transform of + arbitrary type sequence x. + + See `ifft` for more information. + + See Also + -------- + fft2, ifft + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import fft2, ifft2 + >>> y = np.mgrid[:5, :5][0] + >>> y + array([[0, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + [2, 2, 2, 2, 2], + [3, 3, 3, 3, 3], + [4, 4, 4, 4, 4]]) + >>> np.allclose(y, fft2(ifft2(y))) + True + + """ + return ifftn(x,shape,axes,overwrite_x) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_helper.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..ee0dd7b0f8d6dce5fe717fe585f5af82a7d0c651 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_helper.py @@ -0,0 +1,115 @@ +import operator + +import numpy as np +from numpy.fft import fftshift, ifftshift, fftfreq + +import scipy.fft._pocketfft.helper as _helper + +__all__ = ['fftshift', 'ifftshift', 'fftfreq', 'rfftfreq', 'next_fast_len'] + + +def rfftfreq(n, d=1.0): + """DFT sample frequencies (for usage with rfft, irfft). + + The returned float array contains the frequency bins in + cycles/unit (with zero at the start) given a window length `n` and a + sample spacing `d`:: + + f = [0,1,1,2,2,...,n/2-1,n/2-1,n/2]/(d*n) if n is even + f = [0,1,1,2,2,...,n/2-1,n/2-1,n/2,n/2]/(d*n) if n is odd + + Parameters + ---------- + n : int + Window length. + d : scalar, optional + Sample spacing. Default is 1. + + Returns + ------- + out : ndarray + The array of length `n`, containing the sample frequencies. + + Examples + -------- + >>> import numpy as np + >>> from scipy import fftpack + >>> sig = np.array([-2, 8, 6, 4, 1, 0, 3, 5], dtype=float) + >>> sig_fft = fftpack.rfft(sig) + >>> n = sig_fft.size + >>> timestep = 0.1 + >>> freq = fftpack.rfftfreq(n, d=timestep) + >>> freq + array([ 0. , 1.25, 1.25, 2.5 , 2.5 , 3.75, 3.75, 5. ]) + + """ + n = operator.index(n) + if n < 0: + raise ValueError("n = %s is not valid. " + "n must be a nonnegative integer." % n) + + return (np.arange(1, n + 1, dtype=int) // 2) / float(n * d) + + +def next_fast_len(target): + """ + Find the next fast size of input data to `fft`, for zero-padding, etc. + + SciPy's FFTPACK has efficient functions for radix {2, 3, 4, 5}, so this + returns the next composite of the prime factors 2, 3, and 5 which is + greater than or equal to `target`. (These are also known as 5-smooth + numbers, regular numbers, or Hamming numbers.) + + Parameters + ---------- + target : int + Length to start searching from. Must be a positive integer. + + Returns + ------- + out : int + The first 5-smooth number greater than or equal to `target`. + + Notes + ----- + .. versionadded:: 0.18.0 + + Examples + -------- + On a particular machine, an FFT of prime length takes 133 ms: + + >>> from scipy import fftpack + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> min_len = 10007 # prime length is worst case for speed + >>> a = rng.standard_normal(min_len) + >>> b = fftpack.fft(a) + + Zero-padding to the next 5-smooth length reduces computation time to + 211 us, a speedup of 630 times: + + >>> fftpack.next_fast_len(min_len) + 10125 + >>> b = fftpack.fft(a, 10125) + + Rounding up to the next power of 2 is not optimal, taking 367 us to + compute, 1.7 times as long as the 5-smooth size: + + >>> b = fftpack.fft(a, 16384) + + """ + # Real transforms use regular sizes so this is backwards compatible + return _helper.good_size(target, True) + + +def _good_shape(x, shape, axes): + """Ensure that shape argument is valid for scipy.fftpack + + scipy.fftpack does not support len(shape) < x.ndim when axes is not given. + """ + if shape is not None and axes is None: + shape = _helper._iterable_of_int(shape, 'shape') + if len(shape) != np.ndim(x): + raise ValueError("when given, axes and shape arguments" + " have to be of the same length") + return shape diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_pseudo_diffs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_pseudo_diffs.py new file mode 100644 index 0000000000000000000000000000000000000000..b8ef40efc07484b3bf594ae3ff904cd85f498fc9 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_pseudo_diffs.py @@ -0,0 +1,551 @@ +""" +Differential and pseudo-differential operators. +""" +# Created by Pearu Peterson, September 2002 + +__all__ = ['diff', + 'tilbert','itilbert','hilbert','ihilbert', + 'cs_diff','cc_diff','sc_diff','ss_diff', + 'shift'] + +from numpy import pi, asarray, sin, cos, sinh, cosh, tanh, iscomplexobj +from . import convolve + +from scipy.fft._pocketfft.helper import _datacopied + + +_cache = {} + + +def diff(x,order=1,period=None, _cache=_cache): + """ + Return kth derivative (or integral) of a periodic sequence x. + + If x_j and y_j are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = pow(sqrt(-1)*j*2*pi/period, order) * x_j + y_0 = 0 if order is not 0. + + Parameters + ---------- + x : array_like + Input array. + order : int, optional + The order of differentiation. Default order is 1. If order is + negative, then integration is carried out under the assumption + that ``x_0 == 0``. + period : float, optional + The assumed period of the sequence. Default is ``2*pi``. + + Notes + ----- + If ``sum(x, axis=0) = 0`` then ``diff(diff(x, k), -k) == x`` (within + numerical accuracy). + + For odd order and even ``len(x)``, the Nyquist mode is taken zero. + + """ + tmp = asarray(x) + if order == 0: + return tmp + if iscomplexobj(tmp): + return diff(tmp.real,order,period)+1j*diff(tmp.imag,order,period) + if period is not None: + c = 2*pi/period + else: + c = 1.0 + n = len(x) + omega = _cache.get((n,order,c)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k,order=order,c=c): + if k: + return pow(c*k,order) + return 0 + omega = convolve.init_convolution_kernel(n,kernel,d=order, + zero_nyquist=1) + _cache[(n,order,c)] = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,swap_real_imag=order % 2, + overwrite_x=overwrite_x) + + +del _cache + + +_cache = {} + + +def tilbert(x, h, period=None, _cache=_cache): + """ + Return h-Tilbert transform of a periodic sequence x. + + If x_j and y_j are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = sqrt(-1)*coth(j*h*2*pi/period) * x_j + y_0 = 0 + + Parameters + ---------- + x : array_like + The input array to transform. + h : float + Defines the parameter of the Tilbert transform. + period : float, optional + The assumed period of the sequence. Default period is ``2*pi``. + + Returns + ------- + tilbert : ndarray + The result of the transform. + + Notes + ----- + If ``sum(x, axis=0) == 0`` and ``n = len(x)`` is odd, then + ``tilbert(itilbert(x)) == x``. + + If ``2 * pi * h / period`` is approximately 10 or larger, then + numerically ``tilbert == hilbert`` + (theoretically oo-Tilbert == Hilbert). + + For even ``len(x)``, the Nyquist mode of ``x`` is taken zero. + + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return tilbert(tmp.real, h, period) + \ + 1j * tilbert(tmp.imag, h, period) + + if period is not None: + h = h * 2 * pi / period + + n = len(x) + omega = _cache.get((n, h)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k, h=h): + if k: + return 1.0/tanh(h*k) + + return 0 + + omega = convolve.init_convolution_kernel(n, kernel, d=1) + _cache[(n,h)] = omega + + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x) + + +del _cache + + +_cache = {} + + +def itilbert(x,h,period=None, _cache=_cache): + """ + Return inverse h-Tilbert transform of a periodic sequence x. + + If ``x_j`` and ``y_j`` are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = -sqrt(-1)*tanh(j*h*2*pi/period) * x_j + y_0 = 0 + + For more details, see `tilbert`. + + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return itilbert(tmp.real,h,period) + \ + 1j*itilbert(tmp.imag,h,period) + if period is not None: + h = h*2*pi/period + n = len(x) + omega = _cache.get((n,h)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k,h=h): + if k: + return -tanh(h*k) + return 0 + omega = convolve.init_convolution_kernel(n,kernel,d=1) + _cache[(n,h)] = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x) + + +del _cache + + +_cache = {} + + +def hilbert(x, _cache=_cache): + """ + Return Hilbert transform of a periodic sequence x. + + If x_j and y_j are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = sqrt(-1)*sign(j) * x_j + y_0 = 0 + + Parameters + ---------- + x : array_like + The input array, should be periodic. + _cache : dict, optional + Dictionary that contains the kernel used to do a convolution with. + + Returns + ------- + y : ndarray + The transformed input. + + See Also + -------- + scipy.signal.hilbert : Compute the analytic signal, using the Hilbert + transform. + + Notes + ----- + If ``sum(x, axis=0) == 0`` then ``hilbert(ihilbert(x)) == x``. + + For even len(x), the Nyquist mode of x is taken zero. + + The sign of the returned transform does not have a factor -1 that is more + often than not found in the definition of the Hilbert transform. Note also + that `scipy.signal.hilbert` does have an extra -1 factor compared to this + function. + + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return hilbert(tmp.real)+1j*hilbert(tmp.imag) + n = len(x) + omega = _cache.get(n) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k): + if k > 0: + return 1.0 + elif k < 0: + return -1.0 + return 0.0 + omega = convolve.init_convolution_kernel(n,kernel,d=1) + _cache[n] = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x) + + +del _cache + + +def ihilbert(x): + """ + Return inverse Hilbert transform of a periodic sequence x. + + If ``x_j`` and ``y_j`` are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = -sqrt(-1)*sign(j) * x_j + y_0 = 0 + + """ + return -hilbert(x) + + +_cache = {} + + +def cs_diff(x, a, b, period=None, _cache=_cache): + """ + Return (a,b)-cosh/sinh pseudo-derivative of a periodic sequence. + + If ``x_j`` and ``y_j`` are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = -sqrt(-1)*cosh(j*a*2*pi/period)/sinh(j*b*2*pi/period) * x_j + y_0 = 0 + + Parameters + ---------- + x : array_like + The array to take the pseudo-derivative from. + a, b : float + Defines the parameters of the cosh/sinh pseudo-differential + operator. + period : float, optional + The period of the sequence. Default period is ``2*pi``. + + Returns + ------- + cs_diff : ndarray + Pseudo-derivative of periodic sequence `x`. + + Notes + ----- + For even len(`x`), the Nyquist mode of `x` is taken as zero. + + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return cs_diff(tmp.real,a,b,period) + \ + 1j*cs_diff(tmp.imag,a,b,period) + if period is not None: + a = a*2*pi/period + b = b*2*pi/period + n = len(x) + omega = _cache.get((n,a,b)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k,a=a,b=b): + if k: + return -cosh(a*k)/sinh(b*k) + return 0 + omega = convolve.init_convolution_kernel(n,kernel,d=1) + _cache[(n,a,b)] = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x) + + +del _cache + + +_cache = {} + + +def sc_diff(x, a, b, period=None, _cache=_cache): + """ + Return (a,b)-sinh/cosh pseudo-derivative of a periodic sequence x. + + If x_j and y_j are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = sqrt(-1)*sinh(j*a*2*pi/period)/cosh(j*b*2*pi/period) * x_j + y_0 = 0 + + Parameters + ---------- + x : array_like + Input array. + a,b : float + Defines the parameters of the sinh/cosh pseudo-differential + operator. + period : float, optional + The period of the sequence x. Default is 2*pi. + + Notes + ----- + ``sc_diff(cs_diff(x,a,b),b,a) == x`` + For even ``len(x)``, the Nyquist mode of x is taken as zero. + + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return sc_diff(tmp.real,a,b,period) + \ + 1j*sc_diff(tmp.imag,a,b,period) + if period is not None: + a = a*2*pi/period + b = b*2*pi/period + n = len(x) + omega = _cache.get((n,a,b)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k,a=a,b=b): + if k: + return sinh(a*k)/cosh(b*k) + return 0 + omega = convolve.init_convolution_kernel(n,kernel,d=1) + _cache[(n,a,b)] = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x) + + +del _cache + + +_cache = {} + + +def ss_diff(x, a, b, period=None, _cache=_cache): + """ + Return (a,b)-sinh/sinh pseudo-derivative of a periodic sequence x. + + If x_j and y_j are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = sinh(j*a*2*pi/period)/sinh(j*b*2*pi/period) * x_j + y_0 = a/b * x_0 + + Parameters + ---------- + x : array_like + The array to take the pseudo-derivative from. + a,b + Defines the parameters of the sinh/sinh pseudo-differential + operator. + period : float, optional + The period of the sequence x. Default is ``2*pi``. + + Notes + ----- + ``ss_diff(ss_diff(x,a,b),b,a) == x`` + + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return ss_diff(tmp.real,a,b,period) + \ + 1j*ss_diff(tmp.imag,a,b,period) + if period is not None: + a = a*2*pi/period + b = b*2*pi/period + n = len(x) + omega = _cache.get((n,a,b)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k,a=a,b=b): + if k: + return sinh(a*k)/sinh(b*k) + return float(a)/b + omega = convolve.init_convolution_kernel(n,kernel) + _cache[(n,a,b)] = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,overwrite_x=overwrite_x) + + +del _cache + + +_cache = {} + + +def cc_diff(x, a, b, period=None, _cache=_cache): + """ + Return (a,b)-cosh/cosh pseudo-derivative of a periodic sequence. + + If x_j and y_j are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = cosh(j*a*2*pi/period)/cosh(j*b*2*pi/period) * x_j + + Parameters + ---------- + x : array_like + The array to take the pseudo-derivative from. + a,b : float + Defines the parameters of the sinh/sinh pseudo-differential + operator. + period : float, optional + The period of the sequence x. Default is ``2*pi``. + + Returns + ------- + cc_diff : ndarray + Pseudo-derivative of periodic sequence `x`. + + Notes + ----- + ``cc_diff(cc_diff(x,a,b),b,a) == x`` + + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return cc_diff(tmp.real,a,b,period) + \ + 1j*cc_diff(tmp.imag,a,b,period) + if period is not None: + a = a*2*pi/period + b = b*2*pi/period + n = len(x) + omega = _cache.get((n,a,b)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel(k,a=a,b=b): + return cosh(a*k)/cosh(b*k) + omega = convolve.init_convolution_kernel(n,kernel) + _cache[(n,a,b)] = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve(tmp,omega,overwrite_x=overwrite_x) + + +del _cache + + +_cache = {} + + +def shift(x, a, period=None, _cache=_cache): + """ + Shift periodic sequence x by a: y(u) = x(u+a). + + If x_j and y_j are Fourier coefficients of periodic functions x + and y, respectively, then:: + + y_j = exp(j*a*2*pi/period*sqrt(-1)) * x_f + + Parameters + ---------- + x : array_like + The array to take the pseudo-derivative from. + a : float + Defines the parameters of the sinh/sinh pseudo-differential + period : float, optional + The period of the sequences x and y. Default period is ``2*pi``. + """ + tmp = asarray(x) + if iscomplexobj(tmp): + return shift(tmp.real,a,period)+1j*shift(tmp.imag,a,period) + if period is not None: + a = a*2*pi/period + n = len(x) + omega = _cache.get((n,a)) + if omega is None: + if len(_cache) > 20: + while _cache: + _cache.popitem() + + def kernel_real(k,a=a): + return cos(a*k) + + def kernel_imag(k,a=a): + return sin(a*k) + omega_real = convolve.init_convolution_kernel(n,kernel_real,d=0, + zero_nyquist=0) + omega_imag = convolve.init_convolution_kernel(n,kernel_imag,d=1, + zero_nyquist=0) + _cache[(n,a)] = omega_real,omega_imag + else: + omega_real,omega_imag = omega + overwrite_x = _datacopied(tmp, x) + return convolve.convolve_z(tmp,omega_real,omega_imag, + overwrite_x=overwrite_x) + + +del _cache diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_realtransforms.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_realtransforms.py new file mode 100644 index 0000000000000000000000000000000000000000..f56f68fce4ea447b6b946b14d7610dc4ef07c47c --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/_realtransforms.py @@ -0,0 +1,598 @@ +""" +Real spectrum transforms (DCT, DST, MDCT) +""" + +__all__ = ['dct', 'idct', 'dst', 'idst', 'dctn', 'idctn', 'dstn', 'idstn'] + +from scipy.fft import _pocketfft +from ._helper import _good_shape + +_inverse_typemap = {1: 1, 2: 3, 3: 2, 4: 4} + + +def dctn(x, type=2, shape=None, axes=None, norm=None, overwrite_x=False): + """ + Return multidimensional Discrete Cosine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + shape : int or array_like of ints or None, optional + The shape of the result. If both `shape` and `axes` (see below) are + None, `shape` is ``x.shape``; if `shape` is None but `axes` is + not None, then `shape` is ``numpy.take(x.shape, axes, axis=0)``. + If ``shape[i] > x.shape[i]``, the ith dimension is padded with zeros. + If ``shape[i] < x.shape[i]``, the ith dimension is truncated to + length ``shape[i]``. + If any element of `shape` is -1, the size of the corresponding + dimension of `x` is used. + axes : int or array_like of ints or None, optional + Axes along which the DCT is computed. + The default is over all axes. + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + idctn : Inverse multidimensional DCT + + Notes + ----- + For full details of the DCT types and normalization modes, as well as + references, see `dct`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import dctn, idctn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idctn(dctn(y, norm='ortho'), norm='ortho')) + True + + """ + shape = _good_shape(x, shape, axes) + return _pocketfft.dctn(x, type, shape, axes, norm, overwrite_x) + + +def idctn(x, type=2, shape=None, axes=None, norm=None, overwrite_x=False): + """ + Return multidimensional Discrete Cosine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + shape : int or array_like of ints or None, optional + The shape of the result. If both `shape` and `axes` (see below) are + None, `shape` is ``x.shape``; if `shape` is None but `axes` is + not None, then `shape` is ``numpy.take(x.shape, axes, axis=0)``. + If ``shape[i] > x.shape[i]``, the ith dimension is padded with zeros. + If ``shape[i] < x.shape[i]``, the ith dimension is truncated to + length ``shape[i]``. + If any element of `shape` is -1, the size of the corresponding + dimension of `x` is used. + axes : int or array_like of ints or None, optional + Axes along which the IDCT is computed. + The default is over all axes. + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + dctn : multidimensional DCT + + Notes + ----- + For full details of the IDCT types and normalization modes, as well as + references, see `idct`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import dctn, idctn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idctn(dctn(y, norm='ortho'), norm='ortho')) + True + + """ + type = _inverse_typemap[type] + shape = _good_shape(x, shape, axes) + return _pocketfft.dctn(x, type, shape, axes, norm, overwrite_x) + + +def dstn(x, type=2, shape=None, axes=None, norm=None, overwrite_x=False): + """ + Return multidimensional Discrete Sine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + shape : int or array_like of ints or None, optional + The shape of the result. If both `shape` and `axes` (see below) are + None, `shape` is ``x.shape``; if `shape` is None but `axes` is + not None, then `shape` is ``numpy.take(x.shape, axes, axis=0)``. + If ``shape[i] > x.shape[i]``, the ith dimension is padded with zeros. + If ``shape[i] < x.shape[i]``, the ith dimension is truncated to + length ``shape[i]``. + If any element of `shape` is -1, the size of the corresponding + dimension of `x` is used. + axes : int or array_like of ints or None, optional + Axes along which the DCT is computed. + The default is over all axes. + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + idstn : Inverse multidimensional DST + + Notes + ----- + For full details of the DST types and normalization modes, as well as + references, see `dst`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import dstn, idstn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idstn(dstn(y, norm='ortho'), norm='ortho')) + True + + """ + shape = _good_shape(x, shape, axes) + return _pocketfft.dstn(x, type, shape, axes, norm, overwrite_x) + + +def idstn(x, type=2, shape=None, axes=None, norm=None, overwrite_x=False): + """ + Return multidimensional Discrete Sine Transform along the specified axes. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + shape : int or array_like of ints or None, optional + The shape of the result. If both `shape` and `axes` (see below) are + None, `shape` is ``x.shape``; if `shape` is None but `axes` is + not None, then `shape` is ``numpy.take(x.shape, axes, axis=0)``. + If ``shape[i] > x.shape[i]``, the ith dimension is padded with zeros. + If ``shape[i] < x.shape[i]``, the ith dimension is truncated to + length ``shape[i]``. + If any element of `shape` is -1, the size of the corresponding + dimension of `x` is used. + axes : int or array_like of ints or None, optional + Axes along which the IDST is computed. + The default is over all axes. + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + dstn : multidimensional DST + + Notes + ----- + For full details of the IDST types and normalization modes, as well as + references, see `idst`. + + Examples + -------- + >>> import numpy as np + >>> from scipy.fftpack import dstn, idstn + >>> rng = np.random.default_rng() + >>> y = rng.standard_normal((16, 16)) + >>> np.allclose(y, idstn(dstn(y, norm='ortho'), norm='ortho')) + True + + """ + type = _inverse_typemap[type] + shape = _good_shape(x, shape, axes) + return _pocketfft.dstn(x, type, shape, axes, norm, overwrite_x) + + +def dct(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False): + r""" + Return the Discrete Cosine Transform of arbitrary type sequence x. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the dct is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + y : ndarray of real + The transformed input array. + + See Also + -------- + idct : Inverse DCT + + Notes + ----- + For a single dimension array ``x``, ``dct(x, norm='ortho')`` is equal to + MATLAB ``dct(x)``. + + There are, theoretically, 8 types of the DCT, only the first 4 types are + implemented in scipy. 'The' DCT generally refers to DCT type 2, and 'the' + Inverse DCT generally refers to DCT type 3. + + **Type I** + + There are several definitions of the DCT-I; we use the following + (for ``norm=None``) + + .. math:: + + y_k = x_0 + (-1)^k x_{N-1} + 2 \sum_{n=1}^{N-2} x_n \cos\left( + \frac{\pi k n}{N-1} \right) + + If ``norm='ortho'``, ``x[0]`` and ``x[N-1]`` are multiplied by a scaling + factor of :math:`\sqrt{2}`, and ``y[k]`` is multiplied by a scaling factor + ``f`` + + .. math:: + + f = \begin{cases} + \frac{1}{2}\sqrt{\frac{1}{N-1}} & \text{if }k=0\text{ or }N-1, \\ + \frac{1}{2}\sqrt{\frac{2}{N-1}} & \text{otherwise} \end{cases} + + .. versionadded:: 1.2.0 + Orthonormalization in DCT-I. + + .. note:: + The DCT-I is only supported for input size > 1. + + **Type II** + + There are several definitions of the DCT-II; we use the following + (for ``norm=None``) + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \cos\left(\frac{\pi k(2n+1)}{2N} \right) + + If ``norm='ortho'``, ``y[k]`` is multiplied by a scaling factor ``f`` + + .. math:: + f = \begin{cases} + \sqrt{\frac{1}{4N}} & \text{if }k=0, \\ + \sqrt{\frac{1}{2N}} & \text{otherwise} \end{cases} + + which makes the corresponding matrix of coefficients orthonormal + (``O @ O.T = np.eye(N)``). + + **Type III** + + There are several definitions, we use the following (for ``norm=None``) + + .. math:: + + y_k = x_0 + 2 \sum_{n=1}^{N-1} x_n \cos\left(\frac{\pi(2k+1)n}{2N}\right) + + or, for ``norm='ortho'`` + + .. math:: + + y_k = \frac{x_0}{\sqrt{N}} + \sqrt{\frac{2}{N}} \sum_{n=1}^{N-1} x_n + \cos\left(\frac{\pi(2k+1)n}{2N}\right) + + The (unnormalized) DCT-III is the inverse of the (unnormalized) DCT-II, up + to a factor `2N`. The orthonormalized DCT-III is exactly the inverse of + the orthonormalized DCT-II. + + **Type IV** + + There are several definitions of the DCT-IV; we use the following + (for ``norm=None``) + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \cos\left(\frac{\pi(2k+1)(2n+1)}{4N} \right) + + If ``norm='ortho'``, ``y[k]`` is multiplied by a scaling factor ``f`` + + .. math:: + + f = \frac{1}{\sqrt{2N}} + + .. versionadded:: 1.2.0 + Support for DCT-IV. + + References + ---------- + .. [1] 'A Fast Cosine Transform in One and Two Dimensions', by J. + Makhoul, `IEEE Transactions on acoustics, speech and signal + processing` vol. 28(1), pp. 27-34, + :doi:`10.1109/TASSP.1980.1163351` (1980). + .. [2] Wikipedia, "Discrete cosine transform", + https://en.wikipedia.org/wiki/Discrete_cosine_transform + + Examples + -------- + The Type 1 DCT is equivalent to the FFT (though faster) for real, + even-symmetrical inputs. The output is also real and even-symmetrical. + Half of the FFT input is used to generate half of the FFT output: + + >>> from scipy.fftpack import fft, dct + >>> import numpy as np + >>> fft(np.array([4., 3., 5., 10., 5., 3.])).real + array([ 30., -8., 6., -2., 6., -8.]) + >>> dct(np.array([4., 3., 5., 10.]), 1) + array([ 30., -8., 6., -2.]) + + """ + return _pocketfft.dct(x, type, n, axis, norm, overwrite_x) + + +def idct(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False): + """ + Return the Inverse Discrete Cosine Transform of an arbitrary type sequence. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DCT (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the idct is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + idct : ndarray of real + The transformed input array. + + See Also + -------- + dct : Forward DCT + + Notes + ----- + For a single dimension array `x`, ``idct(x, norm='ortho')`` is equal to + MATLAB ``idct(x)``. + + 'The' IDCT is the IDCT of type 2, which is the same as DCT of type 3. + + IDCT of type 1 is the DCT of type 1, IDCT of type 2 is the DCT of type + 3, and IDCT of type 3 is the DCT of type 2. IDCT of type 4 is the DCT + of type 4. For the definition of these types, see `dct`. + + Examples + -------- + The Type 1 DCT is equivalent to the DFT for real, even-symmetrical + inputs. The output is also real and even-symmetrical. Half of the IFFT + input is used to generate half of the IFFT output: + + >>> from scipy.fftpack import ifft, idct + >>> import numpy as np + >>> ifft(np.array([ 30., -8., 6., -2., 6., -8.])).real + array([ 4., 3., 5., 10., 5., 3.]) + >>> idct(np.array([ 30., -8., 6., -2.]), 1) / 6 + array([ 4., 3., 5., 10.]) + + """ + type = _inverse_typemap[type] + return _pocketfft.dct(x, type, n, axis, norm, overwrite_x) + + +def dst(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False): + r""" + Return the Discrete Sine Transform of arbitrary type sequence x. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the dst is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + dst : ndarray of reals + The transformed input array. + + See Also + -------- + idst : Inverse DST + + Notes + ----- + For a single dimension array ``x``. + + There are, theoretically, 8 types of the DST for different combinations of + even/odd boundary conditions and boundary off sets [1]_, only the first + 4 types are implemented in scipy. + + **Type I** + + There are several definitions of the DST-I; we use the following + for ``norm=None``. DST-I assumes the input is odd around `n=-1` and `n=N`. + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \sin\left(\frac{\pi(k+1)(n+1)}{N+1}\right) + + Note that the DST-I is only supported for input size > 1. + The (unnormalized) DST-I is its own inverse, up to a factor `2(N+1)`. + The orthonormalized DST-I is exactly its own inverse. + + **Type II** + + There are several definitions of the DST-II; we use the following for + ``norm=None``. DST-II assumes the input is odd around `n=-1/2` and + `n=N-1/2`; the output is odd around :math:`k=-1` and even around `k=N-1` + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \sin\left(\frac{\pi(k+1)(2n+1)}{2N}\right) + + if ``norm='ortho'``, ``y[k]`` is multiplied by a scaling factor ``f`` + + .. math:: + + f = \begin{cases} + \sqrt{\frac{1}{4N}} & \text{if }k = 0, \\ + \sqrt{\frac{1}{2N}} & \text{otherwise} \end{cases} + + **Type III** + + There are several definitions of the DST-III, we use the following (for + ``norm=None``). DST-III assumes the input is odd around `n=-1` and even + around `n=N-1` + + .. math:: + + y_k = (-1)^k x_{N-1} + 2 \sum_{n=0}^{N-2} x_n \sin\left( + \frac{\pi(2k+1)(n+1)}{2N}\right) + + The (unnormalized) DST-III is the inverse of the (unnormalized) DST-II, up + to a factor `2N`. The orthonormalized DST-III is exactly the inverse of the + orthonormalized DST-II. + + .. versionadded:: 0.11.0 + + **Type IV** + + There are several definitions of the DST-IV, we use the following (for + ``norm=None``). DST-IV assumes the input is odd around `n=-0.5` and even + around `n=N-0.5` + + .. math:: + + y_k = 2 \sum_{n=0}^{N-1} x_n \sin\left(\frac{\pi(2k+1)(2n+1)}{4N}\right) + + The (unnormalized) DST-IV is its own inverse, up to a factor `2N`. The + orthonormalized DST-IV is exactly its own inverse. + + .. versionadded:: 1.2.0 + Support for DST-IV. + + References + ---------- + .. [1] Wikipedia, "Discrete sine transform", + https://en.wikipedia.org/wiki/Discrete_sine_transform + + """ + return _pocketfft.dst(x, type, n, axis, norm, overwrite_x) + + +def idst(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False): + """ + Return the Inverse Discrete Sine Transform of an arbitrary type sequence. + + Parameters + ---------- + x : array_like + The input array. + type : {1, 2, 3, 4}, optional + Type of the DST (see Notes). Default type is 2. + n : int, optional + Length of the transform. If ``n < x.shape[axis]``, `x` is + truncated. If ``n > x.shape[axis]``, `x` is zero-padded. The + default results in ``n = x.shape[axis]``. + axis : int, optional + Axis along which the idst is computed; the default is over the + last axis (i.e., ``axis=-1``). + norm : {None, 'ortho'}, optional + Normalization mode (see Notes). Default is None. + overwrite_x : bool, optional + If True, the contents of `x` can be destroyed; the default is False. + + Returns + ------- + idst : ndarray of real + The transformed input array. + + See Also + -------- + dst : Forward DST + + Notes + ----- + 'The' IDST is the IDST of type 2, which is the same as DST of type 3. + + IDST of type 1 is the DST of type 1, IDST of type 2 is the DST of type + 3, and IDST of type 3 is the DST of type 2. For the definition of these + types, see `dst`. + + .. versionadded:: 0.11.0 + + """ + type = _inverse_typemap[type] + return _pocketfft.dst(x, type, n, axis, norm, overwrite_x) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/basic.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/basic.py new file mode 100644 index 0000000000000000000000000000000000000000..553f456fe1561c28928ecc4ebe2238459cc60443 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/basic.py @@ -0,0 +1,20 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.fftpack` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'fft','ifft','fftn','ifftn','rfft','irfft', + 'fft2','ifft2' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="fftpack", module="basic", + private_modules=["_basic"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/helper.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/helper.py new file mode 100644 index 0000000000000000000000000000000000000000..fcc7000c215f8a7605a2a59b5767b27b2fcd969d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/helper.py @@ -0,0 +1,19 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.fftpack` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'fftshift', 'ifftshift', 'fftfreq', 'rfftfreq', 'next_fast_len' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="fftpack", module="helper", + private_modules=["_helper"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/pseudo_diffs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/pseudo_diffs.py new file mode 100644 index 0000000000000000000000000000000000000000..ecf71ad3256d48d2131c8058072da724cb001af9 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/pseudo_diffs.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.fftpack` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'diff', + 'tilbert', 'itilbert', 'hilbert', 'ihilbert', + 'cs_diff', 'cc_diff', 'sc_diff', 'ss_diff', + 'shift', 'convolve' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="fftpack", module="pseudo_diffs", + private_modules=["_pseudo_diffs"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/realtransforms.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/realtransforms.py new file mode 100644 index 0000000000000000000000000000000000000000..9a392198fccf213bc988a79058bd69515e39f510 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/realtransforms.py @@ -0,0 +1,19 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.fftpack` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__ = [ # noqa: F822 + 'dct', 'idct', 'dst', 'idst', 'dctn', 'idctn', 'dstn', 'idstn' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="fftpack", module="realtransforms", + private_modules=["_realtransforms"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d65c8fc1f4e08d0ef8846bc7b2e0b3326684704d Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_basic.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_basic.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bd009d0c7f53a90296626aaf697e478bc3e4375 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_basic.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_helper.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_helper.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ae3cd65fb3ea1bce9b7f843e208b80b70d11e41 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_helper.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_import.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_import.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27aba9bd7facfcf51ff2f39a63aa55ae66966d7e Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_import.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_pseudo_diffs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_pseudo_diffs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b45f5c0d357f855924439e928196afb2458505c Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_pseudo_diffs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_real_transforms.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_real_transforms.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc3b19da98890dbb3fe0f0856611ae5383adb3c3 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/__pycache__/test_real_transforms.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/fftw_single_ref.npz b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/fftw_single_ref.npz new file mode 100644 index 0000000000000000000000000000000000000000..8953d3303e84f76bdc45dfe0475f1c984d85915b Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/fftw_single_ref.npz differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test.npz b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test.npz new file mode 100644 index 0000000000000000000000000000000000000000..f90294b41d3f21e76318d396ffba6705e6afe1fa Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test.npz differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_basic.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..a7c4b1de867fb4eadc72e17e2e70a02c9ba190a5 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_basic.py @@ -0,0 +1,873 @@ +# Created by Pearu Peterson, September 2002 + +from numpy.testing import (assert_, assert_equal, assert_array_almost_equal, + assert_array_almost_equal_nulp, assert_array_less) +import pytest +from pytest import raises as assert_raises +from scipy.fftpack import ifft, fft, fftn, ifftn, rfft, irfft, fft2 + +from numpy import (arange, array, asarray, zeros, dot, exp, pi, + swapaxes, double, cdouble) +import numpy as np +import numpy.fft +from numpy.random import rand + +# "large" composite numbers supported by FFTPACK +LARGE_COMPOSITE_SIZES = [ + 2**13, + 2**5 * 3**5, + 2**3 * 3**3 * 5**2, +] +SMALL_COMPOSITE_SIZES = [ + 2, + 2*3*5, + 2*2*3*3, +] +# prime +LARGE_PRIME_SIZES = [ + 2011 +] +SMALL_PRIME_SIZES = [ + 29 +] + + +def _assert_close_in_norm(x, y, rtol, size, rdt): + # helper function for testing + err_msg = f"size: {size} rdt: {rdt}" + assert_array_less(np.linalg.norm(x - y), rtol*np.linalg.norm(x), err_msg) + + +def random(size): + return rand(*size) + + +def direct_dft(x): + x = asarray(x) + n = len(x) + y = zeros(n, dtype=cdouble) + w = -arange(n)*(2j*pi/n) + for i in range(n): + y[i] = dot(exp(i*w), x) + return y + + +def direct_idft(x): + x = asarray(x) + n = len(x) + y = zeros(n, dtype=cdouble) + w = arange(n)*(2j*pi/n) + for i in range(n): + y[i] = dot(exp(i*w), x)/n + return y + + +def direct_dftn(x): + x = asarray(x) + for axis in range(len(x.shape)): + x = fft(x, axis=axis) + return x + + +def direct_idftn(x): + x = asarray(x) + for axis in range(len(x.shape)): + x = ifft(x, axis=axis) + return x + + +def direct_rdft(x): + x = asarray(x) + n = len(x) + w = -arange(n)*(2j*pi/n) + r = zeros(n, dtype=double) + for i in range(n//2+1): + y = dot(exp(i*w), x) + if i: + r[2*i-1] = y.real + if 2*i < n: + r[2*i] = y.imag + else: + r[0] = y.real + return r + + +def direct_irdft(x): + x = asarray(x) + n = len(x) + x1 = zeros(n, dtype=cdouble) + for i in range(n//2+1): + if i: + if 2*i < n: + x1[i] = x[2*i-1] + 1j*x[2*i] + x1[n-i] = x[2*i-1] - 1j*x[2*i] + else: + x1[i] = x[2*i-1] + else: + x1[0] = x[0] + return direct_idft(x1).real + + +class _TestFFTBase: + def setup_method(self): + self.cdt = None + self.rdt = None + np.random.seed(1234) + + def test_definition(self): + x = np.array([1,2,3,4+1j,1,2,3,4+2j], dtype=self.cdt) + y = fft(x) + assert_equal(y.dtype, self.cdt) + y1 = direct_dft(x) + assert_array_almost_equal(y,y1) + x = np.array([1,2,3,4+0j,5], dtype=self.cdt) + assert_array_almost_equal(fft(x),direct_dft(x)) + + def test_n_argument_real(self): + x1 = np.array([1,2,3,4], dtype=self.rdt) + x2 = np.array([1,2,3,4], dtype=self.rdt) + y = fft([x1,x2],n=4) + assert_equal(y.dtype, self.cdt) + assert_equal(y.shape,(2,4)) + assert_array_almost_equal(y[0],direct_dft(x1)) + assert_array_almost_equal(y[1],direct_dft(x2)) + + def _test_n_argument_complex(self): + x1 = np.array([1,2,3,4+1j], dtype=self.cdt) + x2 = np.array([1,2,3,4+1j], dtype=self.cdt) + y = fft([x1,x2],n=4) + assert_equal(y.dtype, self.cdt) + assert_equal(y.shape,(2,4)) + assert_array_almost_equal(y[0],direct_dft(x1)) + assert_array_almost_equal(y[1],direct_dft(x2)) + + def test_invalid_sizes(self): + assert_raises(ValueError, fft, []) + assert_raises(ValueError, fft, [[1,1],[2,2]], -5) + + +class TestDoubleFFT(_TestFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + + +class TestSingleFFT(_TestFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + + reason = ("single-precision FFT implementation is partially disabled, " + "until accuracy issues with large prime powers are resolved") + + @pytest.mark.xfail(run=False, reason=reason) + def test_notice(self): + pass + + +class TestFloat16FFT: + + def test_1_argument_real(self): + x1 = np.array([1, 2, 3, 4], dtype=np.float16) + y = fft(x1, n=4) + assert_equal(y.dtype, np.complex64) + assert_equal(y.shape, (4, )) + assert_array_almost_equal(y, direct_dft(x1.astype(np.float32))) + + def test_n_argument_real(self): + x1 = np.array([1, 2, 3, 4], dtype=np.float16) + x2 = np.array([1, 2, 3, 4], dtype=np.float16) + y = fft([x1, x2], n=4) + assert_equal(y.dtype, np.complex64) + assert_equal(y.shape, (2, 4)) + assert_array_almost_equal(y[0], direct_dft(x1.astype(np.float32))) + assert_array_almost_equal(y[1], direct_dft(x2.astype(np.float32))) + + +class _TestIFFTBase: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x = np.array([1,2,3,4+1j,1,2,3,4+2j], self.cdt) + y = ifft(x) + y1 = direct_idft(x) + assert_equal(y.dtype, self.cdt) + assert_array_almost_equal(y,y1) + + x = np.array([1,2,3,4+0j,5], self.cdt) + assert_array_almost_equal(ifft(x),direct_idft(x)) + + def test_definition_real(self): + x = np.array([1,2,3,4,1,2,3,4], self.rdt) + y = ifft(x) + assert_equal(y.dtype, self.cdt) + y1 = direct_idft(x) + assert_array_almost_equal(y,y1) + + x = np.array([1,2,3,4,5], dtype=self.rdt) + assert_equal(y.dtype, self.cdt) + assert_array_almost_equal(ifft(x),direct_idft(x)) + + def test_random_complex(self): + for size in [1,51,111,100,200,64,128,256,1024]: + x = random([size]).astype(self.cdt) + x = random([size]).astype(self.cdt) + 1j*x + y1 = ifft(fft(x)) + y2 = fft(ifft(x)) + assert_equal(y1.dtype, self.cdt) + assert_equal(y2.dtype, self.cdt) + assert_array_almost_equal(y1, x) + assert_array_almost_equal(y2, x) + + def test_random_real(self): + for size in [1,51,111,100,200,64,128,256,1024]: + x = random([size]).astype(self.rdt) + y1 = ifft(fft(x)) + y2 = fft(ifft(x)) + assert_equal(y1.dtype, self.cdt) + assert_equal(y2.dtype, self.cdt) + assert_array_almost_equal(y1, x) + assert_array_almost_equal(y2, x) + + def test_size_accuracy(self): + # Sanity check for the accuracy for prime and non-prime sized inputs + if self.rdt == np.float32: + rtol = 1e-5 + elif self.rdt == np.float64: + rtol = 1e-10 + + for size in LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES: + np.random.seed(1234) + x = np.random.rand(size).astype(self.rdt) + y = ifft(fft(x)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + y = fft(ifft(x)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + + x = (x + 1j*np.random.rand(size)).astype(self.cdt) + y = ifft(fft(x)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + y = fft(ifft(x)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + + def test_invalid_sizes(self): + assert_raises(ValueError, ifft, []) + assert_raises(ValueError, ifft, [[1,1],[2,2]], -5) + + +class TestDoubleIFFT(_TestIFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + + +class TestSingleIFFT(_TestIFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + + +class _TestRFFTBase: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + for t in [[1, 2, 3, 4, 1, 2, 3, 4], [1, 2, 3, 4, 1, 2, 3, 4, 5]]: + x = np.array(t, dtype=self.rdt) + y = rfft(x) + y1 = direct_rdft(x) + assert_array_almost_equal(y,y1) + assert_equal(y.dtype, self.rdt) + + def test_invalid_sizes(self): + assert_raises(ValueError, rfft, []) + assert_raises(ValueError, rfft, [[1,1],[2,2]], -5) + + # See gh-5790 + class MockSeries: + def __init__(self, data): + self.data = np.asarray(data) + + def __getattr__(self, item): + try: + return getattr(self.data, item) + except AttributeError as e: + raise AttributeError("'MockSeries' object " + f"has no attribute '{item}'") from e + + def test_non_ndarray_with_dtype(self): + x = np.array([1., 2., 3., 4., 5.]) + xs = _TestRFFTBase.MockSeries(x) + + expected = [1, 2, 3, 4, 5] + rfft(xs) + + # Data should not have been overwritten + assert_equal(x, expected) + assert_equal(xs.data, expected) + + def test_complex_input(self): + assert_raises(TypeError, rfft, np.arange(4, dtype=np.complex64)) + + +class TestRFFTDouble(_TestRFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + + +class TestRFFTSingle(_TestRFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + + +class _TestIRFFTBase: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x1 = [1,2,3,4,1,2,3,4] + x1_1 = [1,2+3j,4+1j,2+3j,4,2-3j,4-1j,2-3j] + x2 = [1,2,3,4,1,2,3,4,5] + x2_1 = [1,2+3j,4+1j,2+3j,4+5j,4-5j,2-3j,4-1j,2-3j] + + def _test(x, xr): + y = irfft(np.array(x, dtype=self.rdt)) + y1 = direct_irdft(x) + assert_equal(y.dtype, self.rdt) + assert_array_almost_equal(y,y1, decimal=self.ndec) + assert_array_almost_equal(y,ifft(xr), decimal=self.ndec) + + _test(x1, x1_1) + _test(x2, x2_1) + + def test_random_real(self): + for size in [1,51,111,100,200,64,128,256,1024]: + x = random([size]).astype(self.rdt) + y1 = irfft(rfft(x)) + y2 = rfft(irfft(x)) + assert_equal(y1.dtype, self.rdt) + assert_equal(y2.dtype, self.rdt) + assert_array_almost_equal(y1, x, decimal=self.ndec, + err_msg="size=%d" % size) + assert_array_almost_equal(y2, x, decimal=self.ndec, + err_msg="size=%d" % size) + + def test_size_accuracy(self): + # Sanity check for the accuracy for prime and non-prime sized inputs + if self.rdt == np.float32: + rtol = 1e-5 + elif self.rdt == np.float64: + rtol = 1e-10 + + for size in LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES: + np.random.seed(1234) + x = np.random.rand(size).astype(self.rdt) + y = irfft(rfft(x)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + y = rfft(irfft(x)) + _assert_close_in_norm(x, y, rtol, size, self.rdt) + + def test_invalid_sizes(self): + assert_raises(ValueError, irfft, []) + assert_raises(ValueError, irfft, [[1,1],[2,2]], -5) + + def test_complex_input(self): + assert_raises(TypeError, irfft, np.arange(4, dtype=np.complex64)) + + +# self.ndec is bogus; we should have a assert_array_approx_equal for number of +# significant digits + +class TestIRFFTDouble(_TestIRFFTBase): + def setup_method(self): + self.cdt = np.complex128 + self.rdt = np.float64 + self.ndec = 14 + + +class TestIRFFTSingle(_TestIRFFTBase): + def setup_method(self): + self.cdt = np.complex64 + self.rdt = np.float32 + self.ndec = 5 + + +class Testfft2: + def setup_method(self): + np.random.seed(1234) + + def test_regression_244(self): + """FFT returns wrong result with axes parameter.""" + # fftn (and hence fft2) used to break when both axes and shape were + # used + x = numpy.ones((4, 4, 2)) + y = fft2(x, shape=(8, 8), axes=(-3, -2)) + y_r = numpy.fft.fftn(x, s=(8, 8), axes=(-3, -2)) + assert_array_almost_equal(y, y_r) + + def test_invalid_sizes(self): + assert_raises(ValueError, fft2, [[]]) + assert_raises(ValueError, fft2, [[1, 1], [2, 2]], (4, -3)) + + +class TestFftnSingle: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + y = fftn(np.array(x, np.float32)) + assert_(y.dtype == np.complex64, + msg="double precision output with single precision") + + y_r = np.array(fftn(x), np.complex64) + assert_array_almost_equal_nulp(y, y_r) + + @pytest.mark.parametrize('size', SMALL_COMPOSITE_SIZES + SMALL_PRIME_SIZES) + def test_size_accuracy_small(self, size): + x = np.random.rand(size, size) + 1j*np.random.rand(size, size) + y1 = fftn(x.real.astype(np.float32)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 2000) + + @pytest.mark.parametrize('size', LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES) + def test_size_accuracy_large(self, size): + x = np.random.rand(size, 3) + 1j*np.random.rand(size, 3) + y1 = fftn(x.real.astype(np.float32)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 2000) + + def test_definition_float16(self): + x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + y = fftn(np.array(x, np.float16)) + assert_equal(y.dtype, np.complex64) + y_r = np.array(fftn(x), np.complex64) + assert_array_almost_equal_nulp(y, y_r) + + @pytest.mark.parametrize('size', SMALL_COMPOSITE_SIZES + SMALL_PRIME_SIZES) + def test_float16_input_small(self, size): + x = np.random.rand(size, size) + 1j*np.random.rand(size, size) + y1 = fftn(x.real.astype(np.float16)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 5e5) + + @pytest.mark.parametrize('size', LARGE_COMPOSITE_SIZES + LARGE_PRIME_SIZES) + def test_float16_input_large(self, size): + x = np.random.rand(size, 3) + 1j*np.random.rand(size, 3) + y1 = fftn(x.real.astype(np.float16)) + y2 = fftn(x.real.astype(np.float64)).astype(np.complex64) + + assert_equal(y1.dtype, np.complex64) + assert_array_almost_equal_nulp(y1, y2, 2e6) + + +class TestFftn: + def setup_method(self): + np.random.seed(1234) + + def test_definition(self): + x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + y = fftn(x) + assert_array_almost_equal(y, direct_dftn(x)) + + x = random((20, 26)) + assert_array_almost_equal(fftn(x), direct_dftn(x)) + + x = random((5, 4, 3, 20)) + assert_array_almost_equal(fftn(x), direct_dftn(x)) + + def test_axes_argument(self): + # plane == ji_plane, x== kji_space + plane1 = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + plane2 = [[10, 11, 12], + [13, 14, 15], + [16, 17, 18]] + plane3 = [[19, 20, 21], + [22, 23, 24], + [25, 26, 27]] + ki_plane1 = [[1, 2, 3], + [10, 11, 12], + [19, 20, 21]] + ki_plane2 = [[4, 5, 6], + [13, 14, 15], + [22, 23, 24]] + ki_plane3 = [[7, 8, 9], + [16, 17, 18], + [25, 26, 27]] + jk_plane1 = [[1, 10, 19], + [4, 13, 22], + [7, 16, 25]] + jk_plane2 = [[2, 11, 20], + [5, 14, 23], + [8, 17, 26]] + jk_plane3 = [[3, 12, 21], + [6, 15, 24], + [9, 18, 27]] + kj_plane1 = [[1, 4, 7], + [10, 13, 16], [19, 22, 25]] + kj_plane2 = [[2, 5, 8], + [11, 14, 17], [20, 23, 26]] + kj_plane3 = [[3, 6, 9], + [12, 15, 18], [21, 24, 27]] + ij_plane1 = [[1, 4, 7], + [2, 5, 8], + [3, 6, 9]] + ij_plane2 = [[10, 13, 16], + [11, 14, 17], + [12, 15, 18]] + ij_plane3 = [[19, 22, 25], + [20, 23, 26], + [21, 24, 27]] + ik_plane1 = [[1, 10, 19], + [2, 11, 20], + [3, 12, 21]] + ik_plane2 = [[4, 13, 22], + [5, 14, 23], + [6, 15, 24]] + ik_plane3 = [[7, 16, 25], + [8, 17, 26], + [9, 18, 27]] + ijk_space = [jk_plane1, jk_plane2, jk_plane3] + ikj_space = [kj_plane1, kj_plane2, kj_plane3] + jik_space = [ik_plane1, ik_plane2, ik_plane3] + jki_space = [ki_plane1, ki_plane2, ki_plane3] + kij_space = [ij_plane1, ij_plane2, ij_plane3] + x = array([plane1, plane2, plane3]) + + assert_array_almost_equal(fftn(x), + fftn(x, axes=(-3, -2, -1))) # kji_space + assert_array_almost_equal(fftn(x), fftn(x, axes=(0, 1, 2))) + assert_array_almost_equal(fftn(x, axes=(0, 2)), fftn(x, axes=(0, -1))) + y = fftn(x, axes=(2, 1, 0)) # ijk_space + assert_array_almost_equal(swapaxes(y, -1, -3), fftn(ijk_space)) + y = fftn(x, axes=(2, 0, 1)) # ikj_space + assert_array_almost_equal(swapaxes(swapaxes(y, -1, -3), -1, -2), + fftn(ikj_space)) + y = fftn(x, axes=(1, 2, 0)) # jik_space + assert_array_almost_equal(swapaxes(swapaxes(y, -1, -3), -3, -2), + fftn(jik_space)) + y = fftn(x, axes=(1, 0, 2)) # jki_space + assert_array_almost_equal(swapaxes(y, -2, -3), fftn(jki_space)) + y = fftn(x, axes=(0, 2, 1)) # kij_space + assert_array_almost_equal(swapaxes(y, -2, -1), fftn(kij_space)) + + y = fftn(x, axes=(-2, -1)) # ji_plane + assert_array_almost_equal(fftn(plane1), y[0]) + assert_array_almost_equal(fftn(plane2), y[1]) + assert_array_almost_equal(fftn(plane3), y[2]) + + y = fftn(x, axes=(1, 2)) # ji_plane + assert_array_almost_equal(fftn(plane1), y[0]) + assert_array_almost_equal(fftn(plane2), y[1]) + assert_array_almost_equal(fftn(plane3), y[2]) + + y = fftn(x, axes=(-3, -2)) # kj_plane + assert_array_almost_equal(fftn(x[:, :, 0]), y[:, :, 0]) + assert_array_almost_equal(fftn(x[:, :, 1]), y[:, :, 1]) + assert_array_almost_equal(fftn(x[:, :, 2]), y[:, :, 2]) + + y = fftn(x, axes=(-3, -1)) # ki_plane + assert_array_almost_equal(fftn(x[:, 0, :]), y[:, 0, :]) + assert_array_almost_equal(fftn(x[:, 1, :]), y[:, 1, :]) + assert_array_almost_equal(fftn(x[:, 2, :]), y[:, 2, :]) + + y = fftn(x, axes=(-1, -2)) # ij_plane + assert_array_almost_equal(fftn(ij_plane1), swapaxes(y[0], -2, -1)) + assert_array_almost_equal(fftn(ij_plane2), swapaxes(y[1], -2, -1)) + assert_array_almost_equal(fftn(ij_plane3), swapaxes(y[2], -2, -1)) + + y = fftn(x, axes=(-1, -3)) # ik_plane + assert_array_almost_equal(fftn(ik_plane1), + swapaxes(y[:, 0, :], -1, -2)) + assert_array_almost_equal(fftn(ik_plane2), + swapaxes(y[:, 1, :], -1, -2)) + assert_array_almost_equal(fftn(ik_plane3), + swapaxes(y[:, 2, :], -1, -2)) + + y = fftn(x, axes=(-2, -3)) # jk_plane + assert_array_almost_equal(fftn(jk_plane1), + swapaxes(y[:, :, 0], -1, -2)) + assert_array_almost_equal(fftn(jk_plane2), + swapaxes(y[:, :, 1], -1, -2)) + assert_array_almost_equal(fftn(jk_plane3), + swapaxes(y[:, :, 2], -1, -2)) + + y = fftn(x, axes=(-1,)) # i_line + for i in range(3): + for j in range(3): + assert_array_almost_equal(fft(x[i, j, :]), y[i, j, :]) + y = fftn(x, axes=(-2,)) # j_line + for i in range(3): + for j in range(3): + assert_array_almost_equal(fft(x[i, :, j]), y[i, :, j]) + y = fftn(x, axes=(0,)) # k_line + for i in range(3): + for j in range(3): + assert_array_almost_equal(fft(x[:, i, j]), y[:, i, j]) + + y = fftn(x, axes=()) # point + assert_array_almost_equal(y, x) + + def test_shape_argument(self): + small_x = [[1, 2, 3], + [4, 5, 6]] + large_x1 = [[1, 2, 3, 0], + [4, 5, 6, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]] + + y = fftn(small_x, shape=(4, 4)) + assert_array_almost_equal(y, fftn(large_x1)) + + y = fftn(small_x, shape=(3, 4)) + assert_array_almost_equal(y, fftn(large_x1[:-1])) + + def test_shape_axes_argument(self): + small_x = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + large_x1 = array([[1, 2, 3, 0], + [4, 5, 6, 0], + [7, 8, 9, 0], + [0, 0, 0, 0]]) + y = fftn(small_x, shape=(4, 4), axes=(-2, -1)) + assert_array_almost_equal(y, fftn(large_x1)) + y = fftn(small_x, shape=(4, 4), axes=(-1, -2)) + + assert_array_almost_equal(y, swapaxes( + fftn(swapaxes(large_x1, -1, -2)), -1, -2)) + + def test_shape_axes_argument2(self): + # Change shape of the last axis + x = numpy.random.random((10, 5, 3, 7)) + y = fftn(x, axes=(-1,), shape=(8,)) + assert_array_almost_equal(y, fft(x, axis=-1, n=8)) + + # Change shape of an arbitrary axis which is not the last one + x = numpy.random.random((10, 5, 3, 7)) + y = fftn(x, axes=(-2,), shape=(8,)) + assert_array_almost_equal(y, fft(x, axis=-2, n=8)) + + # Change shape of axes: cf #244, where shape and axes were mixed up + x = numpy.random.random((4, 4, 2)) + y = fftn(x, axes=(-3, -2), shape=(8, 8)) + assert_array_almost_equal(y, + numpy.fft.fftn(x, axes=(-3, -2), s=(8, 8))) + + def test_shape_argument_more(self): + x = zeros((4, 4, 2)) + with assert_raises(ValueError, + match="when given, axes and shape arguments" + " have to be of the same length"): + fftn(x, shape=(8, 8, 2, 1)) + + def test_invalid_sizes(self): + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[1, 0\]\) specified"): + fftn([[]]) + + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[4, -3\]\) specified"): + fftn([[1, 1], [2, 2]], (4, -3)) + + +class TestIfftn: + dtype = None + cdtype = None + + def setup_method(self): + np.random.seed(1234) + + @pytest.mark.parametrize('dtype,cdtype,maxnlp', + [(np.float64, np.complex128, 2000), + (np.float32, np.complex64, 3500)]) + def test_definition(self, dtype, cdtype, maxnlp): + x = np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]], dtype=dtype) + y = ifftn(x) + assert_equal(y.dtype, cdtype) + assert_array_almost_equal_nulp(y, direct_idftn(x), maxnlp) + + x = random((20, 26)) + assert_array_almost_equal_nulp(ifftn(x), direct_idftn(x), maxnlp) + + x = random((5, 4, 3, 20)) + assert_array_almost_equal_nulp(ifftn(x), direct_idftn(x), maxnlp) + + @pytest.mark.parametrize('maxnlp', [2000, 3500]) + @pytest.mark.parametrize('size', [1, 2, 51, 32, 64, 92]) + def test_random_complex(self, maxnlp, size): + x = random([size, size]) + 1j*random([size, size]) + assert_array_almost_equal_nulp(ifftn(fftn(x)), x, maxnlp) + assert_array_almost_equal_nulp(fftn(ifftn(x)), x, maxnlp) + + def test_invalid_sizes(self): + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[1, 0\]\) specified"): + ifftn([[]]) + + with assert_raises(ValueError, + match="invalid number of data points" + r" \(\[4, -3\]\) specified"): + ifftn([[1, 1], [2, 2]], (4, -3)) + + +class FakeArray: + def __init__(self, data): + self._data = data + self.__array_interface__ = data.__array_interface__ + + +class FakeArray2: + def __init__(self, data): + self._data = data + + def __array__(self, dtype=None, copy=None): + return self._data + + +class TestOverwrite: + """Check input overwrite behavior of the FFT functions.""" + + real_dtypes = (np.float32, np.float64) + dtypes = real_dtypes + (np.complex64, np.complex128) + fftsizes = [8, 16, 32] + + def _check(self, x, routine, fftsize, axis, overwrite_x): + x2 = x.copy() + for fake in [lambda x: x, FakeArray, FakeArray2]: + routine(fake(x2), fftsize, axis, overwrite_x=overwrite_x) + + sig = "{}({}{!r}, {!r}, axis={!r}, overwrite_x={!r})".format( + routine.__name__, x.dtype, x.shape, fftsize, axis, overwrite_x) + if not overwrite_x: + assert_equal(x2, x, err_msg="spurious overwrite in %s" % sig) + + def _check_1d(self, routine, dtype, shape, axis, overwritable_dtypes, + fftsize, overwrite_x): + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + data = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + data = np.random.randn(*shape) + data = data.astype(dtype) + + self._check(data, routine, fftsize, axis, + overwrite_x=overwrite_x) + + @pytest.mark.parametrize('dtype', dtypes) + @pytest.mark.parametrize('fftsize', fftsizes) + @pytest.mark.parametrize('overwrite_x', [True, False]) + @pytest.mark.parametrize('shape,axes', [((16,), -1), + ((16, 2), 0), + ((2, 16), 1)]) + def test_fft_ifft(self, dtype, fftsize, overwrite_x, shape, axes): + overwritable = (np.complex128, np.complex64) + self._check_1d(fft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + self._check_1d(ifft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + + @pytest.mark.parametrize('dtype', real_dtypes) + @pytest.mark.parametrize('fftsize', fftsizes) + @pytest.mark.parametrize('overwrite_x', [True, False]) + @pytest.mark.parametrize('shape,axes', [((16,), -1), + ((16, 2), 0), + ((2, 16), 1)]) + def test_rfft_irfft(self, dtype, fftsize, overwrite_x, shape, axes): + overwritable = self.real_dtypes + self._check_1d(irfft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + self._check_1d(rfft, dtype, shape, axes, overwritable, + fftsize, overwrite_x) + + def _check_nd_one(self, routine, dtype, shape, axes, overwritable_dtypes, + overwrite_x): + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + data = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + data = np.random.randn(*shape) + data = data.astype(dtype) + + def fftshape_iter(shp): + if len(shp) <= 0: + yield () + else: + for j in (shp[0]//2, shp[0], shp[0]*2): + for rest in fftshape_iter(shp[1:]): + yield (j,) + rest + + if axes is None: + part_shape = shape + else: + part_shape = tuple(np.take(shape, axes)) + + for fftshape in fftshape_iter(part_shape): + self._check(data, routine, fftshape, axes, + overwrite_x=overwrite_x) + if data.ndim > 1: + self._check(data.T, routine, fftshape, axes, + overwrite_x=overwrite_x) + + @pytest.mark.parametrize('dtype', dtypes) + @pytest.mark.parametrize('overwrite_x', [True, False]) + @pytest.mark.parametrize('shape,axes', [((16,), None), + ((16,), (0,)), + ((16, 2), (0,)), + ((2, 16), (1,)), + ((8, 16), None), + ((8, 16), (0, 1)), + ((8, 16, 2), (0, 1)), + ((8, 16, 2), (1, 2)), + ((8, 16, 2), (0,)), + ((8, 16, 2), (1,)), + ((8, 16, 2), (2,)), + ((8, 16, 2), None), + ((8, 16, 2), (0, 1, 2))]) + def test_fftn_ifftn(self, dtype, overwrite_x, shape, axes): + overwritable = (np.complex128, np.complex64) + self._check_nd_one(fftn, dtype, shape, axes, overwritable, + overwrite_x) + self._check_nd_one(ifftn, dtype, shape, axes, overwritable, + overwrite_x) + + +@pytest.mark.parametrize('func', [fftn, ifftn, fft2]) +def test_shape_axes_ndarray(func): + # Test fftn and ifftn work with NumPy arrays for shape and axes arguments + # Regression test for gh-13342 + a = np.random.rand(10, 10) + + expect = func(a, shape=(5, 5)) + actual = func(a, shape=np.array([5, 5])) + assert_equal(expect, actual) + + expect = func(a, axes=(-1,)) + actual = func(a, axes=np.array([-1,])) + assert_equal(expect, actual) + + expect = func(a, shape=(4, 7), axes=(1, 0)) + actual = func(a, shape=np.array([4, 7]), axes=np.array([1, 0])) + assert_equal(expect, actual) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_helper.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..5e7be04f3c0291502b50b101db82d299aadc7772 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_helper.py @@ -0,0 +1,54 @@ +# Created by Pearu Peterson, September 2002 + +__usage__ = """ +Build fftpack: + python setup_fftpack.py build +Run tests if scipy is installed: + python -c 'import scipy;scipy.fftpack.test()' +Run tests if fftpack is not installed: + python tests/test_helper.py [] +""" + +from numpy.testing import assert_array_almost_equal +from scipy.fftpack import fftshift, ifftshift, fftfreq, rfftfreq + +from numpy import pi, random + +class TestFFTShift: + + def test_definition(self): + x = [0,1,2,3,4,-4,-3,-2,-1] + y = [-4,-3,-2,-1,0,1,2,3,4] + assert_array_almost_equal(fftshift(x),y) + assert_array_almost_equal(ifftshift(y),x) + x = [0,1,2,3,4,-5,-4,-3,-2,-1] + y = [-5,-4,-3,-2,-1,0,1,2,3,4] + assert_array_almost_equal(fftshift(x),y) + assert_array_almost_equal(ifftshift(y),x) + + def test_inverse(self): + for n in [1,4,9,100,211]: + x = random.random((n,)) + assert_array_almost_equal(ifftshift(fftshift(x)),x) + + +class TestFFTFreq: + + def test_definition(self): + x = [0,1,2,3,4,-4,-3,-2,-1] + assert_array_almost_equal(9*fftfreq(9),x) + assert_array_almost_equal(9*pi*fftfreq(9,pi),x) + x = [0,1,2,3,4,-5,-4,-3,-2,-1] + assert_array_almost_equal(10*fftfreq(10),x) + assert_array_almost_equal(10*pi*fftfreq(10,pi),x) + + +class TestRFFTFreq: + + def test_definition(self): + x = [0,1,1,2,2,3,3,4,4] + assert_array_almost_equal(9*rfftfreq(9),x) + assert_array_almost_equal(9*pi*rfftfreq(9,pi),x) + x = [0,1,1,2,2,3,3,4,4,5] + assert_array_almost_equal(10*rfftfreq(10),x) + assert_array_almost_equal(10*pi*rfftfreq(10,pi),x) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_import.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_import.py new file mode 100644 index 0000000000000000000000000000000000000000..e71aec9bd07cd4ef486b7e74b9589b6f1634d629 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_import.py @@ -0,0 +1,33 @@ +"""Test possibility of patching fftpack with pyfftw. + +No module source outside of scipy.fftpack should contain an import of +the form `from scipy.fftpack import ...`, so that a simple replacement +of scipy.fftpack by the corresponding fftw interface completely swaps +the two FFT implementations. + +Because this simply inspects source files, we only need to run the test +on one version of Python. +""" + + +from pathlib import Path +import re +import tokenize +import pytest +from numpy.testing import assert_ +import scipy + +class TestFFTPackImport: + @pytest.mark.slow + def test_fftpack_import(self): + base = Path(scipy.__file__).parent + regexp = r"\s*from.+\.fftpack import .*\n" + for path in base.rglob("*.py"): + if base / "fftpack" in path.parents: + continue + # use tokenize to auto-detect encoding on systems where no + # default encoding is defined (e.g., LANG='C') + with tokenize.open(str(path)) as file: + assert_(all(not re.fullmatch(regexp, line) + for line in file), + f"{path} contains an import from fftpack") diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_pseudo_diffs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_pseudo_diffs.py new file mode 100644 index 0000000000000000000000000000000000000000..cec131caced4ccf9cf7c34255f7693769e2ebb12 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_pseudo_diffs.py @@ -0,0 +1,380 @@ +# Created by Pearu Peterson, September 2002 + +__usage__ = """ +Build fftpack: + python setup_fftpack.py build +Run tests if scipy is installed: + python -c 'import scipy;scipy.fftpack.test()' +Run tests if fftpack is not installed: + python tests/test_pseudo_diffs.py [] +""" + +from numpy.testing import (assert_equal, assert_almost_equal, + assert_array_almost_equal) +from scipy.fftpack import (diff, fft, ifft, tilbert, itilbert, hilbert, + ihilbert, shift, fftfreq, cs_diff, sc_diff, + ss_diff, cc_diff) + +import numpy as np +from numpy import arange, sin, cos, pi, exp, tanh, sum, sign +from numpy.random import random + + +def direct_diff(x,k=1,period=None): + fx = fft(x) + n = len(fx) + if period is None: + period = 2*pi + w = fftfreq(n)*2j*pi/period*n + if k < 0: + w = 1 / w**k + w[0] = 0.0 + else: + w = w**k + if n > 2000: + w[250:n-250] = 0.0 + return ifft(w*fx).real + + +def direct_tilbert(x,h=1,period=None): + fx = fft(x) + n = len(fx) + if period is None: + period = 2*pi + w = fftfreq(n)*h*2*pi/period*n + w[0] = 1 + w = 1j/tanh(w) + w[0] = 0j + return ifft(w*fx) + + +def direct_itilbert(x,h=1,period=None): + fx = fft(x) + n = len(fx) + if period is None: + period = 2*pi + w = fftfreq(n)*h*2*pi/period*n + w = -1j*tanh(w) + return ifft(w*fx) + + +def direct_hilbert(x): + fx = fft(x) + n = len(fx) + w = fftfreq(n)*n + w = 1j*sign(w) + return ifft(w*fx) + + +def direct_ihilbert(x): + return -direct_hilbert(x) + + +def direct_shift(x,a,period=None): + n = len(x) + if period is None: + k = fftfreq(n)*1j*n + else: + k = fftfreq(n)*2j*pi/period*n + return ifft(fft(x)*exp(k*a)).real + + +class TestDiff: + + def test_definition(self): + for n in [16,17,64,127,32]: + x = arange(n)*2*pi/n + assert_array_almost_equal(diff(sin(x)),direct_diff(sin(x))) + assert_array_almost_equal(diff(sin(x),2),direct_diff(sin(x),2)) + assert_array_almost_equal(diff(sin(x),3),direct_diff(sin(x),3)) + assert_array_almost_equal(diff(sin(x),4),direct_diff(sin(x),4)) + assert_array_almost_equal(diff(sin(x),5),direct_diff(sin(x),5)) + assert_array_almost_equal(diff(sin(2*x),3),direct_diff(sin(2*x),3)) + assert_array_almost_equal(diff(sin(2*x),4),direct_diff(sin(2*x),4)) + assert_array_almost_equal(diff(cos(x)),direct_diff(cos(x))) + assert_array_almost_equal(diff(cos(x),2),direct_diff(cos(x),2)) + assert_array_almost_equal(diff(cos(x),3),direct_diff(cos(x),3)) + assert_array_almost_equal(diff(cos(x),4),direct_diff(cos(x),4)) + assert_array_almost_equal(diff(cos(2*x)),direct_diff(cos(2*x))) + assert_array_almost_equal(diff(sin(x*n/8)),direct_diff(sin(x*n/8))) + assert_array_almost_equal(diff(cos(x*n/8)),direct_diff(cos(x*n/8))) + for k in range(5): + assert_array_almost_equal(diff(sin(4*x),k),direct_diff(sin(4*x),k)) + assert_array_almost_equal(diff(cos(4*x),k),direct_diff(cos(4*x),k)) + + def test_period(self): + for n in [17,64]: + x = arange(n)/float(n) + assert_array_almost_equal(diff(sin(2*pi*x),period=1), + 2*pi*cos(2*pi*x)) + assert_array_almost_equal(diff(sin(2*pi*x),3,period=1), + -(2*pi)**3*cos(2*pi*x)) + + def test_sin(self): + for n in [32,64,77]: + x = arange(n)*2*pi/n + assert_array_almost_equal(diff(sin(x)),cos(x)) + assert_array_almost_equal(diff(cos(x)),-sin(x)) + assert_array_almost_equal(diff(sin(x),2),-sin(x)) + assert_array_almost_equal(diff(sin(x),4),sin(x)) + assert_array_almost_equal(diff(sin(4*x)),4*cos(4*x)) + assert_array_almost_equal(diff(sin(sin(x))),cos(x)*cos(sin(x))) + + def test_expr(self): + for n in [64,77,100,128,256,512,1024,2048,4096,8192][:5]: + x = arange(n)*2*pi/n + f = sin(x)*cos(4*x)+exp(sin(3*x)) + df = cos(x)*cos(4*x)-4*sin(x)*sin(4*x)+3*cos(3*x)*exp(sin(3*x)) + ddf = -17*sin(x)*cos(4*x)-8*cos(x)*sin(4*x)\ + - 9*sin(3*x)*exp(sin(3*x))+9*cos(3*x)**2*exp(sin(3*x)) + d1 = diff(f) + assert_array_almost_equal(d1,df) + assert_array_almost_equal(diff(df),ddf) + assert_array_almost_equal(diff(f,2),ddf) + assert_array_almost_equal(diff(ddf,-1),df) + + def test_expr_large(self): + for n in [2048,4096]: + x = arange(n)*2*pi/n + f = sin(x)*cos(4*x)+exp(sin(3*x)) + df = cos(x)*cos(4*x)-4*sin(x)*sin(4*x)+3*cos(3*x)*exp(sin(3*x)) + ddf = -17*sin(x)*cos(4*x)-8*cos(x)*sin(4*x)\ + - 9*sin(3*x)*exp(sin(3*x))+9*cos(3*x)**2*exp(sin(3*x)) + assert_array_almost_equal(diff(f),df) + assert_array_almost_equal(diff(df),ddf) + assert_array_almost_equal(diff(ddf,-1),df) + assert_array_almost_equal(diff(f,2),ddf) + + def test_int(self): + n = 64 + x = arange(n)*2*pi/n + assert_array_almost_equal(diff(sin(x),-1),-cos(x)) + assert_array_almost_equal(diff(sin(x),-2),-sin(x)) + assert_array_almost_equal(diff(sin(x),-4),sin(x)) + assert_array_almost_equal(diff(2*cos(2*x),-1),sin(2*x)) + + def test_random_even(self): + for k in [0,2,4,6]: + for n in [60,32,64,56,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + # zeroing Nyquist mode: + f = diff(diff(f,1),-1) + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(diff(diff(f,k),-k),f) + assert_array_almost_equal(diff(diff(f,-k),k),f) + + def test_random_odd(self): + for k in [0,1,2,3,4,5,6]: + for n in [33,65,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(diff(diff(f,k),-k),f) + assert_array_almost_equal(diff(diff(f,-k),k),f) + + def test_zero_nyquist(self): + for k in [0,1,2,3,4,5,6]: + for n in [32,33,64,56,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + # zeroing Nyquist mode: + f = diff(diff(f,1),-1) + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(diff(diff(f,k),-k),f) + assert_array_almost_equal(diff(diff(f,-k),k),f) + + +class TestTilbert: + + def test_definition(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = tilbert(sin(x),h) + y1 = direct_tilbert(sin(x),h) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(tilbert(sin(x),h), + direct_tilbert(sin(x),h)) + assert_array_almost_equal(tilbert(sin(2*x),h), + direct_tilbert(sin(2*x),h)) + + def test_random_even(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [32,64,56]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(direct_tilbert(direct_itilbert(f,h),h),f) + + def test_random_odd(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [33,65,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(itilbert(tilbert(f,h),h),f) + assert_array_almost_equal(tilbert(itilbert(f,h),h),f) + + +class TestITilbert: + + def test_definition(self): + for h in [0.1,0.5,1,5.5,10]: + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = itilbert(sin(x),h) + y1 = direct_itilbert(sin(x),h) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(itilbert(sin(x),h), + direct_itilbert(sin(x),h)) + assert_array_almost_equal(itilbert(sin(2*x),h), + direct_itilbert(sin(2*x),h)) + + +class TestHilbert: + + def test_definition(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = hilbert(sin(x)) + y1 = direct_hilbert(sin(x)) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(hilbert(sin(2*x)), + direct_hilbert(sin(2*x))) + + def test_tilbert_relation(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + f = sin(x)+cos(2*x)*sin(x) + y = hilbert(f) + y1 = direct_hilbert(f) + assert_array_almost_equal(y,y1) + y2 = tilbert(f,h=10) + assert_array_almost_equal(y,y2) + + def test_random_odd(self): + for n in [33,65,55]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(ihilbert(hilbert(f)),f) + assert_array_almost_equal(hilbert(ihilbert(f)),f) + + def test_random_even(self): + for n in [32,64,56]: + f = random((n,)) + af = sum(f,axis=0)/n + f = f-af + # zeroing Nyquist mode: + f = diff(diff(f,1),-1) + assert_almost_equal(sum(f,axis=0),0.0) + assert_array_almost_equal(direct_hilbert(direct_ihilbert(f)),f) + assert_array_almost_equal(hilbert(ihilbert(f)),f) + + +class TestIHilbert: + + def test_definition(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + y = ihilbert(sin(x)) + y1 = direct_ihilbert(sin(x)) + assert_array_almost_equal(y,y1) + assert_array_almost_equal(ihilbert(sin(2*x)), + direct_ihilbert(sin(2*x))) + + def test_itilbert_relation(self): + for n in [16,17,64,127]: + x = arange(n)*2*pi/n + f = sin(x)+cos(2*x)*sin(x) + y = ihilbert(f) + y1 = direct_ihilbert(f) + assert_array_almost_equal(y,y1) + y2 = itilbert(f,h=10) + assert_array_almost_equal(y,y2) + + +class TestShift: + + def test_definition(self): + for n in [18,17,64,127,32,2048,256]: + x = arange(n)*2*pi/n + for a in [0.1,3]: + assert_array_almost_equal(shift(sin(x),a),direct_shift(sin(x),a)) + assert_array_almost_equal(shift(sin(x),a),sin(x+a)) + assert_array_almost_equal(shift(cos(x),a),cos(x+a)) + assert_array_almost_equal(shift(cos(2*x)+sin(x),a), + cos(2*(x+a))+sin(x+a)) + assert_array_almost_equal(shift(exp(sin(x)),a),exp(sin(x+a))) + assert_array_almost_equal(shift(sin(x),2*pi),sin(x)) + assert_array_almost_equal(shift(sin(x),pi),-sin(x)) + assert_array_almost_equal(shift(sin(x),pi/2),cos(x)) + + +class TestOverwrite: + """Check input overwrite behavior """ + + real_dtypes = (np.float32, np.float64) + dtypes = real_dtypes + (np.complex64, np.complex128) + + def _check(self, x, routine, *args, **kwargs): + x2 = x.copy() + routine(x2, *args, **kwargs) + sig = routine.__name__ + if args: + sig += repr(args) + if kwargs: + sig += repr(kwargs) + assert_equal(x2, x, err_msg="spurious overwrite in %s" % sig) + + def _check_1d(self, routine, dtype, shape, *args, **kwargs): + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + data = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + data = np.random.randn(*shape) + data = data.astype(dtype) + self._check(data, routine, *args, **kwargs) + + def test_diff(self): + for dtype in self.dtypes: + self._check_1d(diff, dtype, (16,)) + + def test_tilbert(self): + for dtype in self.dtypes: + self._check_1d(tilbert, dtype, (16,), 1.6) + + def test_itilbert(self): + for dtype in self.dtypes: + self._check_1d(itilbert, dtype, (16,), 1.6) + + def test_hilbert(self): + for dtype in self.dtypes: + self._check_1d(hilbert, dtype, (16,)) + + def test_cs_diff(self): + for dtype in self.dtypes: + self._check_1d(cs_diff, dtype, (16,), 1.0, 4.0) + + def test_sc_diff(self): + for dtype in self.dtypes: + self._check_1d(sc_diff, dtype, (16,), 1.0, 4.0) + + def test_ss_diff(self): + for dtype in self.dtypes: + self._check_1d(ss_diff, dtype, (16,), 1.0, 4.0) + + def test_cc_diff(self): + for dtype in self.dtypes: + self._check_1d(cc_diff, dtype, (16,), 1.0, 4.0) + + def test_shift(self): + for dtype in self.dtypes: + self._check_1d(shift, dtype, (16,), 1.0) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_real_transforms.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_real_transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..6108d460c7864bdc5dd9425bddf93576fac5b39d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/fftpack/tests/test_real_transforms.py @@ -0,0 +1,815 @@ +from os.path import join, dirname + +import numpy as np +from numpy.testing import assert_array_almost_equal, assert_equal +import pytest +from pytest import raises as assert_raises + +from scipy.fftpack._realtransforms import ( + dct, idct, dst, idst, dctn, idctn, dstn, idstn) + +# Matlab reference data +MDATA = np.load(join(dirname(__file__), 'test.npz')) +X = [MDATA['x%d' % i] for i in range(8)] +Y = [MDATA['y%d' % i] for i in range(8)] + +# FFTW reference data: the data are organized as follows: +# * SIZES is an array containing all available sizes +# * for every type (1, 2, 3, 4) and every size, the array dct_type_size +# contains the output of the DCT applied to the input np.linspace(0, size-1, +# size) +FFTWDATA_DOUBLE = np.load(join(dirname(__file__), 'fftw_double_ref.npz')) +FFTWDATA_SINGLE = np.load(join(dirname(__file__), 'fftw_single_ref.npz')) +FFTWDATA_SIZES = FFTWDATA_DOUBLE['sizes'] + + +def fftw_dct_ref(type, size, dt): + x = np.linspace(0, size-1, size).astype(dt) + dt = np.result_type(np.float32, dt) + if dt == np.float64: + data = FFTWDATA_DOUBLE + elif dt == np.float32: + data = FFTWDATA_SINGLE + else: + raise ValueError() + y = (data['dct_%d_%d' % (type, size)]).astype(dt) + return x, y, dt + + +def fftw_dst_ref(type, size, dt): + x = np.linspace(0, size-1, size).astype(dt) + dt = np.result_type(np.float32, dt) + if dt == np.float64: + data = FFTWDATA_DOUBLE + elif dt == np.float32: + data = FFTWDATA_SINGLE + else: + raise ValueError() + y = (data['dst_%d_%d' % (type, size)]).astype(dt) + return x, y, dt + + +def dct_2d_ref(x, **kwargs): + """Calculate reference values for testing dct2.""" + x = np.array(x, copy=True) + for row in range(x.shape[0]): + x[row, :] = dct(x[row, :], **kwargs) + for col in range(x.shape[1]): + x[:, col] = dct(x[:, col], **kwargs) + return x + + +def idct_2d_ref(x, **kwargs): + """Calculate reference values for testing idct2.""" + x = np.array(x, copy=True) + for row in range(x.shape[0]): + x[row, :] = idct(x[row, :], **kwargs) + for col in range(x.shape[1]): + x[:, col] = idct(x[:, col], **kwargs) + return x + + +def dst_2d_ref(x, **kwargs): + """Calculate reference values for testing dst2.""" + x = np.array(x, copy=True) + for row in range(x.shape[0]): + x[row, :] = dst(x[row, :], **kwargs) + for col in range(x.shape[1]): + x[:, col] = dst(x[:, col], **kwargs) + return x + + +def idst_2d_ref(x, **kwargs): + """Calculate reference values for testing idst2.""" + x = np.array(x, copy=True) + for row in range(x.shape[0]): + x[row, :] = idst(x[row, :], **kwargs) + for col in range(x.shape[1]): + x[:, col] = idst(x[:, col], **kwargs) + return x + + +def naive_dct1(x, norm=None): + """Calculate textbook definition version of DCT-I.""" + x = np.array(x, copy=True) + N = len(x) + M = N-1 + y = np.zeros(N) + m0, m = 1, 2 + if norm == 'ortho': + m0 = np.sqrt(1.0/M) + m = np.sqrt(2.0/M) + for k in range(N): + for n in range(1, N-1): + y[k] += m*x[n]*np.cos(np.pi*n*k/M) + y[k] += m0 * x[0] + y[k] += m0 * x[N-1] * (1 if k % 2 == 0 else -1) + if norm == 'ortho': + y[0] *= 1/np.sqrt(2) + y[N-1] *= 1/np.sqrt(2) + return y + + +def naive_dst1(x, norm=None): + """Calculate textbook definition version of DST-I.""" + x = np.array(x, copy=True) + N = len(x) + M = N+1 + y = np.zeros(N) + for k in range(N): + for n in range(N): + y[k] += 2*x[n]*np.sin(np.pi*(n+1.0)*(k+1.0)/M) + if norm == 'ortho': + y *= np.sqrt(0.5/M) + return y + + +def naive_dct4(x, norm=None): + """Calculate textbook definition version of DCT-IV.""" + x = np.array(x, copy=True) + N = len(x) + y = np.zeros(N) + for k in range(N): + for n in range(N): + y[k] += x[n]*np.cos(np.pi*(n+0.5)*(k+0.5)/(N)) + if norm == 'ortho': + y *= np.sqrt(2.0/N) + else: + y *= 2 + return y + + +def naive_dst4(x, norm=None): + """Calculate textbook definition version of DST-IV.""" + x = np.array(x, copy=True) + N = len(x) + y = np.zeros(N) + for k in range(N): + for n in range(N): + y[k] += x[n]*np.sin(np.pi*(n+0.5)*(k+0.5)/(N)) + if norm == 'ortho': + y *= np.sqrt(2.0/N) + else: + y *= 2 + return y + + +class TestComplex: + def test_dct_complex64(self): + y = dct(1j*np.arange(5, dtype=np.complex64)) + x = 1j*dct(np.arange(5)) + assert_array_almost_equal(x, y) + + def test_dct_complex(self): + y = dct(np.arange(5)*1j) + x = 1j*dct(np.arange(5)) + assert_array_almost_equal(x, y) + + def test_idct_complex(self): + y = idct(np.arange(5)*1j) + x = 1j*idct(np.arange(5)) + assert_array_almost_equal(x, y) + + def test_dst_complex64(self): + y = dst(np.arange(5, dtype=np.complex64)*1j) + x = 1j*dst(np.arange(5)) + assert_array_almost_equal(x, y) + + def test_dst_complex(self): + y = dst(np.arange(5)*1j) + x = 1j*dst(np.arange(5)) + assert_array_almost_equal(x, y) + + def test_idst_complex(self): + y = idst(np.arange(5)*1j) + x = 1j*idst(np.arange(5)) + assert_array_almost_equal(x, y) + + +class _TestDCTBase: + def setup_method(self): + self.rdt = None + self.dec = 14 + self.type = None + + def test_definition(self): + for i in FFTWDATA_SIZES: + x, yr, dt = fftw_dct_ref(self.type, i, self.rdt) + y = dct(x, type=self.type) + assert_equal(y.dtype, dt) + # XXX: we divide by np.max(y) because the tests fail otherwise. We + # should really use something like assert_array_approx_equal. The + # difference is due to fftw using a better algorithm w.r.t error + # propagation compared to the ones from fftpack. + assert_array_almost_equal(y / np.max(y), yr / np.max(y), decimal=self.dec, + err_msg="Size %d failed" % i) + + def test_axis(self): + nt = 2 + for i in [7, 8, 9, 16, 32, 64]: + x = np.random.randn(nt, i) + y = dct(x, type=self.type) + for j in range(nt): + assert_array_almost_equal(y[j], dct(x[j], type=self.type), + decimal=self.dec) + + x = x.T + y = dct(x, axis=0, type=self.type) + for j in range(nt): + assert_array_almost_equal(y[:,j], dct(x[:,j], type=self.type), + decimal=self.dec) + + +class _TestDCTIBase(_TestDCTBase): + def test_definition_ortho(self): + # Test orthornomal mode. + dt = np.result_type(np.float32, self.rdt) + for xr in X: + x = np.array(xr, dtype=self.rdt) + y = dct(x, norm='ortho', type=1) + y2 = naive_dct1(x, norm='ortho') + assert_equal(y.dtype, dt) + assert_array_almost_equal(y / np.max(y), y2 / np.max(y), decimal=self.dec) + +class _TestDCTIIBase(_TestDCTBase): + def test_definition_matlab(self): + # Test correspondence with MATLAB (orthornomal mode). + dt = np.result_type(np.float32, self.rdt) + for xr, yr in zip(X, Y): + x = np.array(xr, dtype=dt) + y = dct(x, norm="ortho", type=2) + assert_equal(y.dtype, dt) + assert_array_almost_equal(y, yr, decimal=self.dec) + + +class _TestDCTIIIBase(_TestDCTBase): + def test_definition_ortho(self): + # Test orthornomal mode. + dt = np.result_type(np.float32, self.rdt) + for xr in X: + x = np.array(xr, dtype=self.rdt) + y = dct(x, norm='ortho', type=2) + xi = dct(y, norm="ortho", type=3) + assert_equal(xi.dtype, dt) + assert_array_almost_equal(xi, x, decimal=self.dec) + +class _TestDCTIVBase(_TestDCTBase): + def test_definition_ortho(self): + # Test orthornomal mode. + dt = np.result_type(np.float32, self.rdt) + for xr in X: + x = np.array(xr, dtype=self.rdt) + y = dct(x, norm='ortho', type=4) + y2 = naive_dct4(x, norm='ortho') + assert_equal(y.dtype, dt) + assert_array_almost_equal(y / np.max(y), y2 / np.max(y), decimal=self.dec) + + +class TestDCTIDouble(_TestDCTIBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 10 + self.type = 1 + + +class TestDCTIFloat(_TestDCTIBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 4 + self.type = 1 + + +class TestDCTIInt(_TestDCTIBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 1 + + +class TestDCTIIDouble(_TestDCTIIBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 10 + self.type = 2 + + +class TestDCTIIFloat(_TestDCTIIBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 5 + self.type = 2 + + +class TestDCTIIInt(_TestDCTIIBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 2 + + +class TestDCTIIIDouble(_TestDCTIIIBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 14 + self.type = 3 + + +class TestDCTIIIFloat(_TestDCTIIIBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 5 + self.type = 3 + + +class TestDCTIIIInt(_TestDCTIIIBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 3 + + +class TestDCTIVDouble(_TestDCTIVBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 12 + self.type = 3 + + +class TestDCTIVFloat(_TestDCTIVBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 5 + self.type = 3 + + +class TestDCTIVInt(_TestDCTIVBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 3 + + +class _TestIDCTBase: + def setup_method(self): + self.rdt = None + self.dec = 14 + self.type = None + + def test_definition(self): + for i in FFTWDATA_SIZES: + xr, yr, dt = fftw_dct_ref(self.type, i, self.rdt) + x = idct(yr, type=self.type) + if self.type == 1: + x /= 2 * (i-1) + else: + x /= 2 * i + assert_equal(x.dtype, dt) + # XXX: we divide by np.max(y) because the tests fail otherwise. We + # should really use something like assert_array_approx_equal. The + # difference is due to fftw using a better algorithm w.r.t error + # propagation compared to the ones from fftpack. + assert_array_almost_equal(x / np.max(x), xr / np.max(x), decimal=self.dec, + err_msg="Size %d failed" % i) + + +class TestIDCTIDouble(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 10 + self.type = 1 + + +class TestIDCTIFloat(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 4 + self.type = 1 + + +class TestIDCTIInt(_TestIDCTBase): + def setup_method(self): + self.rdt = int + self.dec = 4 + self.type = 1 + + +class TestIDCTIIDouble(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 10 + self.type = 2 + + +class TestIDCTIIFloat(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 5 + self.type = 2 + + +class TestIDCTIIInt(_TestIDCTBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 2 + + +class TestIDCTIIIDouble(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 14 + self.type = 3 + + +class TestIDCTIIIFloat(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 5 + self.type = 3 + + +class TestIDCTIIIInt(_TestIDCTBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 3 + +class TestIDCTIVDouble(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 12 + self.type = 4 + + +class TestIDCTIVFloat(_TestIDCTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 5 + self.type = 4 + + +class TestIDCTIVInt(_TestIDCTBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 4 + +class _TestDSTBase: + def setup_method(self): + self.rdt = None # dtype + self.dec = None # number of decimals to match + self.type = None # dst type + + def test_definition(self): + for i in FFTWDATA_SIZES: + xr, yr, dt = fftw_dst_ref(self.type, i, self.rdt) + y = dst(xr, type=self.type) + assert_equal(y.dtype, dt) + # XXX: we divide by np.max(y) because the tests fail otherwise. We + # should really use something like assert_array_approx_equal. The + # difference is due to fftw using a better algorithm w.r.t error + # propagation compared to the ones from fftpack. + assert_array_almost_equal(y / np.max(y), yr / np.max(y), decimal=self.dec, + err_msg="Size %d failed" % i) + + +class _TestDSTIBase(_TestDSTBase): + def test_definition_ortho(self): + # Test orthornomal mode. + dt = np.result_type(np.float32, self.rdt) + for xr in X: + x = np.array(xr, dtype=self.rdt) + y = dst(x, norm='ortho', type=1) + y2 = naive_dst1(x, norm='ortho') + assert_equal(y.dtype, dt) + assert_array_almost_equal(y / np.max(y), y2 / np.max(y), decimal=self.dec) + +class _TestDSTIVBase(_TestDSTBase): + def test_definition_ortho(self): + # Test orthornomal mode. + dt = np.result_type(np.float32, self.rdt) + for xr in X: + x = np.array(xr, dtype=self.rdt) + y = dst(x, norm='ortho', type=4) + y2 = naive_dst4(x, norm='ortho') + assert_equal(y.dtype, dt) + assert_array_almost_equal(y, y2, decimal=self.dec) + +class TestDSTIDouble(_TestDSTIBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 12 + self.type = 1 + + +class TestDSTIFloat(_TestDSTIBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 4 + self.type = 1 + + +class TestDSTIInt(_TestDSTIBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 1 + + +class TestDSTIIDouble(_TestDSTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 14 + self.type = 2 + + +class TestDSTIIFloat(_TestDSTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 6 + self.type = 2 + + +class TestDSTIIInt(_TestDSTBase): + def setup_method(self): + self.rdt = int + self.dec = 6 + self.type = 2 + + +class TestDSTIIIDouble(_TestDSTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 14 + self.type = 3 + + +class TestDSTIIIFloat(_TestDSTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 7 + self.type = 3 + + +class TestDSTIIIInt(_TestDSTBase): + def setup_method(self): + self.rdt = int + self.dec = 7 + self.type = 3 + + +class TestDSTIVDouble(_TestDSTIVBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 12 + self.type = 4 + + +class TestDSTIVFloat(_TestDSTIVBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 4 + self.type = 4 + + +class TestDSTIVInt(_TestDSTIVBase): + def setup_method(self): + self.rdt = int + self.dec = 5 + self.type = 4 + + +class _TestIDSTBase: + def setup_method(self): + self.rdt = None + self.dec = None + self.type = None + + def test_definition(self): + for i in FFTWDATA_SIZES: + xr, yr, dt = fftw_dst_ref(self.type, i, self.rdt) + x = idst(yr, type=self.type) + if self.type == 1: + x /= 2 * (i+1) + else: + x /= 2 * i + assert_equal(x.dtype, dt) + # XXX: we divide by np.max(x) because the tests fail otherwise. We + # should really use something like assert_array_approx_equal. The + # difference is due to fftw using a better algorithm w.r.t error + # propagation compared to the ones from fftpack. + assert_array_almost_equal(x / np.max(x), xr / np.max(x), decimal=self.dec, + err_msg="Size %d failed" % i) + + +class TestIDSTIDouble(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 12 + self.type = 1 + + +class TestIDSTIFloat(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 4 + self.type = 1 + + +class TestIDSTIInt(_TestIDSTBase): + def setup_method(self): + self.rdt = int + self.dec = 4 + self.type = 1 + + +class TestIDSTIIDouble(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 14 + self.type = 2 + + +class TestIDSTIIFloat(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 6 + self.type = 2 + + +class TestIDSTIIInt(_TestIDSTBase): + def setup_method(self): + self.rdt = int + self.dec = 6 + self.type = 2 + + +class TestIDSTIIIDouble(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 14 + self.type = 3 + + +class TestIDSTIIIFloat(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 6 + self.type = 3 + + +class TestIDSTIIIInt(_TestIDSTBase): + def setup_method(self): + self.rdt = int + self.dec = 6 + self.type = 3 + + +class TestIDSTIVDouble(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float64 + self.dec = 12 + self.type = 4 + + +class TestIDSTIVFloat(_TestIDSTBase): + def setup_method(self): + self.rdt = np.float32 + self.dec = 6 + self.type = 4 + + +class TestIDSTIVnt(_TestIDSTBase): + def setup_method(self): + self.rdt = int + self.dec = 6 + self.type = 4 + + +class TestOverwrite: + """Check input overwrite behavior.""" + + real_dtypes = [np.float32, np.float64] + + def _check(self, x, routine, type, fftsize, axis, norm, overwrite_x, **kw): + x2 = x.copy() + routine(x2, type, fftsize, axis, norm, overwrite_x=overwrite_x) + + sig = "{}({}{!r}, {!r}, axis={!r}, overwrite_x={!r})".format( + routine.__name__, x.dtype, x.shape, fftsize, axis, overwrite_x) + if not overwrite_x: + assert_equal(x2, x, err_msg="spurious overwrite in %s" % sig) + + def _check_1d(self, routine, dtype, shape, axis): + np.random.seed(1234) + if np.issubdtype(dtype, np.complexfloating): + data = np.random.randn(*shape) + 1j*np.random.randn(*shape) + else: + data = np.random.randn(*shape) + data = data.astype(dtype) + + for type in [1, 2, 3, 4]: + for overwrite_x in [True, False]: + for norm in [None, 'ortho']: + self._check(data, routine, type, None, axis, norm, + overwrite_x) + + def test_dct(self): + for dtype in self.real_dtypes: + self._check_1d(dct, dtype, (16,), -1) + self._check_1d(dct, dtype, (16, 2), 0) + self._check_1d(dct, dtype, (2, 16), 1) + + def test_idct(self): + for dtype in self.real_dtypes: + self._check_1d(idct, dtype, (16,), -1) + self._check_1d(idct, dtype, (16, 2), 0) + self._check_1d(idct, dtype, (2, 16), 1) + + def test_dst(self): + for dtype in self.real_dtypes: + self._check_1d(dst, dtype, (16,), -1) + self._check_1d(dst, dtype, (16, 2), 0) + self._check_1d(dst, dtype, (2, 16), 1) + + def test_idst(self): + for dtype in self.real_dtypes: + self._check_1d(idst, dtype, (16,), -1) + self._check_1d(idst, dtype, (16, 2), 0) + self._check_1d(idst, dtype, (2, 16), 1) + + +class Test_DCTN_IDCTN: + dec = 14 + dct_type = [1, 2, 3, 4] + norms = [None, 'ortho'] + rstate = np.random.RandomState(1234) + shape = (32, 16) + data = rstate.randn(*shape) + + @pytest.mark.parametrize('fforward,finverse', [(dctn, idctn), + (dstn, idstn)]) + @pytest.mark.parametrize('axes', [None, + 1, (1,), [1], + 0, (0,), [0], + (0, 1), [0, 1], + (-2, -1), [-2, -1]]) + @pytest.mark.parametrize('dct_type', dct_type) + @pytest.mark.parametrize('norm', ['ortho']) + def test_axes_round_trip(self, fforward, finverse, axes, dct_type, norm): + tmp = fforward(self.data, type=dct_type, axes=axes, norm=norm) + tmp = finverse(tmp, type=dct_type, axes=axes, norm=norm) + assert_array_almost_equal(self.data, tmp, decimal=12) + + @pytest.mark.parametrize('fforward,fforward_ref', [(dctn, dct_2d_ref), + (dstn, dst_2d_ref)]) + @pytest.mark.parametrize('dct_type', dct_type) + @pytest.mark.parametrize('norm', norms) + def test_dctn_vs_2d_reference(self, fforward, fforward_ref, + dct_type, norm): + y1 = fforward(self.data, type=dct_type, axes=None, norm=norm) + y2 = fforward_ref(self.data, type=dct_type, norm=norm) + assert_array_almost_equal(y1, y2, decimal=11) + + @pytest.mark.parametrize('finverse,finverse_ref', [(idctn, idct_2d_ref), + (idstn, idst_2d_ref)]) + @pytest.mark.parametrize('dct_type', dct_type) + @pytest.mark.parametrize('norm', [None, 'ortho']) + def test_idctn_vs_2d_reference(self, finverse, finverse_ref, + dct_type, norm): + fdata = dctn(self.data, type=dct_type, norm=norm) + y1 = finverse(fdata, type=dct_type, norm=norm) + y2 = finverse_ref(fdata, type=dct_type, norm=norm) + assert_array_almost_equal(y1, y2, decimal=11) + + @pytest.mark.parametrize('fforward,finverse', [(dctn, idctn), + (dstn, idstn)]) + def test_axes_and_shape(self, fforward, finverse): + with assert_raises(ValueError, + match="when given, axes and shape arguments" + " have to be of the same length"): + fforward(self.data, shape=self.data.shape[0], axes=(0, 1)) + + with assert_raises(ValueError, + match="when given, axes and shape arguments" + " have to be of the same length"): + fforward(self.data, shape=self.data.shape[0], axes=None) + + with assert_raises(ValueError, + match="when given, axes and shape arguments" + " have to be of the same length"): + fforward(self.data, shape=self.data.shape, axes=0) + + @pytest.mark.parametrize('fforward', [dctn, dstn]) + def test_shape(self, fforward): + tmp = fforward(self.data, shape=(128, 128), axes=None) + assert_equal(tmp.shape, (128, 128)) + + @pytest.mark.parametrize('fforward,finverse', [(dctn, idctn), + (dstn, idstn)]) + @pytest.mark.parametrize('axes', [1, (1,), [1], + 0, (0,), [0]]) + def test_shape_is_none_with_axes(self, fforward, finverse, axes): + tmp = fforward(self.data, shape=None, axes=axes, norm='ortho') + tmp = finverse(tmp, shape=None, axes=axes, norm='ortho') + assert_array_almost_equal(self.data, tmp, decimal=self.dec) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/bvls.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/bvls.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5875f76a64af1d5640d03f11afb778fee6cd014 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/bvls.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/common.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/common.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fd44c9e01235b592617b6775097802789cf03c9 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/common.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/dogbox.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/dogbox.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b879e6286d96fa4d52a734af1e8870bf872107c7 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/dogbox.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/least_squares.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/least_squares.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..276442acc066b4f594a9a6a2ed22d72b914e8743 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/least_squares.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/lsq_linear.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/lsq_linear.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..339034b2cb554bb83260d8d0f7434b5cdcb27398 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/lsq_linear.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/trf_linear.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/trf_linear.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5caff3e4fadd73d47c5dcfbc8b97e18e6e2e8cb2 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_lsq/__pycache__/trf_linear.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_trlib/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_trlib/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..537b73b3aeb36df09863a0cd24957e5612deb030 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_trlib/__init__.py @@ -0,0 +1,12 @@ +from ._trlib import TRLIBQuadraticSubproblem + +__all__ = ['TRLIBQuadraticSubproblem', 'get_trlib_quadratic_subproblem'] + + +def get_trlib_quadratic_subproblem(tol_rel_i=-2.0, tol_rel_b=-3.0, disp=False): + def subproblem_factory(x, fun, jac, hess, hessp): + return TRLIBQuadraticSubproblem(x, fun, jac, hess, hessp, + tol_rel_i=tol_rel_i, + tol_rel_b=tol_rel_b, + disp=disp) + return subproblem_factory diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_trlib/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_trlib/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6c3de150f5ed629cd33b41f1d976911c324bab4 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/_trlib/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a07250bbeb06542721480c42005307992558fced --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/__init__.py @@ -0,0 +1,133 @@ +""" +Cython optimize root finding API +================================ +The underlying C functions for the following root finders can be accessed +directly using Cython: + +- `~scipy.optimize.bisect` +- `~scipy.optimize.ridder` +- `~scipy.optimize.brenth` +- `~scipy.optimize.brentq` + +The Cython API for the root finding functions is similar except there is no +``disp`` argument. Import the root finding functions using ``cimport`` from +`scipy.optimize.cython_optimize`. :: + + from scipy.optimize.cython_optimize cimport bisect, ridder, brentq, brenth + + +Callback signature +------------------ +The zeros functions in `~scipy.optimize.cython_optimize` expect a callback that +takes a double for the scalar independent variable as the 1st argument and a +user defined ``struct`` with any extra parameters as the 2nd argument. :: + + double (*callback_type)(double, void*) noexcept + + +Examples +-------- +Usage of `~scipy.optimize.cython_optimize` requires Cython to write callbacks +that are compiled into C. For more information on compiling Cython, see the +`Cython Documentation `_. + +These are the basic steps: + +1. Create a Cython ``.pyx`` file, for example: ``myexample.pyx``. +2. Import the desired root finder from `~scipy.optimize.cython_optimize`. +3. Write the callback function, and call the selected root finding function + passing the callback, any extra arguments, and the other solver + parameters. :: + + from scipy.optimize.cython_optimize cimport brentq + + # import math from Cython + from libc cimport math + + myargs = {'C0': 1.0, 'C1': 0.7} # a dictionary of extra arguments + XLO, XHI = 0.5, 1.0 # lower and upper search boundaries + XTOL, RTOL, MITR = 1e-3, 1e-3, 10 # other solver parameters + + # user-defined struct for extra parameters + ctypedef struct test_params: + double C0 + double C1 + + + # user-defined callback + cdef double f(double x, void *args) noexcept: + cdef test_params *myargs = args + return myargs.C0 - math.exp(-(x - myargs.C1)) + + + # Cython wrapper function + cdef double brentq_wrapper_example(dict args, double xa, double xb, + double xtol, double rtol, int mitr): + # Cython automatically casts dictionary to struct + cdef test_params myargs = args + return brentq( + f, xa, xb, &myargs, xtol, rtol, mitr, NULL) + + + # Python function + def brentq_example(args=myargs, xa=XLO, xb=XHI, xtol=XTOL, rtol=RTOL, + mitr=MITR): + '''Calls Cython wrapper from Python.''' + return brentq_wrapper_example(args, xa, xb, xtol, rtol, mitr) + +4. If you want to call your function from Python, create a Cython wrapper, and + a Python function that calls the wrapper, or use ``cpdef``. Then, in Python, + you can import and run the example. :: + + from myexample import brentq_example + + x = brentq_example() + # 0.6999942848231314 + +5. Create a Cython ``.pxd`` file if you need to export any Cython functions. + + +Full output +----------- +The functions in `~scipy.optimize.cython_optimize` can also copy the full +output from the solver to a C ``struct`` that is passed as its last argument. +If you don't want the full output, just pass ``NULL``. The full output +``struct`` must be type ``zeros_full_output``, which is defined in +`scipy.optimize.cython_optimize` with the following fields: + +- ``int funcalls``: number of function calls +- ``int iterations``: number of iterations +- ``int error_num``: error number +- ``double root``: root of function + +The root is copied by `~scipy.optimize.cython_optimize` to the full output +``struct``. An error number of -1 means a sign error, -2 means a convergence +error, and 0 means the solver converged. Continuing from the previous example:: + + from scipy.optimize.cython_optimize cimport zeros_full_output + + + # cython brentq solver with full output + cdef zeros_full_output brentq_full_output_wrapper_example( + dict args, double xa, double xb, double xtol, double rtol, + int mitr): + cdef test_params myargs = args + cdef zeros_full_output my_full_output + # use my_full_output instead of NULL + brentq(f, xa, xb, &myargs, xtol, rtol, mitr, &my_full_output) + return my_full_output + + + # Python function + def brent_full_output_example(args=myargs, xa=XLO, xb=XHI, xtol=XTOL, + rtol=RTOL, mitr=MITR): + '''Returns full output''' + return brentq_full_output_wrapper_example(args, xa, xb, xtol, rtol, + mitr) + + result = brent_full_output_example() + # {'error_num': 0, + # 'funcalls': 6, + # 'iterations': 5, + # 'root': 0.6999942848231314} +""" diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0451d4b244039137df49b9bc1ab7fe134d3707a4 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/_zeros.pxd b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/_zeros.pxd new file mode 100644 index 0000000000000000000000000000000000000000..d3c9e98f0a24d80d15d1f7052f690d608f66dd80 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/_zeros.pxd @@ -0,0 +1,33 @@ +# Legacy public Cython API declarations +# +# NOTE: due to the way Cython ABI compatibility works, **no changes +# should be made to this file** --- any API additions/changes should be +# done in `cython_optimize.pxd` (see gh-11793). + +ctypedef double (*callback_type)(double, void*) noexcept + +ctypedef struct zeros_parameters: + callback_type function + void* args + +ctypedef struct zeros_full_output: + int funcalls + int iterations + int error_num + double root + +cdef double bisect(callback_type f, double xa, double xb, void* args, + double xtol, double rtol, int iter, + zeros_full_output *full_output) noexcept nogil + +cdef double ridder(callback_type f, double xa, double xb, void* args, + double xtol, double rtol, int iter, + zeros_full_output *full_output) noexcept nogil + +cdef double brenth(callback_type f, double xa, double xb, void* args, + double xtol, double rtol, int iter, + zeros_full_output *full_output) noexcept nogil + +cdef double brentq(callback_type f, double xa, double xb, void* args, + double xtol, double rtol, int iter, + zeros_full_output *full_output) noexcept nogil diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/c_zeros.pxd b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/c_zeros.pxd new file mode 100644 index 0000000000000000000000000000000000000000..0d83c80eb886846ddbbd6927e37e05812911f856 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/optimize/cython_optimize/c_zeros.pxd @@ -0,0 +1,26 @@ +cdef extern from "../Zeros/zeros.h": + ctypedef double (*callback_type)(double, void*) noexcept + ctypedef struct scipy_zeros_info: + int funcalls + int iterations + int error_num + +cdef extern from "../Zeros/bisect.c" nogil: + double bisect(callback_type f, double xa, double xb, double xtol, + double rtol, int iter, void *func_data_param, + scipy_zeros_info *solver_stats) + +cdef extern from "../Zeros/ridder.c" nogil: + double ridder(callback_type f, double xa, double xb, double xtol, + double rtol, int iter, void *func_data_param, + scipy_zeros_info *solver_stats) + +cdef extern from "../Zeros/brenth.c" nogil: + double brenth(callback_type f, double xa, double xb, double xtol, + double rtol, int iter, void *func_data_param, + scipy_zeros_info *solver_stats) + +cdef extern from "../Zeros/brentq.c" nogil: + double brentq(callback_type f, double xa, double xb, double xtol, + double rtol, int iter, void *func_data_param, + scipy_zeros_info *solver_stats) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b710a22325cdd15aad7226f6e0b8ff4d1dd729f0 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__init__.py @@ -0,0 +1,323 @@ +""" +===================================== +Sparse matrices (:mod:`scipy.sparse`) +===================================== + +.. currentmodule:: scipy.sparse + +.. toctree:: + :hidden: + + sparse.csgraph + sparse.linalg + +SciPy 2-D sparse array package for numeric data. + +.. note:: + + This package is switching to an array interface, compatible with + NumPy arrays, from the older matrix interface. We recommend that + you use the array objects (`bsr_array`, `coo_array`, etc.) for + all new work. + + When using the array interface, please note that: + + - ``x * y`` no longer performs matrix multiplication, but + element-wise multiplication (just like with NumPy arrays). To + make code work with both arrays and matrices, use ``x @ y`` for + matrix multiplication. + - Operations such as `sum`, that used to produce dense matrices, now + produce arrays, whose multiplication behavior differs similarly. + - Sparse arrays currently must be two-dimensional. This also means + that all *slicing* operations on these objects must produce + two-dimensional results, or they will result in an error. This + will be addressed in a future version. + + The construction utilities (`eye`, `kron`, `random`, `diags`, etc.) + have not yet been ported, but their results can be wrapped into arrays:: + + A = csr_array(eye(3)) + +Contents +======== + +Sparse array classes +-------------------- + +.. autosummary:: + :toctree: generated/ + + bsr_array - Block Sparse Row array + coo_array - A sparse array in COOrdinate format + csc_array - Compressed Sparse Column array + csr_array - Compressed Sparse Row array + dia_array - Sparse array with DIAgonal storage + dok_array - Dictionary Of Keys based sparse array + lil_array - Row-based list of lists sparse array + sparray - Sparse array base class + +Sparse matrix classes +--------------------- + +.. autosummary:: + :toctree: generated/ + + bsr_matrix - Block Sparse Row matrix + coo_matrix - A sparse matrix in COOrdinate format + csc_matrix - Compressed Sparse Column matrix + csr_matrix - Compressed Sparse Row matrix + dia_matrix - Sparse matrix with DIAgonal storage + dok_matrix - Dictionary Of Keys based sparse matrix + lil_matrix - Row-based list of lists sparse matrix + spmatrix - Sparse matrix base class + +Functions +--------- + +Building sparse arrays: + +.. autosummary:: + :toctree: generated/ + + diags_array - Return a sparse array from diagonals + eye_array - Sparse MxN array whose k-th diagonal is all ones + random_array - Random values in a given shape array + block_array - Build a sparse array from sub-blocks + +Building sparse matrices: + +.. autosummary:: + :toctree: generated/ + + eye - Sparse MxN matrix whose k-th diagonal is all ones + identity - Identity matrix in sparse matrix format + diags - Return a sparse matrix from diagonals + spdiags - Return a sparse matrix from diagonals + bmat - Build a sparse matrix from sparse sub-blocks + random - Random values in a given shape matrix + rand - Random values in a given shape matrix (old interface) + +Building larger structures from smaller (array or matrix) + +.. autosummary:: + :toctree: generated/ + + kron - kronecker product of two sparse matrices + kronsum - kronecker sum of sparse matrices + block_diag - Build a block diagonal sparse matrix + tril - Lower triangular portion of a matrix in sparse format + triu - Upper triangular portion of a matrix in sparse format + hstack - Stack sparse matrices horizontally (column wise) + vstack - Stack sparse matrices vertically (row wise) + +Save and load sparse matrices: + +.. autosummary:: + :toctree: generated/ + + save_npz - Save a sparse matrix/array to a file using ``.npz`` format. + load_npz - Load a sparse matrix/array from a file using ``.npz`` format. + +Sparse tools: + +.. autosummary:: + :toctree: generated/ + + find + +Identifying sparse arrays: + +- use `isinstance(A, sp.sparse.sparray)` to check whether an array or matrix. +- use `A.format == 'csr'` to check the sparse format + +Identifying sparse matrices: + +.. autosummary:: + :toctree: generated/ + + issparse + isspmatrix + isspmatrix_csc + isspmatrix_csr + isspmatrix_bsr + isspmatrix_lil + isspmatrix_dok + isspmatrix_coo + isspmatrix_dia + +Submodules +---------- + +.. autosummary:: + + csgraph - Compressed sparse graph routines + linalg - sparse linear algebra routines + +Exceptions +---------- + +.. autosummary:: + :toctree: generated/ + + SparseEfficiencyWarning + SparseWarning + + +Usage information +================= + +There are seven available sparse array types: + + 1. `csc_array`: Compressed Sparse Column format + 2. `csr_array`: Compressed Sparse Row format + 3. `bsr_array`: Block Sparse Row format + 4. `lil_array`: List of Lists format + 5. `dok_array`: Dictionary of Keys format + 6. `coo_array`: COOrdinate format (aka IJV, triplet format) + 7. `dia_array`: DIAgonal format + +To construct an array efficiently, use either `dok_array` or `lil_array`. +The `lil_array` class supports basic slicing and fancy indexing with a +similar syntax to NumPy arrays. As illustrated below, the COO format +may also be used to efficiently construct arrays. Despite their +similarity to NumPy arrays, it is **strongly discouraged** to use NumPy +functions directly on these arrays because NumPy may not properly convert +them for computations, leading to unexpected (and incorrect) results. If you +do want to apply a NumPy function to these arrays, first check if SciPy has +its own implementation for the given sparse array class, or **convert the +sparse array to a NumPy array** (e.g., using the ``toarray`` method of the +class) first before applying the method. + +To perform manipulations such as multiplication or inversion, first +convert the array to either CSC or CSR format. The `lil_array` format is +row-based, so conversion to CSR is efficient, whereas conversion to CSC +is less so. + +All conversions among the CSR, CSC, and COO formats are efficient, +linear-time operations. + +Matrix vector product +--------------------- +To do a vector product between a sparse array and a vector simply use +the array ``dot`` method, as described in its docstring: + +>>> import numpy as np +>>> from scipy.sparse import csr_array +>>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) +>>> v = np.array([1, 0, -1]) +>>> A.dot(v) +array([ 1, -3, -1], dtype=int64) + +.. warning:: As of NumPy 1.7, ``np.dot`` is not aware of sparse arrays, + therefore using it will result on unexpected results or errors. + The corresponding dense array should be obtained first instead: + + >>> np.dot(A.toarray(), v) + array([ 1, -3, -1], dtype=int64) + + but then all the performance advantages would be lost. + +The CSR format is especially suitable for fast matrix vector products. + +Example 1 +--------- +Construct a 1000x1000 `lil_array` and add some values to it: + +>>> from scipy.sparse import lil_array +>>> from scipy.sparse.linalg import spsolve +>>> from numpy.linalg import solve, norm +>>> from numpy.random import rand + +>>> A = lil_array((1000, 1000)) +>>> A[0, :100] = rand(100) +>>> A.setdiag(rand(1000)) + +Now convert it to CSR format and solve A x = b for x: + +>>> A = A.tocsr() +>>> b = rand(1000) +>>> x = spsolve(A, b) + +Convert it to a dense array and solve, and check that the result +is the same: + +>>> x_ = solve(A.toarray(), b) + +Now we can compute norm of the error with: + +>>> err = norm(x-x_) +>>> err < 1e-10 +True + +It should be small :) + + +Example 2 +--------- + +Construct an array in COO format: + +>>> from scipy import sparse +>>> from numpy import array +>>> I = array([0,3,1,0]) +>>> J = array([0,3,1,2]) +>>> V = array([4,5,7,9]) +>>> A = sparse.coo_array((V,(I,J)),shape=(4,4)) + +Notice that the indices do not need to be sorted. + +Duplicate (i,j) entries are summed when converting to CSR or CSC. + +>>> I = array([0,0,1,3,1,0,0]) +>>> J = array([0,2,1,3,1,0,0]) +>>> V = array([1,1,1,1,1,1,1]) +>>> B = sparse.coo_array((V,(I,J)),shape=(4,4)).tocsr() + +This is useful for constructing finite-element stiffness and mass matrices. + +Further details +--------------- + +CSR column indices are not necessarily sorted. Likewise for CSC row +indices. Use the ``.sorted_indices()`` and ``.sort_indices()`` methods when +sorted indices are required (e.g., when passing data to other libraries). + +""" + +# Original code by Travis Oliphant. +# Modified and extended by Ed Schofield, Robert Cimrman, +# Nathan Bell, and Jake Vanderplas. + +import warnings as _warnings + +from ._base import * +from ._csr import * +from ._csc import * +from ._lil import * +from ._dok import * +from ._coo import * +from ._dia import * +from ._bsr import * +from ._construct import * +from ._extract import * +from ._matrix import spmatrix +from ._matrix_io import * + +# For backward compatibility with v0.19. +from . import csgraph + +# Deprecated namespaces, to be removed in v2.0.0 +from . import ( + base, bsr, compressed, construct, coo, csc, csr, data, dia, dok, extract, + lil, sparsetools, sputils +) + +__all__ = [s for s in dir() if not s.startswith('_')] + +# Filter PendingDeprecationWarning for np.matrix introduced with numpy 1.15 +msg = 'the matrix subclass is not the recommended way' +_warnings.filterwarnings('ignore', message=msg) + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c98d981b42cc7c344d240230f223d0ef0bccb5c Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_base.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_base.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5e8d2ff87b133ae3553c79941b0b3e4f3c9e803 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_base.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_bsr.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_bsr.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10d6ba38ede5b4f907814a8031671e040fe3555d Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_bsr.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_compressed.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_compressed.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a084847ed24adba7c73b5328ae65b7e53e249f01 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_compressed.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_construct.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_construct.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4de979b9aaf5ce27b149a695c6b542170f566ea Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_construct.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_coo.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_coo.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3dd46390ec4fa0595e551ad76e951427b5cf7f0 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_coo.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_csc.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_csc.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0b715c1e4577c08625731fc73c961f85d0695bd Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_csc.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_csr.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_csr.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35f012b18092b5f59d038d81e69787a1f4af4e81 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_csr.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_data.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_data.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96d94c08a6863fbdb015bd0f40dea004dd6ffb77 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_data.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_dia.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_dia.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29be76a86584334de9e6279bdd7d020f27428e78 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_dia.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_dok.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_dok.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94c1c133bf720d0c33ddfebcb7b10f2f06213e10 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_dok.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_extract.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_extract.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85d5eb0bb3abb7a0876fe9011fb7da7684fc09ef Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_extract.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_index.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_index.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42bb46636dc47e186464132332b3b2c4e901e63a Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_index.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_lil.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_lil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd339e5bf40eb6fed0f2597a61a4082c8aa2191a Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_lil.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_matrix.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_matrix.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d30b1fdc58c545093bc04fe58a261615724e42b1 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_matrix.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_matrix_io.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_matrix_io.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb7bab9993ef66d6515f69571274cd12c8629ce1 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_matrix_io.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_spfuncs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_spfuncs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eab81680a36bb5f1bab12753a1732e2e3dec1313 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_spfuncs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_sputils.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_sputils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d432050a28a9dc116218bb2928322d1f4ea4405b Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/_sputils.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/base.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/base.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..884d40c8fdcc7f4beb8b90ce2638c31633031993 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/base.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/bsr.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/bsr.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..739493b6cefba920d7c1f407c9d07e4828060b33 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/bsr.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/compressed.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/compressed.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54776de8c2cd96d12f3b259db65ca71a0cc5a474 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/compressed.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/construct.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/construct.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..428e7732fb6fe36a26272514177524aeb77106e7 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/construct.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/coo.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/coo.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4882530fe02b20830be4f57a329e773310c7f7e3 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/coo.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/csc.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/csc.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01d6c65f1b329efad059e8d263a88360cd93aa0d Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/csc.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/csr.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/csr.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..673c052700a930d6acb77c0525d0c7934ef89977 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/csr.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/data.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/data.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6cc733cf59845db0e3ca4bd4a23a48c397e4984 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/data.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/dia.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/dia.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b502659bb18a7868ec5e4011f8f324e8135903c Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/dia.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/dok.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/dok.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afede65ba0d552f6186e991e0a0cd6c0421ea28d Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/dok.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/extract.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/extract.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..186abb9481716db009566f2f8915f664542e5e70 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/extract.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/lil.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/lil.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8cd32c74cbee43db1fb513e2add18031e68341ab Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/lil.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/sparsetools.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/sparsetools.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d1f03120f33b925f00a478c715b39b21ad1aac3 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/sparsetools.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/spfuncs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/spfuncs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75a60d63d6f3d686b8a122d046bb848e74beb217 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/spfuncs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/sputils.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/sputils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9177ec336f089ed331e5dece671b780c0685936 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/__pycache__/sputils.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_base.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_base.py new file mode 100644 index 0000000000000000000000000000000000000000..510f7565065d774d458d61ad33dd493081054a5a --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_base.py @@ -0,0 +1,1390 @@ +"""Base class for sparse matrices""" + +import numpy as np + +from ._sputils import (asmatrix, check_reshape_kwargs, check_shape, + get_sum_dtype, isdense, isscalarlike, + matrix, validateaxis,) + +from ._matrix import spmatrix + +__all__ = ['isspmatrix', 'issparse', 'sparray', + 'SparseWarning', 'SparseEfficiencyWarning'] + + +class SparseWarning(Warning): + pass + + +class SparseFormatWarning(SparseWarning): + pass + + +class SparseEfficiencyWarning(SparseWarning): + pass + + +# The formats that we might potentially understand. +_formats = {'csc': [0, "Compressed Sparse Column"], + 'csr': [1, "Compressed Sparse Row"], + 'dok': [2, "Dictionary Of Keys"], + 'lil': [3, "List of Lists"], + 'dod': [4, "Dictionary of Dictionaries"], + 'sss': [5, "Symmetric Sparse Skyline"], + 'coo': [6, "COOrdinate"], + 'lba': [7, "Linpack BAnded"], + 'egd': [8, "Ellpack-itpack Generalized Diagonal"], + 'dia': [9, "DIAgonal"], + 'bsr': [10, "Block Sparse Row"], + 'msr': [11, "Modified compressed Sparse Row"], + 'bsc': [12, "Block Sparse Column"], + 'msc': [13, "Modified compressed Sparse Column"], + 'ssk': [14, "Symmetric SKyline"], + 'nsk': [15, "Nonsymmetric SKyline"], + 'jad': [16, "JAgged Diagonal"], + 'uss': [17, "Unsymmetric Sparse Skyline"], + 'vbr': [18, "Variable Block Row"], + 'und': [19, "Undefined"] + } + + +# These univariate ufuncs preserve zeros. +_ufuncs_with_fixed_point_at_zero = frozenset([ + np.sin, np.tan, np.arcsin, np.arctan, np.sinh, np.tanh, np.arcsinh, + np.arctanh, np.rint, np.sign, np.expm1, np.log1p, np.deg2rad, + np.rad2deg, np.floor, np.ceil, np.trunc, np.sqrt]) + + +MAXPRINT = 50 + + +class _spbase: + """ This class provides a base class for all sparse arrays. It + cannot be instantiated. Most of the work is provided by subclasses. + """ + + __array_priority__ = 10.1 + _format = 'und' # undefined + + @property + def ndim(self) -> int: + return len(self._shape) + + @property + def _shape_as_2d(self): + s = self._shape + return (1, s[-1]) if len(s) == 1 else s + + @property + def _bsr_container(self): + from ._bsr import bsr_array + return bsr_array + + @property + def _coo_container(self): + from ._coo import coo_array + return coo_array + + @property + def _csc_container(self): + from ._csc import csc_array + return csc_array + + @property + def _csr_container(self): + from ._csr import csr_array + return csr_array + + @property + def _dia_container(self): + from ._dia import dia_array + return dia_array + + @property + def _dok_container(self): + from ._dok import dok_array + return dok_array + + @property + def _lil_container(self): + from ._lil import lil_array + return lil_array + + def __init__(self, arg1, maxprint=MAXPRINT): + self._shape = None + if self.__class__.__name__ == '_spbase': + raise ValueError("This class is not intended" + " to be instantiated directly.") + if isinstance(self, sparray) and np.isscalar(arg1): + raise ValueError( + "scipy sparse array classes do not support instantiation from a scalar" + ) + self.maxprint = maxprint + + @property + def shape(self): + return self._shape + + def reshape(self, *args, **kwargs): + """reshape(self, shape, order='C', copy=False) + + Gives a new shape to a sparse array/matrix without changing its data. + + Parameters + ---------- + shape : length-2 tuple of ints + The new shape should be compatible with the original shape. + order : {'C', 'F'}, optional + Read the elements using this index order. 'C' means to read and + write the elements using C-like index order; e.g., read entire first + row, then second row, etc. 'F' means to read and write the elements + using Fortran-like index order; e.g., read entire first column, then + second column, etc. + copy : bool, optional + Indicates whether or not attributes of self should be copied + whenever possible. The degree to which attributes are copied varies + depending on the type of sparse array being used. + + Returns + ------- + reshaped : sparse array/matrix + A sparse array/matrix with the given `shape`, not necessarily of the same + format as the current object. + + See Also + -------- + numpy.reshape : NumPy's implementation of 'reshape' for ndarrays + """ + # If the shape already matches, don't bother doing an actual reshape + # Otherwise, the default is to convert to COO and use its reshape + is_array = isinstance(self, sparray) + shape = check_shape(args, self.shape, allow_1d=is_array) + order, copy = check_reshape_kwargs(kwargs) + if shape == self.shape: + if copy: + return self.copy() + else: + return self + + return self.tocoo(copy=copy).reshape(shape, order=order, copy=False) + + def resize(self, shape): + """Resize the array/matrix in-place to dimensions given by ``shape`` + + Any elements that lie within the new shape will remain at the same + indices, while non-zero elements lying outside the new shape are + removed. + + Parameters + ---------- + shape : (int, int) + number of rows and columns in the new array/matrix + + Notes + ----- + The semantics are not identical to `numpy.ndarray.resize` or + `numpy.resize`. Here, the same data will be maintained at each index + before and after reshape, if that index is within the new bounds. In + numpy, resizing maintains contiguity of the array, moving elements + around in the logical array but not within a flattened representation. + + We give no guarantees about whether the underlying data attributes + (arrays, etc.) will be modified in place or replaced with new objects. + """ + # As an inplace operation, this requires implementation in each format. + raise NotImplementedError( + f'{type(self).__name__}.resize is not implemented') + + def astype(self, dtype, casting='unsafe', copy=True): + """Cast the array/matrix elements to a specified type. + + Parameters + ---------- + dtype : string or numpy dtype + Typecode or data-type to which to cast the data. + casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional + Controls what kind of data casting may occur. + Defaults to 'unsafe' for backwards compatibility. + 'no' means the data types should not be cast at all. + 'equiv' means only byte-order changes are allowed. + 'safe' means only casts which can preserve values are allowed. + 'same_kind' means only safe casts or casts within a kind, + like float64 to float32, are allowed. + 'unsafe' means any data conversions may be done. + copy : bool, optional + If `copy` is `False`, the result might share some memory with this + array/matrix. If `copy` is `True`, it is guaranteed that the result and + this array/matrix do not share any memory. + """ + + dtype = np.dtype(dtype) + if self.dtype != dtype: + return self.tocsr().astype( + dtype, casting=casting, copy=copy).asformat(self.format) + elif copy: + return self.copy() + else: + return self + + @classmethod + def _ascontainer(cls, X, **kwargs): + if issubclass(cls, sparray): + return np.asarray(X, **kwargs) + else: + return asmatrix(X, **kwargs) + + @classmethod + def _container(cls, X, **kwargs): + if issubclass(cls, sparray): + return np.array(X, **kwargs) + else: + return matrix(X, **kwargs) + + def _asfptype(self): + """Upcast array to a floating point format (if necessary)""" + + fp_types = ['f', 'd', 'F', 'D'] + + if self.dtype.char in fp_types: + return self + else: + for fp_type in fp_types: + if self.dtype <= np.dtype(fp_type): + return self.astype(fp_type) + + raise TypeError('cannot upcast [%s] to a floating ' + 'point format' % self.dtype.name) + + def __iter__(self): + for r in range(self.shape[0]): + yield self[r] + + def _getmaxprint(self): + """Maximum number of elements to display when printed.""" + return self.maxprint + + def count_nonzero(self): + """Number of non-zero entries, equivalent to + + np.count_nonzero(a.toarray()) + + Unlike the nnz property, which return the number of stored + entries (the length of the data attribute), this method counts the + actual number of non-zero entries in data. + """ + raise NotImplementedError("count_nonzero not implemented for %s." % + self.__class__.__name__) + + def _getnnz(self, axis=None): + """Number of stored values, including explicit zeros. + + Parameters + ---------- + axis : None, 0, or 1 + Select between the number of values across the whole array, in + each column, or in each row. + + See also + -------- + count_nonzero : Number of non-zero entries + """ + raise NotImplementedError("getnnz not implemented for %s." % + self.__class__.__name__) + + @property + def nnz(self) -> int: + """Number of stored values, including explicit zeros. + + See also + -------- + count_nonzero : Number of non-zero entries + """ + return self._getnnz() + + @property + def size(self) -> int: + """Number of stored values. + + See also + -------- + count_nonzero : Number of non-zero values. + """ + return self._getnnz() + + @property + def format(self) -> str: + """Format string for matrix.""" + return self._format + + @property + def T(self): + """Transpose.""" + return self.transpose() + + @property + def real(self): + return self._real() + + @property + def imag(self): + return self._imag() + + def __repr__(self): + _, format_name = _formats[self.format] + sparse_cls = 'array' if isinstance(self, sparray) else 'matrix' + return ( + f"<{format_name} sparse {sparse_cls} of dtype '{self.dtype}'\n" + f"\twith {self.nnz} stored elements and shape {self.shape}>" + ) + + def __str__(self): + maxprint = self._getmaxprint() + + A = self.tocoo() + + # helper function, outputs "(i,j) v" + def tostr(coords, data): + pairs = zip(zip(*(c.tolist() for c in coords)), data) + return '\n'.join(f' {idx}\t{val}' for idx, val in pairs) + + out = repr(self) + if self.nnz == 0: + return out + + out += '\n Coords\tValues\n' + if self.nnz > maxprint: + half = maxprint // 2 + out += tostr(tuple(c[:half] for c in A.coords), A.data[:half]) + out += "\n :\t:\n" + half = maxprint - half + out += tostr(tuple(c[-half:] for c in A.coords), A.data[-half:]) + else: + out += tostr(A.coords, A.data) + + return out + + def __bool__(self): # Simple -- other ideas? + if self.shape == (1, 1): + return self.nnz != 0 + else: + raise ValueError("The truth value of an array with more than one " + "element is ambiguous. Use a.any() or a.all().") + __nonzero__ = __bool__ + + # What should len(sparse) return? For consistency with dense matrices, + # perhaps it should be the number of rows? But for some uses the number of + # non-zeros is more important. For now, raise an exception! + def __len__(self): + raise TypeError("sparse array length is ambiguous; use getnnz()" + " or shape[0]") + + def asformat(self, format, copy=False): + """Return this array/matrix in the passed format. + + Parameters + ---------- + format : {str, None} + The desired sparse format ("csr", "csc", "lil", "dok", "array", ...) + or None for no conversion. + copy : bool, optional + If True, the result is guaranteed to not share data with self. + + Returns + ------- + A : This array/matrix in the passed format. + """ + if format is None or format == self.format: + if copy: + return self.copy() + else: + return self + else: + try: + convert_method = getattr(self, 'to' + format) + except AttributeError as e: + raise ValueError(f'Format {format} is unknown.') from e + + # Forward the copy kwarg, if it's accepted. + try: + return convert_method(copy=copy) + except TypeError: + return convert_method() + + ################################################################### + # NOTE: All arithmetic operations use csr_matrix by default. + # Therefore a new sparse array format just needs to define a + # .tocsr() method to provide arithmetic support. Any of these + # methods can be overridden for efficiency. + #################################################################### + + def multiply(self, other): + """Point-wise multiplication by another array/matrix.""" + if isscalarlike(other): + return self._mul_scalar(other) + return self.tocsr().multiply(other) + + def maximum(self, other): + """Element-wise maximum between this and another array/matrix.""" + return self.tocsr().maximum(other) + + def minimum(self, other): + """Element-wise minimum between this and another array/matrix.""" + return self.tocsr().minimum(other) + + def dot(self, other): + """Ordinary dot product + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csr_array + >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) + >>> v = np.array([1, 0, -1]) + >>> A.dot(v) + array([ 1, -3, -1], dtype=int64) + + """ + if np.isscalar(other): + return self * other + else: + return self @ other + + def power(self, n, dtype=None): + """Element-wise power.""" + return self.tocsr().power(n, dtype=dtype) + + def __eq__(self, other): + return self.tocsr().__eq__(other) + + def __ne__(self, other): + return self.tocsr().__ne__(other) + + def __lt__(self, other): + return self.tocsr().__lt__(other) + + def __gt__(self, other): + return self.tocsr().__gt__(other) + + def __le__(self, other): + return self.tocsr().__le__(other) + + def __ge__(self, other): + return self.tocsr().__ge__(other) + + def __abs__(self): + return abs(self.tocsr()) + + def __round__(self, ndigits=0): + return round(self.tocsr(), ndigits=ndigits) + + def _add_sparse(self, other): + return self.tocsr()._add_sparse(other) + + def _add_dense(self, other): + return self.tocoo()._add_dense(other) + + def _sub_sparse(self, other): + return self.tocsr()._sub_sparse(other) + + def _sub_dense(self, other): + return self.todense() - other + + def _rsub_dense(self, other): + # note: this can't be replaced by other + (-self) for unsigned types + return other - self.todense() + + def __add__(self, other): # self + other + if isscalarlike(other): + if other == 0: + return self.copy() + # Now we would add this scalar to every element. + raise NotImplementedError('adding a nonzero scalar to a ' + 'sparse array is not supported') + elif issparse(other): + if other.shape != self.shape: + raise ValueError("inconsistent shapes") + return self._add_sparse(other) + elif isdense(other): + other = np.broadcast_to(other, self.shape) + return self._add_dense(other) + else: + return NotImplemented + + def __radd__(self,other): # other + self + return self.__add__(other) + + def __sub__(self, other): # self - other + if isscalarlike(other): + if other == 0: + return self.copy() + raise NotImplementedError('subtracting a nonzero scalar from a ' + 'sparse array is not supported') + elif issparse(other): + if other.shape != self.shape: + raise ValueError("inconsistent shapes") + return self._sub_sparse(other) + elif isdense(other): + other = np.broadcast_to(other, self.shape) + return self._sub_dense(other) + else: + return NotImplemented + + def __rsub__(self,other): # other - self + if isscalarlike(other): + if other == 0: + return -self.copy() + raise NotImplementedError('subtracting a sparse array from a ' + 'nonzero scalar is not supported') + elif isdense(other): + other = np.broadcast_to(other, self.shape) + return self._rsub_dense(other) + else: + return NotImplemented + + def _matmul_dispatch(self, other): + """np.array-like matmul & `np.matrix`-like mul, i.e. `dot` or `NotImplemented` + + interpret other and call one of the following + self._mul_scalar() + self._matmul_vector() + self._matmul_multivector() + self._matmul_sparse() + """ + # This method has to be different from `__matmul__` because it is also + # called by sparse matrix classes. + + # Currently matrix multiplication is only supported + # for 2D arrays. Hence we unpacked and use only the + # two last axes' lengths. + M, N = self._shape_as_2d + + if other.__class__ is np.ndarray: + # Fast path for the most common case + if other.shape == (N,): + return self._matmul_vector(other) + elif other.shape == (N, 1): + result = self._matmul_vector(other.ravel()) + if self.ndim == 1: + return result + return result.reshape(M, 1) + elif other.ndim == 2 and other.shape[0] == N: + return self._matmul_multivector(other) + + if isscalarlike(other): + # scalar value + return self._mul_scalar(other) + + if issparse(other): + if self.shape[-1] != other.shape[0]: + raise ValueError('dimension mismatch') + return self._matmul_sparse(other) + + # If it's a list or whatever, treat it like an array + other_a = np.asanyarray(other) + + if other_a.ndim == 0 and other_a.dtype == np.object_: + # Not interpretable as an array; return NotImplemented so that + # other's __rmatmul__ can kick in if that's implemented. + return NotImplemented + + try: + other.shape + except AttributeError: + other = other_a + + if other.ndim == 1 or other.ndim == 2 and other.shape[1] == 1: + # dense row or column vector + if other.shape != (N,) and other.shape != (N, 1): + raise ValueError('dimension mismatch') + + result = self._matmul_vector(np.ravel(other)) + + if isinstance(other, np.matrix): + result = self._ascontainer(result) + + if other.ndim == 2 and other.shape[1] == 1: + # If 'other' was an (nx1) column vector, reshape the result + result = result.reshape(-1, 1) + + return result + + elif other.ndim == 2: + ## + # dense 2D array or matrix ("multivector") + + if other.shape[0] != N: + raise ValueError('dimension mismatch') + + result = self._matmul_multivector(np.asarray(other)) + + if isinstance(other, np.matrix): + result = self._ascontainer(result) + + return result + + else: + raise ValueError('could not interpret dimensions') + + def __mul__(self, other): + return self.multiply(other) + + def __rmul__(self, other): # other * self + return self.multiply(other) + + # by default, use CSR for __mul__ handlers + def _mul_scalar(self, other): + return self.tocsr()._mul_scalar(other) + + def _matmul_vector(self, other): + return self.tocsr()._matmul_vector(other) + + def _matmul_multivector(self, other): + return self.tocsr()._matmul_multivector(other) + + def _matmul_sparse(self, other): + return self.tocsr()._matmul_sparse(other) + + def _rmatmul_dispatch(self, other): + if isscalarlike(other): + return self._mul_scalar(other) + else: + # Don't use asarray unless we have to + try: + tr = other.transpose() + except AttributeError: + tr = np.asarray(other).transpose() + ret = self.transpose()._matmul_dispatch(tr) + if ret is NotImplemented: + return NotImplemented + return ret.transpose() + + ####################### + # matmul (@) operator # + ####################### + + def __matmul__(self, other): + if isscalarlike(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self._matmul_dispatch(other) + + def __rmatmul__(self, other): + if isscalarlike(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self._rmatmul_dispatch(other) + + #################### + # Other Arithmetic # + #################### + + def _divide(self, other, true_divide=False, rdivide=False): + if isscalarlike(other): + if rdivide: + if true_divide: + return np.true_divide(other, self.todense()) + else: + return np.divide(other, self.todense()) + + if true_divide and np.can_cast(self.dtype, np.float64): + return self.astype(np.float64)._mul_scalar(1./other) + else: + r = self._mul_scalar(1./other) + + scalar_dtype = np.asarray(other).dtype + if (np.issubdtype(self.dtype, np.integer) and + np.issubdtype(scalar_dtype, np.integer)): + return r.astype(self.dtype) + else: + return r + + elif isdense(other): + if not rdivide: + if true_divide: + recip = np.true_divide(1., other) + else: + recip = np.divide(1., other) + return self.multiply(recip) + else: + if true_divide: + return np.true_divide(other, self.todense()) + else: + return np.divide(other, self.todense()) + elif issparse(other): + if rdivide: + return other._divide(self, true_divide, rdivide=False) + + self_csr = self.tocsr() + if true_divide and np.can_cast(self.dtype, np.float64): + return self_csr.astype(np.float64)._divide_sparse(other) + else: + return self_csr._divide_sparse(other) + else: + return NotImplemented + + def __truediv__(self, other): + return self._divide(other, true_divide=True) + + def __div__(self, other): + # Always do true division + return self._divide(other, true_divide=True) + + def __rtruediv__(self, other): + # Implementing this as the inverse would be too magical -- bail out + return NotImplemented + + def __rdiv__(self, other): + # Implementing this as the inverse would be too magical -- bail out + return NotImplemented + + def __neg__(self): + return -self.tocsr() + + def __iadd__(self, other): + return NotImplemented + + def __isub__(self, other): + return NotImplemented + + def __imul__(self, other): + return NotImplemented + + def __idiv__(self, other): + return self.__itruediv__(other) + + def __itruediv__(self, other): + return NotImplemented + + def __pow__(self, *args, **kwargs): + return self.power(*args, **kwargs) + + def transpose(self, axes=None, copy=False): + """ + Reverses the dimensions of the sparse array/matrix. + + Parameters + ---------- + axes : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except + for the default value. + copy : bool, optional + Indicates whether or not attributes of `self` should be + copied whenever possible. The degree to which attributes + are copied varies depending on the type of sparse array/matrix + being used. + + Returns + ------- + p : `self` with the dimensions reversed. + + Notes + ----- + If `self` is a `csr_array` or a `csc_array`, then this will return a + `csc_array` or a `csr_array`, respectively. + + See Also + -------- + numpy.transpose : NumPy's implementation of 'transpose' for ndarrays + """ + return self.tocsr(copy=copy).transpose(axes=axes, copy=False) + + def conjugate(self, copy=True): + """Element-wise complex conjugation. + + If the array/matrix is of non-complex data type and `copy` is False, + this method does nothing and the data is not copied. + + Parameters + ---------- + copy : bool, optional + If True, the result is guaranteed to not share data with self. + + Returns + ------- + A : The element-wise complex conjugate. + + """ + if np.issubdtype(self.dtype, np.complexfloating): + return self.tocsr(copy=copy).conjugate(copy=False) + elif copy: + return self.copy() + else: + return self + + def conj(self, copy=True): + return self.conjugate(copy=copy) + + conj.__doc__ = conjugate.__doc__ + + def _real(self): + return self.tocsr()._real() + + def _imag(self): + return self.tocsr()._imag() + + def nonzero(self): + """Nonzero indices of the array/matrix. + + Returns a tuple of arrays (row,col) containing the indices + of the non-zero elements of the array. + + Examples + -------- + >>> from scipy.sparse import csr_array + >>> A = csr_array([[1,2,0],[0,0,3],[4,0,5]]) + >>> A.nonzero() + (array([0, 0, 1, 2, 2]), array([0, 1, 2, 0, 2])) + + """ + + # convert to COOrdinate format + A = self.tocoo() + nz_mask = A.data != 0 + return (A.row[nz_mask], A.col[nz_mask]) + + def _getcol(self, j): + """Returns a copy of column j of the array, as an (m x 1) sparse + array (column vector). + """ + if self.ndim == 1: + raise ValueError("getcol not provided for 1d arrays. Use indexing A[j]") + # Subclasses should override this method for efficiency. + # Post-multiply by a (n x 1) column vector 'a' containing all zeros + # except for a_j = 1 + N = self.shape[-1] + if j < 0: + j += N + if j < 0 or j >= N: + raise IndexError("index out of bounds") + col_selector = self._csc_container(([1], [[j], [0]]), + shape=(N, 1), dtype=self.dtype) + result = self @ col_selector + return result + + def _getrow(self, i): + """Returns a copy of row i of the array, as a (1 x n) sparse + array (row vector). + """ + if self.ndim == 1: + raise ValueError("getrow not meaningful for a 1d array") + # Subclasses should override this method for efficiency. + # Pre-multiply by a (1 x m) row vector 'a' containing all zeros + # except for a_i = 1 + M = self.shape[0] + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError("index out of bounds") + row_selector = self._csr_container(([1], [[0], [i]]), + shape=(1, M), dtype=self.dtype) + return row_selector @ self + + # The following dunder methods cannot be implemented. + # + # def __array__(self): + # # Sparse matrices rely on NumPy wrapping them in object arrays under + # # the hood to make unary ufuncs work on them. So we cannot raise + # # TypeError here - which would be handy to not give users object + # # arrays they probably don't want (they're looking for `.toarray()`). + # # + # # Conversion with `toarray()` would also break things because of the + # # behavior discussed above, plus we want to avoid densification by + # # accident because that can too easily blow up memory. + # + # def __array_ufunc__(self): + # # We cannot implement __array_ufunc__ due to mismatching semantics. + # # See gh-7707 and gh-7349 for details. + # + # def __array_function__(self): + # # We cannot implement __array_function__ due to mismatching semantics. + # # See gh-10362 for details. + + def todense(self, order=None, out=None): + """ + Return a dense representation of this sparse array/matrix. + + Parameters + ---------- + order : {'C', 'F'}, optional + Whether to store multi-dimensional data in C (row-major) + or Fortran (column-major) order in memory. The default + is 'None', which provides no ordering guarantees. + Cannot be specified in conjunction with the `out` + argument. + + out : ndarray, 2-D, optional + If specified, uses this array (or `numpy.matrix`) as the + output buffer instead of allocating a new array to + return. The provided array must have the same shape and + dtype as the sparse array/matrix on which you are calling the + method. + + Returns + ------- + arr : numpy.matrix, 2-D + A NumPy matrix object with the same shape and containing + the same data represented by the sparse array/matrix, with the + requested memory order. If `out` was passed and was an + array (rather than a `numpy.matrix`), it will be filled + with the appropriate values and returned wrapped in a + `numpy.matrix` object that shares the same memory. + """ + return self._ascontainer(self.toarray(order=order, out=out)) + + def toarray(self, order=None, out=None): + """ + Return a dense ndarray representation of this sparse array/matrix. + + Parameters + ---------- + order : {'C', 'F'}, optional + Whether to store multidimensional data in C (row-major) + or Fortran (column-major) order in memory. The default + is 'None', which provides no ordering guarantees. + Cannot be specified in conjunction with the `out` + argument. + + out : ndarray, 2-D, optional + If specified, uses this array as the output buffer + instead of allocating a new array to return. The provided + array must have the same shape and dtype as the sparse + array/matrix on which you are calling the method. For most + sparse types, `out` is required to be memory contiguous + (either C or Fortran ordered). + + Returns + ------- + arr : ndarray, 2-D + An array with the same shape and containing the same + data represented by the sparse array/matrix, with the requested + memory order. If `out` was passed, the same object is + returned after being modified in-place to contain the + appropriate values. + """ + return self.tocoo(copy=False).toarray(order=order, out=out) + + # Any sparse array format deriving from _spbase must define one of + # tocsr or tocoo. The other conversion methods may be implemented for + # efficiency, but are not required. + def tocsr(self, copy=False): + """Convert this array/matrix to Compressed Sparse Row format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant csr_array/matrix. + """ + return self.tocoo(copy=copy).tocsr(copy=False) + + def todok(self, copy=False): + """Convert this array/matrix to Dictionary Of Keys format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant dok_array/matrix. + """ + return self.tocoo(copy=copy).todok(copy=False) + + def tocoo(self, copy=False): + """Convert this array/matrix to COOrdinate format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant coo_array/matrix. + """ + return self.tocsr(copy=False).tocoo(copy=copy) + + def tolil(self, copy=False): + """Convert this array/matrix to List of Lists format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant lil_array/matrix. + """ + return self.tocsr(copy=False).tolil(copy=copy) + + def todia(self, copy=False): + """Convert this array/matrix to sparse DIAgonal format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant dia_array/matrix. + """ + return self.tocoo(copy=copy).todia(copy=False) + + def tobsr(self, blocksize=None, copy=False): + """Convert this array/matrix to Block Sparse Row format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant bsr_array/matrix. + + When blocksize=(R, C) is provided, it will be used for construction of + the bsr_array/matrix. + """ + return self.tocsr(copy=False).tobsr(blocksize=blocksize, copy=copy) + + def tocsc(self, copy=False): + """Convert this array/matrix to Compressed Sparse Column format. + + With copy=False, the data/indices may be shared between this array/matrix and + the resultant csc_array/matrix. + """ + return self.tocsr(copy=copy).tocsc(copy=False) + + def copy(self): + """Returns a copy of this array/matrix. + + No data/indices will be shared between the returned value and current + array/matrix. + """ + return self.__class__(self, copy=True) + + def sum(self, axis=None, dtype=None, out=None): + """ + Sum the array/matrix elements over a given axis. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the sum is computed. The default is to + compute the sum of all the array/matrix elements, returning a scalar + (i.e., `axis` = `None`). + dtype : dtype, optional + The type of the returned array/matrix and of the accumulator in which + the elements are summed. The dtype of `a` is used by default + unless `a` has an integer dtype of less precision than the default + platform integer. In that case, if `a` is signed then the platform + integer is used while if `a` is unsigned then an unsigned integer + of the same precision as the platform integer is used. + + .. versionadded:: 0.18.0 + + out : np.matrix, optional + Alternative output matrix in which to place the result. It must + have the same shape as the expected output, but the type of the + output values will be cast if necessary. + + .. versionadded:: 0.18.0 + + Returns + ------- + sum_along_axis : np.matrix + A matrix with the same shape as `self`, with the specified + axis removed. + + See Also + -------- + numpy.matrix.sum : NumPy's implementation of 'sum' for matrices + + """ + validateaxis(axis) + + # Mimic numpy's casting. + res_dtype = get_sum_dtype(self.dtype) + + if self.ndim == 1: + if axis not in (None, -1, 0): + raise ValueError("axis must be None, -1 or 0") + ret = (self @ np.ones(self.shape, dtype=res_dtype)).astype(dtype) + + if out is not None: + if any(dim != 1 for dim in out.shape): + raise ValueError("dimensions do not match") + out[...] = ret + return ret + + # We use multiplication by a matrix of ones to achieve this. + # For some sparse array formats more efficient methods are + # possible -- these should override this function. + M, N = self.shape + + if axis is None: + # sum over rows and columns + return ( + self @ self._ascontainer(np.ones((N, 1), dtype=res_dtype)) + ).sum(dtype=dtype, out=out) + + if axis < 0: + axis += 2 + + # axis = 0 or 1 now + if axis == 0: + # sum over columns + ret = self._ascontainer( + np.ones((1, M), dtype=res_dtype) + ) @ self + else: + # sum over rows + ret = self @ self._ascontainer( + np.ones((N, 1), dtype=res_dtype) + ) + + if out is not None and out.shape != ret.shape: + raise ValueError("dimensions do not match") + + return ret.sum(axis=axis, dtype=dtype, out=out) + + def mean(self, axis=None, dtype=None, out=None): + """ + Compute the arithmetic mean along the specified axis. + + Returns the average of the array/matrix elements. The average is taken + over all elements in the array/matrix by default, otherwise over the + specified axis. `float64` intermediate and return values are used + for integer inputs. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the mean is computed. The default is to compute + the mean of all elements in the array/matrix (i.e., `axis` = `None`). + dtype : data-type, optional + Type to use in computing the mean. For integer inputs, the default + is `float64`; for floating point inputs, it is the same as the + input dtype. + + .. versionadded:: 0.18.0 + + out : np.matrix, optional + Alternative output matrix in which to place the result. It must + have the same shape as the expected output, but the type of the + output values will be cast if necessary. + + .. versionadded:: 0.18.0 + + Returns + ------- + m : np.matrix + + See Also + -------- + numpy.matrix.mean : NumPy's implementation of 'mean' for matrices + + """ + validateaxis(axis) + + res_dtype = self.dtype.type + integral = (np.issubdtype(self.dtype, np.integer) or + np.issubdtype(self.dtype, np.bool_)) + + # output dtype + if dtype is None: + if integral: + res_dtype = np.float64 + else: + res_dtype = np.dtype(dtype).type + + # intermediate dtype for summation + inter_dtype = np.float64 if integral else res_dtype + inter_self = self.astype(inter_dtype) + + if self.ndim == 1: + if axis not in (None, -1, 0): + raise ValueError("axis must be None, -1 or 0") + res = inter_self / self.shape[-1] + return res.sum(dtype=res_dtype, out=out) + + if axis is None: + return (inter_self / (self.shape[0] * self.shape[1]))\ + .sum(dtype=res_dtype, out=out) + + if axis < 0: + axis += 2 + + # axis = 0 or 1 now + if axis == 0: + return (inter_self * (1.0 / self.shape[0])).sum( + axis=0, dtype=res_dtype, out=out) + else: + return (inter_self * (1.0 / self.shape[1])).sum( + axis=1, dtype=res_dtype, out=out) + + def diagonal(self, k=0): + """Returns the kth diagonal of the array/matrix. + + Parameters + ---------- + k : int, optional + Which diagonal to get, corresponding to elements a[i, i+k]. + Default: 0 (the main diagonal). + + .. versionadded:: 1.0 + + See also + -------- + numpy.diagonal : Equivalent numpy function. + + Examples + -------- + >>> from scipy.sparse import csr_array + >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) + >>> A.diagonal() + array([1, 0, 5]) + >>> A.diagonal(k=1) + array([2, 3]) + """ + return self.tocsr().diagonal(k=k) + + def trace(self, offset=0): + """Returns the sum along diagonals of the sparse array/matrix. + + Parameters + ---------- + offset : int, optional + Which diagonal to get, corresponding to elements a[i, i+offset]. + Default: 0 (the main diagonal). + + """ + return self.diagonal(k=offset).sum() + + def setdiag(self, values, k=0): + """ + Set diagonal or off-diagonal elements of the array/matrix. + + Parameters + ---------- + values : array_like + New values of the diagonal elements. + + Values may have any length. If the diagonal is longer than values, + then the remaining diagonal entries will not be set. If values are + longer than the diagonal, then the remaining values are ignored. + + If a scalar value is given, all of the diagonal is set to it. + + k : int, optional + Which off-diagonal to set, corresponding to elements a[i,i+k]. + Default: 0 (the main diagonal). + + """ + M, N = self.shape + if (k > 0 and k >= N) or (k < 0 and -k >= M): + raise ValueError("k exceeds array dimensions") + self._setdiag(np.asarray(values), k) + + def _setdiag(self, values, k): + """This part of the implementation gets overridden by the + different formats. + """ + M, N = self.shape + if k < 0: + if values.ndim == 0: + # broadcast + max_index = min(M+k, N) + for i in range(max_index): + self[i - k, i] = values + else: + max_index = min(M+k, N, len(values)) + if max_index <= 0: + return + for i, v in enumerate(values[:max_index]): + self[i - k, i] = v + else: + if values.ndim == 0: + # broadcast + max_index = min(M, N-k) + for i in range(max_index): + self[i, i + k] = values + else: + max_index = min(M, N-k, len(values)) + if max_index <= 0: + return + for i, v in enumerate(values[:max_index]): + self[i, i + k] = v + + def _process_toarray_args(self, order, out): + if out is not None: + if order is not None: + raise ValueError('order cannot be specified if out ' + 'is not None') + if out.shape != self.shape or out.dtype != self.dtype: + raise ValueError('out array must be same dtype and shape as ' + 'sparse array') + out[...] = 0. + return out + else: + return np.zeros(self.shape, dtype=self.dtype, order=order) + + def _get_index_dtype(self, arrays=(), maxval=None, check_contents=False): + """ + Determine index dtype for array. + + This wraps _sputils.get_index_dtype, providing compatibility for both + array and matrix API sparse matrices. Matrix API sparse matrices would + attempt to downcast the indices - which can be computationally + expensive and undesirable for users. The array API changes this + behaviour. + + See discussion: https://github.com/scipy/scipy/issues/16774 + + The get_index_dtype import is due to implementation details of the test + suite. It allows the decorator ``with_64bit_maxval_limit`` to mock a + lower int32 max value for checks on the matrix API's downcasting + behaviour. + """ + from ._sputils import get_index_dtype + + # Don't check contents for array API + return get_index_dtype(arrays, + maxval, + (check_contents and not isinstance(self, sparray))) + + +class sparray: + """A namespace class to separate sparray from spmatrix""" + + +sparray.__doc__ = _spbase.__doc__ + + +def issparse(x): + """Is `x` of a sparse array or sparse matrix type? + + Parameters + ---------- + x + object to check for being a sparse array or sparse matrix + + Returns + ------- + bool + True if `x` is a sparse array or a sparse matrix, False otherwise + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csr_array, csr_matrix, issparse + >>> issparse(csr_matrix([[5]])) + True + >>> issparse(csr_array([[5]])) + True + >>> issparse(np.array([[5]])) + False + >>> issparse(5) + False + """ + return isinstance(x, _spbase) + + +def isspmatrix(x): + """Is `x` of a sparse matrix type? + + Parameters + ---------- + x + object to check for being a sparse matrix + + Returns + ------- + bool + True if `x` is a sparse matrix, False otherwise + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csr_array, csr_matrix, isspmatrix + >>> isspmatrix(csr_matrix([[5]])) + True + >>> isspmatrix(csr_array([[5]])) + False + >>> isspmatrix(np.array([[5]])) + False + >>> isspmatrix(5) + False + """ + return isinstance(x, spmatrix) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_bsr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_bsr.py new file mode 100644 index 0000000000000000000000000000000000000000..6a8a1be7dabe455c7948b4fc9c53de367e3cb99c --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_bsr.py @@ -0,0 +1,856 @@ +"""Compressed Block Sparse Row format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['bsr_array', 'bsr_matrix', 'isspmatrix_bsr'] + +from warnings import warn + +import numpy as np + +from scipy._lib._util import copy_if_needed +from ._matrix import spmatrix +from ._data import _data_matrix, _minmax_mixin +from ._compressed import _cs_matrix +from ._base import issparse, _formats, _spbase, sparray +from ._sputils import (isshape, getdtype, getdata, to_native, upcast, + check_shape) +from . import _sparsetools +from ._sparsetools import (bsr_matvec, bsr_matvecs, csr_matmat_maxnnz, + bsr_matmat, bsr_transpose, bsr_sort_indices, + bsr_tocsr) + + +class _bsr_base(_cs_matrix, _minmax_mixin): + _format = 'bsr' + + def __init__(self, arg1, shape=None, dtype=None, copy=False, blocksize=None): + _data_matrix.__init__(self, arg1) + + if issparse(arg1): + if arg1.format == self.format and copy: + arg1 = arg1.copy() + else: + arg1 = arg1.tobsr(blocksize=blocksize) + self.indptr, self.indices, self.data, self._shape = ( + arg1.indptr, arg1.indices, arg1.data, arg1._shape + ) + + elif isinstance(arg1,tuple): + if isshape(arg1): + # it's a tuple of matrix dimensions (M,N) + self._shape = check_shape(arg1) + M,N = self.shape + # process blocksize + if blocksize is None: + blocksize = (1,1) + else: + if not isshape(blocksize): + raise ValueError('invalid blocksize=%s' % blocksize) + blocksize = tuple(blocksize) + self.data = np.zeros((0,) + blocksize, getdtype(dtype, default=float)) + + R,C = blocksize + if (M % R) != 0 or (N % C) != 0: + raise ValueError('shape must be multiple of blocksize') + + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + idx_dtype = self._get_index_dtype(maxval=max(M//R, N//C, R, C)) + self.indices = np.zeros(0, dtype=idx_dtype) + self.indptr = np.zeros(M//R + 1, dtype=idx_dtype) + + elif len(arg1) == 2: + # (data,(row,col)) format + coo = self._coo_container(arg1, dtype=dtype, shape=shape) + bsr = coo.tobsr(blocksize=blocksize) + self.indptr, self.indices, self.data, self._shape = ( + bsr.indptr, bsr.indices, bsr.data, bsr._shape + ) + + elif len(arg1) == 3: + # (data,indices,indptr) format + (data, indices, indptr) = arg1 + + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + maxval = 1 + if shape is not None: + maxval = max(shape) + if blocksize is not None: + maxval = max(maxval, max(blocksize)) + idx_dtype = self._get_index_dtype((indices, indptr), maxval=maxval, + check_contents=True) + if not copy: + copy = copy_if_needed + self.indices = np.array(indices, copy=copy, dtype=idx_dtype) + self.indptr = np.array(indptr, copy=copy, dtype=idx_dtype) + self.data = getdata(data, copy=copy, dtype=dtype) + if self.data.ndim != 3: + raise ValueError( + f'BSR data must be 3-dimensional, got shape={self.data.shape}' + ) + if blocksize is not None: + if not isshape(blocksize): + raise ValueError(f'invalid blocksize={blocksize}') + if tuple(blocksize) != self.data.shape[1:]: + raise ValueError( + f'mismatching blocksize={blocksize}' + f' vs {self.data.shape[1:]}' + ) + else: + raise ValueError('unrecognized bsr_array constructor usage') + else: + # must be dense + try: + arg1 = np.asarray(arg1) + except Exception as e: + raise ValueError("unrecognized form for" + " %s_matrix constructor" % self.format) from e + if isinstance(self, sparray) and arg1.ndim != 2: + raise ValueError(f"BSR arrays don't support {arg1.ndim}D input. Use 2D") + arg1 = self._coo_container(arg1, dtype=dtype).tobsr(blocksize=blocksize) + self.indptr, self.indices, self.data, self._shape = ( + arg1.indptr, arg1.indices, arg1.data, arg1._shape + ) + + if shape is not None: + self._shape = check_shape(shape) + else: + if self.shape is None: + # shape not already set, try to infer dimensions + try: + M = len(self.indptr) - 1 + N = self.indices.max() + 1 + except Exception as e: + raise ValueError('unable to infer matrix dimensions') from e + else: + R,C = self.blocksize + self._shape = check_shape((M*R,N*C)) + + if self.shape is None: + if shape is None: + # TODO infer shape here + raise ValueError('need to infer shape') + else: + self._shape = check_shape(shape) + + if dtype is not None: + self.data = self.data.astype(dtype, copy=False) + + self.check_format(full_check=False) + + def check_format(self, full_check=True): + """Check whether the array/matrix respects the BSR format. + + Parameters + ---------- + full_check : bool, optional + If `True`, run rigorous check, scanning arrays for valid values. + Note that activating those check might copy arrays for casting, + modifying indices and index pointers' inplace. + If `False`, run basic checks on attributes. O(1) operations. + Default is `True`. + """ + M,N = self.shape + R,C = self.blocksize + + # index arrays should have integer data types + if self.indptr.dtype.kind != 'i': + warn(f"indptr array has non-integer dtype ({self.indptr.dtype.name})", + stacklevel=2) + if self.indices.dtype.kind != 'i': + warn(f"indices array has non-integer dtype ({self.indices.dtype.name})", + stacklevel=2) + + # check array shapes + if self.indices.ndim != 1 or self.indptr.ndim != 1: + raise ValueError("indices, and indptr should be 1-D") + if self.data.ndim != 3: + raise ValueError("data should be 3-D") + + # check index pointer + if (len(self.indptr) != M//R + 1): + raise ValueError("index pointer size (%d) should be (%d)" % + (len(self.indptr), M//R + 1)) + if (self.indptr[0] != 0): + raise ValueError("index pointer should start with 0") + + # check index and data arrays + if (len(self.indices) != len(self.data)): + raise ValueError("indices and data should have the same size") + if (self.indptr[-1] > len(self.indices)): + raise ValueError("Last value of index pointer should be less than " + "the size of index and data arrays") + + self.prune() + + if full_check: + # check format validity (more expensive) + if self.nnz > 0: + if self.indices.max() >= N//C: + raise ValueError("column index values must be < %d (now max %d)" + % (N//C, self.indices.max())) + if self.indices.min() < 0: + raise ValueError("column index values must be >= 0") + if np.diff(self.indptr).min() < 0: + raise ValueError("index pointer values must form a " + "non-decreasing sequence") + + idx_dtype = self._get_index_dtype((self.indices, self.indptr)) + self.indptr = np.asarray(self.indptr, dtype=idx_dtype) + self.indices = np.asarray(self.indices, dtype=idx_dtype) + self.data = to_native(self.data) + # if not self.has_sorted_indices(): + # warn('Indices were not in sorted order. Sorting indices.') + # self.sort_indices(check_first=False) + + @property + def blocksize(self) -> tuple: + """Block size of the matrix.""" + return self.data.shape[1:] + + def _getnnz(self, axis=None): + if axis is not None: + raise NotImplementedError("_getnnz over an axis is not implemented " + "for BSR format") + R,C = self.blocksize + return int(self.indptr[-1] * R * C) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + + def __repr__(self): + _, fmt = _formats[self.format] + sparse_cls = 'array' if isinstance(self, sparray) else 'matrix' + b = 'x'.join(str(x) for x in self.blocksize) + return ( + f"<{fmt} sparse {sparse_cls} of dtype '{self.dtype}'\n" + f"\twith {self.nnz} stored elements (blocksize={b}) and shape {self.shape}>" + ) + + def diagonal(self, k=0): + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + R, C = self.blocksize + y = np.zeros(min(rows + min(k, 0), cols - max(k, 0)), + dtype=upcast(self.dtype)) + _sparsetools.bsr_diagonal(k, rows // R, cols // C, R, C, + self.indptr, self.indices, + np.ravel(self.data), y) + return y + + diagonal.__doc__ = _spbase.diagonal.__doc__ + + ########################## + # NotImplemented methods # + ########################## + + def __getitem__(self,key): + raise NotImplementedError + + def __setitem__(self,key,val): + raise NotImplementedError + + ###################### + # Arithmetic methods # + ###################### + + def _add_dense(self, other): + return self.tocoo(copy=False)._add_dense(other) + + def _matmul_vector(self, other): + M,N = self.shape + R,C = self.blocksize + + result = np.zeros(self.shape[0], dtype=upcast(self.dtype, other.dtype)) + + bsr_matvec(M//R, N//C, R, C, + self.indptr, self.indices, self.data.ravel(), + other, result) + + return result + + def _matmul_multivector(self,other): + R,C = self.blocksize + M,N = self.shape + n_vecs = other.shape[1] # number of column vectors + + result = np.zeros((M,n_vecs), dtype=upcast(self.dtype,other.dtype)) + + bsr_matvecs(M//R, N//C, n_vecs, R, C, + self.indptr, self.indices, self.data.ravel(), + other.ravel(), result.ravel()) + + return result + + def _matmul_sparse(self, other): + M, K1 = self.shape + K2, N = other.shape + + R,n = self.blocksize + + # convert to this format + if other.format == "bsr": + C = other.blocksize[1] + else: + C = 1 + + if other.format == "csr" and n == 1: + other = other.tobsr(blocksize=(n,C), copy=False) # lightweight conversion + else: + other = other.tobsr(blocksize=(n,C)) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices)) + + bnnz = csr_matmat_maxnnz(M//R, N//C, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + other.indptr.astype(idx_dtype), + other.indices.astype(idx_dtype)) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=bnnz) + indptr = np.empty(self.indptr.shape, dtype=idx_dtype) + indices = np.empty(bnnz, dtype=idx_dtype) + data = np.empty(R*C*bnnz, dtype=upcast(self.dtype,other.dtype)) + + bsr_matmat(bnnz, M//R, N//C, R, C, n, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + np.ravel(self.data), + other.indptr.astype(idx_dtype), + other.indices.astype(idx_dtype), + np.ravel(other.data), + indptr, + indices, + data) + + data = data.reshape(-1,R,C) + + # TODO eliminate zeros + + return self._bsr_container( + (data, indices, indptr), shape=(M, N), blocksize=(R, C) + ) + + ###################### + # Conversion methods # + ###################### + + def tobsr(self, blocksize=None, copy=False): + """Convert this array/matrix into Block Sparse Row Format. + + With copy=False, the data/indices may be shared between this + array/matrix and the resultant bsr_array/bsr_matrix. + + If blocksize=(R, C) is provided, it will be used for determining + block size of the bsr_array/bsr_matrix. + """ + if blocksize not in [None, self.blocksize]: + return self.tocsr().tobsr(blocksize=blocksize) + if copy: + return self.copy() + else: + return self + + def tocsr(self, copy=False): + M, N = self.shape + R, C = self.blocksize + nnz = self.nnz + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(nnz, N)) + indptr = np.empty(M + 1, dtype=idx_dtype) + indices = np.empty(nnz, dtype=idx_dtype) + data = np.empty(nnz, dtype=upcast(self.dtype)) + + bsr_tocsr(M // R, # n_brow + N // C, # n_bcol + R, C, + self.indptr.astype(idx_dtype, copy=False), + self.indices.astype(idx_dtype, copy=False), + self.data, + indptr, + indices, + data) + return self._csr_container((data, indices, indptr), shape=self.shape) + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + def tocsc(self, copy=False): + return self.tocsr(copy=False).tocsc(copy=copy) + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tocoo(self, copy=True): + """Convert this array/matrix to COOrdinate format. + + When copy=False the data array will be shared between + this array/matrix and the resultant coo_array/coo_matrix. + """ + + M,N = self.shape + R,C = self.blocksize + + indptr_diff = np.diff(self.indptr) + if indptr_diff.dtype.itemsize > np.dtype(np.intp).itemsize: + # Check for potential overflow + indptr_diff_limited = indptr_diff.astype(np.intp) + if np.any(indptr_diff_limited != indptr_diff): + raise ValueError("Matrix too big to convert") + indptr_diff = indptr_diff_limited + + idx_dtype = self._get_index_dtype(maxval=max(M, N)) + row = (R * np.arange(M//R, dtype=idx_dtype)).repeat(indptr_diff) + row = row.repeat(R*C).reshape(-1,R,C) + row += np.tile(np.arange(R, dtype=idx_dtype).reshape(-1,1), (1,C)) + row = row.reshape(-1) + + col = ((C * self.indices).astype(idx_dtype, copy=False) + .repeat(R*C).reshape(-1,R,C)) + col += np.tile(np.arange(C, dtype=idx_dtype), (R,1)) + col = col.reshape(-1) + + data = self.data.reshape(-1) + + if copy: + data = data.copy() + + return self._coo_container( + (data, (row, col)), shape=self.shape + ) + + def toarray(self, order=None, out=None): + return self.tocoo(copy=False).toarray(order=order, out=out) + + toarray.__doc__ = _spbase.toarray.__doc__ + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + R, C = self.blocksize + M, N = self.shape + NBLK = self.nnz//(R*C) + + if self.nnz == 0: + return self._bsr_container((N, M), blocksize=(C, R), + dtype=self.dtype, copy=copy) + + indptr = np.empty(N//C + 1, dtype=self.indptr.dtype) + indices = np.empty(NBLK, dtype=self.indices.dtype) + data = np.empty((NBLK, C, R), dtype=self.data.dtype) + + bsr_transpose(M//R, N//C, R, C, + self.indptr, self.indices, self.data.ravel(), + indptr, indices, data.ravel()) + + return self._bsr_container((data, indices, indptr), + shape=(N, M), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + ############################################################## + # methods that examine or modify the internal data structure # + ############################################################## + + def eliminate_zeros(self): + """Remove zero elements in-place.""" + + if not self.nnz: + return # nothing to do + + R,C = self.blocksize + M,N = self.shape + + mask = (self.data != 0).reshape(-1,R*C).sum(axis=1) # nonzero blocks + + nonzero_blocks = mask.nonzero()[0] + + self.data[:len(nonzero_blocks)] = self.data[nonzero_blocks] + + # modifies self.indptr and self.indices *in place* + _sparsetools.csr_eliminate_zeros(M//R, N//C, self.indptr, + self.indices, mask) + self.prune() + + def sum_duplicates(self): + """Eliminate duplicate array/matrix entries by adding them together + + The is an *in place* operation + """ + if self.has_canonical_format: + return + self.sort_indices() + R, C = self.blocksize + M, N = self.shape + + # port of _sparsetools.csr_sum_duplicates + n_row = M // R + nnz = 0 + row_end = 0 + for i in range(n_row): + jj = row_end + row_end = self.indptr[i+1] + while jj < row_end: + j = self.indices[jj] + x = self.data[jj] + jj += 1 + while jj < row_end and self.indices[jj] == j: + x += self.data[jj] + jj += 1 + self.indices[nnz] = j + self.data[nnz] = x + nnz += 1 + self.indptr[i+1] = nnz + + self.prune() # nnz may have changed + self.has_canonical_format = True + + def sort_indices(self): + """Sort the indices of this array/matrix *in place* + """ + if self.has_sorted_indices: + return + + R,C = self.blocksize + M,N = self.shape + + bsr_sort_indices(M//R, N//C, R, C, self.indptr, self.indices, self.data.ravel()) + + self.has_sorted_indices = True + + def prune(self): + """Remove empty space after all non-zero elements. + """ + + R,C = self.blocksize + M,N = self.shape + + if len(self.indptr) != M//R + 1: + raise ValueError("index pointer has invalid length") + + bnnz = self.indptr[-1] + + if len(self.indices) < bnnz: + raise ValueError("indices array has too few elements") + if len(self.data) < bnnz: + raise ValueError("data array has too few elements") + + self.data = self.data[:bnnz] + self.indices = self.indices[:bnnz] + + # utility functions + def _binopt(self, other, op, in_shape=None, out_shape=None): + """Apply the binary operation fn to two sparse matrices.""" + + # Ideally we'd take the GCDs of the blocksize dimensions + # and explode self and other to match. + other = self.__class__(other, blocksize=self.blocksize) + + # e.g. bsr_plus_bsr, etc. + fn = getattr(_sparsetools, self.format + op + self.format) + + R,C = self.blocksize + + max_bnnz = len(self.data) + len(other.data) + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=max_bnnz) + indptr = np.empty(self.indptr.shape, dtype=idx_dtype) + indices = np.empty(max_bnnz, dtype=idx_dtype) + + bool_ops = ['_ne_', '_lt_', '_gt_', '_le_', '_ge_'] + if op in bool_ops: + data = np.empty(R*C*max_bnnz, dtype=np.bool_) + else: + data = np.empty(R*C*max_bnnz, dtype=upcast(self.dtype,other.dtype)) + + fn(self.shape[0]//R, self.shape[1]//C, R, C, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + other.indptr.astype(idx_dtype), + other.indices.astype(idx_dtype), + np.ravel(other.data), + indptr, + indices, + data) + + actual_bnnz = indptr[-1] + indices = indices[:actual_bnnz] + data = data[:R*C*actual_bnnz] + + if actual_bnnz < max_bnnz/2: + indices = indices.copy() + data = data.copy() + + data = data.reshape(-1,R,C) + + return self.__class__((data, indices, indptr), shape=self.shape) + + # needed by _data_matrix + def _with_data(self,data,copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the structure arrays + (i.e. .indptr and .indices) are copied. + """ + if copy: + return self.__class__((data,self.indices.copy(),self.indptr.copy()), + shape=self.shape,dtype=data.dtype) + else: + return self.__class__((data,self.indices,self.indptr), + shape=self.shape,dtype=data.dtype) + +# # these functions are used by the parent class +# # to remove redundancy between bsc_matrix and bsr_matrix +# def _swap(self,x): +# """swap the members of x if this is a column-oriented matrix +# """ +# return (x[0],x[1]) + + +def isspmatrix_bsr(x): + """Is `x` of a bsr_matrix type? + + Parameters + ---------- + x + object to check for being a bsr matrix + + Returns + ------- + bool + True if `x` is a bsr matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import bsr_array, bsr_matrix, csr_matrix, isspmatrix_bsr + >>> isspmatrix_bsr(bsr_matrix([[5]])) + True + >>> isspmatrix_bsr(bsr_array([[5]])) + False + >>> isspmatrix_bsr(csr_matrix([[5]])) + False + """ + return isinstance(x, bsr_matrix) + + +# This namespace class separates array from matrix with isinstance +class bsr_array(_bsr_base, sparray): + """ + Block Sparse Row format sparse array. + + This can be instantiated in several ways: + bsr_array(D, [blocksize=(R,C)]) + where D is a 2-D ndarray. + + bsr_array(S, [blocksize=(R,C)]) + with another sparse array or matrix S (equivalent to S.tobsr()) + + bsr_array((M, N), [blocksize=(R,C), dtype]) + to construct an empty sparse array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + bsr_array((data, ij), [blocksize=(R,C), shape=(M, N)]) + where ``data`` and ``ij`` satisfy ``a[ij[0, k], ij[1, k]] = data[k]`` + + bsr_array((data, indices, indptr), [shape=(M, N)]) + is the standard BSR representation where the block column + indices for row i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding block values are stored in + ``data[ indptr[i]: indptr[i+1] ]``. If the shape parameter is not + supplied, the array dimensions are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + BSR format data array of the array + indices + BSR format index array of the array + indptr + BSR format index pointer array of the array + blocksize + Block size + has_sorted_indices : bool + Whether indices are sorted + has_canonical_format : bool + T + + Notes + ----- + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + **Summary of BSR format** + + The Block Sparse Row (BSR) format is very similar to the Compressed + Sparse Row (CSR) format. BSR is appropriate for sparse matrices with dense + sub matrices like the last example below. Such sparse block matrices often + arise in vector-valued finite element discretizations. In such cases, BSR is + considerably more efficient than CSR and CSC for many sparse arithmetic + operations. + + **Blocksize** + + The blocksize (R,C) must evenly divide the shape of the sparse array (M,N). + That is, R and C must satisfy the relationship ``M % R = 0`` and + ``N % C = 0``. + + If no blocksize is specified, a simple heuristic is applied to determine + an appropriate blocksize. + + **Canonical Format** + + In canonical format, there are no duplicate blocks and indices are sorted + per row. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import bsr_array + >>> bsr_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3 ,4, 5, 6]) + >>> bsr_array((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]).repeat(4).reshape(6, 2, 2) + >>> bsr_array((data,indices,indptr), shape=(6, 6)).toarray() + array([[1, 1, 0, 0, 2, 2], + [1, 1, 0, 0, 2, 2], + [0, 0, 0, 0, 3, 3], + [0, 0, 0, 0, 3, 3], + [4, 4, 5, 5, 6, 6], + [4, 4, 5, 5, 6, 6]]) + + """ + + +class bsr_matrix(spmatrix, _bsr_base): + """ + Block Sparse Row format sparse matrix. + + This can be instantiated in several ways: + bsr_matrix(D, [blocksize=(R,C)]) + where D is a 2-D ndarray. + + bsr_matrix(S, [blocksize=(R,C)]) + with another sparse array or matrix S (equivalent to S.tobsr()) + + bsr_matrix((M, N), [blocksize=(R,C), dtype]) + to construct an empty sparse matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + bsr_matrix((data, ij), [blocksize=(R,C), shape=(M, N)]) + where ``data`` and ``ij`` satisfy ``a[ij[0, k], ij[1, k]] = data[k]`` + + bsr_matrix((data, indices, indptr), [shape=(M, N)]) + is the standard BSR representation where the block column + indices for row i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding block values are stored in + ``data[ indptr[i]: indptr[i+1] ]``. If the shape parameter is not + supplied, the matrix dimensions are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + BSR format data array of the matrix + indices + BSR format index array of the matrix + indptr + BSR format index pointer array of the matrix + blocksize + Block size + has_sorted_indices : bool + Whether indices are sorted + has_canonical_format : bool + T + + Notes + ----- + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + **Summary of BSR format** + + The Block Sparse Row (BSR) format is very similar to the Compressed + Sparse Row (CSR) format. BSR is appropriate for sparse matrices with dense + sub matrices like the last example below. Such sparse block matrices often + arise in vector-valued finite element discretizations. In such cases, BSR is + considerably more efficient than CSR and CSC for many sparse arithmetic + operations. + + **Blocksize** + + The blocksize (R,C) must evenly divide the shape of the sparse matrix (M,N). + That is, R and C must satisfy the relationship ``M % R = 0`` and + ``N % C = 0``. + + If no blocksize is specified, a simple heuristic is applied to determine + an appropriate blocksize. + + **Canonical Format** + + In canonical format, there are no duplicate blocks and indices are sorted + per row. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import bsr_matrix + >>> bsr_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3 ,4, 5, 6]) + >>> bsr_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]).repeat(4).reshape(6, 2, 2) + >>> bsr_matrix((data,indices,indptr), shape=(6, 6)).toarray() + array([[1, 1, 0, 0, 2, 2], + [1, 1, 0, 0, 2, 2], + [0, 0, 0, 0, 3, 3], + [0, 0, 0, 0, 3, 3], + [4, 4, 5, 5, 6, 6], + [4, 4, 5, 5, 6, 6]]) + + """ + diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_compressed.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_compressed.py new file mode 100644 index 0000000000000000000000000000000000000000..a86c09fc5edfd914680de415cefe8f35ee9c4209 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_compressed.py @@ -0,0 +1,1463 @@ +"""Base class for sparse matrix formats using compressed storage.""" +__all__ = [] + +from warnings import warn +import operator + +import numpy as np +from scipy._lib._util import _prune_array, copy_if_needed + +from ._base import _spbase, issparse, sparray, SparseEfficiencyWarning +from ._data import _data_matrix, _minmax_mixin +from . import _sparsetools +from ._sparsetools import (get_csr_submatrix, csr_sample_offsets, csr_todense, + csr_sample_values, csr_row_index, csr_row_slice, + csr_column_index1, csr_column_index2) +from ._index import IndexMixin +from ._sputils import (upcast, upcast_char, to_native, isdense, isshape, + getdtype, isscalarlike, isintlike, downcast_intp_index, + get_sum_dtype, check_shape, is_pydata_spmatrix) + + +class _cs_matrix(_data_matrix, _minmax_mixin, IndexMixin): + """ + base array/matrix class for compressed row- and column-oriented arrays/matrices + """ + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _data_matrix.__init__(self, arg1) + is_array = isinstance(self, sparray) + + if issparse(arg1): + if arg1.format == self.format and copy: + arg1 = arg1.copy() + else: + arg1 = arg1.asformat(self.format) + self.indptr, self.indices, self.data, self._shape = ( + arg1.indptr, arg1.indices, arg1.data, arg1._shape + ) + + elif isinstance(arg1, tuple): + if isshape(arg1, allow_1d=is_array): + # It's a tuple of matrix dimensions (M, N) + # create empty matrix + self._shape = check_shape(arg1, allow_1d=is_array) + M, N = self._swap(self._shape_as_2d) + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + idx_dtype = self._get_index_dtype(maxval=max(self.shape)) + self.data = np.zeros(0, getdtype(dtype, default=float)) + self.indices = np.zeros(0, idx_dtype) + self.indptr = np.zeros(M + 1, dtype=idx_dtype) + else: + if len(arg1) == 2: + # (data, ij) format + coo = self._coo_container(arg1, shape=shape, dtype=dtype) + arrays = coo._coo_to_compressed(self._swap) + self.indptr, self.indices, self.data, self._shape = arrays + self.sum_duplicates() + elif len(arg1) == 3: + # (data, indices, indptr) format + (data, indices, indptr) = arg1 + + # Select index dtype large enough to pass array and + # scalar parameters to sparsetools + maxval = None + if shape is not None and 0 not in shape: + maxval = max(shape) + idx_dtype = self._get_index_dtype((indices, indptr), + maxval=maxval, + check_contents=True) + + if not copy: + copy = copy_if_needed + self.indices = np.array(indices, copy=copy, dtype=idx_dtype) + self.indptr = np.array(indptr, copy=copy, dtype=idx_dtype) + self.data = np.array(data, copy=copy, dtype=dtype) + else: + raise ValueError(f"unrecognized {self.__class__.__name__} " + f"constructor input: {arg1}") + + else: + # must be dense + try: + arg1 = np.asarray(arg1) + except Exception as e: + raise ValueError(f"unrecognized {self.__class__.__name__} " + f"constructor input: {arg1}") from e + if isinstance(self, sparray) and arg1.ndim < 2 and self.format == "csc": + raise ValueError( + f"CSC arrays don't support {arg1.ndim}D input. Use 2D" + ) + coo = self._coo_container(arg1, dtype=dtype) + arrays = coo._coo_to_compressed(self._swap) + self.indptr, self.indices, self.data, self._shape = arrays + + # Read matrix dimensions given, if any + if shape is not None: + self._shape = check_shape(shape, allow_1d=is_array) + elif self.shape is None: + # shape not already set, try to infer dimensions + try: + major_d = len(self.indptr) - 1 + minor_d = self.indices.max() + 1 + except Exception as e: + raise ValueError('unable to infer matrix dimensions') from e + + self._shape = check_shape(self._swap((major_d, minor_d)), allow_1d=is_array) + + if dtype is not None: + self.data = self.data.astype(dtype, copy=False) + + self.check_format(full_check=False) + + def _getnnz(self, axis=None): + if axis is None: + return int(self.indptr[-1]) + elif self.ndim == 1: + if axis in (0, -1): + return int(self.indptr[-1]) + raise ValueError('axis out of bounds') + else: + if axis < 0: + axis += 2 + axis, _ = self._swap((axis, 1 - axis)) + _, N = self._swap(self.shape) + if axis == 0: + return np.bincount(downcast_intp_index(self.indices), + minlength=N) + elif axis == 1: + return np.diff(self.indptr) + raise ValueError('axis out of bounds') + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + + def check_format(self, full_check=True): + """Check whether the array/matrix respects the CSR or CSC format. + + Parameters + ---------- + full_check : bool, optional + If `True`, run rigorous check, scanning arrays for valid values. + Note that activating those check might copy arrays for casting, + modifying indices and index pointers' inplace. + If `False`, run basic checks on attributes. O(1) operations. + Default is `True`. + """ + # index arrays should have integer data types + if self.indptr.dtype.kind != 'i': + warn(f"indptr array has non-integer dtype ({self.indptr.dtype.name})", + stacklevel=3) + if self.indices.dtype.kind != 'i': + warn(f"indices array has non-integer dtype ({self.indices.dtype.name})", + stacklevel=3) + + # check array shapes + for x in [self.data.ndim, self.indices.ndim, self.indptr.ndim]: + if x != 1: + raise ValueError('data, indices, and indptr should be 1-D') + + # check index pointer. Use _swap to determine proper bounds + M, N = self._swap(self._shape_as_2d) + + if (len(self.indptr) != M + 1): + raise ValueError(f"index pointer size {len(self.indptr)} should be {M + 1}") + if (self.indptr[0] != 0): + raise ValueError("index pointer should start with 0") + + # check index and data arrays + if (len(self.indices) != len(self.data)): + raise ValueError("indices and data should have the same size") + if (self.indptr[-1] > len(self.indices)): + raise ValueError("Last value of index pointer should be less than " + "the size of index and data arrays") + + self.prune() + + if full_check: + # check format validity (more expensive) + if self.nnz > 0: + if self.indices.max() >= N: + raise ValueError(f"indices must be < {N}") + if self.indices.min() < 0: + raise ValueError("indices must be >= 0") + if np.diff(self.indptr).min() < 0: + raise ValueError("indptr must be a non-decreasing sequence") + + idx_dtype = self._get_index_dtype((self.indptr, self.indices)) + self.indptr = np.asarray(self.indptr, dtype=idx_dtype) + self.indices = np.asarray(self.indices, dtype=idx_dtype) + self.data = to_native(self.data) + + # if not self.has_sorted_indices(): + # warn('Indices were not in sorted order. Sorting indices.') + # self.sort_indices() + # assert(self.has_sorted_indices()) + # TODO check for duplicates? + + ####################### + # Boolean comparisons # + ####################### + + def _scalar_binopt(self, other, op): + """Scalar version of self._binopt, for cases in which no new nonzeros + are added. Produces a new sparse array in canonical form. + """ + self.sum_duplicates() + res = self._with_data(op(self.data, other), copy=True) + res.eliminate_zeros() + return res + + def __eq__(self, other): + # Scalar other. + if isscalarlike(other): + if np.isnan(other): + return self.__class__(self.shape, dtype=np.bool_) + + if other == 0: + warn("Comparing a sparse matrix with 0 using == is inefficient" + ", try using != instead.", SparseEfficiencyWarning, + stacklevel=3) + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + inv = self._scalar_binopt(other, operator.ne) + return all_true - inv + else: + return self._scalar_binopt(other, operator.eq) + # Dense other. + elif isdense(other): + return self.todense() == other + # Pydata sparse other. + elif is_pydata_spmatrix(other): + return NotImplemented + # Sparse other. + elif issparse(other): + warn("Comparing sparse matrices using == is inefficient, try using" + " != instead.", SparseEfficiencyWarning, stacklevel=3) + # TODO sparse broadcasting + if self.shape != other.shape: + return False + elif self.format != other.format: + other = other.asformat(self.format) + res = self._binopt(other, '_ne_') + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + return all_true - res + else: + return NotImplemented + + def __ne__(self, other): + # Scalar other. + if isscalarlike(other): + if np.isnan(other): + warn("Comparing a sparse matrix with nan using != is" + " inefficient", SparseEfficiencyWarning, stacklevel=3) + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + return all_true + elif other != 0: + warn("Comparing a sparse matrix with a nonzero scalar using !=" + " is inefficient, try using == instead.", + SparseEfficiencyWarning, stacklevel=3) + all_true = self.__class__(np.ones(self.shape), dtype=np.bool_) + inv = self._scalar_binopt(other, operator.eq) + return all_true - inv + else: + return self._scalar_binopt(other, operator.ne) + # Dense other. + elif isdense(other): + return self.todense() != other + # Pydata sparse other. + elif is_pydata_spmatrix(other): + return NotImplemented + # Sparse other. + elif issparse(other): + # TODO sparse broadcasting + if self.shape != other.shape: + return True + elif self.format != other.format: + other = other.asformat(self.format) + return self._binopt(other, '_ne_') + else: + return NotImplemented + + def _inequality(self, other, op, op_name, bad_scalar_msg): + # Scalar other. + if isscalarlike(other): + if 0 == other and op_name in ('_le_', '_ge_'): + raise NotImplementedError(" >= and <= don't work with 0.") + elif op(0, other): + warn(bad_scalar_msg, SparseEfficiencyWarning, stacklevel=3) + other_arr = np.empty(self.shape, dtype=np.result_type(other)) + other_arr.fill(other) + other_arr = self.__class__(other_arr) + return self._binopt(other_arr, op_name) + else: + return self._scalar_binopt(other, op) + # Dense other. + elif isdense(other): + return op(self.todense(), other) + # Sparse other. + elif issparse(other): + # TODO sparse broadcasting + if self.shape != other.shape: + raise ValueError("inconsistent shapes") + elif self.format != other.format: + other = other.asformat(self.format) + if op_name not in ('_ge_', '_le_'): + return self._binopt(other, op_name) + + warn("Comparing sparse matrices using >= and <= is inefficient, " + "using <, >, or !=, instead.", + SparseEfficiencyWarning, stacklevel=3) + all_true = self.__class__(np.ones(self.shape, dtype=np.bool_)) + res = self._binopt(other, '_gt_' if op_name == '_le_' else '_lt_') + return all_true - res + else: + return NotImplemented + + def __lt__(self, other): + return self._inequality(other, operator.lt, '_lt_', + "Comparing a sparse matrix with a scalar " + "greater than zero using < is inefficient, " + "try using >= instead.") + + def __gt__(self, other): + return self._inequality(other, operator.gt, '_gt_', + "Comparing a sparse matrix with a scalar " + "less than zero using > is inefficient, " + "try using <= instead.") + + def __le__(self, other): + return self._inequality(other, operator.le, '_le_', + "Comparing a sparse matrix with a scalar " + "greater than zero using <= is inefficient, " + "try using > instead.") + + def __ge__(self, other): + return self._inequality(other, operator.ge, '_ge_', + "Comparing a sparse matrix with a scalar " + "less than zero using >= is inefficient, " + "try using < instead.") + + ################################# + # Arithmetic operator overrides # + ################################# + + def _add_dense(self, other): + if other.shape != self.shape: + raise ValueError(f'Incompatible shapes ({self.shape} and {other.shape})') + dtype = upcast_char(self.dtype.char, other.dtype.char) + order = self._swap('CF')[0] + result = np.array(other, dtype=dtype, order=order, copy=True) + y = result if result.flags.c_contiguous else result.T + M, N = self._swap(self._shape_as_2d) + csr_todense(M, N, self.indptr, self.indices, self.data, y) + return self._container(result, copy=False) + + def _add_sparse(self, other): + return self._binopt(other, '_plus_') + + def _sub_sparse(self, other): + return self._binopt(other, '_minus_') + + def multiply(self, other): + """Point-wise multiplication by array/matrix, vector, or scalar.""" + # Scalar multiplication. + if isscalarlike(other): + return self._mul_scalar(other) + # Sparse matrix or vector. + if issparse(other): + if self.shape == other.shape: + other = self.__class__(other) + return self._binopt(other, '_elmul_') + # Single element. + if other.shape == (1, 1): + result = self._mul_scalar(other.toarray()[0, 0]) + if self.ndim == 1: + return result.reshape((1, self.shape[0])) + return result + if other.shape == (1,): + return self._mul_scalar(other.toarray()[0]) + if self.shape in ((1,), (1, 1)): + return other._mul_scalar(self.data.sum()) + + # broadcast. treat 1d like a row + sM, sN = self._shape_as_2d + oM, oN = other._shape_as_2d + # A row times a column. + if sM == 1 and oN == 1: + return other._matmul_sparse(self.reshape(sM, sN).tocsc()) + if sN == 1 and oM == 1: + return self._matmul_sparse(other.reshape(oM, oN).tocsc()) + + is_array = isinstance(self, sparray) + # Other is a row. + if oM == 1 and sN == oN: + new_other = _make_diagonal_csr(other.toarray().ravel(), is_array) + result = self._matmul_sparse(new_other) + return result if self.ndim == 2 else result.reshape((1, oN)) + # self is a row. + if sM == 1 and sN == oN: + copy = _make_diagonal_csr(self.toarray().ravel(), is_array) + return other._matmul_sparse(copy) + + # Other is a column. + if oN == 1 and sM == oM: + new_other = _make_diagonal_csr(other.toarray().ravel(), is_array) + return new_other._matmul_sparse(self) + # self is a column. + if sN == 1 and sM == oM: + new_self = _make_diagonal_csr(self.toarray().ravel(), is_array) + return new_self._matmul_sparse(other) + raise ValueError("inconsistent shapes") + + # Assume other is a dense matrix/array, which produces a single-item + # object array if other isn't convertible to ndarray. + other = np.asanyarray(other) + + if other.ndim > 2: + return np.multiply(self.toarray(), other) + # Single element / wrapped object. + if other.size == 1: + if other.dtype == np.object_: + # 'other' not convertible to ndarray. + return NotImplemented + bshape = np.broadcast_shapes(self.shape, other.shape) + return self._mul_scalar(other.flat[0]).reshape(bshape) + # Fast case for trivial sparse matrix. + if self.shape in ((1,), (1, 1)): + bshape = np.broadcast_shapes(self.shape, other.shape) + return np.multiply(self.data.sum(), other).reshape(bshape) + + ret = self.tocoo() + # Matching shapes. + if self.shape == other.shape: + data = np.multiply(ret.data, other[ret.coords]) + ret.data = data.view(np.ndarray).ravel() + return ret + + # convert other to 2d + other2d = np.atleast_2d(other) + # Sparse row vector times... + if self.shape[0] == 1 or self.ndim == 1: + if other2d.shape[1] == 1: # Dense column vector. + data = np.multiply(ret.data, other2d) + elif other2d.shape[1] == self.shape[-1]: # Dense 2d matrix. + data = np.multiply(ret.data, other2d[:, ret.col]) + else: + raise ValueError("inconsistent shapes") + row = np.repeat(np.arange(other2d.shape[0]), ret.nnz) + col = np.tile(ret.col, other2d.shape[0]) + return self._coo_container( + (data.view(np.ndarray).ravel(), (row, col)), + shape=(other2d.shape[0], self.shape[-1]), + copy=False + ) + # Sparse column vector times... + if self.shape[1] == 1: + if other2d.shape[0] == 1: # Dense row vector. + data = np.multiply(ret.data[:, None], other2d) + elif other2d.shape[0] == self.shape[0]: # Dense 2d array. + data = np.multiply(ret.data[:, None], other2d[ret.row]) + else: + raise ValueError("inconsistent shapes") + row = np.repeat(ret.row, other2d.shape[1]) + col = np.tile(np.arange(other2d.shape[1]), len(ret.col)) + return self._coo_container( + (data.view(np.ndarray).ravel(), (row, col)), + shape=(self.shape[0], other2d.shape[1]), + copy=False + ) + # Sparse matrix times dense row vector. + if other2d.shape[0] == 1 and self.shape[1] == other2d.shape[1]: + data = np.multiply(ret.data, other2d[:, ret.col].ravel()) + # Sparse matrix times dense column vector. + elif other2d.shape[1] == 1 and self.shape[0] == other2d.shape[0]: + data = np.multiply(ret.data, other2d[ret.row].ravel()) + else: + raise ValueError("inconsistent shapes") + ret.data = data.view(np.ndarray).ravel() + return ret + + ########################### + # Multiplication handlers # + ########################### + + def _matmul_vector(self, other): + M, N = self._shape_as_2d + + # output array + result = np.zeros(M, dtype=upcast_char(self.dtype.char, other.dtype.char)) + + # csr_matvec or csc_matvec + fn = getattr(_sparsetools, self.format + '_matvec') + fn(M, N, self.indptr, self.indices, self.data, other, result) + + return result[0] if self.ndim == 1 else result + + def _matmul_multivector(self, other): + M, N = self._shape_as_2d + n_vecs = other.shape[-1] # number of column vectors + + result = np.zeros((M, n_vecs), + dtype=upcast_char(self.dtype.char, other.dtype.char)) + + # csr_matvecs or csc_matvecs + fn = getattr(_sparsetools, self.format + '_matvecs') + fn(M, N, n_vecs, self.indptr, self.indices, self.data, + other.ravel(), result.ravel()) + + if self.ndim == 1: + return result.reshape((n_vecs,)) + return result + + def _matmul_sparse(self, other): + M, K1 = self._shape_as_2d + # if other is 1d, treat as a **column** + o_ndim = other.ndim + if o_ndim == 1: + # convert 1d array to a 2d column when on the right of @ + other = other.reshape((1, other.shape[0])).T # Note: converts to CSC + K2, N = other._shape + + # find new_shape: (M, N), (M,), (N,) or () + new_shape = () + if self.ndim == 2: + new_shape += (M,) + if o_ndim == 2: + new_shape += (N,) + + major_dim = self._swap((M, N))[0] + other = self.__class__(other) # convert to this format + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices)) + + fn = getattr(_sparsetools, self.format + '_matmat_maxnnz') + nnz = fn(M, N, + np.asarray(self.indptr, dtype=idx_dtype), + np.asarray(self.indices, dtype=idx_dtype), + np.asarray(other.indptr, dtype=idx_dtype), + np.asarray(other.indices, dtype=idx_dtype)) + if nnz == 0: + if new_shape == (): + return np.array(0, dtype=upcast(self.dtype, other.dtype)) + return self.__class__(new_shape, dtype=upcast(self.dtype, other.dtype)) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=nnz) + + indptr = np.empty(major_dim + 1, dtype=idx_dtype) + indices = np.empty(nnz, dtype=idx_dtype) + data = np.empty(nnz, dtype=upcast(self.dtype, other.dtype)) + + fn = getattr(_sparsetools, self.format + '_matmat') + fn(M, N, np.asarray(self.indptr, dtype=idx_dtype), + np.asarray(self.indices, dtype=idx_dtype), + self.data, + np.asarray(other.indptr, dtype=idx_dtype), + np.asarray(other.indices, dtype=idx_dtype), + other.data, + indptr, indices, data) + + if new_shape == (): + return np.array(data[0]) + return self.__class__((data, indices, indptr), shape=new_shape) + + def diagonal(self, k=0): + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + fn = getattr(_sparsetools, self.format + "_diagonal") + y = np.empty(min(rows + min(k, 0), cols - max(k, 0)), + dtype=upcast(self.dtype)) + fn(k, self.shape[0], self.shape[1], self.indptr, self.indices, + self.data, y) + return y + + diagonal.__doc__ = _spbase.diagonal.__doc__ + + ##################### + # Other binary ops # + ##################### + + def _maximum_minimum(self, other, npop, op_name, dense_check): + if isscalarlike(other): + if dense_check(other): + warn("Taking maximum (minimum) with > 0 (< 0) number results" + " to a dense matrix.", SparseEfficiencyWarning, + stacklevel=3) + other_arr = np.empty(self.shape, dtype=np.asarray(other).dtype) + other_arr.fill(other) + other_arr = self.__class__(other_arr) + return self._binopt(other_arr, op_name) + else: + self.sum_duplicates() + new_data = npop(self.data, np.asarray(other)) + mat = self.__class__((new_data, self.indices, self.indptr), + dtype=new_data.dtype, shape=self.shape) + return mat + elif isdense(other): + return npop(self.todense(), other) + elif issparse(other): + return self._binopt(other, op_name) + else: + raise ValueError("Operands not compatible.") + + def maximum(self, other): + return self._maximum_minimum(other, np.maximum, + '_maximum_', lambda x: np.asarray(x) > 0) + + maximum.__doc__ = _spbase.maximum.__doc__ + + def minimum(self, other): + return self._maximum_minimum(other, np.minimum, + '_minimum_', lambda x: np.asarray(x) < 0) + + minimum.__doc__ = _spbase.minimum.__doc__ + + ##################### + # Reduce operations # + ##################### + + def sum(self, axis=None, dtype=None, out=None): + """Sum the array/matrix over the given axis. If the axis is None, sum + over both rows and columns, returning a scalar. + """ + # The _spbase base class already does axis=0 and axis=1 efficiently + # so we only do the case axis=None here + if (self.ndim == 2 and not hasattr(self, 'blocksize') and + axis in self._swap(((1, -1), (0, -2)))[0]): + # faster than multiplication for large minor axis in CSC/CSR + res_dtype = get_sum_dtype(self.dtype) + ret = np.zeros(len(self.indptr) - 1, dtype=res_dtype) + + major_index, value = self._minor_reduce(np.add) + ret[major_index] = value + ret = self._ascontainer(ret) + if axis % 2 == 1: + ret = ret.T + + if out is not None and out.shape != ret.shape: + raise ValueError('dimensions do not match') + + return ret.sum(axis=(), dtype=dtype, out=out) + else: + # _spbase handles the situations when axis is in {None, -2, -1, 0, 1} + return _spbase.sum(self, axis=axis, dtype=dtype, out=out) + + sum.__doc__ = _spbase.sum.__doc__ + + def _minor_reduce(self, ufunc, data=None): + """Reduce nonzeros with a ufunc over the minor axis when non-empty + + Can be applied to a function of self.data by supplying data parameter. + + Warning: this does not call sum_duplicates() + + Returns + ------- + major_index : array of ints + Major indices where nonzero + + value : array of self.dtype + Reduce result for nonzeros in each major_index + """ + if data is None: + data = self.data + major_index = np.flatnonzero(np.diff(self.indptr)) + value = ufunc.reduceat(data, + downcast_intp_index(self.indptr[major_index])) + return major_index, value + + ####################### + # Getting and Setting # + ####################### + + def _get_int(self, idx): + if 0 <= idx <= self.shape[0]: + spot = np.flatnonzero(self.indices == idx) + if spot.size: + return self.data[spot[0]] + return self.data.dtype.type(0) + raise IndexError(f'index ({idx}) out of range') + +# For now, 1d only has integer indexing. Soon we will add get_slice/array +# def _get_slice(self, idx): +# if idx == slice(None): +# return self.copy() +# if idx.step in (1, None): +# major, minor = self._swap((0, idx)) +# ret = self._get_submatrix(major, minor, copy=True) +# return ret.reshape(ret.shape[-1]) +# +# _slice = self._swap((self._minor_slice, self._major_slice))[0] +# return _slice(idx) +# +# def _get_array(self, idx): +# idx = np.asarray(idx) +# idx_dtype = self.indices.dtype +# M, N = self._swap((1, self.shape[0])) +# row = np.zeros_like(idx, dtype=idx_dtype) +# major, minor = self._swap((row, idx)) +# major = np.asarray(major, dtype=idx_dtype) +# minor = np.asarray(minor, dtype=idx_dtype) +# if minor.size == 0: +# return self.__class__([], dtype=self.dtype) +# new_shape = minor.shape if minor.shape[0] > 1 else (minor.shape[-1],) +# +# val = np.empty(major.size, dtype=self.dtype) +# csr_sample_values(M, N, self.indptr, self.indices, self.data, +# major.size, major.ravel(), minor.ravel(), val) +# return self.__class__(val.reshape(new_shape)) + + def _get_intXint(self, row, col): + M, N = self._swap(self.shape) + major, minor = self._swap((row, col)) + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, + major, major + 1, minor, minor + 1) + return data.sum(dtype=self.dtype) + + def _get_sliceXslice(self, row, col): + major, minor = self._swap((row, col)) + if major.step in (1, None) and minor.step in (1, None): + return self._get_submatrix(major, minor, copy=True) + return self._major_slice(major)._minor_slice(minor) + + def _get_arrayXarray(self, row, col): + # inner indexing + idx_dtype = self.indices.dtype + M, N = self._swap(self.shape) + major, minor = self._swap((row, col)) + major = np.asarray(major, dtype=idx_dtype) + minor = np.asarray(minor, dtype=idx_dtype) + + val = np.empty(major.size, dtype=self.dtype) + csr_sample_values(M, N, self.indptr, self.indices, self.data, + major.size, major.ravel(), minor.ravel(), val) + if major.ndim == 1: + return self._ascontainer(val) + return self.__class__(val.reshape(major.shape)) + + def _get_columnXarray(self, row, col): + # outer indexing + major, minor = self._swap((row, col)) + return self._major_index_fancy(major)._minor_index_fancy(minor) + + def _major_index_fancy(self, idx): + """Index along the major axis where idx is an array of ints. + """ + idx_dtype = self._get_index_dtype((self.indptr, self.indices)) + indices = np.asarray(idx, dtype=idx_dtype).ravel() + + N = self._swap(self._shape_as_2d)[1] + M = len(indices) + new_shape = self._swap((M, N)) if self.ndim == 2 else (M,) + if M == 0: + return self.__class__(new_shape, dtype=self.dtype) + + row_nnz = (self.indptr[indices + 1] - self.indptr[indices]).astype(idx_dtype) + + res_indptr = np.zeros(M+1, dtype=idx_dtype) + np.cumsum(row_nnz, out=res_indptr[1:]) + + nnz = res_indptr[-1] + res_indices = np.empty(nnz, dtype=idx_dtype) + res_data = np.empty(nnz, dtype=self.dtype) + csr_row_index( + M, + indices, + self.indptr.astype(idx_dtype, copy=False), + self.indices.astype(idx_dtype, copy=False), + self.data, + res_indices, + res_data + ) + + return self.__class__((res_data, res_indices, res_indptr), + shape=new_shape, copy=False) + + def _major_slice(self, idx, copy=False): + """Index along the major axis where idx is a slice object. + """ + if idx == slice(None): + return self.copy() if copy else self + + M, N = self._swap(self._shape_as_2d) + start, stop, step = idx.indices(M) + M = len(range(start, stop, step)) + new_shape = self._swap((M, N)) if self.ndim == 2 else (M,) + if M == 0: + return self.__class__(new_shape, dtype=self.dtype) + + # Work out what slices are needed for `row_nnz` + # start,stop can be -1, only if step is negative + start0, stop0 = start, stop + if stop == -1 and start >= 0: + stop0 = None + start1, stop1 = start + 1, stop + 1 + + row_nnz = self.indptr[start1:stop1:step] - \ + self.indptr[start0:stop0:step] + idx_dtype = self.indices.dtype + res_indptr = np.zeros(M+1, dtype=idx_dtype) + np.cumsum(row_nnz, out=res_indptr[1:]) + + if step == 1: + all_idx = slice(self.indptr[start], self.indptr[stop]) + res_indices = np.array(self.indices[all_idx], copy=copy) + res_data = np.array(self.data[all_idx], copy=copy) + else: + nnz = res_indptr[-1] + res_indices = np.empty(nnz, dtype=idx_dtype) + res_data = np.empty(nnz, dtype=self.dtype) + csr_row_slice(start, stop, step, self.indptr, self.indices, + self.data, res_indices, res_data) + + return self.__class__((res_data, res_indices, res_indptr), + shape=new_shape, copy=False) + + def _minor_index_fancy(self, idx): + """Index along the minor axis where idx is an array of ints. + """ + idx_dtype = self._get_index_dtype((self.indices, self.indptr)) + indices = self.indices.astype(idx_dtype, copy=False) + indptr = self.indptr.astype(idx_dtype, copy=False) + + idx = np.asarray(idx, dtype=idx_dtype).ravel() + + M, N = self._swap(self._shape_as_2d) + k = len(idx) + new_shape = self._swap((M, k)) if self.ndim == 2 else (k,) + if k == 0: + return self.__class__(new_shape, dtype=self.dtype) + + # pass 1: count idx entries and compute new indptr + col_offsets = np.zeros(N, dtype=idx_dtype) + res_indptr = np.empty_like(self.indptr, dtype=idx_dtype) + csr_column_index1( + k, + idx, + M, + N, + indptr, + indices, + col_offsets, + res_indptr, + ) + + # pass 2: copy indices/data for selected idxs + col_order = np.argsort(idx).astype(idx_dtype, copy=False) + nnz = res_indptr[-1] + res_indices = np.empty(nnz, dtype=idx_dtype) + res_data = np.empty(nnz, dtype=self.dtype) + csr_column_index2(col_order, col_offsets, len(self.indices), + indices, self.data, res_indices, res_data) + return self.__class__((res_data, res_indices, res_indptr), + shape=new_shape, copy=False) + + def _minor_slice(self, idx, copy=False): + """Index along the minor axis where idx is a slice object. + """ + if idx == slice(None): + return self.copy() if copy else self + + M, N = self._swap(self._shape_as_2d) + start, stop, step = idx.indices(N) + N = len(range(start, stop, step)) + if N == 0: + return self.__class__(self._swap((M, N)), dtype=self.dtype) + if step == 1: + return self._get_submatrix(minor=idx, copy=copy) + # TODO: don't fall back to fancy indexing here + return self._minor_index_fancy(np.arange(start, stop, step)) + + def _get_submatrix(self, major=None, minor=None, copy=False): + """Return a submatrix of this matrix. + + major, minor: None, int, or slice with step 1 + """ + M, N = self._swap(self._shape_as_2d) + i0, i1 = _process_slice(major, M) + j0, j1 = _process_slice(minor, N) + + if i0 == 0 and j0 == 0 and i1 == M and j1 == N: + return self.copy() if copy else self + + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, i0, i1, j0, j1) + + shape = self._swap((i1 - i0, j1 - j0)) + if self.ndim == 1: + shape = (shape[1],) + return self.__class__((data, indices, indptr), shape=shape, + dtype=self.dtype, copy=False) + + def _set_int(self, idx, x): + major, minor = self._swap((0, idx)) + self._set_many(major, minor, x) + + def _set_array(self, idx, x): + major, minor = self._swap((np.zeros_like(idx), idx)) + broadcast = x.shape[-1] == 1 and minor.shape[-1] != 1 + if broadcast: + x = np.repeat(x.data, idx.shape[-1]) + self._set_many(major, minor, x) + + def _set_intXint(self, row, col, x): + i, j = self._swap((row, col)) + self._set_many(i, j, x) + + def _set_arrayXarray(self, row, col, x): + i, j = self._swap((row, col)) + self._set_many(i, j, x) + + def _set_arrayXarray_sparse(self, row, col, x): + # clear entries that will be overwritten + self._zero_many(*self._swap((row, col))) + + M, N = row.shape # matches col.shape + broadcast_row = M != 1 and x.shape[0] == 1 + broadcast_col = N != 1 and x.shape[1] == 1 + r, c = x.row, x.col + + x = np.asarray(x.data, dtype=self.dtype) + if x.size == 0: + return + + if broadcast_row: + r = np.repeat(np.arange(M), len(r)) + c = np.tile(c, M) + x = np.tile(x, M) + if broadcast_col: + r = np.repeat(r, N) + c = np.tile(np.arange(N), len(c)) + x = np.repeat(x, N) + # only assign entries in the new sparsity structure + i, j = self._swap((row[r, c], col[r, c])) + self._set_many(i, j, x) + + def _setdiag(self, values, k): + if 0 in self.shape: + return + if self.ndim == 1: + raise NotImplementedError('diagonals cant be set in 1d arrays') + + M, N = self.shape + broadcast = (values.ndim == 0) + + if k < 0: + if broadcast: + max_index = min(M + k, N) + else: + max_index = min(M + k, N, len(values)) + i = np.arange(-k, max_index - k, dtype=self.indices.dtype) + j = np.arange(max_index, dtype=self.indices.dtype) + + else: + if broadcast: + max_index = min(M, N - k) + else: + max_index = min(M, N - k, len(values)) + i = np.arange(max_index, dtype=self.indices.dtype) + j = np.arange(k, k + max_index, dtype=self.indices.dtype) + + if not broadcast: + values = values[:len(i)] + + x = np.atleast_1d(np.asarray(values, dtype=self.dtype)).ravel() + if x.squeeze().shape != i.squeeze().shape: + x = np.broadcast_to(x, i.shape) + if x.size == 0: + return + + M, N = self._swap((M, N)) + i, j = self._swap((i, j)) + n_samples = x.size + offsets = np.empty(n_samples, dtype=self.indices.dtype) + ret = csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if ret == 1: + # rinse and repeat + self.sum_duplicates() + csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if -1 not in offsets: + # only affects existing non-zero cells + self.data[offsets] = x + return + + mask = (offsets <= -1) + # Boundary between csc and convert to coo + # The value 0.001 is justified in gh-19962#issuecomment-1920499678 + if mask.sum() < self.nnz * 0.001: + # create new entries + i = i[mask] + j = j[mask] + self._insert_many(i, j, x[mask]) + # replace existing entries + mask = ~mask + self.data[offsets[mask]] = x[mask] + else: + # convert to coo for _set_diag + coo = self.tocoo() + coo._setdiag(values, k) + arrays = coo._coo_to_compressed(self._swap) + self.indptr, self.indices, self.data, _ = arrays + + def _prepare_indices(self, i, j): + M, N = self._swap(self._shape_as_2d) + + def check_bounds(indices, bound): + idx = indices.max() + if idx >= bound: + raise IndexError('index (%d) out of range (>= %d)' % + (idx, bound)) + idx = indices.min() + if idx < -bound: + raise IndexError('index (%d) out of range (< -%d)' % + (idx, bound)) + + i = np.atleast_1d(np.asarray(i, dtype=self.indices.dtype)).ravel() + j = np.atleast_1d(np.asarray(j, dtype=self.indices.dtype)).ravel() + check_bounds(i, M) + check_bounds(j, N) + return i, j, M, N + + def _set_many(self, i, j, x): + """Sets value at each (i, j) to x + + Here (i,j) index major and minor respectively, and must not contain + duplicate entries. + """ + i, j, M, N = self._prepare_indices(i, j) + x = np.atleast_1d(np.asarray(x, dtype=self.dtype)).ravel() + + n_samples = x.size + offsets = np.empty(n_samples, dtype=self.indices.dtype) + ret = csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if ret == 1: + # rinse and repeat + self.sum_duplicates() + csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + + if -1 not in offsets: + # only affects existing non-zero cells + self.data[offsets] = x + return + + else: + warn(f"Changing the sparsity structure of a {self.__class__.__name__} is" + " expensive. lil and dok are more efficient.", + SparseEfficiencyWarning, stacklevel=3) + # replace where possible + mask = offsets > -1 + self.data[offsets[mask]] = x[mask] + # only insertions remain + mask = ~mask + i = i[mask] + i[i < 0] += M + j = j[mask] + j[j < 0] += N + self._insert_many(i, j, x[mask]) + + def _zero_many(self, i, j): + """Sets value at each (i, j) to zero, preserving sparsity structure. + + Here (i,j) index major and minor respectively. + """ + i, j, M, N = self._prepare_indices(i, j) + + n_samples = len(i) + offsets = np.empty(n_samples, dtype=self.indices.dtype) + ret = csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + if ret == 1: + # rinse and repeat + self.sum_duplicates() + csr_sample_offsets(M, N, self.indptr, self.indices, n_samples, + i, j, offsets) + + # only assign zeros to the existing sparsity structure + self.data[offsets[offsets > -1]] = 0 + + def _insert_many(self, i, j, x): + """Inserts new nonzero at each (i, j) with value x + + Here (i,j) index major and minor respectively. + i, j and x must be non-empty, 1d arrays. + Inserts each major group (e.g. all entries per row) at a time. + Maintains has_sorted_indices property. + Modifies i, j, x in place. + """ + order = np.argsort(i, kind='mergesort') # stable for duplicates + i = i.take(order, mode='clip') + j = j.take(order, mode='clip') + x = x.take(order, mode='clip') + + do_sort = self.has_sorted_indices + + # Update index data type + idx_dtype = self._get_index_dtype((self.indices, self.indptr), + maxval=(self.indptr[-1] + x.size)) + self.indptr = np.asarray(self.indptr, dtype=idx_dtype) + self.indices = np.asarray(self.indices, dtype=idx_dtype) + i = np.asarray(i, dtype=idx_dtype) + j = np.asarray(j, dtype=idx_dtype) + + # Collate old and new in chunks by major index + indices_parts = [] + data_parts = [] + ui, ui_indptr = np.unique(i, return_index=True) + ui_indptr = np.append(ui_indptr, len(j)) + new_nnzs = np.diff(ui_indptr) + prev = 0 + for c, (ii, js, je) in enumerate(zip(ui, ui_indptr, ui_indptr[1:])): + # old entries + start = self.indptr[prev] + stop = self.indptr[ii] + indices_parts.append(self.indices[start:stop]) + data_parts.append(self.data[start:stop]) + + # handle duplicate j: keep last setting + uj, uj_indptr = np.unique(j[js:je][::-1], return_index=True) + if len(uj) == je - js: + indices_parts.append(j[js:je]) + data_parts.append(x[js:je]) + else: + indices_parts.append(j[js:je][::-1][uj_indptr]) + data_parts.append(x[js:je][::-1][uj_indptr]) + new_nnzs[c] = len(uj) + + prev = ii + + # remaining old entries + start = self.indptr[ii] + indices_parts.append(self.indices[start:]) + data_parts.append(self.data[start:]) + + # update attributes + self.indices = np.concatenate(indices_parts) + self.data = np.concatenate(data_parts) + nnzs = np.empty(self.indptr.shape, dtype=idx_dtype) + nnzs[0] = idx_dtype(0) + indptr_diff = np.diff(self.indptr) + indptr_diff[ui] += new_nnzs + nnzs[1:] = indptr_diff + self.indptr = np.cumsum(nnzs, out=nnzs) + + if do_sort: + # TODO: only sort where necessary + self.has_sorted_indices = False + self.sort_indices() + + self.check_format(full_check=False) + + ###################### + # Conversion methods # + ###################### + + def tocoo(self, copy=True): + if self.ndim == 1: + csr = self.tocsr() + return self._coo_container((csr.data, (csr.indices,)), csr.shape, copy=copy) + major_dim, minor_dim = self._swap(self.shape) + minor_indices = self.indices + major_indices = np.empty(len(minor_indices), dtype=self.indices.dtype) + _sparsetools.expandptr(major_dim, self.indptr, major_indices) + coords = self._swap((major_indices, minor_indices)) + + return self._coo_container( + (self.data, coords), self.shape, copy=copy, dtype=self.dtype + ) + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + def toarray(self, order=None, out=None): + if out is None and order is None: + order = self._swap('cf')[0] + out = self._process_toarray_args(order, out) + if not (out.flags.c_contiguous or out.flags.f_contiguous): + raise ValueError('Output array must be C or F contiguous') + # align ideal order with output array order + if out.flags.c_contiguous: + x = self.tocsr() + y = out + else: + x = self.tocsc() + y = out.T + M, N = x._swap(x._shape_as_2d) + csr_todense(M, N, x.indptr, x.indices, x.data, y) + return out + + toarray.__doc__ = _spbase.toarray.__doc__ + + ############################################################## + # methods that examine or modify the internal data structure # + ############################################################## + + def eliminate_zeros(self): + """Remove zero entries from the array/matrix + + This is an *in place* operation. + """ + M, N = self._swap(self._shape_as_2d) + _sparsetools.csr_eliminate_zeros(M, N, self.indptr, self.indices, self.data) + self.prune() # nnz may have changed + + @property + def has_canonical_format(self) -> bool: + """Whether the array/matrix has sorted indices and no duplicates + + Returns + - True: if the above applies + - False: otherwise + + has_canonical_format implies has_sorted_indices, so if the latter flag + is False, so will the former be; if the former is found True, the + latter flag is also set. + """ + # first check to see if result was cached + if not getattr(self, '_has_sorted_indices', True): + # not sorted => not canonical + self._has_canonical_format = False + elif not hasattr(self, '_has_canonical_format'): + self.has_canonical_format = bool( + _sparsetools.csr_has_canonical_format( + len(self.indptr) - 1, self.indptr, self.indices) + ) + return self._has_canonical_format + + @has_canonical_format.setter + def has_canonical_format(self, val: bool): + self._has_canonical_format = bool(val) + if val: + self.has_sorted_indices = True + + def sum_duplicates(self): + """Eliminate duplicate entries by adding them together + + This is an *in place* operation. + """ + if self.has_canonical_format: + return + self.sort_indices() + + M, N = self._swap(self._shape_as_2d) + _sparsetools.csr_sum_duplicates(M, N, self.indptr, self.indices, self.data) + + self.prune() # nnz may have changed + self.has_canonical_format = True + + @property + def has_sorted_indices(self) -> bool: + """Whether the indices are sorted + + Returns + - True: if the indices of the array/matrix are in sorted order + - False: otherwise + """ + # first check to see if result was cached + if not hasattr(self, '_has_sorted_indices'): + self._has_sorted_indices = bool( + _sparsetools.csr_has_sorted_indices( + len(self.indptr) - 1, self.indptr, self.indices) + ) + return self._has_sorted_indices + + @has_sorted_indices.setter + def has_sorted_indices(self, val: bool): + self._has_sorted_indices = bool(val) + + + def sorted_indices(self): + """Return a copy of this array/matrix with sorted indices + """ + A = self.copy() + A.sort_indices() + return A + + # an alternative that has linear complexity is the following + # although the previous option is typically faster + # return self.toother().toother() + + def sort_indices(self): + """Sort the indices of this array/matrix *in place* + """ + + if not self.has_sorted_indices: + _sparsetools.csr_sort_indices(len(self.indptr) - 1, self.indptr, + self.indices, self.data) + self.has_sorted_indices = True + + def prune(self): + """Remove empty space after all non-zero elements. + """ + major_dim = self._swap(self._shape_as_2d)[0] + + if len(self.indptr) != major_dim + 1: + raise ValueError('index pointer has invalid length') + if len(self.indices) < self.nnz: + raise ValueError('indices array has fewer than nnz elements') + if len(self.data) < self.nnz: + raise ValueError('data array has fewer than nnz elements') + + self.indices = _prune_array(self.indices[:self.nnz]) + self.data = _prune_array(self.data[:self.nnz]) + + def resize(self, *shape): + shape = check_shape(shape, allow_1d=isinstance(self, sparray)) + + if hasattr(self, 'blocksize'): + bm, bn = self.blocksize + new_M, rm = divmod(shape[0], bm) + new_N, rn = divmod(shape[1], bn) + if rm or rn: + raise ValueError(f"shape must be divisible into {self.blocksize}" + f" blocks. Got {shape}") + M, N = self.shape[0] // bm, self.shape[1] // bn + else: + new_M, new_N = self._swap(shape if len(shape)>1 else (1, shape[0])) + M, N = self._swap(self._shape_as_2d) + + if new_M < M: + self.indices = self.indices[:self.indptr[new_M]] + self.data = self.data[:self.indptr[new_M]] + self.indptr = self.indptr[:new_M + 1] + elif new_M > M: + self.indptr = np.resize(self.indptr, new_M + 1) + self.indptr[M + 1:].fill(self.indptr[M]) + + if new_N < N: + mask = self.indices < new_N + if not np.all(mask): + self.indices = self.indices[mask] + self.data = self.data[mask] + major_index, val = self._minor_reduce(np.add, mask) + self.indptr.fill(0) + self.indptr[1:][major_index] = val + np.cumsum(self.indptr, out=self.indptr) + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + ################### + # utility methods # + ################### + + # needed by _data_matrix + def _with_data(self, data, copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the structure arrays + (i.e. .indptr and .indices) are copied. + """ + if copy: + return self.__class__((data, self.indices.copy(), + self.indptr.copy()), + shape=self.shape, + dtype=data.dtype) + else: + return self.__class__((data, self.indices, self.indptr), + shape=self.shape, dtype=data.dtype) + + def _binopt(self, other, op): + """apply the binary operation fn to two sparse matrices.""" + other = self.__class__(other) + + # e.g. csr_plus_csr, csr_minus_csr, etc. + fn = getattr(_sparsetools, self.format + op + self.format) + + maxnnz = self.nnz + other.nnz + idx_dtype = self._get_index_dtype((self.indptr, self.indices, + other.indptr, other.indices), + maxval=maxnnz) + indptr = np.empty(self.indptr.shape, dtype=idx_dtype) + indices = np.empty(maxnnz, dtype=idx_dtype) + + bool_ops = ['_ne_', '_lt_', '_gt_', '_le_', '_ge_'] + if op in bool_ops: + data = np.empty(maxnnz, dtype=np.bool_) + else: + data = np.empty(maxnnz, dtype=upcast(self.dtype, other.dtype)) + + M, N = self._shape_as_2d + fn(M, N, + np.asarray(self.indptr, dtype=idx_dtype), + np.asarray(self.indices, dtype=idx_dtype), + self.data, + np.asarray(other.indptr, dtype=idx_dtype), + np.asarray(other.indices, dtype=idx_dtype), + other.data, + indptr, indices, data) + + A = self.__class__((data, indices, indptr), shape=self.shape) + A.prune() + + return A + + def _divide_sparse(self, other): + """ + Divide this matrix by a second sparse matrix. + """ + if other.shape != self.shape: + raise ValueError('inconsistent shapes') + + r = self._binopt(other, '_eldiv_') + + if np.issubdtype(r.dtype, np.inexact): + # Eldiv leaves entries outside the combined sparsity + # pattern empty, so they must be filled manually. + # Everything outside of other's sparsity is NaN, and everything + # inside it is either zero or defined by eldiv. + out = np.empty(self.shape, dtype=self.dtype) + out.fill(np.nan) + coords = other.nonzero() + if self.ndim == 1: + coords = (coords[-1],) + out[coords] = 0 + r = r.tocoo() + out[r.coords] = r.data + return self._container(out) + else: + # integers types go with nan <-> 0 + out = r + return out + + +def _make_diagonal_csr(data, is_array=False): + """build diagonal csc_array/csr_array => self._csr_container + + Parameter `data` should be a raveled numpy array holding the + values on the diagonal of the resulting sparse matrix. + """ + from ._csr import csr_array, csr_matrix + csr_array = csr_array if is_array else csr_matrix + + N = len(data) + indptr = np.arange(N + 1) + indices = indptr[:-1] + + return csr_array((data, indices, indptr), shape=(N, N)) + + +def _process_slice(sl, num): + if sl is None: + i0, i1 = 0, num + elif isinstance(sl, slice): + i0, i1, stride = sl.indices(num) + if stride != 1: + raise ValueError('slicing with step != 1 not supported') + i0 = min(i0, i1) # give an empty slice when i0 > i1 + elif isintlike(sl): + if sl < 0: + sl += num + i0, i1 = sl, sl + 1 + if i0 < 0 or i1 > num: + raise IndexError(f'index out of bounds: 0 <= {i0} < {i1} <= {num}') + else: + raise TypeError('expected slice or scalar') + + return i0, i1 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_construct.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_construct.py new file mode 100644 index 0000000000000000000000000000000000000000..61ac8d716c523d41d3b5c9942986e7f63ba69d8b --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_construct.py @@ -0,0 +1,1410 @@ +"""Functions to construct sparse matrices and arrays +""" + +__docformat__ = "restructuredtext en" + +__all__ = ['spdiags', 'eye', 'identity', 'kron', 'kronsum', + 'hstack', 'vstack', 'bmat', 'rand', 'random', 'diags', 'block_diag', + 'diags_array', 'block_array', 'eye_array', 'random_array'] + +import numbers +import math +import numpy as np + +from scipy._lib._util import check_random_state, rng_integers +from ._sputils import upcast, get_index_dtype, isscalarlike + +from ._sparsetools import csr_hstack +from ._bsr import bsr_matrix, bsr_array +from ._coo import coo_matrix, coo_array +from ._csc import csc_matrix, csc_array +from ._csr import csr_matrix, csr_array +from ._dia import dia_matrix, dia_array + +from ._base import issparse, sparray + + +def spdiags(data, diags, m=None, n=None, format=None): + """ + Return a sparse matrix from diagonals. + + Parameters + ---------- + data : array_like + Matrix diagonals stored row-wise + diags : sequence of int or an int + Diagonals to set: + + * k = 0 the main diagonal + * k > 0 the kth upper diagonal + * k < 0 the kth lower diagonal + m, n : int, tuple, optional + Shape of the result. If `n` is None and `m` is a given tuple, + the shape is this tuple. If omitted, the matrix is square and + its shape is len(data[0]). + format : str, optional + Format of the result. By default (format=None) an appropriate sparse + matrix format is returned. This choice is subject to change. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``diags_array`` to take advantage + of the sparse array functionality. + + See Also + -------- + diags_array : more convenient form of this function + diags : matrix version of diags_array + dia_matrix : the sparse DIAgonal format. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import spdiags + >>> data = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]) + >>> diags = np.array([0, -1, 2]) + >>> spdiags(data, diags, 4, 4).toarray() + array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + """ + if m is None and n is None: + m = n = len(data[0]) + elif n is None: + m, n = m + return dia_matrix((data, diags), shape=(m, n)).asformat(format) + + +def diags_array(diagonals, /, *, offsets=0, shape=None, format=None, dtype=None): + """ + Construct a sparse array from diagonals. + + Parameters + ---------- + diagonals : sequence of array_like + Sequence of arrays containing the array diagonals, + corresponding to `offsets`. + offsets : sequence of int or an int, optional + Diagonals to set: + - k = 0 the main diagonal (default) + - k > 0 the kth upper diagonal + - k < 0 the kth lower diagonal + shape : tuple of int, optional + Shape of the result. If omitted, a square array large enough + to contain the diagonals is returned. + format : {"dia", "csr", "csc", "lil", ...}, optional + Matrix format of the result. By default (format=None) an + appropriate sparse array format is returned. This choice is + subject to change. + dtype : dtype, optional + Data type of the array. + + Notes + ----- + The result from `diags_array` is the sparse equivalent of:: + + np.diag(diagonals[0], offsets[0]) + + ... + + np.diag(diagonals[k], offsets[k]) + + Repeated diagonal offsets are disallowed. + + .. versionadded:: 1.11 + + Examples + -------- + >>> from scipy.sparse import diags_array + >>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]] + >>> diags_array(diagonals, offsets=[0, -1, 2]).toarray() + array([[1, 0, 1, 0], + [1, 2, 0, 2], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + Broadcasting of scalars is supported (but shape needs to be + specified): + + >>> diags_array([1, -2, 1], offsets=[-1, 0, 1], shape=(4, 4)).toarray() + array([[-2., 1., 0., 0.], + [ 1., -2., 1., 0.], + [ 0., 1., -2., 1.], + [ 0., 0., 1., -2.]]) + + + If only one diagonal is wanted (as in `numpy.diag`), the following + works as well: + + >>> diags_array([1, 2, 3], offsets=1).toarray() + array([[ 0., 1., 0., 0.], + [ 0., 0., 2., 0.], + [ 0., 0., 0., 3.], + [ 0., 0., 0., 0.]]) + """ + # if offsets is not a sequence, assume that there's only one diagonal + if isscalarlike(offsets): + # now check that there's actually only one diagonal + if len(diagonals) == 0 or isscalarlike(diagonals[0]): + diagonals = [np.atleast_1d(diagonals)] + else: + raise ValueError("Different number of diagonals and offsets.") + else: + diagonals = list(map(np.atleast_1d, diagonals)) + + offsets = np.atleast_1d(offsets) + + # Basic check + if len(diagonals) != len(offsets): + raise ValueError("Different number of diagonals and offsets.") + + # Determine shape, if omitted + if shape is None: + m = len(diagonals[0]) + abs(int(offsets[0])) + shape = (m, m) + + # Determine data type, if omitted + if dtype is None: + dtype = np.common_type(*diagonals) + + # Construct data array + m, n = shape + + M = max([min(m + offset, n - offset) + max(0, offset) + for offset in offsets]) + M = max(0, M) + data_arr = np.zeros((len(offsets), M), dtype=dtype) + + K = min(m, n) + + for j, diagonal in enumerate(diagonals): + offset = offsets[j] + k = max(0, offset) + length = min(m + offset, n - offset, K) + if length < 0: + raise ValueError("Offset %d (index %d) out of bounds" % (offset, j)) + try: + data_arr[j, k:k+length] = diagonal[...,:length] + except ValueError as e: + if len(diagonal) != length and len(diagonal) != 1: + raise ValueError( + "Diagonal length (index %d: %d at offset %d) does not " + "agree with array size (%d, %d)." % ( + j, len(diagonal), offset, m, n)) from e + raise + + return dia_array((data_arr, offsets), shape=(m, n)).asformat(format) + + +def diags(diagonals, offsets=0, shape=None, format=None, dtype=None): + """ + Construct a sparse matrix from diagonals. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``diags_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + diagonals : sequence of array_like + Sequence of arrays containing the matrix diagonals, + corresponding to `offsets`. + offsets : sequence of int or an int, optional + Diagonals to set: + - k = 0 the main diagonal (default) + - k > 0 the kth upper diagonal + - k < 0 the kth lower diagonal + shape : tuple of int, optional + Shape of the result. If omitted, a square matrix large enough + to contain the diagonals is returned. + format : {"dia", "csr", "csc", "lil", ...}, optional + Matrix format of the result. By default (format=None) an + appropriate sparse matrix format is returned. This choice is + subject to change. + dtype : dtype, optional + Data type of the matrix. + + See Also + -------- + spdiags : construct matrix from diagonals + diags_array : construct sparse array instead of sparse matrix + + Notes + ----- + This function differs from `spdiags` in the way it handles + off-diagonals. + + The result from `diags` is the sparse equivalent of:: + + np.diag(diagonals[0], offsets[0]) + + ... + + np.diag(diagonals[k], offsets[k]) + + Repeated diagonal offsets are disallowed. + + .. versionadded:: 0.11 + + Examples + -------- + >>> from scipy.sparse import diags + >>> diagonals = [[1, 2, 3, 4], [1, 2, 3], [1, 2]] + >>> diags(diagonals, [0, -1, 2]).toarray() + array([[1, 0, 1, 0], + [1, 2, 0, 2], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + Broadcasting of scalars is supported (but shape needs to be + specified): + + >>> diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)).toarray() + array([[-2., 1., 0., 0.], + [ 1., -2., 1., 0.], + [ 0., 1., -2., 1.], + [ 0., 0., 1., -2.]]) + + + If only one diagonal is wanted (as in `numpy.diag`), the following + works as well: + + >>> diags([1, 2, 3], 1).toarray() + array([[ 0., 1., 0., 0.], + [ 0., 0., 2., 0.], + [ 0., 0., 0., 3.], + [ 0., 0., 0., 0.]]) + """ + A = diags_array(diagonals, offsets=offsets, shape=shape, dtype=dtype) + return dia_matrix(A).asformat(format) + + +def identity(n, dtype='d', format=None): + """Identity matrix in sparse format + + Returns an identity matrix with shape (n,n) using a given + sparse format and dtype. This differs from `eye_array` in + that it has a square shape with ones only on the main diagonal. + It is thus the multiplicative identity. `eye_array` allows + rectangular shapes and the diagonal can be offset from the main one. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``eye_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + n : int + Shape of the identity matrix. + dtype : dtype, optional + Data type of the matrix + format : str, optional + Sparse format of the result, e.g., format="csr", etc. + + Examples + -------- + >>> import scipy as sp + >>> sp.sparse.identity(3).toarray() + array([[ 1., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 1.]]) + >>> sp.sparse.identity(3, dtype='int8', format='dia') + + >>> sp.sparse.eye_array(3, dtype='int8', format='dia') + + + """ + return eye(n, n, dtype=dtype, format=format) + + +def eye_array(m, n=None, *, k=0, dtype=float, format=None): + """Identity matrix in sparse array format + + Return a sparse array with ones on diagonal. + Specifically a sparse array (m x n) where the kth diagonal + is all ones and everything else is zeros. + + Parameters + ---------- + m : int or tuple of ints + Number of rows requested. + n : int, optional + Number of columns. Default: `m`. + k : int, optional + Diagonal to place ones on. Default: 0 (main diagonal). + dtype : dtype, optional + Data type of the array + format : str, optional (default: "dia") + Sparse format of the result, e.g., format="csr", etc. + + Examples + -------- + >>> import numpy as np + >>> import scipy as sp + >>> sp.sparse.eye_array(3).toarray() + array([[ 1., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 1.]]) + >>> sp.sparse.eye_array(3, dtype=np.int8) + + + """ + # TODO: delete next 15 lines [combine with _eye()] once spmatrix removed + return _eye(m, n, k, dtype, format) + + +def _eye(m, n, k, dtype, format, as_sparray=True): + if as_sparray: + csr_sparse = csr_array + csc_sparse = csc_array + coo_sparse = coo_array + diags_sparse = diags_array + else: + csr_sparse = csr_matrix + csc_sparse = csc_matrix + coo_sparse = coo_matrix + diags_sparse = diags + + if n is None: + n = m + m, n = int(m), int(n) + + if m == n and k == 0: + # fast branch for special formats + if format in ['csr', 'csc']: + idx_dtype = get_index_dtype(maxval=n) + indptr = np.arange(n+1, dtype=idx_dtype) + indices = np.arange(n, dtype=idx_dtype) + data = np.ones(n, dtype=dtype) + cls = {'csr': csr_sparse, 'csc': csc_sparse}[format] + return cls((data, indices, indptr), (n, n)) + + elif format == 'coo': + idx_dtype = get_index_dtype(maxval=n) + row = np.arange(n, dtype=idx_dtype) + col = np.arange(n, dtype=idx_dtype) + data = np.ones(n, dtype=dtype) + return coo_sparse((data, (row, col)), (n, n)) + + data = np.ones((1, max(0, min(m + k, n))), dtype=dtype) + return diags_sparse(data, offsets=[k], shape=(m, n), dtype=dtype).asformat(format) + + +def eye(m, n=None, k=0, dtype=float, format=None): + """Sparse matrix with ones on diagonal + + Returns a sparse matrix (m x n) where the kth diagonal + is all ones and everything else is zeros. + + Parameters + ---------- + m : int + Number of rows in the matrix. + n : int, optional + Number of columns. Default: `m`. + k : int, optional + Diagonal to place ones on. Default: 0 (main diagonal). + dtype : dtype, optional + Data type of the matrix. + format : str, optional + Sparse format of the result, e.g., format="csr", etc. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``eye_array`` to take advantage + of the sparse array functionality. + + Examples + -------- + >>> import numpy as np + >>> import scipy as sp + >>> sp.sparse.eye(3).toarray() + array([[ 1., 0., 0.], + [ 0., 1., 0.], + [ 0., 0., 1.]]) + >>> sp.sparse.eye(3, dtype=np.int8) + + + """ + return _eye(m, n, k, dtype, format, False) + + +def kron(A, B, format=None): + """kronecker product of sparse matrices A and B + + Parameters + ---------- + A : sparse or dense matrix + first matrix of the product + B : sparse or dense matrix + second matrix of the product + format : str, optional (default: 'bsr' or 'coo') + format of the result (e.g. "csr") + If None, choose 'bsr' for relatively dense array and 'coo' for others + + Returns + ------- + kronecker product in a sparse format. + Returns a sparse matrix unless either A or B is a + sparse array in which case returns a sparse array. + + Examples + -------- + >>> import numpy as np + >>> import scipy as sp + >>> A = sp.sparse.csr_array(np.array([[0, 2], [5, 0]])) + >>> B = sp.sparse.csr_array(np.array([[1, 2], [3, 4]])) + >>> sp.sparse.kron(A, B).toarray() + array([[ 0, 0, 2, 4], + [ 0, 0, 6, 8], + [ 5, 10, 0, 0], + [15, 20, 0, 0]]) + + >>> sp.sparse.kron(A, [[1, 2], [3, 4]]).toarray() + array([[ 0, 0, 2, 4], + [ 0, 0, 6, 8], + [ 5, 10, 0, 0], + [15, 20, 0, 0]]) + + """ + # TODO: delete next 10 lines and replace _sparse with _array when spmatrix removed + if isinstance(A, sparray) or isinstance(B, sparray): + # convert to local variables + bsr_sparse = bsr_array + csr_sparse = csr_array + coo_sparse = coo_array + else: # use spmatrix + bsr_sparse = bsr_matrix + csr_sparse = csr_matrix + coo_sparse = coo_matrix + + B = coo_sparse(B) + if B.ndim != 2: + raise ValueError(f"kron requires 2D input arrays. `B` is {B.ndim}D.") + + # B is fairly dense, use BSR + if (format is None or format == "bsr") and 2*B.nnz >= B.shape[0] * B.shape[1]: + A = csr_sparse(A,copy=True) + if A.ndim != 2: + raise ValueError(f"kron requires 2D input arrays. `A` is {A.ndim}D.") + output_shape = (A.shape[0]*B.shape[0], A.shape[1]*B.shape[1]) + + if A.nnz == 0 or B.nnz == 0: + # kronecker product is the zero matrix + return coo_sparse(output_shape).asformat(format) + + B = B.toarray() + data = A.data.repeat(B.size).reshape(-1,B.shape[0],B.shape[1]) + data = data * B + + return bsr_sparse((data,A.indices,A.indptr), shape=output_shape) + else: + # use COO + A = coo_sparse(A) + if A.ndim != 2: + raise ValueError(f"kron requires 2D input arrays. `A` is {A.ndim}D.") + output_shape = (A.shape[0]*B.shape[0], A.shape[1]*B.shape[1]) + + if A.nnz == 0 or B.nnz == 0: + # kronecker product is the zero matrix + return coo_sparse(output_shape).asformat(format) + + # expand entries of a into blocks + row = A.row.repeat(B.nnz) + col = A.col.repeat(B.nnz) + data = A.data.repeat(B.nnz) + + if max(A.shape[0]*B.shape[0], A.shape[1]*B.shape[1]) > np.iinfo('int32').max: + row = row.astype(np.int64) + col = col.astype(np.int64) + + row *= B.shape[0] + col *= B.shape[1] + + # increment block indices + row,col = row.reshape(-1,B.nnz),col.reshape(-1,B.nnz) + row += B.row + col += B.col + row,col = row.reshape(-1),col.reshape(-1) + + # compute block entries + data = data.reshape(-1,B.nnz) * B.data + data = data.reshape(-1) + + return coo_sparse((data,(row,col)), shape=output_shape).asformat(format) + + +def kronsum(A, B, format=None): + """kronecker sum of square sparse matrices A and B + + Kronecker sum of two sparse matrices is a sum of two Kronecker + products kron(I_n,A) + kron(B,I_m) where A has shape (m,m) + and B has shape (n,n) and I_m and I_n are identity matrices + of shape (m,m) and (n,n), respectively. + + Parameters + ---------- + A + square matrix + B + square matrix + format : str + format of the result (e.g. "csr") + + Returns + ------- + kronecker sum in a sparse matrix format + + """ + # TODO: delete next 8 lines and replace _sparse with _array when spmatrix removed + if isinstance(A, sparray) or isinstance(B, sparray): + # convert to local variables + coo_sparse = coo_array + identity_sparse = eye_array + else: + coo_sparse = coo_matrix + identity_sparse = identity + + A = coo_sparse(A) + B = coo_sparse(B) + + if A.ndim != 2: + raise ValueError(f"kronsum requires 2D inputs. `A` is {A.ndim}D.") + if B.ndim != 2: + raise ValueError(f"kronsum requires 2D inputs. `B` is {B.ndim}D.") + if A.shape[0] != A.shape[1]: + raise ValueError('A is not square') + if B.shape[0] != B.shape[1]: + raise ValueError('B is not square') + + dtype = upcast(A.dtype, B.dtype) + + I_n = identity_sparse(A.shape[0], dtype=dtype) + I_m = identity_sparse(B.shape[0], dtype=dtype) + L = kron(I_m, A, format='coo') + R = kron(B, I_n, format='coo') + + return (L + R).asformat(format) + + +def _compressed_sparse_stack(blocks, axis, return_spmatrix): + """ + Stacking fast path for CSR/CSC matrices or arrays + (i) vstack for CSR, (ii) hstack for CSC. + """ + other_axis = 1 if axis == 0 else 0 + data = np.concatenate([b.data for b in blocks]) + constant_dim = blocks[0]._shape_as_2d[other_axis] + idx_dtype = get_index_dtype(arrays=[b.indptr for b in blocks], + maxval=max(data.size, constant_dim)) + indices = np.empty(data.size, dtype=idx_dtype) + indptr = np.empty(sum(b._shape_as_2d[axis] for b in blocks) + 1, dtype=idx_dtype) + last_indptr = idx_dtype(0) + sum_dim = 0 + sum_indices = 0 + for b in blocks: + if b._shape_as_2d[other_axis] != constant_dim: + raise ValueError(f'incompatible dimensions for axis {other_axis}') + indices[sum_indices:sum_indices+b.indices.size] = b.indices + sum_indices += b.indices.size + idxs = slice(sum_dim, sum_dim + b._shape_as_2d[axis]) + indptr[idxs] = b.indptr[:-1] + indptr[idxs] += last_indptr + sum_dim += b._shape_as_2d[axis] + last_indptr += b.indptr[-1] + indptr[-1] = last_indptr + # TODO remove this if-structure when sparse matrices removed + if return_spmatrix: + if axis == 0: + return csr_matrix((data, indices, indptr), + shape=(sum_dim, constant_dim)) + else: + return csc_matrix((data, indices, indptr), + shape=(constant_dim, sum_dim)) + + if axis == 0: + return csr_array((data, indices, indptr), + shape=(sum_dim, constant_dim)) + else: + return csc_array((data, indices, indptr), + shape=(constant_dim, sum_dim)) + + +def _stack_along_minor_axis(blocks, axis): + """ + Stacking fast path for CSR/CSC matrices along the minor axis + (i) hstack for CSR, (ii) vstack for CSC. + """ + n_blocks = len(blocks) + if n_blocks == 0: + raise ValueError('Missing block matrices') + + if n_blocks == 1: + return blocks[0] + + # check for incompatible dimensions + other_axis = 1 if axis == 0 else 0 + other_axis_dims = {b._shape_as_2d[other_axis] for b in blocks} + if len(other_axis_dims) > 1: + raise ValueError(f'Mismatching dimensions along axis {other_axis}: ' + f'{other_axis_dims}') + constant_dim, = other_axis_dims + + # Do the stacking + indptr_list = [b.indptr for b in blocks] + data_cat = np.concatenate([b.data for b in blocks]) + + # Need to check if any indices/indptr, would be too large post- + # concatenation for np.int32: + # - The max value of indices is the output array's stacking-axis length - 1 + # - The max value in indptr is the number of non-zero entries. This is + # exceedingly unlikely to require int64, but is checked out of an + # abundance of caution. + sum_dim = sum(b._shape_as_2d[axis] for b in blocks) + nnz = sum(len(b.indices) for b in blocks) + idx_dtype = get_index_dtype(maxval=max(sum_dim - 1, nnz)) + stack_dim_cat = np.array([b._shape_as_2d[axis] for b in blocks], dtype=idx_dtype) + if data_cat.size > 0: + indptr_cat = np.concatenate(indptr_list).astype(idx_dtype) + indices_cat = (np.concatenate([b.indices for b in blocks]) + .astype(idx_dtype)) + indptr = np.empty(constant_dim + 1, dtype=idx_dtype) + indices = np.empty_like(indices_cat) + data = np.empty_like(data_cat) + csr_hstack(n_blocks, constant_dim, stack_dim_cat, + indptr_cat, indices_cat, data_cat, + indptr, indices, data) + else: + indptr = np.zeros(constant_dim + 1, dtype=idx_dtype) + indices = np.empty(0, dtype=idx_dtype) + data = np.empty(0, dtype=data_cat.dtype) + + if axis == 0: + return blocks[0]._csc_container((data, indices, indptr), + shape=(sum_dim, constant_dim)) + else: + return blocks[0]._csr_container((data, indices, indptr), + shape=(constant_dim, sum_dim)) + + +def hstack(blocks, format=None, dtype=None): + """ + Stack sparse matrices horizontally (column wise) + + Parameters + ---------- + blocks + sequence of sparse matrices with compatible shapes + format : str + sparse format of the result (e.g., "csr") + by default an appropriate sparse matrix format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output matrix. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + new_array : sparse matrix or array + If any block in blocks is a sparse array, return a sparse array. + Otherwise return a sparse matrix. + + If you want a sparse array built from blocks that are not sparse + arrays, use `block(hstack(blocks))` or convert one block + e.g. `blocks[0] = csr_array(blocks[0])`. + + See Also + -------- + vstack : stack sparse matrices vertically (row wise) + + Examples + -------- + >>> from scipy.sparse import coo_matrix, hstack + >>> A = coo_matrix([[1, 2], [3, 4]]) + >>> B = coo_matrix([[5], [6]]) + >>> hstack([A,B]).toarray() + array([[1, 2, 5], + [3, 4, 6]]) + + """ + blocks = np.asarray(blocks, dtype='object') + if any(isinstance(b, sparray) for b in blocks.flat): + return _block([blocks], format, dtype) + else: + return _block([blocks], format, dtype, return_spmatrix=True) + + +def vstack(blocks, format=None, dtype=None): + """ + Stack sparse arrays vertically (row wise) + + Parameters + ---------- + blocks + sequence of sparse arrays with compatible shapes + format : str, optional + sparse format of the result (e.g., "csr") + by default an appropriate sparse array format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output array. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + new_array : sparse matrix or array + If any block in blocks is a sparse array, return a sparse array. + Otherwise return a sparse matrix. + + If you want a sparse array built from blocks that are not sparse + arrays, use `block(vstack(blocks))` or convert one block + e.g. `blocks[0] = csr_array(blocks[0])`. + + See Also + -------- + hstack : stack sparse matrices horizontally (column wise) + + Examples + -------- + >>> from scipy.sparse import coo_array, vstack + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5, 6]]) + >>> vstack([A, B]).toarray() + array([[1, 2], + [3, 4], + [5, 6]]) + + """ + blocks = np.asarray(blocks, dtype='object') + if any(isinstance(b, sparray) for b in blocks.flat): + return _block([[b] for b in blocks], format, dtype) + else: + return _block([[b] for b in blocks], format, dtype, return_spmatrix=True) + + +def bmat(blocks, format=None, dtype=None): + """ + Build a sparse array or matrix from sparse sub-blocks + + Note: `block_array` is preferred over `bmat`. They are the same function + except that `bmat` can return a deprecated sparse matrix. + `bmat` returns a coo_matrix if none of the inputs are a sparse array. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``block_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + blocks : array_like + Grid of sparse matrices with compatible shapes. + An entry of None implies an all-zero matrix. + format : {'bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'}, optional + The sparse format of the result (e.g. "csr"). By default an + appropriate sparse matrix format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output matrix. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + bmat : sparse matrix or array + If any block in blocks is a sparse array, return a sparse array. + Otherwise return a sparse matrix. + + If you want a sparse array built from blocks that are not sparse + arrays, use `block_array()`. + + See Also + -------- + block_array + + Examples + -------- + >>> from scipy.sparse import coo_array, bmat + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5], [6]]) + >>> C = coo_array([[7]]) + >>> bmat([[A, B], [None, C]]).toarray() + array([[1, 2, 5], + [3, 4, 6], + [0, 0, 7]]) + + >>> bmat([[A, None], [None, C]]).toarray() + array([[1, 2, 0], + [3, 4, 0], + [0, 0, 7]]) + + """ + blocks = np.asarray(blocks, dtype='object') + if any(isinstance(b, sparray) for b in blocks.flat): + return _block(blocks, format, dtype) + else: + return _block(blocks, format, dtype, return_spmatrix=True) + + +def block_array(blocks, *, format=None, dtype=None): + """ + Build a sparse array from sparse sub-blocks + + Parameters + ---------- + blocks : array_like + Grid of sparse arrays with compatible shapes. + An entry of None implies an all-zero array. + format : {'bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'}, optional + The sparse format of the result (e.g. "csr"). By default an + appropriate sparse array format is returned. + This choice is subject to change. + dtype : dtype, optional + The data-type of the output array. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + block : sparse array + + See Also + -------- + block_diag : specify blocks along the main diagonals + diags : specify (possibly offset) diagonals + + Examples + -------- + >>> from scipy.sparse import coo_array, block_array + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5], [6]]) + >>> C = coo_array([[7]]) + >>> block_array([[A, B], [None, C]]).toarray() + array([[1, 2, 5], + [3, 4, 6], + [0, 0, 7]]) + + >>> block_array([[A, None], [None, C]]).toarray() + array([[1, 2, 0], + [3, 4, 0], + [0, 0, 7]]) + + """ + return _block(blocks, format, dtype) + + +def _block(blocks, format, dtype, return_spmatrix=False): + blocks = np.asarray(blocks, dtype='object') + + if blocks.ndim != 2: + raise ValueError('blocks must be 2-D') + + M,N = blocks.shape + + # check for fast path cases + if (format in (None, 'csr') and + all(issparse(b) and b.format == 'csr' for b in blocks.flat) + ): + if N > 1: + # stack along columns (axis 1): must have shape (M, 1) + blocks = [[_stack_along_minor_axis(blocks[b, :], 1)] for b in range(M)] + blocks = np.asarray(blocks, dtype='object') + + # stack along rows (axis 0): + A = _compressed_sparse_stack(blocks[:, 0], 0, return_spmatrix) + if dtype is not None: + A = A.astype(dtype) + return A + elif (format in (None, 'csc') and + all(issparse(b) and b.format == 'csc' for b in blocks.flat) + ): + if M > 1: + # stack along rows (axis 0): must have shape (1, N) + blocks = [[_stack_along_minor_axis(blocks[:, b], 0) for b in range(N)]] + blocks = np.asarray(blocks, dtype='object') + + # stack along columns (axis 1): + A = _compressed_sparse_stack(blocks[0, :], 1, return_spmatrix) + if dtype is not None: + A = A.astype(dtype) + return A + + block_mask = np.zeros(blocks.shape, dtype=bool) + brow_lengths = np.zeros(M, dtype=np.int64) + bcol_lengths = np.zeros(N, dtype=np.int64) + + # convert everything to COO format + for i in range(M): + for j in range(N): + if blocks[i,j] is not None: + A = coo_array(blocks[i,j]) + blocks[i,j] = A + block_mask[i,j] = True + + if brow_lengths[i] == 0: + brow_lengths[i] = A._shape_as_2d[0] + elif brow_lengths[i] != A._shape_as_2d[0]: + msg = (f'blocks[{i},:] has incompatible row dimensions. ' + f'Got blocks[{i},{j}].shape[0] == {A._shape_as_2d[0]}, ' + f'expected {brow_lengths[i]}.') + raise ValueError(msg) + + if bcol_lengths[j] == 0: + bcol_lengths[j] = A._shape_as_2d[1] + elif bcol_lengths[j] != A._shape_as_2d[1]: + msg = (f'blocks[:,{j}] has incompatible column ' + f'dimensions. ' + f'Got blocks[{i},{j}].shape[1] == {A._shape_as_2d[1]}, ' + f'expected {bcol_lengths[j]}.') + raise ValueError(msg) + + nnz = sum(block.nnz for block in blocks[block_mask]) + if dtype is None: + all_dtypes = [blk.dtype for blk in blocks[block_mask]] + dtype = upcast(*all_dtypes) if all_dtypes else None + + row_offsets = np.append(0, np.cumsum(brow_lengths)) + col_offsets = np.append(0, np.cumsum(bcol_lengths)) + + shape = (row_offsets[-1], col_offsets[-1]) + + data = np.empty(nnz, dtype=dtype) + idx_dtype = get_index_dtype(maxval=max(shape)) + row = np.empty(nnz, dtype=idx_dtype) + col = np.empty(nnz, dtype=idx_dtype) + + nnz = 0 + ii, jj = np.nonzero(block_mask) + for i, j in zip(ii, jj): + B = blocks[i, j] + idx = slice(nnz, nnz + B.nnz) + data[idx] = B.data + np.add(B.row, row_offsets[i], out=row[idx], dtype=idx_dtype) + np.add(B.col, col_offsets[j], out=col[idx], dtype=idx_dtype) + nnz += B.nnz + + if return_spmatrix: + return coo_matrix((data, (row, col)), shape=shape).asformat(format) + return coo_array((data, (row, col)), shape=shape).asformat(format) + + +def block_diag(mats, format=None, dtype=None): + """ + Build a block diagonal sparse matrix or array from provided matrices. + + Parameters + ---------- + mats : sequence of matrices or arrays + Input matrices or arrays. + format : str, optional + The sparse format of the result (e.g., "csr"). If not given, the result + is returned in "coo" format. + dtype : dtype specifier, optional + The data-type of the output. If not given, the dtype is + determined from that of `blocks`. + + Returns + ------- + res : sparse matrix or array + If at least one input is a sparse array, the output is a sparse array. + Otherwise the output is a sparse matrix. + + Notes + ----- + + .. versionadded:: 0.11.0 + + See Also + -------- + block_array + diags_array + + Examples + -------- + >>> from scipy.sparse import coo_array, block_diag + >>> A = coo_array([[1, 2], [3, 4]]) + >>> B = coo_array([[5], [6]]) + >>> C = coo_array([[7]]) + >>> block_diag((A, B, C)).toarray() + array([[1, 2, 0, 0], + [3, 4, 0, 0], + [0, 0, 5, 0], + [0, 0, 6, 0], + [0, 0, 0, 7]]) + + """ + if any(isinstance(a, sparray) for a in mats): + container = coo_array + else: + container = coo_matrix + + row = [] + col = [] + data = [] + r_idx = 0 + c_idx = 0 + for a in mats: + if isinstance(a, (list, numbers.Number)): + a = coo_array(np.atleast_2d(a)) + if issparse(a): + a = a.tocoo() + nrows, ncols = a._shape_as_2d + row.append(a.row + r_idx) + col.append(a.col + c_idx) + data.append(a.data) + else: + nrows, ncols = a.shape + a_row, a_col = np.divmod(np.arange(nrows*ncols), ncols) + row.append(a_row + r_idx) + col.append(a_col + c_idx) + data.append(a.ravel()) + r_idx += nrows + c_idx += ncols + row = np.concatenate(row) + col = np.concatenate(col) + data = np.concatenate(data) + return container((data, (row, col)), + shape=(r_idx, c_idx), + dtype=dtype).asformat(format) + + +def random_array(shape, *, density=0.01, format='coo', dtype=None, + random_state=None, data_sampler=None): + """Return a sparse array of uniformly random numbers in [0, 1) + + Returns a sparse array with the given shape and density + where values are generated uniformly randomly in the range [0, 1). + + .. warning:: + + Since numpy 1.17, passing a ``np.random.Generator`` (e.g. + ``np.random.default_rng``) for ``random_state`` will lead to much + faster execution times. + + A much slower implementation is used by default for backwards + compatibility. + + Parameters + ---------- + shape : int or tuple of ints + shape of the array + density : real, optional (default: 0.01) + density of the generated matrix: density equal to one means a full + matrix, density of 0 means a matrix with no non-zero items. + format : str, optional (default: 'coo') + sparse matrix format. + dtype : dtype, optional (default: np.float64) + type of the returned matrix values. + random_state : {None, int, `Generator`, `RandomState`}, optional + A random number generator to determine nonzero structure. We recommend using + a `numpy.random.Generator` manually provided for every call as it is much + faster than RandomState. + + - If `None` (or `np.random`), the `numpy.random.RandomState` + singleton is used. + - If an int, a new ``Generator`` instance is used, + seeded with the int. + - If a ``Generator`` or ``RandomState`` instance then + that instance is used. + + This random state will be used for sampling `indices` (the sparsity + structure), and by default for the data values too (see `data_sampler`). + + data_sampler : callable, optional (default depends on dtype) + Sampler of random data values with keyword arg `size`. + This function should take a single keyword argument `size` specifying + the length of its returned ndarray. It is used to generate the nonzero + values in the matrix after the locations of those values are chosen. + By default, uniform [0, 1) random values are used unless `dtype` is + an integer (default uniform integers from that dtype) or + complex (default uniform over the unit square in the complex plane). + For these, the `random_state` rng is used e.g. `rng.uniform(size=size)`. + + Returns + ------- + res : sparse array + + Examples + -------- + + Passing a ``np.random.Generator`` instance for better performance: + + >>> import numpy as np + >>> import scipy as sp + >>> rng = np.random.default_rng() + + Default sampling uniformly from [0, 1): + + >>> S = sp.sparse.random_array((3, 4), density=0.25, random_state=rng) + + Providing a sampler for the values: + + >>> rvs = sp.stats.poisson(25, loc=10).rvs + >>> S = sp.sparse.random_array((3, 4), density=0.25, + ... random_state=rng, data_sampler=rvs) + >>> S.toarray() + array([[ 36., 0., 33., 0.], # random + [ 0., 0., 0., 0.], + [ 0., 0., 36., 0.]]) + + Building a custom distribution. + This example builds a squared normal from np.random: + + >>> def np_normal_squared(size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> S = sp.sparse.random_array((3, 4), density=0.25, random_state=rng, + ... data_sampler=np_normal_squared) + + Or we can build it from sp.stats style rvs functions: + + >>> def sp_stats_normal_squared(size=None, random_state=rng): + ... std_normal = sp.stats.distributions.norm_gen().rvs + ... return std_normal(size=size, random_state=random_state) ** 2 + >>> S = sp.sparse.random_array((3, 4), density=0.25, random_state=rng, + ... data_sampler=sp_stats_normal_squared) + + Or we can subclass sp.stats rv_continous or rv_discrete: + + >>> class NormalSquared(sp.stats.rv_continuous): + ... def _rvs(self, size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> X = NormalSquared() + >>> Y = X().rvs + >>> S = sp.sparse.random_array((3, 4), density=0.25, + ... random_state=rng, data_sampler=Y) + """ + # Use the more efficient RNG by default. + if random_state is None: + random_state = np.random.default_rng() + data, ind = _random(shape, density, format, dtype, random_state, data_sampler) + return coo_array((data, ind), shape=shape).asformat(format) + + +def _random(shape, density=0.01, format=None, dtype=None, + random_state=None, data_sampler=None): + if density < 0 or density > 1: + raise ValueError("density expected to be 0 <= density <= 1") + + tot_prod = math.prod(shape) # use `math` for when prod is >= 2**64 + + # Number of non zero values + size = int(round(density * tot_prod)) + + rng = check_random_state(random_state) + + if data_sampler is None: + if np.issubdtype(dtype, np.integer): + def data_sampler(size): + return rng_integers(rng, + np.iinfo(dtype).min, + np.iinfo(dtype).max, + size, + dtype=dtype) + elif np.issubdtype(dtype, np.complexfloating): + def data_sampler(size): + return (rng.uniform(size=size) + + rng.uniform(size=size) * 1j) + else: + data_sampler = rng.uniform + + # rng.choice uses int64 if first arg is an int + if tot_prod < np.iinfo(np.int64).max: + raveled_ind = rng.choice(tot_prod, size=size, replace=False) + ind = np.unravel_index(raveled_ind, shape=shape, order='F') + else: + # for ravel indices bigger than dtype max, use sets to remove duplicates + ndim = len(shape) + seen = set() + while len(seen) < size: + dsize = size - len(seen) + seen.update(map(tuple, rng_integers(rng, shape, size=(dsize, ndim)))) + ind = tuple(np.array(list(seen)).T) + + # size kwarg allows eg data_sampler=partial(np.random.poisson, lam=5) + vals = data_sampler(size=size).astype(dtype, copy=False) + return vals, ind + + +def random(m, n, density=0.01, format='coo', dtype=None, + random_state=None, data_rvs=None): + """Generate a sparse matrix of the given shape and density with randomly + distributed values. + + .. warning:: + + Since numpy 1.17, passing a ``np.random.Generator`` (e.g. + ``np.random.default_rng``) for ``random_state`` will lead to much + faster execution times. + + A much slower implementation is used by default for backwards + compatibility. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``random_array`` to take advantage of the + sparse array functionality. + + Parameters + ---------- + m, n : int + shape of the matrix + density : real, optional + density of the generated matrix: density equal to one means a full + matrix, density of 0 means a matrix with no non-zero items. + format : str, optional + sparse matrix format. + dtype : dtype, optional + type of the returned matrix values. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + - If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + - If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + - If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + This random state will be used for sampling the sparsity structure, but + not necessarily for sampling the values of the structurally nonzero + entries of the matrix. + data_rvs : callable, optional + Samples a requested number of random values. + This function should take a single argument specifying the length + of the ndarray that it will return. The structurally nonzero entries + of the sparse random matrix will be taken from the array sampled + by this function. By default, uniform [0, 1) random values will be + sampled using the same random state as is used for sampling + the sparsity structure. + + Returns + ------- + res : sparse matrix + + See Also + -------- + random_array : constructs sparse arrays instead of sparse matrices + + Examples + -------- + + Passing a ``np.random.Generator`` instance for better performance: + + >>> import scipy as sp + >>> import numpy as np + >>> rng = np.random.default_rng() + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng) + + Providing a sampler for the values: + + >>> rvs = sp.stats.poisson(25, loc=10).rvs + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, data_rvs=rvs) + >>> S.toarray() + array([[ 36., 0., 33., 0.], # random + [ 0., 0., 0., 0.], + [ 0., 0., 36., 0.]]) + + Building a custom distribution. + This example builds a squared normal from np.random: + + >>> def np_normal_squared(size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, + ... data_rvs=np_normal_squared) + + Or we can build it from sp.stats style rvs functions: + + >>> def sp_stats_normal_squared(size=None, random_state=rng): + ... std_normal = sp.stats.distributions.norm_gen().rvs + ... return std_normal(size=size, random_state=random_state) ** 2 + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, + ... data_rvs=sp_stats_normal_squared) + + Or we can subclass sp.stats rv_continous or rv_discrete: + + >>> class NormalSquared(sp.stats.rv_continuous): + ... def _rvs(self, size=None, random_state=rng): + ... return random_state.standard_normal(size) ** 2 + >>> X = NormalSquared() + >>> Y = X() # get a frozen version of the distribution + >>> S = sp.sparse.random(3, 4, density=0.25, random_state=rng, data_rvs=Y.rvs) + """ + if n is None: + n = m + m, n = int(m), int(n) + # make keyword syntax work for data_rvs e.g. data_rvs(size=7) + if data_rvs is not None: + def data_rvs_kw(size): + return data_rvs(size) + else: + data_rvs_kw = None + vals, ind = _random((m, n), density, format, dtype, random_state, data_rvs_kw) + return coo_matrix((vals, ind), shape=(m, n)).asformat(format) + + +def rand(m, n, density=0.01, format="coo", dtype=None, random_state=None): + """Generate a sparse matrix of the given shape and density with uniformly + distributed values. + + .. warning:: + + This function returns a sparse matrix -- not a sparse array. + You are encouraged to use ``random_array`` to take advantage + of the sparse array functionality. + + Parameters + ---------- + m, n : int + shape of the matrix + density : real, optional + density of the generated matrix: density equal to one means a full + matrix, density of 0 means a matrix with no non-zero items. + format : str, optional + sparse matrix format. + dtype : dtype, optional + type of the returned matrix values. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + If `seed` is None (or `np.random`), the `numpy.random.RandomState` + singleton is used. + If `seed` is an int, a new ``RandomState`` instance is used, + seeded with `seed`. + If `seed` is already a ``Generator`` or ``RandomState`` instance then + that instance is used. + + Returns + ------- + res : sparse matrix + + Notes + ----- + Only float types are supported for now. + + See Also + -------- + random : Similar function allowing a custom random data sampler + random_array : Similar to random() but returns a sparse array + + Examples + -------- + >>> from scipy.sparse import rand + >>> matrix = rand(3, 4, density=0.25, format="csr", random_state=42) + >>> matrix + + >>> matrix.toarray() + array([[0.05641158, 0. , 0. , 0.65088847], # random + [0. , 0. , 0. , 0.14286682], + [0. , 0. , 0. , 0. ]]) + + """ + return random(m, n, density, format, dtype, random_state) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_coo.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_coo.py new file mode 100644 index 0000000000000000000000000000000000000000..2f4580b3f004b7ad71c5d1dc369b87047e93720d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_coo.py @@ -0,0 +1,866 @@ +""" A sparse matrix in COOrdinate or 'triplet' format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['coo_array', 'coo_matrix', 'isspmatrix_coo'] + +import math +from warnings import warn + +import numpy as np + +from .._lib._util import copy_if_needed +from ._matrix import spmatrix +from ._sparsetools import coo_tocsr, coo_todense, coo_matvec +from ._base import issparse, SparseEfficiencyWarning, _spbase, sparray +from ._data import _data_matrix, _minmax_mixin +from ._sputils import (upcast_char, to_native, isshape, getdtype, + getdata, downcast_intp_index, get_index_dtype, + check_shape, check_reshape_kwargs) + +import operator + + +class _coo_base(_data_matrix, _minmax_mixin): + _format = 'coo' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _data_matrix.__init__(self, arg1) + is_array = isinstance(self, sparray) + if not copy: + copy = copy_if_needed + + if isinstance(arg1, tuple): + if isshape(arg1, allow_1d=is_array): + self._shape = check_shape(arg1, allow_1d=is_array) + idx_dtype = self._get_index_dtype(maxval=max(self._shape)) + data_dtype = getdtype(dtype, default=float) + self.coords = tuple(np.array([], dtype=idx_dtype) + for _ in range(len(self._shape))) + self.data = np.array([], dtype=data_dtype) + self.has_canonical_format = True + else: + try: + obj, coords = arg1 + except (TypeError, ValueError) as e: + raise TypeError('invalid input format') from e + + if shape is None: + if any(len(idx) == 0 for idx in coords): + raise ValueError('cannot infer dimensions from zero ' + 'sized index arrays') + shape = tuple(operator.index(np.max(idx)) + 1 + for idx in coords) + self._shape = check_shape(shape, allow_1d=is_array) + + idx_dtype = self._get_index_dtype(coords, + maxval=max(self.shape), + check_contents=True) + self.coords = tuple(np.array(idx, copy=copy, dtype=idx_dtype) + for idx in coords) + self.data = getdata(obj, copy=copy, dtype=dtype) + self.has_canonical_format = False + else: + if issparse(arg1): + if arg1.format == self.format and copy: + self.coords = tuple(idx.copy() for idx in arg1.coords) + self.data = arg1.data.copy() + self._shape = check_shape(arg1.shape, allow_1d=is_array) + self.has_canonical_format = arg1.has_canonical_format + else: + coo = arg1.tocoo() + self.coords = tuple(coo.coords) + self.data = coo.data + self._shape = check_shape(coo.shape, allow_1d=is_array) + self.has_canonical_format = False + else: + # dense argument + M = np.asarray(arg1) + if not is_array: + M = np.atleast_2d(M) + if M.ndim != 2: + raise TypeError(f'expected 2D array or matrix, not {M.ndim}D') + + self._shape = check_shape(M.shape, allow_1d=is_array) + if shape is not None: + if check_shape(shape, allow_1d=is_array) != self._shape: + message = f'inconsistent shapes: {shape} != {self._shape}' + raise ValueError(message) + index_dtype = self._get_index_dtype(maxval=max(self._shape)) + coords = M.nonzero() + self.coords = tuple(idx.astype(index_dtype, copy=False) + for idx in coords) + self.data = M[coords] + self.has_canonical_format = True + + if dtype is not None: + self.data = self.data.astype(dtype, copy=False) + + self._check() + + @property + def row(self): + if self.ndim > 1: + return self.coords[-2] + result = np.zeros_like(self.col) + result.setflags(write=False) + return result + + + @row.setter + def row(self, new_row): + if self.ndim < 2: + raise ValueError('cannot set row attribute of a 1-dimensional sparse array') + new_row = np.asarray(new_row, dtype=self.coords[-2].dtype) + self.coords = self.coords[:-2] + (new_row,) + self.coords[-1:] + + @property + def col(self): + return self.coords[-1] + + @col.setter + def col(self, new_col): + new_col = np.asarray(new_col, dtype=self.coords[-1].dtype) + self.coords = self.coords[:-1] + (new_col,) + + def reshape(self, *args, **kwargs): + is_array = isinstance(self, sparray) + shape = check_shape(args, self.shape, allow_1d=is_array) + order, copy = check_reshape_kwargs(kwargs) + + # Return early if reshape is not required + if shape == self.shape: + if copy: + return self.copy() + else: + return self + + # When reducing the number of dimensions, we need to be careful about + # index overflow. This is why we can't simply call + # `np.ravel_multi_index()` followed by `np.unravel_index()` here. + flat_coords = _ravel_coords(self.coords, self.shape, order=order) + if len(shape) == 2: + if order == 'C': + new_coords = divmod(flat_coords, shape[1]) + else: + new_coords = divmod(flat_coords, shape[0])[::-1] + else: + new_coords = np.unravel_index(flat_coords, shape, order=order) + + # Handle copy here rather than passing on to the constructor so that no + # copy will be made of `new_coords` regardless. + if copy: + new_data = self.data.copy() + else: + new_data = self.data + + return self.__class__((new_data, new_coords), shape=shape, copy=False) + + reshape.__doc__ = _spbase.reshape.__doc__ + + def _getnnz(self, axis=None): + if axis is None or (axis == 0 and self.ndim == 1): + nnz = len(self.data) + if any(len(idx) != nnz for idx in self.coords): + raise ValueError('all index and data arrays must have the ' + 'same length') + + if self.data.ndim != 1 or any(idx.ndim != 1 for idx in self.coords): + raise ValueError('row, column, and data arrays must be 1-D') + + return int(nnz) + + if axis < 0: + axis += self.ndim + if axis >= self.ndim: + raise ValueError('axis out of bounds') + if self.ndim > 2: + raise NotImplementedError('per-axis nnz for COO arrays with >2 ' + 'dimensions is not supported') + return np.bincount(downcast_intp_index(self.coords[1 - axis]), + minlength=self.shape[1 - axis]) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + + def _check(self): + """ Checks data structure for consistency """ + if self.ndim != len(self.coords): + raise ValueError('mismatching number of index arrays for shape; ' + f'got {len(self.coords)}, expected {self.ndim}') + + # index arrays should have integer data types + for i, idx in enumerate(self.coords): + if idx.dtype.kind != 'i': + warn(f'index array {i} has non-integer dtype ({idx.dtype.name})', + stacklevel=3) + + idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.shape)) + self.coords = tuple(np.asarray(idx, dtype=idx_dtype) + for idx in self.coords) + self.data = to_native(self.data) + + if self.nnz > 0: + for i, idx in enumerate(self.coords): + if idx.max() >= self.shape[i]: + raise ValueError(f'axis {i} index {idx.max()} exceeds ' + f'matrix dimension {self.shape[i]}') + if idx.min() < 0: + raise ValueError(f'negative axis {i} index: {idx.min()}') + + def transpose(self, axes=None, copy=False): + if axes is None: + axes = range(self.ndim)[::-1] + elif isinstance(self, sparray): + if len(axes) != self.ndim: + raise ValueError("axes don't match matrix dimensions") + if len(set(axes)) != self.ndim: + raise ValueError("repeated axis in transpose") + elif axes != (1, 0): + raise ValueError("Sparse matrices do not support an 'axes' " + "parameter because swapping dimensions is the " + "only logical permutation.") + + permuted_shape = tuple(self._shape[i] for i in axes) + permuted_coords = tuple(self.coords[i] for i in axes) + return self.__class__((self.data, permuted_coords), + shape=permuted_shape, copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def resize(self, *shape) -> None: + is_array = isinstance(self, sparray) + shape = check_shape(shape, allow_1d=is_array) + + # Check for added dimensions. + if len(shape) > self.ndim: + flat_coords = _ravel_coords(self.coords, self.shape) + max_size = math.prod(shape) + self.coords = np.unravel_index(flat_coords[:max_size], shape) + self.data = self.data[:max_size] + self._shape = shape + return + + # Check for removed dimensions. + if len(shape) < self.ndim: + tmp_shape = ( + self._shape[:len(shape) - 1] # Original shape without last axis + + (-1,) # Last axis is used to flatten the array + + (1,) * (self.ndim - len(shape)) # Pad with ones + ) + tmp = self.reshape(tmp_shape) + self.coords = tmp.coords[:len(shape)] + self._shape = tmp.shape[:len(shape)] + + # Handle truncation of existing dimensions. + is_truncating = any(old > new for old, new in zip(self.shape, shape)) + if is_truncating: + mask = np.logical_and.reduce([ + idx < size for idx, size in zip(self.coords, shape) + ]) + if not mask.all(): + self.coords = tuple(idx[mask] for idx in self.coords) + self.data = self.data[mask] + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + def toarray(self, order=None, out=None): + B = self._process_toarray_args(order, out) + fortran = int(B.flags.f_contiguous) + if not fortran and not B.flags.c_contiguous: + raise ValueError("Output array must be C or F contiguous") + if self.ndim > 2: + raise ValueError("Cannot densify higher-rank sparse array") + # This handles both 0D and 1D cases correctly regardless of the + # original shape. + M, N = self._shape_as_2d + coo_todense(M, N, self.nnz, self.row, self.col, self.data, + B.ravel('A'), fortran) + # Note: reshape() doesn't copy here, but does return a new array (view). + return B.reshape(self.shape) + + toarray.__doc__ = _spbase.toarray.__doc__ + + def tocsc(self, copy=False): + """Convert this array/matrix to Compressed Sparse Column format + + Duplicate entries will be summed together. + + Examples + -------- + >>> from numpy import array + >>> from scipy.sparse import coo_array + >>> row = array([0, 0, 1, 3, 1, 0, 0]) + >>> col = array([0, 2, 1, 3, 1, 0, 0]) + >>> data = array([1, 1, 1, 1, 1, 1, 1]) + >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsc() + >>> A.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to csc format") + if self.nnz == 0: + return self._csc_container(self.shape, dtype=self.dtype) + else: + from ._csc import csc_array + indptr, indices, data, shape = self._coo_to_compressed(csc_array._swap) + + x = self._csc_container((data, indices, indptr), shape=shape) + if not self.has_canonical_format: + x.sum_duplicates() + return x + + def tocsr(self, copy=False): + """Convert this array/matrix to Compressed Sparse Row format + + Duplicate entries will be summed together. + + Examples + -------- + >>> from numpy import array + >>> from scipy.sparse import coo_array + >>> row = array([0, 0, 1, 3, 1, 0, 0]) + >>> col = array([0, 2, 1, 3, 1, 0, 0]) + >>> data = array([1, 1, 1, 1, 1, 1, 1]) + >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsr() + >>> A.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + if self.nnz == 0: + return self._csr_container(self.shape, dtype=self.dtype) + else: + from ._csr import csr_array + arrays = self._coo_to_compressed(csr_array._swap, copy=copy) + indptr, indices, data, shape = arrays + + x = self._csr_container((data, indices, indptr), shape=self.shape) + if not self.has_canonical_format: + x.sum_duplicates() + return x + + def _coo_to_compressed(self, swap, copy=False): + """convert (shape, coords, data) to (indptr, indices, data, shape)""" + M, N = swap(self._shape_as_2d) + # convert idx_dtype intc to int32 for pythran. + # tested in scipy/optimize/tests/test__numdiff.py::test_group_columns + idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.nnz, N)) + + if self.ndim == 1: + indices = self.coords[0].copy() if copy else self.coords[0] + nnz = len(indices) + indptr = np.array([0, nnz], dtype=idx_dtype) + data = self.data.copy() if copy else self.data + return indptr, indices, data, self.shape + + # ndim == 2 + major, minor = swap(self.coords) + nnz = len(major) + major = major.astype(idx_dtype, copy=False) + minor = minor.astype(idx_dtype, copy=False) + + indptr = np.empty(M + 1, dtype=idx_dtype) + indices = np.empty_like(minor, dtype=idx_dtype) + data = np.empty_like(self.data, dtype=self.dtype) + + coo_tocsr(M, N, nnz, major, minor, self.data, indptr, indices, data) + return indptr, indices, data, self.shape + + def tocoo(self, copy=False): + if copy: + return self.copy() + else: + return self + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + def todia(self, copy=False): + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to dia format") + self.sum_duplicates() + ks = self.col - self.row # the diagonal for each nonzero + diags, diag_idx = np.unique(ks, return_inverse=True) + + if len(diags) > 100: + # probably undesired, should todia() have a maxdiags parameter? + warn("Constructing a DIA matrix with %d diagonals " + "is inefficient" % len(diags), + SparseEfficiencyWarning, stacklevel=2) + + #initialize and fill in data array + if self.data.size == 0: + data = np.zeros((0, 0), dtype=self.dtype) + else: + data = np.zeros((len(diags), self.col.max()+1), dtype=self.dtype) + data[diag_idx, self.col] = self.data + + return self._dia_container((data, diags), shape=self.shape) + + todia.__doc__ = _spbase.todia.__doc__ + + def todok(self, copy=False): + self.sum_duplicates() + dok = self._dok_container(self.shape, dtype=self.dtype) + # ensure that 1d coordinates are not tuples + if self.ndim == 1: + coords = self.coords[0] + else: + coords = zip(*self.coords) + + dok._dict = dict(zip(coords, self.data)) + return dok + + todok.__doc__ = _spbase.todok.__doc__ + + def diagonal(self, k=0): + if self.ndim != 2: + raise ValueError("diagonal requires two dimensions") + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + diag = np.zeros(min(rows + min(k, 0), cols - max(k, 0)), + dtype=self.dtype) + diag_mask = (self.row + k) == self.col + + if self.has_canonical_format: + row = self.row[diag_mask] + data = self.data[diag_mask] + else: + inds = tuple(idx[diag_mask] for idx in self.coords) + (row, _), data = self._sum_duplicates(inds, self.data[diag_mask]) + diag[row + min(k, 0)] = data + + return diag + + diagonal.__doc__ = _data_matrix.diagonal.__doc__ + + def _setdiag(self, values, k): + if self.ndim != 2: + raise ValueError("setting a diagonal requires two dimensions") + M, N = self.shape + if values.ndim and not len(values): + return + idx_dtype = self.row.dtype + + # Determine which triples to keep and where to put the new ones. + full_keep = self.col - self.row != k + if k < 0: + max_index = min(M+k, N) + if values.ndim: + max_index = min(max_index, len(values)) + keep = np.logical_or(full_keep, self.col >= max_index) + new_row = np.arange(-k, -k + max_index, dtype=idx_dtype) + new_col = np.arange(max_index, dtype=idx_dtype) + else: + max_index = min(M, N-k) + if values.ndim: + max_index = min(max_index, len(values)) + keep = np.logical_or(full_keep, self.row >= max_index) + new_row = np.arange(max_index, dtype=idx_dtype) + new_col = np.arange(k, k + max_index, dtype=idx_dtype) + + # Define the array of data consisting of the entries to be added. + if values.ndim: + new_data = values[:max_index] + else: + new_data = np.empty(max_index, dtype=self.dtype) + new_data[:] = values + + # Update the internal structure. + self.coords = (np.concatenate((self.row[keep], new_row)), + np.concatenate((self.col[keep], new_col))) + self.data = np.concatenate((self.data[keep], new_data)) + self.has_canonical_format = False + + # needed by _data_matrix + def _with_data(self, data, copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the index arrays are copied. + """ + if copy: + coords = tuple(idx.copy() for idx in self.coords) + else: + coords = self.coords + return self.__class__((data, coords), shape=self.shape, dtype=data.dtype) + + def sum_duplicates(self) -> None: + """Eliminate duplicate entries by adding them together + + This is an *in place* operation + """ + if self.has_canonical_format: + return + summed = self._sum_duplicates(self.coords, self.data) + self.coords, self.data = summed + self.has_canonical_format = True + + def _sum_duplicates(self, coords, data): + # Assumes coords not in canonical format. + if len(data) == 0: + return coords, data + # Sort coords w.r.t. rows, then cols. This corresponds to C-order, + # which we rely on for argmin/argmax to return the first index in the + # same way that numpy does (in the case of ties). + order = np.lexsort(coords[::-1]) + coords = tuple(idx[order] for idx in coords) + data = data[order] + unique_mask = np.logical_or.reduce([ + idx[1:] != idx[:-1] for idx in coords + ]) + unique_mask = np.append(True, unique_mask) + coords = tuple(idx[unique_mask] for idx in coords) + unique_inds, = np.nonzero(unique_mask) + data = np.add.reduceat(data, unique_inds, dtype=self.dtype) + return coords, data + + def eliminate_zeros(self): + """Remove zero entries from the array/matrix + + This is an *in place* operation + """ + mask = self.data != 0 + self.data = self.data[mask] + self.coords = tuple(idx[mask] for idx in self.coords) + + ####################### + # Arithmetic handlers # + ####################### + + def _add_dense(self, other): + if other.shape != self.shape: + raise ValueError(f'Incompatible shapes ({self.shape} and {other.shape})') + dtype = upcast_char(self.dtype.char, other.dtype.char) + result = np.array(other, dtype=dtype, copy=True) + fortran = int(result.flags.f_contiguous) + M, N = self._shape_as_2d + coo_todense(M, N, self.nnz, self.row, self.col, self.data, + result.ravel('A'), fortran) + return self._container(result, copy=False) + + def _matmul_vector(self, other): + result_shape = self.shape[0] if self.ndim > 1 else 1 + result = np.zeros(result_shape, + dtype=upcast_char(self.dtype.char, other.dtype.char)) + + if self.ndim == 2: + col = self.col + row = self.row + elif self.ndim == 1: + col = self.coords[0] + row = np.zeros_like(col) + else: + raise NotImplementedError( + f"coo_matvec not implemented for ndim={self.ndim}") + + coo_matvec(self.nnz, row, col, self.data, other, result) + # Array semantics return a scalar here, not a single-element array. + if isinstance(self, sparray) and result_shape == 1: + return result[0] + return result + + def _matmul_multivector(self, other): + result_dtype = upcast_char(self.dtype.char, other.dtype.char) + if self.ndim == 2: + result_shape = (other.shape[1], self.shape[0]) + col = self.col + row = self.row + elif self.ndim == 1: + result_shape = (other.shape[1],) + col = self.coords[0] + row = np.zeros_like(col) + else: + raise NotImplementedError( + f"coo_matvec not implemented for ndim={self.ndim}") + + result = np.zeros(result_shape, dtype=result_dtype) + for i, other_col in enumerate(other.T): + coo_matvec(self.nnz, row, col, self.data, other_col, result[i:i + 1]) + return result.T.view(type=type(other)) + + +def _ravel_coords(coords, shape, order='C'): + """Like np.ravel_multi_index, but avoids some overflow issues.""" + if len(coords) == 1: + return coords[0] + # Handle overflow as in https://github.com/scipy/scipy/pull/9132 + if len(coords) == 2: + nrows, ncols = shape + row, col = coords + if order == 'C': + maxval = (ncols * max(0, nrows - 1) + max(0, ncols - 1)) + idx_dtype = get_index_dtype(maxval=maxval) + return np.multiply(ncols, row, dtype=idx_dtype) + col + elif order == 'F': + maxval = (nrows * max(0, ncols - 1) + max(0, nrows - 1)) + idx_dtype = get_index_dtype(maxval=maxval) + return np.multiply(nrows, col, dtype=idx_dtype) + row + else: + raise ValueError("'order' must be 'C' or 'F'") + return np.ravel_multi_index(coords, shape, order=order) + + +def isspmatrix_coo(x): + """Is `x` of coo_matrix type? + + Parameters + ---------- + x + object to check for being a coo matrix + + Returns + ------- + bool + True if `x` is a coo matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import coo_array, coo_matrix, csr_matrix, isspmatrix_coo + >>> isspmatrix_coo(coo_matrix([[5]])) + True + >>> isspmatrix_coo(coo_array([[5]])) + False + >>> isspmatrix_coo(csr_matrix([[5]])) + False + """ + return isinstance(x, coo_matrix) + + +# This namespace class separates array from matrix with isinstance +class coo_array(_coo_base, sparray): + """ + A sparse array in COOrdinate format. + + Also known as the 'ijv' or 'triplet' format. + + This can be instantiated in several ways: + coo_array(D) + where D is an ndarray + + coo_array(S) + with another sparse array or matrix S (equivalent to S.tocoo()) + + coo_array(shape, [dtype]) + to construct an empty sparse array with shape `shape` + dtype is optional, defaulting to dtype='d'. + + coo_array((data, coords), [shape]) + to construct from existing data and index arrays: + 1. data[:] the entries of the sparse array, in any order + 2. coords[i][:] the axis-i coordinates of the data entries + + Where ``A[coords] = data``, and coords is a tuple of index arrays. + When shape is not specified, it is inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the sparse array + shape : tuple of integers + Shape of the sparse array + ndim : int + Number of dimensions of the sparse array + nnz + size + data + COO format data array of the sparse array + coords + COO format tuple of index arrays + has_canonical_format : bool + Whether the matrix has sorted coordinates and no duplicates + format + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the COO format + - facilitates fast conversion among sparse formats + - permits duplicate entries (see example) + - very fast conversion to and from CSR/CSC formats + + Disadvantages of the COO format + - does not directly support: + + arithmetic operations + + slicing + + Intended Usage + - COO is a fast format for constructing sparse arrays + - Once a COO array has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - By default when converting to CSR or CSC format, duplicate (i,j) + entries will be summed together. This facilitates efficient + construction of finite element matrices and the like. (see example) + + Canonical format + - Entries and coordinates sorted by row, then column. + - There are no duplicate entries (i.e. duplicate (i,j) locations) + - Data arrays MAY have explicit zeros. + + Examples + -------- + + >>> # Constructing an empty sparse array + >>> import numpy as np + >>> from scipy.sparse import coo_array + >>> coo_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> # Constructing a sparse array using ijv format + >>> row = np.array([0, 3, 1, 0]) + >>> col = np.array([0, 3, 1, 2]) + >>> data = np.array([4, 5, 7, 9]) + >>> coo_array((data, (row, col)), shape=(4, 4)).toarray() + array([[4, 0, 9, 0], + [0, 7, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 5]]) + + >>> # Constructing a sparse array with duplicate coordinates + >>> row = np.array([0, 0, 1, 3, 1, 0, 0]) + >>> col = np.array([0, 2, 1, 3, 1, 0, 0]) + >>> data = np.array([1, 1, 1, 1, 1, 1, 1]) + >>> coo = coo_array((data, (row, col)), shape=(4, 4)) + >>> # Duplicate coordinates are maintained until implicitly or explicitly summed + >>> np.max(coo.data) + 1 + >>> coo.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + + +class coo_matrix(spmatrix, _coo_base): + """ + A sparse matrix in COOrdinate format. + + Also known as the 'ijv' or 'triplet' format. + + This can be instantiated in several ways: + coo_matrix(D) + where D is a 2-D ndarray + + coo_matrix(S) + with another sparse array or matrix S (equivalent to S.tocoo()) + + coo_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + coo_matrix((data, (i, j)), [shape=(M, N)]) + to construct from three arrays: + 1. data[:] the entries of the matrix, in any order + 2. i[:] the row indices of the matrix entries + 3. j[:] the column indices of the matrix entries + + Where ``A[i[k], j[k]] = data[k]``. When shape is not + specified, it is inferred from the index arrays + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + COO format data array of the matrix + row + COO format row index array of the matrix + col + COO format column index array of the matrix + has_canonical_format : bool + Whether the matrix has sorted indices and no duplicates + format + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the COO format + - facilitates fast conversion among sparse formats + - permits duplicate entries (see example) + - very fast conversion to and from CSR/CSC formats + + Disadvantages of the COO format + - does not directly support: + + arithmetic operations + + slicing + + Intended Usage + - COO is a fast format for constructing sparse matrices + - Once a COO matrix has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - By default when converting to CSR or CSC format, duplicate (i,j) + entries will be summed together. This facilitates efficient + construction of finite element matrices and the like. (see example) + + Canonical format + - Entries and coordinates sorted by row, then column. + - There are no duplicate entries (i.e. duplicate (i,j) locations) + - Data arrays MAY have explicit zeros. + + Examples + -------- + + >>> # Constructing an empty matrix + >>> import numpy as np + >>> from scipy.sparse import coo_matrix + >>> coo_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> # Constructing a matrix using ijv format + >>> row = np.array([0, 3, 1, 0]) + >>> col = np.array([0, 3, 1, 2]) + >>> data = np.array([4, 5, 7, 9]) + >>> coo_matrix((data, (row, col)), shape=(4, 4)).toarray() + array([[4, 0, 9, 0], + [0, 7, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 5]]) + + >>> # Constructing a matrix with duplicate coordinates + >>> row = np.array([0, 0, 1, 3, 1, 0, 0]) + >>> col = np.array([0, 2, 1, 3, 1, 0, 0]) + >>> data = np.array([1, 1, 1, 1, 1, 1, 1]) + >>> coo = coo_matrix((data, (row, col)), shape=(4, 4)) + >>> # Duplicate coordinates are maintained until implicitly or explicitly summed + >>> np.max(coo.data) + 1 + >>> coo.toarray() + array([[3, 0, 1, 0], + [0, 2, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + """ + + def __setstate__(self, state): + if 'coords' not in state: + # For retro-compatibility with the previous attributes + # storing nnz coordinates for 2D COO matrix. + state['coords'] = (state.pop('row'), state.pop('col')) + self.__dict__.update(state) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_csc.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_csc.py new file mode 100644 index 0000000000000000000000000000000000000000..3fcdeb49cc0a951b9a2df955b971d5148916f289 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_csc.py @@ -0,0 +1,364 @@ +"""Compressed Sparse Column matrix format""" +__docformat__ = "restructuredtext en" + +__all__ = ['csc_array', 'csc_matrix', 'isspmatrix_csc'] + + +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray +from ._sparsetools import csc_tocsr, expandptr +from ._sputils import upcast + +from ._compressed import _cs_matrix + + +class _csc_base(_cs_matrix): + _format = 'csc' + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + M, N = self.shape + + return self._csr_container((self.data, self.indices, + self.indptr), (N, M), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def __iter__(self): + yield from self.tocsr() + + def tocsc(self, copy=False): + if copy: + return self.copy() + else: + return self + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tocsr(self, copy=False): + M,N = self.shape + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(self.nnz, N)) + indptr = np.empty(M + 1, dtype=idx_dtype) + indices = np.empty(self.nnz, dtype=idx_dtype) + data = np.empty(self.nnz, dtype=upcast(self.dtype)) + + csc_tocsr(M, N, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + indptr, + indices, + data) + + A = self._csr_container( + (data, indices, indptr), + shape=self.shape, copy=False + ) + A.has_sorted_indices = True + return A + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + def nonzero(self): + # CSC can't use _cs_matrix's .nonzero method because it + # returns the indices sorted for self transposed. + + # Get row and col indices, from _cs_matrix.tocoo + major_dim, minor_dim = self._swap(self.shape) + minor_indices = self.indices + major_indices = np.empty(len(minor_indices), dtype=self.indices.dtype) + expandptr(major_dim, self.indptr, major_indices) + row, col = self._swap((major_indices, minor_indices)) + + # Remove explicit zeros + nz_mask = self.data != 0 + row = row[nz_mask] + col = col[nz_mask] + + # Sort them to be in C-style order + ind = np.argsort(row, kind='mergesort') + row = row[ind] + col = col[ind] + + return row, col + + nonzero.__doc__ = _cs_matrix.nonzero.__doc__ + + def _getrow(self, i): + """Returns a copy of row i of the matrix, as a (1 x n) + CSR matrix (row vector). + """ + M, N = self.shape + i = int(i) + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError('index (%d) out of range' % i) + return self._get_submatrix(minor=i).tocsr() + + def _getcol(self, i): + """Returns a copy of column i of the matrix, as a (m x 1) + CSC matrix (column vector). + """ + M, N = self.shape + i = int(i) + if i < 0: + i += N + if i < 0 or i >= N: + raise IndexError('index (%d) out of range' % i) + return self._get_submatrix(major=i, copy=True) + + def _get_intXarray(self, row, col): + return self._major_index_fancy(col)._get_submatrix(minor=row) + + def _get_intXslice(self, row, col): + if col.step in (1, None): + return self._get_submatrix(major=col, minor=row, copy=True) + return self._major_slice(col)._get_submatrix(minor=row) + + def _get_sliceXint(self, row, col): + if row.step in (1, None): + return self._get_submatrix(major=col, minor=row, copy=True) + return self._get_submatrix(major=col)._minor_slice(row) + + def _get_sliceXarray(self, row, col): + return self._major_index_fancy(col)._minor_slice(row) + + def _get_arrayXint(self, row, col): + return self._get_submatrix(major=col)._minor_index_fancy(row) + + def _get_arrayXslice(self, row, col): + return self._major_slice(col)._minor_index_fancy(row) + + # these functions are used by the parent class (_cs_matrix) + # to remove redundancy between csc_array and csr_matrix + @staticmethod + def _swap(x): + """swap the members of x if this is a column-oriented matrix + """ + return x[1], x[0] + + +def isspmatrix_csc(x): + """Is `x` of csc_matrix type? + + Parameters + ---------- + x + object to check for being a csc matrix + + Returns + ------- + bool + True if `x` is a csc matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import csc_array, csc_matrix, coo_matrix, isspmatrix_csc + >>> isspmatrix_csc(csc_matrix([[5]])) + True + >>> isspmatrix_csc(csc_array([[5]])) + False + >>> isspmatrix_csc(coo_matrix([[5]])) + False + """ + return isinstance(x, csc_matrix) + + +# This namespace class separates array from matrix with isinstance +class csc_array(_csc_base, sparray): + """ + Compressed Sparse Column array. + + This can be instantiated in several ways: + csc_array(D) + where D is a 2-D ndarray + + csc_array(S) + with another sparse array or matrix S (equivalent to S.tocsc()) + + csc_array((M, N), [dtype]) + to construct an empty array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csc_array((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csc_array((data, indices, indptr), [shape=(M, N)]) + is the standard CSC representation where the row indices for + column i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding values are stored in + ``data[indptr[i]:indptr[i+1]]``. If the shape parameter is + not supplied, the array dimensions are inferred from + the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSC format data array of the array + indices + CSC format index array of the array + indptr + CSC format index pointer array of the array + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSC format + - efficient arithmetic operations CSC + CSC, CSC * CSC, etc. + - efficient column slicing + - fast matrix vector products (CSR, BSR may be faster) + + Disadvantages of the CSC format + - slow row slicing operations (consider CSR) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical format + - Within each column, indices are sorted by row. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csc_array + >>> csc_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 2, 2, 0, 1, 2]) + >>> col = np.array([0, 0, 1, 2, 2, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_array((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_array((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + """ + + +class csc_matrix(spmatrix, _csc_base): + """ + Compressed Sparse Column matrix. + + This can be instantiated in several ways: + csc_matrix(D) + where D is a 2-D ndarray + + csc_matrix(S) + with another sparse array or matrix S (equivalent to S.tocsc()) + + csc_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csc_matrix((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csc_matrix((data, indices, indptr), [shape=(M, N)]) + is the standard CSC representation where the row indices for + column i are stored in ``indices[indptr[i]:indptr[i+1]]`` + and their corresponding values are stored in + ``data[indptr[i]:indptr[i+1]]``. If the shape parameter is + not supplied, the matrix dimensions are inferred from + the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSC format data array of the matrix + indices + CSC format index array of the matrix + indptr + CSC format index pointer array of the matrix + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSC format + - efficient arithmetic operations CSC + CSC, CSC * CSC, etc. + - efficient column slicing + - fast matrix vector products (CSR, BSR may be faster) + + Disadvantages of the CSC format + - slow row slicing operations (consider CSR) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical format + - Within each column, indices are sorted by row. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> csc_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 2, 2, 0, 1, 2]) + >>> col = np.array([0, 0, 1, 2, 2, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csc_matrix((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 4], + [0, 0, 5], + [2, 3, 6]]) + + """ + diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_csr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_csr.py new file mode 100644 index 0000000000000000000000000000000000000000..ffa2aa7e78227371d68c95033a7cb817c0b4f59d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_csr.py @@ -0,0 +1,551 @@ +"""Compressed Sparse Row matrix format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['csr_array', 'csr_matrix', 'isspmatrix_csr'] + +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray +from ._sparsetools import (csr_tocsc, csr_tobsr, csr_count_blocks, + get_csr_submatrix) +from ._sputils import upcast + +from ._compressed import _cs_matrix + + +class _csr_base(_cs_matrix): + _format = 'csr' + + # override IndexMixin.__getitem__ for 1d case until fully implemented + def __getitem__(self, key): + if self.ndim == 2: + return super().__getitem__(key) + + if isinstance(key, tuple) and len(key) == 1: + key = key[0] + INT_TYPES = (int, np.integer) + if isinstance(key, INT_TYPES): + if key < 0: + key += self.shape[-1] + if key < 0 or key >= self.shape[-1]: + raise IndexError('index value out of bounds') + return self._get_int(key) + else: + raise IndexError('array/slice index for 1d csr_array not yet supported') + + # override IndexMixin.__setitem__ for 1d case until fully implemented + def __setitem__(self, key, value): + if self.ndim == 2: + return super().__setitem__(key, value) + + if isinstance(key, tuple) and len(key) == 1: + key = key[0] + INT_TYPES = (int, np.integer) + if isinstance(key, INT_TYPES): + if key < 0: + key += self.shape[-1] + if key < 0 or key >= self.shape[-1]: + raise IndexError('index value out of bounds') + return self._set_int(key, value) + else: + raise IndexError('array index for 1d csr_array not yet provided') + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + if self.ndim == 1: + return self.copy() if copy else self + M, N = self.shape + return self._csc_container((self.data, self.indices, + self.indptr), shape=(N, M), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def tolil(self, copy=False): + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to lil format") + lil = self._lil_container(self.shape, dtype=self.dtype) + + self.sum_duplicates() + ptr,ind,dat = self.indptr,self.indices,self.data + rows, data = lil.rows, lil.data + + for n in range(self.shape[0]): + start = ptr[n] + end = ptr[n+1] + rows[n] = ind[start:end].tolist() + data[n] = dat[start:end].tolist() + + return lil + + tolil.__doc__ = _spbase.tolil.__doc__ + + def tocsr(self, copy=False): + if copy: + return self.copy() + else: + return self + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + def tocsc(self, copy=False): + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to csc format") + M, N = self.shape + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(self.nnz, M)) + indptr = np.empty(N + 1, dtype=idx_dtype) + indices = np.empty(self.nnz, dtype=idx_dtype) + data = np.empty(self.nnz, dtype=upcast(self.dtype)) + + csr_tocsc(M, N, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + indptr, + indices, + data) + + A = self._csc_container((data, indices, indptr), shape=self.shape) + A.has_sorted_indices = True + return A + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tobsr(self, blocksize=None, copy=True): + if self.ndim != 2: + raise ValueError("Cannot convert a 1d sparse array to bsr format") + if blocksize is None: + from ._spfuncs import estimate_blocksize + return self.tobsr(blocksize=estimate_blocksize(self)) + + elif blocksize == (1,1): + arg1 = (self.data.reshape(-1,1,1),self.indices,self.indptr) + return self._bsr_container(arg1, shape=self.shape, copy=copy) + + else: + R,C = blocksize + M,N = self.shape + + if R < 1 or C < 1 or M % R != 0 or N % C != 0: + raise ValueError('invalid blocksize %s' % blocksize) + + blks = csr_count_blocks(M,N,R,C,self.indptr,self.indices) + + idx_dtype = self._get_index_dtype((self.indptr, self.indices), + maxval=max(N//C, blks)) + indptr = np.empty(M//R+1, dtype=idx_dtype) + indices = np.empty(blks, dtype=idx_dtype) + data = np.zeros((blks,R,C), dtype=self.dtype) + + csr_tobsr(M, N, R, C, + self.indptr.astype(idx_dtype), + self.indices.astype(idx_dtype), + self.data, + indptr, indices, data.ravel()) + + return self._bsr_container( + (data, indices, indptr), shape=self.shape + ) + + tobsr.__doc__ = _spbase.tobsr.__doc__ + + # these functions are used by the parent class (_cs_matrix) + # to remove redundancy between csc_matrix and csr_array + @staticmethod + def _swap(x): + """swap the members of x if this is a column-oriented matrix + """ + return x + + def __iter__(self): + if self.ndim == 1: + zero = self.dtype.type(0) + u = 0 + for v, d in zip(self.indices, self.data): + for _ in range(v - u): + yield zero + yield d + u = v + 1 + for _ in range(self.shape[0] - u): + yield zero + return + + indptr = np.zeros(2, dtype=self.indptr.dtype) + # return 1d (sparray) or 2drow (spmatrix) + shape = self.shape[1:] if isinstance(self, sparray) else (1, self.shape[1]) + i0 = 0 + for i1 in self.indptr[1:]: + indptr[1] = i1 - i0 + indices = self.indices[i0:i1] + data = self.data[i0:i1] + yield self.__class__((data, indices, indptr), shape=shape, copy=True) + i0 = i1 + + def _getrow(self, i): + """Returns a copy of row i of the matrix, as a (1 x n) + CSR matrix (row vector). + """ + if self.ndim == 1: + if i not in (0, -1): + raise IndexError(f'index ({i}) out of range') + return self.reshape((1, self.shape[0]), copy=True) + + M, N = self.shape + i = int(i) + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError('index (%d) out of range' % i) + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, i, i + 1, 0, N) + return self.__class__((data, indices, indptr), shape=(1, N), + dtype=self.dtype, copy=False) + + def _getcol(self, i): + """Returns a copy of column i. A (m x 1) sparse array (column vector). + """ + if self.ndim == 1: + raise ValueError("getcol not provided for 1d arrays. Use indexing A[j]") + M, N = self.shape + i = int(i) + if i < 0: + i += N + if i < 0 or i >= N: + raise IndexError('index (%d) out of range' % i) + indptr, indices, data = get_csr_submatrix( + M, N, self.indptr, self.indices, self.data, 0, M, i, i + 1) + return self.__class__((data, indices, indptr), shape=(M, 1), + dtype=self.dtype, copy=False) + + def _get_intXarray(self, row, col): + return self._getrow(row)._minor_index_fancy(col) + + def _get_intXslice(self, row, col): + if col.step in (1, None): + return self._get_submatrix(row, col, copy=True) + # TODO: uncomment this once it's faster: + # return self._getrow(row)._minor_slice(col) + + M, N = self.shape + start, stop, stride = col.indices(N) + + ii, jj = self.indptr[row:row+2] + row_indices = self.indices[ii:jj] + row_data = self.data[ii:jj] + + if stride > 0: + ind = (row_indices >= start) & (row_indices < stop) + else: + ind = (row_indices <= start) & (row_indices > stop) + + if abs(stride) > 1: + ind &= (row_indices - start) % stride == 0 + + row_indices = (row_indices[ind] - start) // stride + row_data = row_data[ind] + row_indptr = np.array([0, len(row_indices)]) + + if stride < 0: + row_data = row_data[::-1] + row_indices = abs(row_indices[::-1]) + + shape = (1, max(0, int(np.ceil(float(stop - start) / stride)))) + return self.__class__((row_data, row_indices, row_indptr), shape=shape, + dtype=self.dtype, copy=False) + + def _get_sliceXint(self, row, col): + if row.step in (1, None): + return self._get_submatrix(row, col, copy=True) + return self._major_slice(row)._get_submatrix(minor=col) + + def _get_sliceXarray(self, row, col): + return self._major_slice(row)._minor_index_fancy(col) + + def _get_arrayXint(self, row, col): + return self._major_index_fancy(row)._get_submatrix(minor=col) + + def _get_arrayXslice(self, row, col): + if col.step not in (1, None): + col = np.arange(*col.indices(self.shape[1])) + return self._get_arrayXarray(row, col) + return self._major_index_fancy(row)._get_submatrix(minor=col) + + +def isspmatrix_csr(x): + """Is `x` of csr_matrix type? + + Parameters + ---------- + x + object to check for being a csr matrix + + Returns + ------- + bool + True if `x` is a csr matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import csr_array, csr_matrix, coo_matrix, isspmatrix_csr + >>> isspmatrix_csr(csr_matrix([[5]])) + True + >>> isspmatrix_csr(csr_array([[5]])) + False + >>> isspmatrix_csr(coo_matrix([[5]])) + False + """ + return isinstance(x, csr_matrix) + + +# This namespace class separates array from matrix with isinstance +class csr_array(_csr_base, sparray): + """ + Compressed Sparse Row array. + + This can be instantiated in several ways: + csr_array(D) + where D is a 2-D ndarray + + csr_array(S) + with another sparse array or matrix S (equivalent to S.tocsr()) + + csr_array((M, N), [dtype]) + to construct an empty array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csr_array((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csr_array((data, indices, indptr), [shape=(M, N)]) + is the standard CSR representation where the column indices for + row i are stored in ``indices[indptr[i]:indptr[i+1]]`` and their + corresponding values are stored in ``data[indptr[i]:indptr[i+1]]``. + If the shape parameter is not supplied, the array dimensions + are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSR format data array of the array + indices + CSR format index array of the array + indptr + CSR format index pointer array of the array + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSR format + - efficient arithmetic operations CSR + CSR, CSR * CSR, etc. + - efficient row slicing + - fast matrix vector products + + Disadvantages of the CSR format + - slow column slicing operations (consider CSC) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical Format + - Within each row, indices are sorted by column. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csr_array + >>> csr_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_array((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_array((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + Duplicate entries are summed together: + + >>> row = np.array([0, 1, 2, 0]) + >>> col = np.array([0, 1, 1, 0]) + >>> data = np.array([1, 2, 4, 8]) + >>> csr_array((data, (row, col)), shape=(3, 3)).toarray() + array([[9, 0, 0], + [0, 2, 0], + [0, 4, 0]]) + + As an example of how to construct a CSR array incrementally, + the following snippet builds a term-document array from texts: + + >>> docs = [["hello", "world", "hello"], ["goodbye", "cruel", "world"]] + >>> indptr = [0] + >>> indices = [] + >>> data = [] + >>> vocabulary = {} + >>> for d in docs: + ... for term in d: + ... index = vocabulary.setdefault(term, len(vocabulary)) + ... indices.append(index) + ... data.append(1) + ... indptr.append(len(indices)) + ... + >>> csr_array((data, indices, indptr), dtype=int).toarray() + array([[2, 1, 0, 0], + [0, 1, 1, 1]]) + + """ + + +class csr_matrix(spmatrix, _csr_base): + """ + Compressed Sparse Row matrix. + + This can be instantiated in several ways: + csr_matrix(D) + where D is a 2-D ndarray + + csr_matrix(S) + with another sparse array or matrix S (equivalent to S.tocsr()) + + csr_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + csr_matrix((data, (row_ind, col_ind)), [shape=(M, N)]) + where ``data``, ``row_ind`` and ``col_ind`` satisfy the + relationship ``a[row_ind[k], col_ind[k]] = data[k]``. + + csr_matrix((data, indices, indptr), [shape=(M, N)]) + is the standard CSR representation where the column indices for + row i are stored in ``indices[indptr[i]:indptr[i+1]]`` and their + corresponding values are stored in ``data[indptr[i]:indptr[i+1]]``. + If the shape parameter is not supplied, the matrix dimensions + are inferred from the index arrays. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + CSR format data array of the matrix + indices + CSR format index array of the matrix + indptr + CSR format index pointer array of the matrix + has_sorted_indices + has_canonical_format + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the CSR format + - efficient arithmetic operations CSR + CSR, CSR * CSR, etc. + - efficient row slicing + - fast matrix vector products + + Disadvantages of the CSR format + - slow column slicing operations (consider CSC) + - changes to the sparsity structure are expensive (consider LIL or DOK) + + Canonical Format + - Within each row, indices are sorted by column. + - There are no duplicate entries. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import csr_matrix + >>> csr_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> row = np.array([0, 0, 1, 2, 2, 2]) + >>> col = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + >>> indptr = np.array([0, 2, 3, 6]) + >>> indices = np.array([0, 2, 2, 0, 1, 2]) + >>> data = np.array([1, 2, 3, 4, 5, 6]) + >>> csr_matrix((data, indices, indptr), shape=(3, 3)).toarray() + array([[1, 0, 2], + [0, 0, 3], + [4, 5, 6]]) + + Duplicate entries are summed together: + + >>> row = np.array([0, 1, 2, 0]) + >>> col = np.array([0, 1, 1, 0]) + >>> data = np.array([1, 2, 4, 8]) + >>> csr_matrix((data, (row, col)), shape=(3, 3)).toarray() + array([[9, 0, 0], + [0, 2, 0], + [0, 4, 0]]) + + As an example of how to construct a CSR matrix incrementally, + the following snippet builds a term-document matrix from texts: + + >>> docs = [["hello", "world", "hello"], ["goodbye", "cruel", "world"]] + >>> indptr = [0] + >>> indices = [] + >>> data = [] + >>> vocabulary = {} + >>> for d in docs: + ... for term in d: + ... index = vocabulary.setdefault(term, len(vocabulary)) + ... indices.append(index) + ... data.append(1) + ... indptr.append(len(indices)) + ... + >>> csr_matrix((data, indices, indptr), dtype=int).toarray() + array([[2, 1, 0, 0], + [0, 1, 1, 1]]) + + """ + diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_data.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_data.py new file mode 100644 index 0000000000000000000000000000000000000000..139888ee43e6678e438f71ed5db52a96c1f1d02d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_data.py @@ -0,0 +1,515 @@ +"""Base class for sparse matrice with a .data attribute + + subclasses must provide a _with_data() method that + creates a new matrix with the same sparsity pattern + as self but with a different data array + +""" + +import math +import numpy as np + +from ._base import _spbase, sparray, _ufuncs_with_fixed_point_at_zero +from ._sputils import isscalarlike, validateaxis + +__all__ = [] + + +# TODO implement all relevant operations +# use .data.__methods__() instead of /=, *=, etc. +class _data_matrix(_spbase): + def __init__(self, arg1): + _spbase.__init__(self, arg1) + + @property + def dtype(self): + return self.data.dtype + + @dtype.setter + def dtype(self, newtype): + self.data.dtype = newtype + + def _deduped_data(self): + if hasattr(self, 'sum_duplicates'): + self.sum_duplicates() + return self.data + + def __abs__(self): + return self._with_data(abs(self._deduped_data())) + + def __round__(self, ndigits=0): + return self._with_data(np.around(self._deduped_data(), decimals=ndigits)) + + def _real(self): + return self._with_data(self.data.real) + + def _imag(self): + return self._with_data(self.data.imag) + + def __neg__(self): + if self.dtype.kind == 'b': + raise NotImplementedError('negating a boolean sparse array is not ' + 'supported') + return self._with_data(-self.data) + + def __imul__(self, other): # self *= other + if isscalarlike(other): + self.data *= other + return self + else: + return NotImplemented + + def __itruediv__(self, other): # self /= other + if isscalarlike(other): + recip = 1.0 / other + self.data *= recip + return self + else: + return NotImplemented + + def astype(self, dtype, casting='unsafe', copy=True): + dtype = np.dtype(dtype) + if self.dtype != dtype: + matrix = self._with_data( + self.data.astype(dtype, casting=casting, copy=True), + copy=True + ) + return matrix._with_data(matrix._deduped_data(), copy=False) + elif copy: + return self.copy() + else: + return self + + astype.__doc__ = _spbase.astype.__doc__ + + def conjugate(self, copy=True): + if np.issubdtype(self.dtype, np.complexfloating): + return self._with_data(self.data.conjugate(), copy=copy) + elif copy: + return self.copy() + else: + return self + + conjugate.__doc__ = _spbase.conjugate.__doc__ + + def copy(self): + return self._with_data(self.data.copy(), copy=True) + + copy.__doc__ = _spbase.copy.__doc__ + + def count_nonzero(self): + return np.count_nonzero(self._deduped_data()) + + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def power(self, n, dtype=None): + """ + This function performs element-wise power. + + Parameters + ---------- + n : scalar + n is a non-zero scalar (nonzero avoids dense ones creation) + If zero power is desired, special case it to use `np.ones` + + dtype : If dtype is not specified, the current dtype will be preserved. + + Raises + ------ + NotImplementedError : if n is a zero scalar + If zero power is desired, special case it to use + `np.ones(A.shape, dtype=A.dtype)` + """ + if not isscalarlike(n): + raise NotImplementedError("input is not scalar") + if not n: + raise NotImplementedError( + "zero power is not supported as it would densify the matrix.\n" + "Use `np.ones(A.shape, dtype=A.dtype)` for this case." + ) + + data = self._deduped_data() + if dtype is not None: + data = data.astype(dtype) + return self._with_data(data ** n) + + ########################### + # Multiplication handlers # + ########################### + + def _mul_scalar(self, other): + return self._with_data(self.data * other) + + +# Add the numpy unary ufuncs for which func(0) = 0 to _data_matrix. +for npfunc in _ufuncs_with_fixed_point_at_zero: + name = npfunc.__name__ + + def _create_method(op): + def method(self): + result = op(self._deduped_data()) + return self._with_data(result, copy=True) + + method.__doc__ = (f"Element-wise {name}.\n\n" + f"See `numpy.{name}` for more information.") + method.__name__ = name + + return method + + setattr(_data_matrix, name, _create_method(npfunc)) + + +def _find_missing_index(ind, n): + for k, a in enumerate(ind): + if k != a: + return k + + k += 1 + if k < n: + return k + else: + return -1 + + +class _minmax_mixin: + """Mixin for min and max methods. + + These are not implemented for dia_matrix, hence the separate class. + """ + + def _min_or_max_axis(self, axis, min_or_max): + N = self.shape[axis] + if N == 0: + raise ValueError("zero-size array to reduction operation") + M = self.shape[1 - axis] + idx_dtype = self._get_index_dtype(maxval=M) + + mat = self.tocsc() if axis == 0 else self.tocsr() + mat.sum_duplicates() + + major_index, value = mat._minor_reduce(min_or_max) + not_full = np.diff(mat.indptr)[major_index] < N + value[not_full] = min_or_max(value[not_full], 0) + + mask = value != 0 + major_index = np.compress(mask, major_index) + value = np.compress(mask, value) + + if isinstance(self, sparray): + coords = (major_index,) + shape = (M,) + return self._coo_container((value, coords), shape=shape, dtype=self.dtype) + + if axis == 0: + return self._coo_container( + (value, (np.zeros(len(value), dtype=idx_dtype), major_index)), + dtype=self.dtype, shape=(1, M) + ) + else: + return self._coo_container( + (value, (major_index, np.zeros(len(value), dtype=idx_dtype))), + dtype=self.dtype, shape=(M, 1) + ) + + def _min_or_max(self, axis, out, min_or_max): + if out is not None: + raise ValueError("Sparse arrays do not support an 'out' parameter.") + + validateaxis(axis) + if self.ndim == 1: + if axis not in (None, 0, -1): + raise ValueError("axis out of range") + axis = None # avoid calling special axis case. no impact on 1d + + if axis is None: + if 0 in self.shape: + raise ValueError("zero-size array to reduction operation") + + zero = self.dtype.type(0) + if self.nnz == 0: + return zero + m = min_or_max.reduce(self._deduped_data().ravel()) + if self.nnz != math.prod(self.shape): + m = min_or_max(zero, m) + return m + + if axis < 0: + axis += 2 + + if (axis == 0) or (axis == 1): + return self._min_or_max_axis(axis, min_or_max) + else: + raise ValueError("axis out of range") + + def _arg_min_or_max_axis(self, axis, argmin_or_argmax, compare): + if self.shape[axis] == 0: + raise ValueError("Cannot apply the operation along a zero-sized dimension.") + + if axis < 0: + axis += 2 + + zero = self.dtype.type(0) + + mat = self.tocsc() if axis == 0 else self.tocsr() + mat.sum_duplicates() + + ret_size, line_size = mat._swap(mat.shape) + ret = np.zeros(ret_size, dtype=int) + + nz_lines, = np.nonzero(np.diff(mat.indptr)) + for i in nz_lines: + p, q = mat.indptr[i:i + 2] + data = mat.data[p:q] + indices = mat.indices[p:q] + extreme_index = argmin_or_argmax(data) + extreme_value = data[extreme_index] + if compare(extreme_value, zero) or q - p == line_size: + ret[i] = indices[extreme_index] + else: + zero_ind = _find_missing_index(indices, line_size) + if extreme_value == zero: + ret[i] = min(extreme_index, zero_ind) + else: + ret[i] = zero_ind + + if isinstance(self, sparray): + return ret + + if axis == 1: + ret = ret.reshape(-1, 1) + + return self._ascontainer(ret) + + def _arg_min_or_max(self, axis, out, argmin_or_argmax, compare): + if out is not None: + raise ValueError("Sparse types do not support an 'out' parameter.") + + validateaxis(axis) + + if self.ndim == 1: + if axis not in (None, 0, -1): + raise ValueError("axis out of range") + axis = None # avoid calling special axis case. no impact on 1d + + if axis is not None: + return self._arg_min_or_max_axis(axis, argmin_or_argmax, compare) + + if 0 in self.shape: + raise ValueError("Cannot apply the operation to an empty matrix.") + + if self.nnz == 0: + return 0 + + zero = self.dtype.type(0) + mat = self.tocoo() + # Convert to canonical form: no duplicates, sorted indices. + mat.sum_duplicates() + extreme_index = argmin_or_argmax(mat.data) + extreme_value = mat.data[extreme_index] + num_col = mat.shape[-1] + + # If the min value is less than zero, or max is greater than zero, + # then we do not need to worry about implicit zeros. + if compare(extreme_value, zero): + # cast to Python int to avoid overflow and RuntimeError + return int(mat.row[extreme_index]) * num_col + int(mat.col[extreme_index]) + + # Cheap test for the rare case where we have no implicit zeros. + size = math.prod(self.shape) + if size == mat.nnz: + return int(mat.row[extreme_index]) * num_col + int(mat.col[extreme_index]) + + # At this stage, any implicit zero could be the min or max value. + # After sum_duplicates(), the `row` and `col` arrays are guaranteed to + # be sorted in C-order, which means the linearized indices are sorted. + linear_indices = mat.row * num_col + mat.col + first_implicit_zero_index = _find_missing_index(linear_indices, size) + if extreme_value == zero: + return min(first_implicit_zero_index, extreme_index) + return first_implicit_zero_index + + def max(self, axis=None, out=None): + """ + Return the maximum of the array/matrix or maximum along an axis. + This takes all elements into account, not just the non-zero ones. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the sum is computed. The default is to + compute the maximum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except + for the default value, as this argument is not used. + + Returns + ------- + amax : coo_matrix or scalar + Maximum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + min : The minimum value of a sparse array/matrix along a given axis. + numpy.matrix.max : NumPy's implementation of 'max' for matrices + + """ + return self._min_or_max(axis, out, np.maximum) + + def min(self, axis=None, out=None): + """ + Return the minimum of the array/matrix or maximum along an axis. + This takes all elements into account, not just the non-zero ones. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the sum is computed. The default is to + compute the minimum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + amin : coo_matrix or scalar + Minimum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + max : The maximum value of a sparse array/matrix along a given axis. + numpy.matrix.min : NumPy's implementation of 'min' for matrices + + """ + return self._min_or_max(axis, out, np.minimum) + + def nanmax(self, axis=None, out=None): + """ + Return the maximum of the array/matrix or maximum along an axis, ignoring any + NaNs. This takes all elements into account, not just the non-zero + ones. + + .. versionadded:: 1.11.0 + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the maximum is computed. The default is to + compute the maximum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except + for the default value, as this argument is not used. + + Returns + ------- + amax : coo_matrix or scalar + Maximum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + nanmin : The minimum value of a sparse array/matrix along a given axis, + ignoring NaNs. + max : The maximum value of a sparse array/matrix along a given axis, + propagating NaNs. + numpy.nanmax : NumPy's implementation of 'nanmax'. + + """ + return self._min_or_max(axis, out, np.fmax) + + def nanmin(self, axis=None, out=None): + """ + Return the minimum of the array/matrix or minimum along an axis, ignoring any + NaNs. This takes all elements into account, not just the non-zero + ones. + + .. versionadded:: 1.11.0 + + Parameters + ---------- + axis : {-2, -1, 0, 1, None} optional + Axis along which the minimum is computed. The default is to + compute the minimum over all elements, returning + a scalar (i.e., `axis` = `None`). + + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + amin : coo_matrix or scalar + Minimum of `a`. If `axis` is None, the result is a scalar value. + If `axis` is given, the result is a sparse.coo_matrix of dimension + ``a.ndim - 1``. + + See Also + -------- + nanmax : The maximum value of a sparse array/matrix along a given axis, + ignoring NaNs. + min : The minimum value of a sparse array/matrix along a given axis, + propagating NaNs. + numpy.nanmin : NumPy's implementation of 'nanmin'. + + """ + return self._min_or_max(axis, out, np.fmin) + + def argmax(self, axis=None, out=None): + """Return indices of maximum elements along an axis. + + Implicit zero elements are also taken into account. If there are + several maximum values, the index of the first occurrence is returned. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None}, optional + Axis along which the argmax is computed. If None (default), index + of the maximum element in the flatten data is returned. + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + ind : numpy.matrix or int + Indices of maximum elements. If matrix, its size along `axis` is 1. + """ + return self._arg_min_or_max(axis, out, np.argmax, np.greater) + + def argmin(self, axis=None, out=None): + """Return indices of minimum elements along an axis. + + Implicit zero elements are also taken into account. If there are + several minimum values, the index of the first occurrence is returned. + + Parameters + ---------- + axis : {-2, -1, 0, 1, None}, optional + Axis along which the argmin is computed. If None (default), index + of the minimum element in the flatten data is returned. + out : None, optional + This argument is in the signature *solely* for NumPy + compatibility reasons. Do not pass in anything except for + the default value, as this argument is not used. + + Returns + ------- + ind : numpy.matrix or int + Indices of minimum elements. If matrix, its size along `axis` is 1. + """ + return self._arg_min_or_max(axis, out, np.argmin, np.less) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_dia.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_dia.py new file mode 100644 index 0000000000000000000000000000000000000000..ab6d5dcdccadbadbf0a2b15a80e39d49bf513349 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_dia.py @@ -0,0 +1,585 @@ +"""Sparse DIAgonal format""" + +__docformat__ = "restructuredtext en" + +__all__ = ['dia_array', 'dia_matrix', 'isspmatrix_dia'] + +import numpy as np + +from .._lib._util import copy_if_needed +from ._matrix import spmatrix +from ._base import issparse, _formats, _spbase, sparray +from ._data import _data_matrix +from ._sputils import ( + isshape, upcast_char, getdtype, get_sum_dtype, validateaxis, check_shape +) +from ._sparsetools import dia_matvec + + +class _dia_base(_data_matrix): + _format = 'dia' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _data_matrix.__init__(self, arg1) + + if issparse(arg1): + if arg1.format == "dia": + if copy: + arg1 = arg1.copy() + self.data = arg1.data + self.offsets = arg1.offsets + self._shape = check_shape(arg1.shape) + else: + if arg1.format == self.format and copy: + A = arg1.copy() + else: + A = arg1.todia() + self.data = A.data + self.offsets = A.offsets + self._shape = check_shape(A.shape) + elif isinstance(arg1, tuple): + if isshape(arg1): + # It's a tuple of matrix dimensions (M, N) + # create empty matrix + self._shape = check_shape(arg1) + self.data = np.zeros((0,0), getdtype(dtype, default=float)) + idx_dtype = self._get_index_dtype(maxval=max(self.shape)) + self.offsets = np.zeros((0), dtype=idx_dtype) + else: + try: + # Try interpreting it as (data, offsets) + data, offsets = arg1 + except Exception as e: + message = 'unrecognized form for dia_array constructor' + raise ValueError(message) from e + else: + if shape is None: + raise ValueError('expected a shape argument') + if not copy: + copy = copy_if_needed + self.data = np.atleast_2d(np.array(arg1[0], dtype=dtype, copy=copy)) + offsets = np.array(arg1[1], + dtype=self._get_index_dtype(maxval=max(shape)), + copy=copy) + self.offsets = np.atleast_1d(offsets) + self._shape = check_shape(shape) + else: + # must be dense, convert to COO first, then to DIA + try: + arg1 = np.asarray(arg1) + except Exception as e: + raise ValueError("unrecognized form for" + " %s_matrix constructor" % self.format) from e + if isinstance(self, sparray) and arg1.ndim != 2: + raise ValueError(f"DIA arrays don't support {arg1.ndim}D input. Use 2D") + A = self._coo_container(arg1, dtype=dtype, shape=shape).todia() + self.data = A.data + self.offsets = A.offsets + self._shape = check_shape(A.shape) + + if dtype is not None: + self.data = self.data.astype(dtype) + + # check format + if self.offsets.ndim != 1: + raise ValueError('offsets array must have rank 1') + + if self.data.ndim != 2: + raise ValueError('data array must have rank 2') + + if self.data.shape[0] != len(self.offsets): + raise ValueError('number of diagonals (%d) ' + 'does not match the number of offsets (%d)' + % (self.data.shape[0], len(self.offsets))) + + if len(np.unique(self.offsets)) != len(self.offsets): + raise ValueError('offset array contains duplicate values') + + def __repr__(self): + _, fmt = _formats[self.format] + sparse_cls = 'array' if isinstance(self, sparray) else 'matrix' + d = self.data.shape[0] + return ( + f"<{fmt} sparse {sparse_cls} of dtype '{self.dtype}'\n" + f"\twith {self.nnz} stored elements ({d} diagonals) and shape {self.shape}>" + ) + + def _data_mask(self): + """Returns a mask of the same shape as self.data, where + mask[i,j] is True when data[i,j] corresponds to a stored element.""" + num_rows, num_cols = self.shape + offset_inds = np.arange(self.data.shape[1]) + row = offset_inds - self.offsets[:,None] + mask = (row >= 0) + mask &= (row < num_rows) + mask &= (offset_inds < num_cols) + return mask + + def count_nonzero(self): + mask = self._data_mask() + return np.count_nonzero(self.data[mask]) + + def _getnnz(self, axis=None): + if axis is not None: + raise NotImplementedError("_getnnz over an axis is not implemented " + "for DIA format") + M,N = self.shape + nnz = 0 + for k in self.offsets: + if k > 0: + nnz += min(M,N-k) + else: + nnz += min(M+k,N) + return int(nnz) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def sum(self, axis=None, dtype=None, out=None): + validateaxis(axis) + + if axis is not None and axis < 0: + axis += 2 + + res_dtype = get_sum_dtype(self.dtype) + num_rows, num_cols = self.shape + ret = None + + if axis == 0: + mask = self._data_mask() + x = (self.data * mask).sum(axis=0) + if x.shape[0] == num_cols: + res = x + else: + res = np.zeros(num_cols, dtype=x.dtype) + res[:x.shape[0]] = x + ret = self._ascontainer(res, dtype=res_dtype) + + else: + row_sums = np.zeros((num_rows, 1), dtype=res_dtype) + one = np.ones(num_cols, dtype=res_dtype) + dia_matvec(num_rows, num_cols, len(self.offsets), + self.data.shape[1], self.offsets, self.data, one, row_sums) + + row_sums = self._ascontainer(row_sums) + + if axis is None: + return row_sums.sum(dtype=dtype, out=out) + + ret = self._ascontainer(row_sums.sum(axis=axis)) + + if out is not None and out.shape != ret.shape: + raise ValueError("dimensions do not match") + + return ret.sum(axis=(), dtype=dtype, out=out) + + sum.__doc__ = _spbase.sum.__doc__ + + def _add_sparse(self, other): + # If other is not DIA format, let them handle us instead. + if not isinstance(other, _dia_base): + return other._add_sparse(self) + + # Fast path for exact equality of the sparsity structure. + if np.array_equal(self.offsets, other.offsets): + return self._with_data(self.data + other.data) + + # Find the union of the offsets (which will be sorted and unique). + new_offsets = np.union1d(self.offsets, other.offsets) + self_idx = np.searchsorted(new_offsets, self.offsets) + other_idx = np.searchsorted(new_offsets, other.offsets) + + self_d = self.data.shape[1] + other_d = other.data.shape[1] + # Fast path for a sparsity structure where the final offsets are a + # permutation of the existing offsets and the diagonal lengths match. + if self_d == other_d and len(new_offsets) == len(self.offsets): + new_data = self.data[_invert_index(self_idx)] + new_data[other_idx, :] += other.data + elif self_d == other_d and len(new_offsets) == len(other.offsets): + new_data = other.data[_invert_index(other_idx)] + new_data[self_idx, :] += self.data + else: + # Maximum diagonal length of the result. + d = min(self.shape[0] + new_offsets[-1], self.shape[1]) + + # Add all diagonals to a freshly-allocated data array. + new_data = np.zeros( + (len(new_offsets), d), + dtype=np.result_type(self.data, other.data), + ) + new_data[self_idx, :self_d] += self.data[:, :d] + new_data[other_idx, :other_d] += other.data[:, :d] + return self._dia_container((new_data, new_offsets), shape=self.shape) + + def _mul_scalar(self, other): + return self._with_data(self.data * other) + + def _matmul_vector(self, other): + x = other + + y = np.zeros(self.shape[0], dtype=upcast_char(self.dtype.char, + x.dtype.char)) + + L = self.data.shape[1] + + M,N = self.shape + + dia_matvec(M,N, len(self.offsets), L, self.offsets, self.data, + x.ravel(), y.ravel()) + + return y + + def _setdiag(self, values, k=0): + M, N = self.shape + + if values.ndim == 0: + # broadcast + values_n = np.inf + else: + values_n = len(values) + + if k < 0: + n = min(M + k, N, values_n) + min_index = 0 + max_index = n + else: + n = min(M, N - k, values_n) + min_index = k + max_index = k + n + + if values.ndim != 0: + # allow also longer sequences + values = values[:n] + + data_rows, data_cols = self.data.shape + if k in self.offsets: + if max_index > data_cols: + data = np.zeros((data_rows, max_index), dtype=self.data.dtype) + data[:, :data_cols] = self.data + self.data = data + self.data[self.offsets == k, min_index:max_index] = values + else: + self.offsets = np.append(self.offsets, self.offsets.dtype.type(k)) + m = max(max_index, data_cols) + data = np.zeros((data_rows + 1, m), dtype=self.data.dtype) + data[:-1, :data_cols] = self.data + data[-1, min_index:max_index] = values + self.data = data + + def todia(self, copy=False): + if copy: + return self.copy() + else: + return self + + todia.__doc__ = _spbase.todia.__doc__ + + def transpose(self, axes=None, copy=False): + if axes is not None and axes != (1, 0): + raise ValueError("Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation.") + + num_rows, num_cols = self.shape + max_dim = max(self.shape) + + # flip diagonal offsets + offsets = -self.offsets + + # re-align the data matrix + r = np.arange(len(offsets), dtype=np.intc)[:, None] + c = np.arange(num_rows, dtype=np.intc) - (offsets % max_dim)[:, None] + pad_amount = max(0, max_dim-self.data.shape[1]) + data = np.hstack((self.data, np.zeros((self.data.shape[0], pad_amount), + dtype=self.data.dtype))) + data = data[r, c] + return self._dia_container((data, offsets), shape=( + num_cols, num_rows), copy=copy) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def diagonal(self, k=0): + rows, cols = self.shape + if k <= -rows or k >= cols: + return np.empty(0, dtype=self.data.dtype) + idx, = np.nonzero(self.offsets == k) + first_col = max(0, k) + last_col = min(rows + k, cols) + result_size = last_col - first_col + if idx.size == 0: + return np.zeros(result_size, dtype=self.data.dtype) + result = self.data[idx[0], first_col:last_col] + padding = result_size - len(result) + if padding > 0: + result = np.pad(result, (0, padding), mode='constant') + return result + + diagonal.__doc__ = _spbase.diagonal.__doc__ + + def tocsc(self, copy=False): + if self.nnz == 0: + return self._csc_container(self.shape, dtype=self.dtype) + + num_rows, num_cols = self.shape + num_offsets, offset_len = self.data.shape + offset_inds = np.arange(offset_len) + + row = offset_inds - self.offsets[:,None] + mask = (row >= 0) + mask &= (row < num_rows) + mask &= (offset_inds < num_cols) + mask &= (self.data != 0) + + idx_dtype = self._get_index_dtype(maxval=max(self.shape)) + indptr = np.zeros(num_cols + 1, dtype=idx_dtype) + indptr[1:offset_len+1] = np.cumsum(mask.sum(axis=0)[:num_cols]) + if offset_len < num_cols: + indptr[offset_len+1:] = indptr[offset_len] + indices = row.T[mask.T].astype(idx_dtype, copy=False) + data = self.data.T[mask.T] + return self._csc_container((data, indices, indptr), shape=self.shape, + dtype=self.dtype) + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def tocoo(self, copy=False): + num_rows, num_cols = self.shape + num_offsets, offset_len = self.data.shape + offset_inds = np.arange(offset_len) + + row = offset_inds - self.offsets[:,None] + mask = (row >= 0) + mask &= (row < num_rows) + mask &= (offset_inds < num_cols) + mask &= (self.data != 0) + row = row[mask] + col = np.tile(offset_inds, num_offsets)[mask.ravel()] + idx_dtype = self._get_index_dtype( + arrays=(self.offsets,), maxval=max(self.shape) + ) + row = row.astype(idx_dtype, copy=False) + col = col.astype(idx_dtype, copy=False) + data = self.data[mask] + # Note: this cannot set has_canonical_format=True, because despite the + # lack of duplicates, we do not generate sorted indices. + return self._coo_container( + (data, (row, col)), shape=self.shape, dtype=self.dtype, copy=False + ) + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + # needed by _data_matrix + def _with_data(self, data, copy=True): + """Returns a matrix with the same sparsity structure as self, + but with different data. By default the structure arrays are copied. + """ + if copy: + return self._dia_container( + (data, self.offsets.copy()), shape=self.shape + ) + else: + return self._dia_container( + (data, self.offsets), shape=self.shape + ) + + def resize(self, *shape): + shape = check_shape(shape) + M, N = shape + # we do not need to handle the case of expanding N + self.data = self.data[:, :N] + + if (M > self.shape[0] and + np.any(self.offsets + self.shape[0] < self.data.shape[1])): + # explicitly clear values that were previously hidden + mask = (self.offsets[:, None] + self.shape[0] <= + np.arange(self.data.shape[1])) + self.data[mask] = 0 + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + +def _invert_index(idx): + """Helper function to invert an index array.""" + inv = np.zeros_like(idx) + inv[idx] = np.arange(len(idx)) + return inv + + +def isspmatrix_dia(x): + """Is `x` of dia_matrix type? + + Parameters + ---------- + x + object to check for being a dia matrix + + Returns + ------- + bool + True if `x` is a dia matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import dia_array, dia_matrix, coo_matrix, isspmatrix_dia + >>> isspmatrix_dia(dia_matrix([[5]])) + True + >>> isspmatrix_dia(dia_array([[5]])) + False + >>> isspmatrix_dia(coo_matrix([[5]])) + False + """ + return isinstance(x, dia_matrix) + + +# This namespace class separates array from matrix with isinstance +class dia_array(_dia_base, sparray): + """ + Sparse array with DIAgonal storage. + + This can be instantiated in several ways: + dia_array(D) + where D is a 2-D ndarray + + dia_array(S) + with another sparse array or matrix S (equivalent to S.todia()) + + dia_array((M, N), [dtype]) + to construct an empty array with shape (M, N), + dtype is optional, defaulting to dtype='d'. + + dia_array((data, offsets), shape=(M, N)) + where the ``data[k,:]`` stores the diagonal entries for + diagonal ``offsets[k]`` (See example below) + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + DIA format data array of the array + offsets + DIA format offset array of the array + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import dia_array + >>> dia_array((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) + >>> offsets = np.array([0, -1, 2]) + >>> dia_array((data, offsets), shape=(4, 4)).toarray() + array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + >>> from scipy.sparse import dia_array + >>> n = 10 + >>> ex = np.ones(n) + >>> data = np.array([ex, 2 * ex, ex]) + >>> offsets = np.array([-1, 0, 1]) + >>> dia_array((data, offsets), shape=(n, n)).toarray() + array([[2., 1., 0., ..., 0., 0., 0.], + [1., 2., 1., ..., 0., 0., 0.], + [0., 1., 2., ..., 0., 0., 0.], + ..., + [0., 0., 0., ..., 2., 1., 0.], + [0., 0., 0., ..., 1., 2., 1.], + [0., 0., 0., ..., 0., 1., 2.]]) + """ + + +class dia_matrix(spmatrix, _dia_base): + """ + Sparse matrix with DIAgonal storage. + + This can be instantiated in several ways: + dia_matrix(D) + where D is a 2-D ndarray + + dia_matrix(S) + with another sparse array or matrix S (equivalent to S.todia()) + + dia_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N), + dtype is optional, defaulting to dtype='d'. + + dia_matrix((data, offsets), shape=(M, N)) + where the ``data[k,:]`` stores the diagonal entries for + diagonal ``offsets[k]`` (See example below) + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + DIA format data array of the matrix + offsets + DIA format offset array of the matrix + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Examples + -------- + + >>> import numpy as np + >>> from scipy.sparse import dia_matrix + >>> dia_matrix((3, 4), dtype=np.int8).toarray() + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], dtype=int8) + + >>> data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) + >>> offsets = np.array([0, -1, 2]) + >>> dia_matrix((data, offsets), shape=(4, 4)).toarray() + array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + + >>> from scipy.sparse import dia_matrix + >>> n = 10 + >>> ex = np.ones(n) + >>> data = np.array([ex, 2 * ex, ex]) + >>> offsets = np.array([-1, 0, 1]) + >>> dia_matrix((data, offsets), shape=(n, n)).toarray() + array([[2., 1., 0., ..., 0., 0., 0.], + [1., 2., 1., ..., 0., 0., 0.], + [0., 1., 2., ..., 0., 0., 0.], + ..., + [0., 0., 0., ..., 2., 1., 0.], + [0., 0., 0., ..., 1., 2., 1.], + [0., 0., 0., ..., 0., 1., 2.]]) + """ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_dok.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_dok.py new file mode 100644 index 0000000000000000000000000000000000000000..08a039136ff3d5474468976297eb2120adbaa5b4 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_dok.py @@ -0,0 +1,684 @@ +"""Dictionary Of Keys based matrix""" + +__docformat__ = "restructuredtext en" + +__all__ = ['dok_array', 'dok_matrix', 'isspmatrix_dok'] + +import itertools +from warnings import warn +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray, issparse +from ._index import IndexMixin +from ._sputils import (isdense, getdtype, isshape, isintlike, isscalarlike, + upcast, upcast_scalar, check_shape) + + +class _dok_base(_spbase, IndexMixin, dict): + _format = 'dok' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _spbase.__init__(self, arg1) + + is_array = isinstance(self, sparray) + if isinstance(arg1, tuple) and isshape(arg1, allow_1d=is_array): + self._shape = check_shape(arg1, allow_1d=is_array) + self._dict = {} + self.dtype = getdtype(dtype, default=float) + elif issparse(arg1): # Sparse ctor + if arg1.format == self.format: + arg1 = arg1.copy() if copy else arg1 + else: + arg1 = arg1.todok() + + if dtype is not None: + arg1 = arg1.astype(dtype, copy=False) + + self._dict = arg1._dict + self._shape = check_shape(arg1.shape, allow_1d=is_array) + self.dtype = arg1.dtype + else: # Dense ctor + try: + arg1 = np.asarray(arg1) + except Exception as e: + raise TypeError('Invalid input format.') from e + + if arg1.ndim > 2: + raise TypeError('Expected rank <=2 dense array or matrix.') + + if arg1.ndim == 1: + if dtype is not None: + arg1 = arg1.astype(dtype) + self._dict = {i: v for i, v in enumerate(arg1) if v != 0} + self.dtype = arg1.dtype + else: + d = self._coo_container(arg1, dtype=dtype).todok() + self._dict = d._dict + self.dtype = d.dtype + self._shape = check_shape(arg1.shape, allow_1d=is_array) + + def update(self, val): + # Prevent direct usage of update + raise NotImplementedError("Direct update to DOK sparse format is not allowed.") + + def _getnnz(self, axis=None): + if axis is not None: + raise NotImplementedError( + "_getnnz over an axis is not implemented for DOK format." + ) + return len(self._dict) + + def count_nonzero(self): + return sum(x != 0 for x in self.values()) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def __len__(self): + return len(self._dict) + + def __contains__(self, key): + return key in self._dict + + def setdefault(self, key, default=None, /): + return self._dict.setdefault(key, default) + + def __delitem__(self, key, /): + del self._dict[key] + + def clear(self): + return self._dict.clear() + + def pop(self, /, *args): + return self._dict.pop(*args) + + def __reversed__(self): + raise TypeError("reversed is not defined for dok_array type") + + def __or__(self, other): + type_names = f"{type(self).__name__} and {type(other).__name__}" + raise TypeError(f"unsupported operand type for |: {type_names}") + + def __ror__(self, other): + type_names = f"{type(self).__name__} and {type(other).__name__}" + raise TypeError(f"unsupported operand type for |: {type_names}") + + def __ior__(self, other): + type_names = f"{type(self).__name__} and {type(other).__name__}" + raise TypeError(f"unsupported operand type for |: {type_names}") + + def popitem(self): + return self._dict.popitem() + + def items(self): + return self._dict.items() + + def keys(self): + return self._dict.keys() + + def values(self): + return self._dict.values() + + def get(self, key, default=0.0): + """This provides dict.get method functionality with type checking""" + if key in self._dict: + return self._dict[key] + if isintlike(key) and self.ndim == 1: + key = (key,) + if self.ndim != len(key): + raise IndexError(f'Index {key} length needs to match self.shape') + try: + for i in key: + assert isintlike(i) + except (AssertionError, TypeError, ValueError) as e: + raise IndexError('Index must be or consist of integers.') from e + key = tuple(i + M if i < 0 else i for i, M in zip(key, self.shape)) + if any(i < 0 or i >= M for i, M in zip(key, self.shape)): + raise IndexError('Index out of bounds.') + if self.ndim == 1: + key = key[0] + return self._dict.get(key, default) + + # override IndexMixin.__getitem__ for 1d case until fully implemented + def __getitem__(self, key): + if self.ndim == 2: + return super().__getitem__(key) + + if isinstance(key, tuple) and len(key) == 1: + key = key[0] + INT_TYPES = (int, np.integer) + if isinstance(key, INT_TYPES): + if key < 0: + key += self.shape[-1] + if key < 0 or key >= self.shape[-1]: + raise IndexError('index value out of bounds') + return self._get_int(key) + else: + raise IndexError('array/slice index for 1d dok_array not yet supported') + + # 1D get methods + def _get_int(self, idx): + return self._dict.get(idx, self.dtype.type(0)) + + # 2D get methods + def _get_intXint(self, row, col): + return self._dict.get((row, col), self.dtype.type(0)) + + def _get_intXslice(self, row, col): + return self._get_sliceXslice(slice(row, row + 1), col) + + def _get_sliceXint(self, row, col): + return self._get_sliceXslice(row, slice(col, col + 1)) + + def _get_sliceXslice(self, row, col): + row_start, row_stop, row_step = row.indices(self.shape[0]) + col_start, col_stop, col_step = col.indices(self.shape[1]) + row_range = range(row_start, row_stop, row_step) + col_range = range(col_start, col_stop, col_step) + shape = (len(row_range), len(col_range)) + # Switch paths only when advantageous + # (count the iterations in the loops, adjust for complexity) + if len(self) >= 2 * shape[0] * shape[1]: + # O(nr*nc) path: loop over + return self._get_columnXarray(row_range, col_range) + # O(nnz) path: loop over entries of self + newdok = self._dok_container(shape, dtype=self.dtype) + for key in self.keys(): + i, ri = divmod(int(key[0]) - row_start, row_step) + if ri != 0 or i < 0 or i >= shape[0]: + continue + j, rj = divmod(int(key[1]) - col_start, col_step) + if rj != 0 or j < 0 or j >= shape[1]: + continue + newdok._dict[i, j] = self._dict[key] + return newdok + + def _get_intXarray(self, row, col): + col = col.squeeze() + return self._get_columnXarray([row], col) + + def _get_arrayXint(self, row, col): + row = row.squeeze() + return self._get_columnXarray(row, [col]) + + def _get_sliceXarray(self, row, col): + row = list(range(*row.indices(self.shape[0]))) + return self._get_columnXarray(row, col) + + def _get_arrayXslice(self, row, col): + col = list(range(*col.indices(self.shape[1]))) + return self._get_columnXarray(row, col) + + def _get_columnXarray(self, row, col): + # outer indexing + newdok = self._dok_container((len(row), len(col)), dtype=self.dtype) + + for i, r in enumerate(row): + for j, c in enumerate(col): + v = self._dict.get((r, c), 0) + if v: + newdok._dict[i, j] = v + return newdok + + def _get_arrayXarray(self, row, col): + # inner indexing + i, j = map(np.atleast_2d, np.broadcast_arrays(row, col)) + newdok = self._dok_container(i.shape, dtype=self.dtype) + + for key in itertools.product(range(i.shape[0]), range(i.shape[1])): + v = self._dict.get((i[key], j[key]), 0) + if v: + newdok._dict[key] = v + return newdok + + # override IndexMixin.__setitem__ for 1d case until fully implemented + def __setitem__(self, key, value): + if self.ndim == 2: + return super().__setitem__(key, value) + + if isinstance(key, tuple) and len(key) == 1: + key = key[0] + INT_TYPES = (int, np.integer) + if isinstance(key, INT_TYPES): + if key < 0: + key += self.shape[-1] + if key < 0 or key >= self.shape[-1]: + raise IndexError('index value out of bounds') + return self._set_int(key, value) + else: + raise IndexError('array index for 1d dok_array not yet provided') + + # 1D set methods + def _set_int(self, idx, x): + if x: + self._dict[idx] = x + elif idx in self._dict: + del self._dict[idx] + + # 2D set methods + def _set_intXint(self, row, col, x): + key = (row, col) + if x: + self._dict[key] = x + elif key in self._dict: + del self._dict[key] + + def _set_arrayXarray(self, row, col, x): + row = list(map(int, row.ravel())) + col = list(map(int, col.ravel())) + x = x.ravel() + self._dict.update(zip(zip(row, col), x)) + + for i in np.nonzero(x == 0)[0]: + key = (row[i], col[i]) + if self._dict[key] == 0: + # may have been superseded by later update + del self._dict[key] + + def __add__(self, other): + if isscalarlike(other): + res_dtype = upcast_scalar(self.dtype, other) + new = self._dok_container(self.shape, dtype=res_dtype) + # Add this scalar to each element. + for key in itertools.product(*[range(d) for d in self.shape]): + aij = self._dict.get(key, 0) + other + if aij: + new[key] = aij + elif issparse(other): + if other.shape != self.shape: + raise ValueError("Matrix dimensions are not equal.") + res_dtype = upcast(self.dtype, other.dtype) + new = self._dok_container(self.shape, dtype=res_dtype) + new._dict = self._dict.copy() + if other.format == "dok": + o_items = other.items() + else: + other = other.tocoo() + if self.ndim == 1: + o_items = zip(other.coords[0], other.data) + else: + o_items = zip(zip(*other.coords), other.data) + with np.errstate(over='ignore'): + new._dict.update((k, new[k] + v) for k, v in o_items) + elif isdense(other): + new = self.todense() + other + else: + return NotImplemented + return new + + def __radd__(self, other): + return self + other # addition is comutative + + def __neg__(self): + if self.dtype.kind == 'b': + raise NotImplementedError( + 'Negating a sparse boolean matrix is not supported.' + ) + new = self._dok_container(self.shape, dtype=self.dtype) + new._dict.update((k, -v) for k, v in self.items()) + return new + + def _mul_scalar(self, other): + res_dtype = upcast_scalar(self.dtype, other) + # Multiply this scalar by every element. + new = self._dok_container(self.shape, dtype=res_dtype) + new._dict.update(((k, v * other) for k, v in self.items())) + return new + + def _matmul_vector(self, other): + res_dtype = upcast(self.dtype, other.dtype) + + # vector @ vector + if self.ndim == 1: + if issparse(other): + if other.format == "dok": + keys = self.keys() & other.keys() + else: + keys = self.keys() & other.tocoo().coords[0] + return res_dtype(sum(self._dict[k] * other._dict[k] for k in keys)) + elif isdense(other): + return res_dtype(sum(other[k] * v for k, v in self.items())) + else: + return NotImplemented + + # matrix @ vector + result = np.zeros(self.shape[0], dtype=res_dtype) + for (i, j), v in self.items(): + result[i] += v * other[j] + return result + + def _matmul_multivector(self, other): + result_dtype = upcast(self.dtype, other.dtype) + # vector @ multivector + if self.ndim == 1: + # works for other 1d or 2d + return sum(v * other[j] for j, v in self._dict.items()) + + # matrix @ multivector + M = self.shape[0] + new_shape = (M,) if other.ndim == 1 else (M, other.shape[1]) + result = np.zeros(new_shape, dtype=result_dtype) + for (i, j), v in self.items(): + result[i] += v * other[j] + return result + + def __imul__(self, other): + if isscalarlike(other): + self._dict.update((k, v * other) for k, v in self.items()) + return self + return NotImplemented + + def __truediv__(self, other): + if isscalarlike(other): + res_dtype = upcast_scalar(self.dtype, other) + new = self._dok_container(self.shape, dtype=res_dtype) + new._dict.update(((k, v / other) for k, v in self.items())) + return new + return self.tocsr() / other + + def __itruediv__(self, other): + if isscalarlike(other): + self._dict.update((k, v / other) for k, v in self.items()) + return self + return NotImplemented + + def __reduce__(self): + # this approach is necessary because __setstate__ is called after + # __setitem__ upon unpickling and since __init__ is not called there + # is no shape attribute hence it is not possible to unpickle it. + return dict.__reduce__(self) + + def diagonal(self, k=0): + if self.ndim == 2: + return super().diagonal(k) + raise ValueError("diagonal requires two dimensions") + + def transpose(self, axes=None, copy=False): + if self.ndim == 1: + return self.copy() + + if axes is not None and axes != (1, 0): + raise ValueError( + "Sparse arrays/matrices do not support " + "an 'axes' parameter because swapping " + "dimensions is the only logical permutation." + ) + + M, N = self.shape + new = self._dok_container((N, M), dtype=self.dtype, copy=copy) + new._dict.update((((right, left), val) for (left, right), val in self.items())) + return new + + transpose.__doc__ = _spbase.transpose.__doc__ + + def conjtransp(self): + """DEPRECATED: Return the conjugate transpose. + + .. deprecated:: 1.14.0 + + `conjtransp` is deprecated and will be removed in v1.16.0. + Use `.T.conj()` instead. + """ + msg = ("`conjtransp` is deprecated and will be removed in v1.16.0. " + "Use `.T.conj()` instead.") + warn(msg, DeprecationWarning, stacklevel=2) + + if self.ndim == 1: + new = self.tocoo() + new.data = new.data.conjugate() + return new + + M, N = self.shape + new = self._dok_container((N, M), dtype=self.dtype) + new._dict = {(right, left): np.conj(val) for (left, right), val in self.items()} + return new + + def copy(self): + new = self._dok_container(self.shape, dtype=self.dtype) + new._dict.update(self._dict) + return new + + copy.__doc__ = _spbase.copy.__doc__ + + @classmethod + def fromkeys(cls, iterable, value=1, /): + tmp = dict.fromkeys(iterable, value) + if isinstance(next(iter(tmp)), tuple): + shape = tuple(max(idx) + 1 for idx in zip(*tmp)) + else: + shape = (max(tmp) + 1,) + result = cls(shape, dtype=type(value)) + result._dict = tmp + return result + + def tocoo(self, copy=False): + nnz = self.nnz + if nnz == 0: + return self._coo_container(self.shape, dtype=self.dtype) + + idx_dtype = self._get_index_dtype(maxval=max(self.shape)) + data = np.fromiter(self.values(), dtype=self.dtype, count=nnz) + # handle 1d keys specially b/c not a tuple + inds = zip(*self.keys()) if self.ndim > 1 else (self.keys(),) + coords = tuple(np.fromiter(ix, dtype=idx_dtype, count=nnz) for ix in inds) + A = self._coo_container((data, coords), shape=self.shape, dtype=self.dtype) + A.has_canonical_format = True + return A + + tocoo.__doc__ = _spbase.tocoo.__doc__ + + def todok(self, copy=False): + if copy: + return self.copy() + return self + + todok.__doc__ = _spbase.todok.__doc__ + + def tocsc(self, copy=False): + if self.ndim == 1: + raise NotImplementedError("tocsr() not valid for 1d sparse array") + return self.tocoo(copy=False).tocsc(copy=copy) + + tocsc.__doc__ = _spbase.tocsc.__doc__ + + def resize(self, *shape): + is_array = isinstance(self, sparray) + shape = check_shape(shape, allow_1d=is_array) + if len(shape) != len(self.shape): + # TODO implement resize across dimensions + raise NotImplementedError + + if self.ndim == 1: + newN = shape[-1] + for i in list(self._dict): + if i >= newN: + del self._dict[i] + self._shape = shape + return + + newM, newN = shape + M, N = self.shape + if newM < M or newN < N: + # Remove all elements outside new dimensions + for i, j in list(self.keys()): + if i >= newM or j >= newN: + del self._dict[i, j] + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + # Added for 1d to avoid `tocsr` from _base.py + def astype(self, dtype, casting='unsafe', copy=True): + dtype = np.dtype(dtype) + if self.dtype != dtype: + result = self._dok_container(self.shape, dtype=dtype) + data = np.array(list(self._dict.values()), dtype=dtype) + result._dict = dict(zip(self._dict, data)) + return result + elif copy: + return self.copy() + return self + + +def isspmatrix_dok(x): + """Is `x` of dok_array type? + + Parameters + ---------- + x + object to check for being a dok matrix + + Returns + ------- + bool + True if `x` is a dok matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import dok_array, dok_matrix, coo_matrix, isspmatrix_dok + >>> isspmatrix_dok(dok_matrix([[5]])) + True + >>> isspmatrix_dok(dok_array([[5]])) + False + >>> isspmatrix_dok(coo_matrix([[5]])) + False + """ + return isinstance(x, dok_matrix) + + +# This namespace class separates array from matrix with isinstance +class dok_array(_dok_base, sparray): + """ + Dictionary Of Keys based sparse array. + + This is an efficient structure for constructing sparse + arrays incrementally. + + This can be instantiated in several ways: + dok_array(D) + where D is a 2-D ndarray + + dok_array(S) + with another sparse array or matrix S (equivalent to S.todok()) + + dok_array((M,N), [dtype]) + create the array with initial shape (M,N) + dtype is optional, defaulting to dtype='d' + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + Number of nonzero elements + size + T + + Notes + ----- + + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + - Allows for efficient O(1) access of individual elements. + - Duplicates are not allowed. + - Can be efficiently converted to a coo_array once constructed. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import dok_array + >>> S = dok_array((5, 5), dtype=np.float32) + >>> for i in range(5): + ... for j in range(5): + ... S[i, j] = i + j # Update element + + """ + + +class dok_matrix(spmatrix, _dok_base): + """ + Dictionary Of Keys based sparse matrix. + + This is an efficient structure for constructing sparse + matrices incrementally. + + This can be instantiated in several ways: + dok_matrix(D) + where D is a 2-D ndarray + + dok_matrix(S) + with another sparse array or matrix S (equivalent to S.todok()) + + dok_matrix((M,N), [dtype]) + create the matrix with initial shape (M,N) + dtype is optional, defaulting to dtype='d' + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + Number of nonzero elements + size + T + + Notes + ----- + + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + - Allows for efficient O(1) access of individual elements. + - Duplicates are not allowed. + - Can be efficiently converted to a coo_matrix once constructed. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import dok_matrix + >>> S = dok_matrix((5, 5), dtype=np.float32) + >>> for i in range(5): + ... for j in range(5): + ... S[i, j] = i + j # Update element + + """ + + def set_shape(self, shape): + new_matrix = self.reshape(shape, copy=False).asformat(self.format) + self.__dict__ = new_matrix.__dict__ + + def get_shape(self): + """Get shape of a sparse matrix.""" + return self._shape + + shape = property(fget=get_shape, fset=set_shape) + + def __reversed__(self): + return self._dict.__reversed__() + + def __or__(self, other): + if isinstance(other, _dok_base): + return self._dict | other._dict + return self._dict | other + + def __ror__(self, other): + if isinstance(other, _dok_base): + return self._dict | other._dict + return self._dict | other + + def __ior__(self, other): + if isinstance(other, _dok_base): + self._dict |= other._dict + else: + self._dict |= other + return self diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_extract.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_extract.py new file mode 100644 index 0000000000000000000000000000000000000000..052ef506f9ab4796c0cca8495e4071069beb8e42 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_extract.py @@ -0,0 +1,178 @@ +"""Functions to extract parts of sparse matrices +""" + +__docformat__ = "restructuredtext en" + +__all__ = ['find', 'tril', 'triu'] + + +from ._coo import coo_matrix, coo_array +from ._base import sparray + + +def find(A): + """Return the indices and values of the nonzero elements of a matrix + + Parameters + ---------- + A : dense or sparse array or matrix + Matrix whose nonzero elements are desired. + + Returns + ------- + (I,J,V) : tuple of arrays + I,J, and V contain the row indices, column indices, and values + of the nonzero entries. + + + Examples + -------- + >>> from scipy.sparse import csr_array, find + >>> A = csr_array([[7.0, 8.0, 0],[0, 0, 9.0]]) + >>> find(A) + (array([0, 0, 1], dtype=int32), + array([0, 1, 2], dtype=int32), + array([ 7., 8., 9.])) + + """ + + A = coo_array(A, copy=True) + A.sum_duplicates() + # remove explicit zeros + nz_mask = A.data != 0 + return A.row[nz_mask], A.col[nz_mask], A.data[nz_mask] + + +def tril(A, k=0, format=None): + """Return the lower triangular portion of a sparse array or matrix + + Returns the elements on or below the k-th diagonal of A. + - k = 0 corresponds to the main diagonal + - k > 0 is above the main diagonal + - k < 0 is below the main diagonal + + Parameters + ---------- + A : dense or sparse array or matrix + Matrix whose lower trianglar portion is desired. + k : integer : optional + The top-most diagonal of the lower triangle. + format : string + Sparse format of the result, e.g. format="csr", etc. + + Returns + ------- + L : sparse matrix + Lower triangular portion of A in sparse format. + + See Also + -------- + triu : upper triangle in sparse format + + Examples + -------- + >>> from scipy.sparse import csr_array, tril + >>> A = csr_array([[1, 2, 0, 0, 3], [4, 5, 0, 6, 7], [0, 0, 8, 9, 0]], + ... dtype='int32') + >>> A.toarray() + array([[1, 2, 0, 0, 3], + [4, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> tril(A).toarray() + array([[1, 0, 0, 0, 0], + [4, 5, 0, 0, 0], + [0, 0, 8, 0, 0]]) + >>> tril(A).nnz + 4 + >>> tril(A, k=1).toarray() + array([[1, 2, 0, 0, 0], + [4, 5, 0, 0, 0], + [0, 0, 8, 9, 0]]) + >>> tril(A, k=-1).toarray() + array([[0, 0, 0, 0, 0], + [4, 0, 0, 0, 0], + [0, 0, 0, 0, 0]]) + >>> tril(A, format='csc') + + + """ + coo_sparse = coo_array if isinstance(A, sparray) else coo_matrix + + # convert to COOrdinate format where things are easy + A = coo_sparse(A, copy=False) + mask = A.row + k >= A.col + + row = A.row[mask] + col = A.col[mask] + data = A.data[mask] + new_coo = coo_sparse((data, (row, col)), shape=A.shape, dtype=A.dtype) + return new_coo.asformat(format) + + +def triu(A, k=0, format=None): + """Return the upper triangular portion of a sparse array or matrix + + Returns the elements on or above the k-th diagonal of A. + - k = 0 corresponds to the main diagonal + - k > 0 is above the main diagonal + - k < 0 is below the main diagonal + + Parameters + ---------- + A : dense or sparse array or matrix + Matrix whose upper trianglar portion is desired. + k : integer : optional + The bottom-most diagonal of the upper triangle. + format : string + Sparse format of the result, e.g. format="csr", etc. + + Returns + ------- + L : sparse array or matrix + Upper triangular portion of A in sparse format. + Sparse array if A is a sparse array, otherwise matrix. + + See Also + -------- + tril : lower triangle in sparse format + + Examples + -------- + >>> from scipy.sparse import csr_array, triu + >>> A = csr_array([[1, 2, 0, 0, 3], [4, 5, 0, 6, 7], [0, 0, 8, 9, 0]], + ... dtype='int32') + >>> A.toarray() + array([[1, 2, 0, 0, 3], + [4, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> triu(A).toarray() + array([[1, 2, 0, 0, 3], + [0, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> triu(A).nnz + 8 + >>> triu(A, k=1).toarray() + array([[0, 2, 0, 0, 3], + [0, 0, 0, 6, 7], + [0, 0, 0, 9, 0]]) + >>> triu(A, k=-1).toarray() + array([[1, 2, 0, 0, 3], + [4, 5, 0, 6, 7], + [0, 0, 8, 9, 0]]) + >>> triu(A, format='csc') + + + """ + coo_sparse = coo_array if isinstance(A, sparray) else coo_matrix + + # convert to COOrdinate format where things are easy + A = coo_sparse(A, copy=False) + mask = A.row + k <= A.col + + row = A.row[mask] + col = A.col[mask] + data = A.data[mask] + new_coo = coo_sparse((data, (row, col)), shape=A.shape, dtype=A.dtype) + return new_coo.asformat(format) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_index.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_index.py new file mode 100644 index 0000000000000000000000000000000000000000..c0fc3d01b0ebd153703a76af431626d958b7de64 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_index.py @@ -0,0 +1,392 @@ +"""Indexing mixin for sparse array/matrix classes. +""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import numpy as np +from ._sputils import isintlike + +if TYPE_CHECKING: + import numpy.typing as npt + +INT_TYPES = (int, np.integer) + + +def _broadcast_arrays(a, b): + """ + Same as np.broadcast_arrays(a, b) but old writeability rules. + + NumPy >= 1.17.0 transitions broadcast_arrays to return + read-only arrays. Set writeability explicitly to avoid warnings. + Retain the old writeability rules, as our Cython code assumes + the old behavior. + """ + x, y = np.broadcast_arrays(a, b) + x.flags.writeable = a.flags.writeable + y.flags.writeable = b.flags.writeable + return x, y + + +class IndexMixin: + """ + This class provides common dispatching and validation logic for indexing. + """ + def _raise_on_1d_array_slice(self): + """We do not currently support 1D sparse arrays. + + This function is called each time that a 1D array would + result, raising an error instead. + + Once 1D sparse arrays are implemented, it should be removed. + """ + from scipy.sparse import sparray + + if isinstance(self, sparray): + raise NotImplementedError( + 'We have not yet implemented 1D sparse slices; ' + 'please index using explicit indices, e.g. `x[:, [0]]`' + ) + + def __getitem__(self, key): + row, col = self._validate_indices(key) + + # Dispatch to specialized methods. + if isinstance(row, INT_TYPES): + if isinstance(col, INT_TYPES): + return self._get_intXint(row, col) + elif isinstance(col, slice): + self._raise_on_1d_array_slice() + return self._get_intXslice(row, col) + elif col.ndim == 1: + self._raise_on_1d_array_slice() + return self._get_intXarray(row, col) + elif col.ndim == 2: + return self._get_intXarray(row, col) + raise IndexError('index results in >2 dimensions') + elif isinstance(row, slice): + if isinstance(col, INT_TYPES): + self._raise_on_1d_array_slice() + return self._get_sliceXint(row, col) + elif isinstance(col, slice): + if row == slice(None) and row == col: + return self.copy() + return self._get_sliceXslice(row, col) + elif col.ndim == 1: + return self._get_sliceXarray(row, col) + raise IndexError('index results in >2 dimensions') + elif row.ndim == 1: + if isinstance(col, INT_TYPES): + self._raise_on_1d_array_slice() + return self._get_arrayXint(row, col) + elif isinstance(col, slice): + return self._get_arrayXslice(row, col) + else: # row.ndim == 2 + if isinstance(col, INT_TYPES): + return self._get_arrayXint(row, col) + elif isinstance(col, slice): + raise IndexError('index results in >2 dimensions') + elif row.shape[1] == 1 and (col.ndim == 1 or col.shape[0] == 1): + # special case for outer indexing + return self._get_columnXarray(row[:,0], col.ravel()) + + # The only remaining case is inner (fancy) indexing + row, col = _broadcast_arrays(row, col) + if row.shape != col.shape: + raise IndexError('number of row and column indices differ') + if row.size == 0: + return self.__class__(np.atleast_2d(row).shape, dtype=self.dtype) + return self._get_arrayXarray(row, col) + + def __setitem__(self, key, x): + row, col = self._validate_indices(key) + + if isinstance(row, INT_TYPES) and isinstance(col, INT_TYPES): + x = np.asarray(x, dtype=self.dtype) + if x.size != 1: + raise ValueError('Trying to assign a sequence to an item') + self._set_intXint(row, col, x.flat[0]) + return + + if isinstance(row, slice): + row = np.arange(*row.indices(self.shape[0]))[:, None] + else: + row = np.atleast_1d(row) + + if isinstance(col, slice): + col = np.arange(*col.indices(self.shape[1]))[None, :] + if row.ndim == 1: + row = row[:, None] + else: + col = np.atleast_1d(col) + + i, j = _broadcast_arrays(row, col) + if i.shape != j.shape: + raise IndexError('number of row and column indices differ') + + from ._base import issparse + if issparse(x): + if i.ndim == 1: + # Inner indexing, so treat them like row vectors. + i = i[None] + j = j[None] + broadcast_row = x.shape[0] == 1 and i.shape[0] != 1 + broadcast_col = x.shape[1] == 1 and i.shape[1] != 1 + if not ((broadcast_row or x.shape[0] == i.shape[0]) and + (broadcast_col or x.shape[1] == i.shape[1])): + raise ValueError('shape mismatch in assignment') + if x.shape[0] == 0 or x.shape[1] == 0: + return + x = x.tocoo(copy=True) + x.sum_duplicates() + self._set_arrayXarray_sparse(i, j, x) + else: + # Make x and i into the same shape + x = np.asarray(x, dtype=self.dtype) + if x.squeeze().shape != i.squeeze().shape: + x = np.broadcast_to(x, i.shape) + if x.size == 0: + return + x = x.reshape(i.shape) + self._set_arrayXarray(i, j, x) + + def _validate_indices(self, key): + # First, check if indexing with single boolean matrix. + from ._base import _spbase + if (isinstance(key, (_spbase, np.ndarray)) and + key.ndim == 2 and key.dtype.kind == 'b'): + if key.shape != self.shape: + raise IndexError('boolean index shape does not match array shape') + row, col = key.nonzero() + else: + row, col = _unpack_index(key) + M, N = self.shape + + def _validate_bool_idx( + idx: npt.NDArray[np.bool_], + axis_size: int, + axis_name: str + ) -> npt.NDArray[np.int_]: + if len(idx) != axis_size: + raise IndexError( + f"boolean {axis_name} index has incorrect length: {len(idx)} " + f"instead of {axis_size}" + ) + return _boolean_index_to_array(idx) + + if isintlike(row): + row = int(row) + if row < -M or row >= M: + raise IndexError('row index (%d) out of range' % row) + if row < 0: + row += M + elif (bool_row := _compatible_boolean_index(row)) is not None: + row = _validate_bool_idx(bool_row, M, "row") + elif not isinstance(row, slice): + row = self._asindices(row, M) + + if isintlike(col): + col = int(col) + if col < -N or col >= N: + raise IndexError('column index (%d) out of range' % col) + if col < 0: + col += N + elif (bool_col := _compatible_boolean_index(col)) is not None: + col = _validate_bool_idx(bool_col, N, "column") + elif not isinstance(col, slice): + col = self._asindices(col, N) + + return row, col + + def _asindices(self, idx, length): + """Convert `idx` to a valid index for an axis with a given length. + + Subclasses that need special validation can override this method. + """ + try: + x = np.asarray(idx) + except (ValueError, TypeError, MemoryError) as e: + raise IndexError('invalid index') from e + + if x.ndim not in (1, 2): + raise IndexError('Index dimension must be 1 or 2') + + if x.size == 0: + return x + + # Check bounds + max_indx = x.max() + if max_indx >= length: + raise IndexError('index (%d) out of range' % max_indx) + + min_indx = x.min() + if min_indx < 0: + if min_indx < -length: + raise IndexError('index (%d) out of range' % min_indx) + if x is idx or not x.flags.owndata: + x = x.copy() + x[x < 0] += length + return x + + def _getrow(self, i): + """Return a copy of row i of the matrix, as a (1 x n) row vector. + """ + M, N = self.shape + i = int(i) + if i < -M or i >= M: + raise IndexError('index (%d) out of range' % i) + if i < 0: + i += M + return self._get_intXslice(i, slice(None)) + + def _getcol(self, i): + """Return a copy of column i of the matrix, as a (m x 1) column vector. + """ + M, N = self.shape + i = int(i) + if i < -N or i >= N: + raise IndexError('index (%d) out of range' % i) + if i < 0: + i += N + return self._get_sliceXint(slice(None), i) + + def _get_intXint(self, row, col): + raise NotImplementedError() + + def _get_intXarray(self, row, col): + raise NotImplementedError() + + def _get_intXslice(self, row, col): + raise NotImplementedError() + + def _get_sliceXint(self, row, col): + raise NotImplementedError() + + def _get_sliceXslice(self, row, col): + raise NotImplementedError() + + def _get_sliceXarray(self, row, col): + raise NotImplementedError() + + def _get_arrayXint(self, row, col): + raise NotImplementedError() + + def _get_arrayXslice(self, row, col): + raise NotImplementedError() + + def _get_columnXarray(self, row, col): + raise NotImplementedError() + + def _get_arrayXarray(self, row, col): + raise NotImplementedError() + + def _set_intXint(self, row, col, x): + raise NotImplementedError() + + def _set_arrayXarray(self, row, col, x): + raise NotImplementedError() + + def _set_arrayXarray_sparse(self, row, col, x): + # Fall back to densifying x + x = np.asarray(x.toarray(), dtype=self.dtype) + x, _ = _broadcast_arrays(x, row) + self._set_arrayXarray(row, col, x) + + +def _unpack_index(index) -> tuple[ + int | slice | npt.NDArray[np.bool_ | np.int_], + int | slice | npt.NDArray[np.bool_ | np.int_] +]: + """ Parse index. Always return a tuple of the form (row, col). + Valid type for row/col is integer, slice, array of bool, or array of integers. + """ + # Parse any ellipses. + index = _check_ellipsis(index) + + # Next, parse the tuple or object + if isinstance(index, tuple): + if len(index) == 2: + row, col = index + elif len(index) == 1: + row, col = index[0], slice(None) + else: + raise IndexError('invalid number of indices') + else: + idx = _compatible_boolean_index(index) + if idx is None: + row, col = index, slice(None) + elif idx.ndim < 2: + return idx, slice(None) + elif idx.ndim == 2: + return idx.nonzero() + # Next, check for validity and transform the index as needed. + from ._base import issparse + if issparse(row) or issparse(col): + # Supporting sparse boolean indexing with both row and col does + # not work because spmatrix.ndim is always 2. + raise IndexError( + 'Indexing with sparse matrices is not supported ' + 'except boolean indexing where matrix and index ' + 'are equal shapes.') + return row, col + + +def _check_ellipsis(index): + """Process indices with Ellipsis. Returns modified index.""" + if index is Ellipsis: + return (slice(None), slice(None)) + + if not isinstance(index, tuple): + return index + + # Find any Ellipsis objects. + ellipsis_indices = [i for i, v in enumerate(index) if v is Ellipsis] + if not ellipsis_indices: + return index + if len(ellipsis_indices) > 1: + raise IndexError("an index can only have a single ellipsis ('...')") + + # Replace the Ellipsis object with 0, 1, or 2 null-slices as needed. + i, = ellipsis_indices + num_slices = max(0, 3 - len(index)) + return index[:i] + (slice(None),) * num_slices + index[i + 1:] + + +def _maybe_bool_ndarray(idx): + """Returns a compatible array if elements are boolean. + """ + idx = np.asanyarray(idx) + if idx.dtype.kind == 'b': + return idx + return None + + +def _first_element_bool(idx, max_dim=2): + """Returns True if first element of the incompatible + array type is boolean. + """ + if max_dim < 1: + return None + try: + first = next(iter(idx), None) + except TypeError: + return None + if isinstance(first, bool): + return True + return _first_element_bool(first, max_dim-1) + + +def _compatible_boolean_index(idx): + """Returns a boolean index array that can be converted to + integer array. Returns None if no such array exists. + """ + # Presence of attribute `ndim` indicates a compatible array type. + if hasattr(idx, 'ndim') or _first_element_bool(idx): + return _maybe_bool_ndarray(idx) + return None + + +def _boolean_index_to_array(idx): + if idx.ndim > 1: + raise IndexError('invalid index shape') + return np.where(idx)[0] diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_lil.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_lil.py new file mode 100644 index 0000000000000000000000000000000000000000..2503aa628b58ebfed53308f13873226e081346b8 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_lil.py @@ -0,0 +1,612 @@ +"""List of Lists sparse matrix class +""" + +__docformat__ = "restructuredtext en" + +__all__ = ['lil_array', 'lil_matrix', 'isspmatrix_lil'] + +from bisect import bisect_left + +import numpy as np + +from ._matrix import spmatrix +from ._base import _spbase, sparray, issparse +from ._index import IndexMixin, INT_TYPES, _broadcast_arrays +from ._sputils import (getdtype, isshape, isscalarlike, upcast_scalar, + check_shape, check_reshape_kwargs) +from . import _csparsetools + + +class _lil_base(_spbase, IndexMixin): + _format = 'lil' + + def __init__(self, arg1, shape=None, dtype=None, copy=False): + _spbase.__init__(self, arg1) + self.dtype = getdtype(dtype, arg1, default=float) + + # First get the shape + if issparse(arg1): + if arg1.format == "lil" and copy: + A = arg1.copy() + else: + A = arg1.tolil() + + if dtype is not None: + A = A.astype(dtype, copy=False) + + self._shape = check_shape(A.shape) + self.dtype = A.dtype + self.rows = A.rows + self.data = A.data + elif isinstance(arg1,tuple): + if isshape(arg1): + if shape is not None: + raise ValueError('invalid use of shape parameter') + M, N = arg1 + self._shape = check_shape((M, N)) + self.rows = np.empty((M,), dtype=object) + self.data = np.empty((M,), dtype=object) + for i in range(M): + self.rows[i] = [] + self.data[i] = [] + else: + raise TypeError('unrecognized lil_array constructor usage') + else: + # assume A is dense + try: + A = self._ascontainer(arg1) + except TypeError as e: + raise TypeError('unsupported matrix type') from e + if isinstance(self, sparray) and A.ndim != 2: + raise ValueError(f"LIL arrays don't support {A.ndim}D input. Use 2D") + A = self._csr_container(A, dtype=dtype).tolil() + + self._shape = check_shape(A.shape) + self.dtype = A.dtype + self.rows = A.rows + self.data = A.data + + def __iadd__(self,other): + self[:,:] = self + other + return self + + def __isub__(self,other): + self[:,:] = self - other + return self + + def __imul__(self,other): + if isscalarlike(other): + self[:,:] = self * other + return self + else: + return NotImplemented + + def __itruediv__(self,other): + if isscalarlike(other): + self[:,:] = self / other + return self + else: + return NotImplemented + + # Whenever the dimensions change, empty lists should be created for each + # row + + def _getnnz(self, axis=None): + if axis is None: + return sum([len(rowvals) for rowvals in self.data]) + if axis < 0: + axis += 2 + if axis == 0: + out = np.zeros(self.shape[1], dtype=np.intp) + for row in self.rows: + out[row] += 1 + return out + elif axis == 1: + return np.array([len(rowvals) for rowvals in self.data], dtype=np.intp) + else: + raise ValueError('axis out of bounds') + + def count_nonzero(self): + return sum(np.count_nonzero(rowvals) for rowvals in self.data) + + _getnnz.__doc__ = _spbase._getnnz.__doc__ + count_nonzero.__doc__ = _spbase.count_nonzero.__doc__ + + def getrowview(self, i): + """Returns a view of the 'i'th row (without copying). + """ + new = self._lil_container((1, self.shape[1]), dtype=self.dtype) + new.rows[0] = self.rows[i] + new.data[0] = self.data[i] + return new + + def getrow(self, i): + """Returns a copy of the 'i'th row. + """ + M, N = self.shape + if i < 0: + i += M + if i < 0 or i >= M: + raise IndexError('row index out of bounds') + new = self._lil_container((1, N), dtype=self.dtype) + new.rows[0] = self.rows[i][:] + new.data[0] = self.data[i][:] + return new + + def __getitem__(self, key): + # Fast path for simple (int, int) indexing. + if (isinstance(key, tuple) and len(key) == 2 and + isinstance(key[0], INT_TYPES) and + isinstance(key[1], INT_TYPES)): + # lil_get1 handles validation for us. + return self._get_intXint(*key) + # Everything else takes the normal path. + return IndexMixin.__getitem__(self, key) + + def _asindices(self, idx, N): + # LIL routines handle bounds-checking for us, so don't do it here. + try: + x = np.asarray(idx) + except (ValueError, TypeError, MemoryError) as e: + raise IndexError('invalid index') from e + if x.ndim not in (1, 2): + raise IndexError('Index dimension must be <= 2') + return x + + def _get_intXint(self, row, col): + v = _csparsetools.lil_get1(self.shape[0], self.shape[1], self.rows, + self.data, row, col) + return self.dtype.type(v) + + def _get_sliceXint(self, row, col): + row = range(*row.indices(self.shape[0])) + return self._get_row_ranges(row, slice(col, col+1)) + + def _get_arrayXint(self, row, col): + row = row.squeeze() + return self._get_row_ranges(row, slice(col, col+1)) + + def _get_intXslice(self, row, col): + return self._get_row_ranges((row,), col) + + def _get_sliceXslice(self, row, col): + row = range(*row.indices(self.shape[0])) + return self._get_row_ranges(row, col) + + def _get_arrayXslice(self, row, col): + return self._get_row_ranges(row, col) + + def _get_intXarray(self, row, col): + row = np.array(row, dtype=col.dtype, ndmin=1) + return self._get_columnXarray(row, col) + + def _get_sliceXarray(self, row, col): + row = np.arange(*row.indices(self.shape[0])) + return self._get_columnXarray(row, col) + + def _get_columnXarray(self, row, col): + # outer indexing + row, col = _broadcast_arrays(row[:,None], col) + return self._get_arrayXarray(row, col) + + def _get_arrayXarray(self, row, col): + # inner indexing + i, j = map(np.atleast_2d, _prepare_index_for_memoryview(row, col)) + new = self._lil_container(i.shape, dtype=self.dtype) + _csparsetools.lil_fancy_get(self.shape[0], self.shape[1], + self.rows, self.data, + new.rows, new.data, + i, j) + return new + + def _get_row_ranges(self, rows, col_slice): + """ + Fast path for indexing in the case where column index is slice. + + This gains performance improvement over brute force by more + efficient skipping of zeros, by accessing the elements + column-wise in order. + + Parameters + ---------- + rows : sequence or range + Rows indexed. If range, must be within valid bounds. + col_slice : slice + Columns indexed + + """ + j_start, j_stop, j_stride = col_slice.indices(self.shape[1]) + col_range = range(j_start, j_stop, j_stride) + nj = len(col_range) + new = self._lil_container((len(rows), nj), dtype=self.dtype) + + _csparsetools.lil_get_row_ranges(self.shape[0], self.shape[1], + self.rows, self.data, + new.rows, new.data, + rows, + j_start, j_stop, j_stride, nj) + + return new + + def _set_intXint(self, row, col, x): + _csparsetools.lil_insert(self.shape[0], self.shape[1], self.rows, + self.data, row, col, x) + + def _set_arrayXarray(self, row, col, x): + i, j, x = map(np.atleast_2d, _prepare_index_for_memoryview(row, col, x)) + _csparsetools.lil_fancy_set(self.shape[0], self.shape[1], + self.rows, self.data, + i, j, x) + + def _set_arrayXarray_sparse(self, row, col, x): + # Fall back to densifying x + x = np.asarray(x.toarray(), dtype=self.dtype) + x, _ = _broadcast_arrays(x, row) + self._set_arrayXarray(row, col, x) + + def __setitem__(self, key, x): + if isinstance(key, tuple) and len(key) == 2: + row, col = key + # Fast path for simple (int, int) indexing. + if isinstance(row, INT_TYPES) and isinstance(col, INT_TYPES): + x = self.dtype.type(x) + if x.size > 1: + raise ValueError("Trying to assign a sequence to an item") + return self._set_intXint(row, col, x) + # Fast path for full-matrix sparse assignment. + if (isinstance(row, slice) and isinstance(col, slice) and + row == slice(None) and col == slice(None) and + issparse(x) and x.shape == self.shape): + x = self._lil_container(x, dtype=self.dtype) + self.rows = x.rows + self.data = x.data + return + # Everything else takes the normal path. + IndexMixin.__setitem__(self, key, x) + + def _mul_scalar(self, other): + if other == 0: + # Multiply by zero: return the zero matrix + new = self._lil_container(self.shape, dtype=self.dtype) + else: + res_dtype = upcast_scalar(self.dtype, other) + + new = self.copy() + new = new.astype(res_dtype) + # Multiply this scalar by every element. + for j, rowvals in enumerate(new.data): + new.data[j] = [val*other for val in rowvals] + return new + + def __truediv__(self, other): # self / other + if isscalarlike(other): + new = self.copy() + new.dtype = np.result_type(self, other) + # Divide every element by this scalar + for j, rowvals in enumerate(new.data): + new.data[j] = [val/other for val in rowvals] + return new + else: + return self.tocsr() / other + + def copy(self): + M, N = self.shape + new = self._lil_container(self.shape, dtype=self.dtype) + # This is ~14x faster than calling deepcopy() on rows and data. + _csparsetools.lil_get_row_ranges(M, N, self.rows, self.data, + new.rows, new.data, range(M), + 0, N, 1, N) + return new + + copy.__doc__ = _spbase.copy.__doc__ + + def reshape(self, *args, **kwargs): + shape = check_shape(args, self.shape) + order, copy = check_reshape_kwargs(kwargs) + + # Return early if reshape is not required + if shape == self.shape: + if copy: + return self.copy() + else: + return self + + new = self._lil_container(shape, dtype=self.dtype) + + if order == 'C': + ncols = self.shape[1] + for i, row in enumerate(self.rows): + for col, j in enumerate(row): + new_r, new_c = np.unravel_index(i * ncols + j, shape) + new[new_r, new_c] = self[i, j] + elif order == 'F': + nrows = self.shape[0] + for i, row in enumerate(self.rows): + for col, j in enumerate(row): + new_r, new_c = np.unravel_index(i + j * nrows, shape, order) + new[new_r, new_c] = self[i, j] + else: + raise ValueError("'order' must be 'C' or 'F'") + + return new + + reshape.__doc__ = _spbase.reshape.__doc__ + + def resize(self, *shape): + shape = check_shape(shape) + new_M, new_N = shape + M, N = self.shape + + if new_M < M: + self.rows = self.rows[:new_M] + self.data = self.data[:new_M] + elif new_M > M: + self.rows = np.resize(self.rows, new_M) + self.data = np.resize(self.data, new_M) + for i in range(M, new_M): + self.rows[i] = [] + self.data[i] = [] + + if new_N < N: + for row, data in zip(self.rows, self.data): + trunc = bisect_left(row, new_N) + del row[trunc:] + del data[trunc:] + + self._shape = shape + + resize.__doc__ = _spbase.resize.__doc__ + + def toarray(self, order=None, out=None): + d = self._process_toarray_args(order, out) + for i, row in enumerate(self.rows): + for pos, j in enumerate(row): + d[i, j] = self.data[i][pos] + return d + + toarray.__doc__ = _spbase.toarray.__doc__ + + def transpose(self, axes=None, copy=False): + return self.tocsr(copy=copy).transpose(axes=axes, copy=False).tolil(copy=False) + + transpose.__doc__ = _spbase.transpose.__doc__ + + def tolil(self, copy=False): + if copy: + return self.copy() + else: + return self + + tolil.__doc__ = _spbase.tolil.__doc__ + + def tocsr(self, copy=False): + M, N = self.shape + if M == 0 or N == 0: + return self._csr_container((M, N), dtype=self.dtype) + + # construct indptr array + if M*N <= np.iinfo(np.int32).max: + # fast path: it is known that 64-bit indexing will not be needed. + idx_dtype = np.int32 + indptr = np.empty(M + 1, dtype=idx_dtype) + indptr[0] = 0 + _csparsetools.lil_get_lengths(self.rows, indptr[1:]) + np.cumsum(indptr, out=indptr) + nnz = indptr[-1] + else: + idx_dtype = self._get_index_dtype(maxval=N) + lengths = np.empty(M, dtype=idx_dtype) + _csparsetools.lil_get_lengths(self.rows, lengths) + nnz = lengths.sum(dtype=np.int64) + idx_dtype = self._get_index_dtype(maxval=max(N, nnz)) + indptr = np.empty(M + 1, dtype=idx_dtype) + indptr[0] = 0 + np.cumsum(lengths, dtype=idx_dtype, out=indptr[1:]) + + indices = np.empty(nnz, dtype=idx_dtype) + data = np.empty(nnz, dtype=self.dtype) + _csparsetools.lil_flatten_to_array(self.rows, indices) + _csparsetools.lil_flatten_to_array(self.data, data) + + # init csr matrix + return self._csr_container((data, indices, indptr), shape=self.shape) + + tocsr.__doc__ = _spbase.tocsr.__doc__ + + +def _prepare_index_for_memoryview(i, j, x=None): + """ + Convert index and data arrays to form suitable for passing to the + Cython fancy getset routines. + + The conversions are necessary since to (i) ensure the integer + index arrays are in one of the accepted types, and (ii) to ensure + the arrays are writable so that Cython memoryview support doesn't + choke on them. + + Parameters + ---------- + i, j + Index arrays + x : optional + Data arrays + + Returns + ------- + i, j, x + Re-formatted arrays (x is omitted, if input was None) + + """ + if i.dtype > j.dtype: + j = j.astype(i.dtype) + elif i.dtype < j.dtype: + i = i.astype(j.dtype) + + if not i.flags.writeable or i.dtype not in (np.int32, np.int64): + i = i.astype(np.intp) + if not j.flags.writeable or j.dtype not in (np.int32, np.int64): + j = j.astype(np.intp) + + if x is not None: + if not x.flags.writeable: + x = x.copy() + return i, j, x + else: + return i, j + + +def isspmatrix_lil(x): + """Is `x` of lil_matrix type? + + Parameters + ---------- + x + object to check for being a lil matrix + + Returns + ------- + bool + True if `x` is a lil matrix, False otherwise + + Examples + -------- + >>> from scipy.sparse import lil_array, lil_matrix, coo_matrix, isspmatrix_lil + >>> isspmatrix_lil(lil_matrix([[5]])) + True + >>> isspmatrix_lil(lil_array([[5]])) + False + >>> isspmatrix_lil(coo_matrix([[5]])) + False + """ + return isinstance(x, lil_matrix) + + +# This namespace class separates array from matrix with isinstance +class lil_array(_lil_base, sparray): + """ + Row-based LIst of Lists sparse array. + + This is a structure for constructing sparse arrays incrementally. + Note that inserting a single item can take linear time in the worst case; + to construct the array efficiently, make sure the items are pre-sorted by + index, per row. + + This can be instantiated in several ways: + lil_array(D) + where D is a 2-D ndarray + + lil_array(S) + with another sparse array or matrix S (equivalent to S.tolil()) + + lil_array((M, N), [dtype]) + to construct an empty array with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + Attributes + ---------- + dtype : dtype + Data type of the array + shape : 2-tuple + Shape of the array + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + LIL format data array of the array + rows + LIL format row index array of the array + T + + Notes + ----- + Sparse arrays can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the LIL format + - supports flexible slicing + - changes to the array sparsity structure are efficient + + Disadvantages of the LIL format + - arithmetic operations LIL + LIL are slow (consider CSR or CSC) + - slow column slicing (consider CSC) + - slow matrix vector products (consider CSR or CSC) + + Intended Usage + - LIL is a convenient format for constructing sparse arrays + - once an array has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - consider using the COO format when constructing large arrays + + Data Structure + - An array (``self.rows``) of rows, each of which is a sorted + list of column indices of non-zero elements. + - The corresponding nonzero values are stored in similar + fashion in ``self.data``. + + """ + + +class lil_matrix(spmatrix, _lil_base): + """ + Row-based LIst of Lists sparse matrix. + + This is a structure for constructing sparse matrices incrementally. + Note that inserting a single item can take linear time in the worst case; + to construct the matrix efficiently, make sure the items are pre-sorted by + index, per row. + + This can be instantiated in several ways: + lil_matrix(D) + where D is a 2-D ndarray + + lil_matrix(S) + with another sparse array or matrix S (equivalent to S.tolil()) + + lil_matrix((M, N), [dtype]) + to construct an empty matrix with shape (M, N) + dtype is optional, defaulting to dtype='d'. + + Attributes + ---------- + dtype : dtype + Data type of the matrix + shape : 2-tuple + Shape of the matrix + ndim : int + Number of dimensions (this is always 2) + nnz + size + data + LIL format data array of the matrix + rows + LIL format row index array of the matrix + T + + Notes + ----- + Sparse matrices can be used in arithmetic operations: they support + addition, subtraction, multiplication, division, and matrix power. + + Advantages of the LIL format + - supports flexible slicing + - changes to the matrix sparsity structure are efficient + + Disadvantages of the LIL format + - arithmetic operations LIL + LIL are slow (consider CSR or CSC) + - slow column slicing (consider CSC) + - slow matrix vector products (consider CSR or CSC) + + Intended Usage + - LIL is a convenient format for constructing sparse matrices + - once a matrix has been constructed, convert to CSR or + CSC format for fast arithmetic and matrix vector operations + - consider using the COO format when constructing large matrices + + Data Structure + - An array (``self.rows``) of rows, each of which is a sorted + list of column indices of non-zero elements. + - The corresponding nonzero values are stored in similar + fashion in ``self.data``. + + """ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_matrix.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_matrix.py new file mode 100644 index 0000000000000000000000000000000000000000..1ab8749423833b78f7efc17feb6e1a8e6405408a --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_matrix.py @@ -0,0 +1,113 @@ +class spmatrix: + """This class provides a base class for all sparse matrix classes. + + It cannot be instantiated. Most of the work is provided by subclasses. + """ + + @property + def _bsr_container(self): + from ._bsr import bsr_matrix + return bsr_matrix + + @property + def _coo_container(self): + from ._coo import coo_matrix + return coo_matrix + + @property + def _csc_container(self): + from ._csc import csc_matrix + return csc_matrix + + @property + def _csr_container(self): + from ._csr import csr_matrix + return csr_matrix + + @property + def _dia_container(self): + from ._dia import dia_matrix + return dia_matrix + + @property + def _dok_container(self): + from ._dok import dok_matrix + return dok_matrix + + @property + def _lil_container(self): + from ._lil import lil_matrix + return lil_matrix + + # Restore matrix multiplication + def __mul__(self, other): + return self._matmul_dispatch(other) + + def __rmul__(self, other): + return self._rmatmul_dispatch(other) + + # Restore matrix power + def __pow__(self, power): + from .linalg import matrix_power + + return matrix_power(self, power) + + ## Backward compatibility + + def set_shape(self, shape): + """Set the shape of the matrix in-place""" + # Make sure copy is False since this is in place + # Make sure format is unchanged because we are doing a __dict__ swap + new_self = self.reshape(shape, copy=False).asformat(self.format) + self.__dict__ = new_self.__dict__ + + def get_shape(self): + """Get the shape of the matrix""" + return self._shape + + shape = property(fget=get_shape, fset=set_shape, + doc="Shape of the matrix") + + def asfptype(self): + """Upcast matrix to a floating point format (if necessary)""" + return self._asfptype() + + def getmaxprint(self): + """Maximum number of elements to display when printed.""" + return self._getmaxprint() + + def getformat(self): + """Matrix storage format""" + return self.format + + def getnnz(self, axis=None): + """Number of stored values, including explicit zeros. + + Parameters + ---------- + axis : None, 0, or 1 + Select between the number of values across the whole array, in + each column, or in each row. + """ + return self._getnnz(axis=axis) + + def getH(self): + """Return the Hermitian transpose of this matrix. + + See Also + -------- + numpy.matrix.getH : NumPy's implementation of `getH` for matrices + """ + return self.conjugate().transpose() + + def getcol(self, j): + """Returns a copy of column j of the matrix, as an (m x 1) sparse + matrix (column vector). + """ + return self._getcol(j) + + def getrow(self, i): + """Returns a copy of row i of the matrix, as a (1 x n) sparse + matrix (row vector). + """ + return self._getrow(i) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_matrix_io.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_matrix_io.py new file mode 100644 index 0000000000000000000000000000000000000000..5b7f533926fd415a379cb08420b4a65a14baeb43 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_matrix_io.py @@ -0,0 +1,167 @@ +import numpy as np +import scipy as sp + +__all__ = ['save_npz', 'load_npz'] + + +# Make loading safe vs. malicious input +PICKLE_KWARGS = dict(allow_pickle=False) + + +def save_npz(file, matrix, compressed=True): + """ Save a sparse matrix or array to a file using ``.npz`` format. + + Parameters + ---------- + file : str or file-like object + Either the file name (string) or an open file (file-like object) + where the data will be saved. If file is a string, the ``.npz`` + extension will be appended to the file name if it is not already + there. + matrix: spmatrix or sparray + The sparse matrix or array to save. + Supported formats: ``csc``, ``csr``, ``bsr``, ``dia`` or ``coo``. + compressed : bool, optional + Allow compressing the file. Default: True + + See Also + -------- + scipy.sparse.load_npz: Load a sparse matrix from a file using ``.npz`` format. + numpy.savez: Save several arrays into a ``.npz`` archive. + numpy.savez_compressed : Save several arrays into a compressed ``.npz`` archive. + + Examples + -------- + Store sparse matrix to disk, and load it again: + + >>> import numpy as np + >>> import scipy as sp + >>> sparse_matrix = sp.sparse.csc_matrix([[0, 0, 3], [4, 0, 0]]) + >>> sparse_matrix + + >>> sparse_matrix.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + + >>> sp.sparse.save_npz('/tmp/sparse_matrix.npz', sparse_matrix) + >>> sparse_matrix = sp.sparse.load_npz('/tmp/sparse_matrix.npz') + + >>> sparse_matrix + + >>> sparse_matrix.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + """ + arrays_dict = {} + if matrix.format in ('csc', 'csr', 'bsr'): + arrays_dict.update(indices=matrix.indices, indptr=matrix.indptr) + elif matrix.format == 'dia': + arrays_dict.update(offsets=matrix.offsets) + elif matrix.format == 'coo': + arrays_dict.update(row=matrix.row, col=matrix.col) + else: + msg = f'Save is not implemented for sparse matrix of format {matrix.format}.' + raise NotImplementedError(msg) + arrays_dict.update( + format=matrix.format.encode('ascii'), + shape=matrix.shape, + data=matrix.data + ) + if isinstance(matrix, sp.sparse.sparray): + arrays_dict.update(_is_array=True) + if compressed: + np.savez_compressed(file, **arrays_dict) + else: + np.savez(file, **arrays_dict) + + +def load_npz(file): + """ Load a sparse array/matrix from a file using ``.npz`` format. + + Parameters + ---------- + file : str or file-like object + Either the file name (string) or an open file (file-like object) + where the data will be loaded. + + Returns + ------- + result : csc_array, csr_array, bsr_array, dia_array or coo_array + A sparse array/matrix containing the loaded data. + + Raises + ------ + OSError + If the input file does not exist or cannot be read. + + See Also + -------- + scipy.sparse.save_npz: Save a sparse array/matrix to a file using ``.npz`` format. + numpy.load: Load several arrays from a ``.npz`` archive. + + Examples + -------- + Store sparse array/matrix to disk, and load it again: + + >>> import numpy as np + >>> import scipy as sp + >>> sparse_array = sp.sparse.csc_array([[0, 0, 3], [4, 0, 0]]) + >>> sparse_array + + >>> sparse_array.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + + >>> sp.sparse.save_npz('/tmp/sparse_array.npz', sparse_array) + >>> sparse_array = sp.sparse.load_npz('/tmp/sparse_array.npz') + + >>> sparse_array + + >>> sparse_array.toarray() + array([[0, 0, 3], + [4, 0, 0]], dtype=int64) + + In this example we force the result to be csr_array from csr_matrix + >>> sparse_matrix = sp.sparse.csc_matrix([[0, 0, 3], [4, 0, 0]]) + >>> sp.sparse.save_npz('/tmp/sparse_matrix.npz', sparse_matrix) + >>> tmp = sp.sparse.load_npz('/tmp/sparse_matrix.npz') + >>> sparse_array = sp.sparse.csr_array(tmp) + """ + with np.load(file, **PICKLE_KWARGS) as loaded: + sparse_format = loaded.get('format') + if sparse_format is None: + raise ValueError(f'The file {file} does not contain ' + f'a sparse array or matrix.') + sparse_format = sparse_format.item() + + if not isinstance(sparse_format, str): + # Play safe with Python 2 vs 3 backward compatibility; + # files saved with SciPy < 1.0.0 may contain unicode or bytes. + sparse_format = sparse_format.decode('ascii') + + if loaded.get('_is_array'): + sparse_type = sparse_format + '_array' + else: + sparse_type = sparse_format + '_matrix' + + try: + cls = getattr(sp.sparse, f'{sparse_type}') + except AttributeError as e: + raise ValueError(f'Unknown format "{sparse_type}"') from e + + if sparse_format in ('csc', 'csr', 'bsr'): + return cls((loaded['data'], loaded['indices'], loaded['indptr']), + shape=loaded['shape']) + elif sparse_format == 'dia': + return cls((loaded['data'], loaded['offsets']), + shape=loaded['shape']) + elif sparse_format == 'coo': + return cls((loaded['data'], (loaded['row'], loaded['col'])), + shape=loaded['shape']) + else: + raise NotImplementedError(f'Load is not implemented for ' + f'sparse matrix of format {sparse_format}.') diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_spfuncs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_spfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..8e9b0abcede6387e74538baf839a303c6cc1b6be --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_spfuncs.py @@ -0,0 +1,76 @@ +""" Functions that operate on sparse matrices +""" + +__all__ = ['count_blocks','estimate_blocksize'] + +from ._base import issparse +from ._csr import csr_array +from ._sparsetools import csr_count_blocks + + +def estimate_blocksize(A,efficiency=0.7): + """Attempt to determine the blocksize of a sparse matrix + + Returns a blocksize=(r,c) such that + - A.nnz / A.tobsr( (r,c) ).nnz > efficiency + """ + if not (issparse(A) and A.format in ("csc", "csr")): + A = csr_array(A) + + if A.nnz == 0: + return (1,1) + + if not 0 < efficiency < 1.0: + raise ValueError('efficiency must satisfy 0.0 < efficiency < 1.0') + + high_efficiency = (1.0 + efficiency) / 2.0 + nnz = float(A.nnz) + M,N = A.shape + + if M % 2 == 0 and N % 2 == 0: + e22 = nnz / (4 * count_blocks(A,(2,2))) + else: + e22 = 0.0 + + if M % 3 == 0 and N % 3 == 0: + e33 = nnz / (9 * count_blocks(A,(3,3))) + else: + e33 = 0.0 + + if e22 > high_efficiency and e33 > high_efficiency: + e66 = nnz / (36 * count_blocks(A,(6,6))) + if e66 > efficiency: + return (6,6) + else: + return (3,3) + else: + if M % 4 == 0 and N % 4 == 0: + e44 = nnz / (16 * count_blocks(A,(4,4))) + else: + e44 = 0.0 + + if e44 > efficiency: + return (4,4) + elif e33 > efficiency: + return (3,3) + elif e22 > efficiency: + return (2,2) + else: + return (1,1) + + +def count_blocks(A,blocksize): + """For a given blocksize=(r,c) count the number of occupied + blocks in a sparse matrix A + """ + r,c = blocksize + if r < 1 or c < 1: + raise ValueError('r and c must be positive') + + if issparse(A): + if A.format == "csr": + M,N = A.shape + return csr_count_blocks(M,N,r,c,A.indptr,A.indices) + elif A.format == "csc": + return count_blocks(A.T,(c,r)) + return count_blocks(csr_array(A),blocksize) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_sputils.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_sputils.py new file mode 100644 index 0000000000000000000000000000000000000000..fa515606006d5084799cd6ac8578e1f88ed51bb9 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/_sputils.py @@ -0,0 +1,451 @@ +""" Utility functions for sparse matrix module +""" + +import sys +from typing import Any, Literal, Optional, Union +import operator +import numpy as np +from math import prod +import scipy.sparse as sp +from scipy._lib._util import np_long, np_ulong + + +__all__ = ['upcast', 'getdtype', 'getdata', 'isscalarlike', 'isintlike', + 'isshape', 'issequence', 'isdense', 'ismatrix', 'get_sum_dtype'] + +supported_dtypes = [np.bool_, np.byte, np.ubyte, np.short, np.ushort, np.intc, + np.uintc, np_long, np_ulong, np.longlong, np.ulonglong, + np.float32, np.float64, np.longdouble, + np.complex64, np.complex128, np.clongdouble] + +_upcast_memo = {} + + +def upcast(*args): + """Returns the nearest supported sparse dtype for the + combination of one or more types. + + upcast(t0, t1, ..., tn) -> T where T is a supported dtype + + Examples + -------- + >>> from scipy.sparse._sputils import upcast + >>> upcast('int32') + + >>> upcast('bool') + + >>> upcast('int32','float32') + + >>> upcast('bool',complex,float) + + + """ + + t = _upcast_memo.get(hash(args)) + if t is not None: + return t + + upcast = np.result_type(*args) + + for t in supported_dtypes: + if np.can_cast(upcast, t): + _upcast_memo[hash(args)] = t + return t + + raise TypeError(f'no supported conversion for types: {args!r}') + + +def upcast_char(*args): + """Same as `upcast` but taking dtype.char as input (faster).""" + t = _upcast_memo.get(args) + if t is not None: + return t + t = upcast(*map(np.dtype, args)) + _upcast_memo[args] = t + return t + + +def upcast_scalar(dtype, scalar): + """Determine data type for binary operation between an array of + type `dtype` and a scalar. + """ + return (np.array([0], dtype=dtype) * scalar).dtype + + +def downcast_intp_index(arr): + """ + Down-cast index array to np.intp dtype if it is of a larger dtype. + + Raise an error if the array contains a value that is too large for + intp. + """ + if arr.dtype.itemsize > np.dtype(np.intp).itemsize: + if arr.size == 0: + return arr.astype(np.intp) + maxval = arr.max() + minval = arr.min() + if maxval > np.iinfo(np.intp).max or minval < np.iinfo(np.intp).min: + raise ValueError("Cannot deal with arrays with indices larger " + "than the machine maximum address size " + "(e.g. 64-bit indices on 32-bit machine).") + return arr.astype(np.intp) + return arr + + +def to_native(A): + """ + Ensure that the data type of the NumPy array `A` has native byte order. + + `A` must be a NumPy array. If the data type of `A` does not have native + byte order, a copy of `A` with a native byte order is returned. Otherwise + `A` is returned. + """ + dt = A.dtype + if dt.isnative: + # Don't call `asarray()` if A is already native, to avoid unnecessarily + # creating a view of the input array. + return A + return np.asarray(A, dtype=dt.newbyteorder('native')) + + +def getdtype(dtype, a=None, default=None): + """Function used to simplify argument processing. If 'dtype' is not + specified (is None), returns a.dtype; otherwise returns a np.dtype + object created from the specified dtype argument. If 'dtype' and 'a' + are both None, construct a data type out of the 'default' parameter. + Furthermore, 'dtype' must be in 'allowed' set. + """ + # TODO is this really what we want? + if dtype is None: + try: + newdtype = a.dtype + except AttributeError as e: + if default is not None: + newdtype = np.dtype(default) + else: + raise TypeError("could not interpret data type") from e + else: + newdtype = np.dtype(dtype) + if newdtype == np.object_: + raise ValueError( + "object dtype is not supported by sparse matrices" + ) + + return newdtype + + +def getdata(obj, dtype=None, copy=False) -> np.ndarray: + """ + This is a wrapper of `np.array(obj, dtype=dtype, copy=copy)` + that will generate a warning if the result is an object array. + """ + data = np.array(obj, dtype=dtype, copy=copy) + # Defer to getdtype for checking that the dtype is OK. + # This is called for the validation only; we don't need the return value. + getdtype(data.dtype) + return data + + +def get_index_dtype(arrays=(), maxval=None, check_contents=False): + """ + Based on input (integer) arrays `a`, determine a suitable index data + type that can hold the data in the arrays. + + Parameters + ---------- + arrays : tuple of array_like + Input arrays whose types/contents to check + maxval : float, optional + Maximum value needed + check_contents : bool, optional + Whether to check the values in the arrays and not just their types. + Default: False (check only the types) + + Returns + ------- + dtype : dtype + Suitable index data type (int32 or int64) + + """ + + int32min = np.int32(np.iinfo(np.int32).min) + int32max = np.int32(np.iinfo(np.int32).max) + + # not using intc directly due to misinteractions with pythran + dtype = np.int32 if np.intc().itemsize == 4 else np.int64 + if maxval is not None: + maxval = np.int64(maxval) + if maxval > int32max: + dtype = np.int64 + + if isinstance(arrays, np.ndarray): + arrays = (arrays,) + + for arr in arrays: + arr = np.asarray(arr) + if not np.can_cast(arr.dtype, np.int32): + if check_contents: + if arr.size == 0: + # a bigger type not needed + continue + elif np.issubdtype(arr.dtype, np.integer): + maxval = arr.max() + minval = arr.min() + if minval >= int32min and maxval <= int32max: + # a bigger type not needed + continue + + dtype = np.int64 + break + + return dtype + + +def get_sum_dtype(dtype: np.dtype) -> np.dtype: + """Mimic numpy's casting for np.sum""" + if dtype.kind == 'u' and np.can_cast(dtype, np.uint): + return np.uint + if np.can_cast(dtype, np.int_): + return np.int_ + return dtype + + +def isscalarlike(x) -> bool: + """Is x either a scalar, an array scalar, or a 0-dim array?""" + return np.isscalar(x) or (isdense(x) and x.ndim == 0) + + +def isintlike(x) -> bool: + """Is x appropriate as an index into a sparse matrix? Returns True + if it can be cast safely to a machine int. + """ + # Fast-path check to eliminate non-scalar values. operator.index would + # catch this case too, but the exception catching is slow. + if np.ndim(x) != 0: + return False + try: + operator.index(x) + except (TypeError, ValueError): + try: + loose_int = bool(int(x) == x) + except (TypeError, ValueError): + return False + if loose_int: + msg = "Inexact indices into sparse matrices are not allowed" + raise ValueError(msg) + return loose_int + return True + + +def isshape(x, nonneg=False, *, allow_1d=False) -> bool: + """Is x a valid tuple of dimensions? + + If nonneg, also checks that the dimensions are non-negative. + If allow_1d, shapes of length 1 or 2 are allowed. + """ + ndim = len(x) + if ndim != 2 and not (allow_1d and ndim == 1): + return False + for d in x: + if not isintlike(d): + return False + if nonneg and d < 0: + return False + return True + + +def issequence(t) -> bool: + return ((isinstance(t, (list, tuple)) and + (len(t) == 0 or np.isscalar(t[0]))) or + (isinstance(t, np.ndarray) and (t.ndim == 1))) + + +def ismatrix(t) -> bool: + return ((isinstance(t, (list, tuple)) and + len(t) > 0 and issequence(t[0])) or + (isinstance(t, np.ndarray) and t.ndim == 2)) + + +def isdense(x) -> bool: + return isinstance(x, np.ndarray) + + +def validateaxis(axis) -> None: + if axis is None: + return + axis_type = type(axis) + + # In NumPy, you can pass in tuples for 'axis', but they are + # not very useful for sparse matrices given their limited + # dimensions, so let's make it explicit that they are not + # allowed to be passed in + if axis_type == tuple: + raise TypeError("Tuples are not accepted for the 'axis' parameter. " + "Please pass in one of the following: " + "{-2, -1, 0, 1, None}.") + + # If not a tuple, check that the provided axis is actually + # an integer and raise a TypeError similar to NumPy's + if not np.issubdtype(np.dtype(axis_type), np.integer): + raise TypeError(f"axis must be an integer, not {axis_type.__name__}") + + if not (-2 <= axis <= 1): + raise ValueError("axis out of range") + + +def check_shape(args, current_shape=None, *, allow_1d=False) -> tuple[int, ...]: + """Imitate numpy.matrix handling of shape arguments + + Parameters + ---------- + args : array_like + Data structures providing information about the shape of the sparse array. + current_shape : tuple, optional + The current shape of the sparse array or matrix. + If None (default), the current shape will be inferred from args. + allow_1d : bool, optional + If True, then 1-D or 2-D arrays are accepted. + If False (default), then only 2-D arrays are accepted and an error is + raised otherwise. + + Returns + ------- + new_shape: tuple + The new shape after validation. + """ + if len(args) == 0: + raise TypeError("function missing 1 required positional argument: " + "'shape'") + if len(args) == 1: + try: + shape_iter = iter(args[0]) + except TypeError: + new_shape = (operator.index(args[0]), ) + else: + new_shape = tuple(operator.index(arg) for arg in shape_iter) + else: + new_shape = tuple(operator.index(arg) for arg in args) + + if current_shape is None: + if allow_1d: + if len(new_shape) not in (1, 2): + raise ValueError('shape must be a 1- or 2-tuple of positive ' + 'integers') + elif len(new_shape) != 2: + raise ValueError('shape must be a 2-tuple of positive integers') + if any(d < 0 for d in new_shape): + raise ValueError("'shape' elements cannot be negative") + else: + # Check the current size only if needed + current_size = prod(current_shape) + + # Check for negatives + negative_indexes = [i for i, x in enumerate(new_shape) if x < 0] + if not negative_indexes: + new_size = prod(new_shape) + if new_size != current_size: + raise ValueError('cannot reshape array of size {} into shape {}' + .format(current_size, new_shape)) + elif len(negative_indexes) == 1: + skip = negative_indexes[0] + specified = prod(new_shape[:skip] + new_shape[skip+1:]) + unspecified, remainder = divmod(current_size, specified) + if remainder != 0: + err_shape = tuple('newshape' if x < 0 else x for x in new_shape) + raise ValueError('cannot reshape array of size {} into shape {}' + ''.format(current_size, err_shape)) + new_shape = new_shape[:skip] + (unspecified,) + new_shape[skip+1:] + else: + raise ValueError('can only specify one unknown dimension') + + if len(new_shape) != 2 and not (allow_1d and len(new_shape) == 1): + raise ValueError('matrix shape must be two-dimensional') + + return new_shape + + +def check_reshape_kwargs(kwargs): + """Unpack keyword arguments for reshape function. + + This is useful because keyword arguments after star arguments are not + allowed in Python 2, but star keyword arguments are. This function unpacks + 'order' and 'copy' from the star keyword arguments (with defaults) and + throws an error for any remaining. + """ + + order = kwargs.pop('order', 'C') + copy = kwargs.pop('copy', False) + if kwargs: # Some unused kwargs remain + raise TypeError('reshape() got unexpected keywords arguments: {}' + .format(', '.join(kwargs.keys()))) + return order, copy + + +def is_pydata_spmatrix(m) -> bool: + """ + Check whether object is pydata/sparse matrix, avoiding importing the module. + """ + base_cls = getattr(sys.modules.get('sparse'), 'SparseArray', None) + return base_cls is not None and isinstance(m, base_cls) + + +def convert_pydata_sparse_to_scipy( + arg: Any, target_format: Optional[Literal["csc", "csr"]] = None +) -> Union[Any, "sp.spmatrix"]: + """ + Convert a pydata/sparse array to scipy sparse matrix, + pass through anything else. + """ + if is_pydata_spmatrix(arg): + arg = arg.to_scipy_sparse() + if target_format is not None: + arg = arg.asformat(target_format) + elif arg.format not in ("csc", "csr"): + arg = arg.tocsc() + return arg + + +############################################################################### +# Wrappers for NumPy types that are deprecated + +# Numpy versions of these functions raise deprecation warnings, the +# ones below do not. + +def matrix(*args, **kwargs): + return np.array(*args, **kwargs).view(np.matrix) + + +def asmatrix(data, dtype=None): + if isinstance(data, np.matrix) and (dtype is None or data.dtype == dtype): + return data + return np.asarray(data, dtype=dtype).view(np.matrix) + +############################################################################### + + +def _todata(s) -> np.ndarray: + """Access nonzero values, possibly after summing duplicates. + + Parameters + ---------- + s : sparse array + Input sparse array. + + Returns + ------- + data: ndarray + Nonzero values of the array, with shape (s.nnz,) + + """ + if isinstance(s, sp._data._data_matrix): + return s._deduped_data() + + if isinstance(s, sp.dok_array): + return np.fromiter(s.values(), dtype=s.dtype, count=s.nnz) + + if isinstance(s, sp.lil_array): + data = np.empty(s.nnz, dtype=s.dtype) + sp._csparsetools.lil_flatten_to_array(s.data, data) + return data + + return s.tocoo()._deduped_data() diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/base.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/base.py new file mode 100644 index 0000000000000000000000000000000000000000..d0a427e4570e07cc71e9e45bf98c7cf61798125b --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/base.py @@ -0,0 +1,33 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'MAXPRINT', + 'SparseEfficiencyWarning', + 'SparseFormatWarning', + 'SparseWarning', + 'asmatrix', + 'check_reshape_kwargs', + 'check_shape', + 'get_sum_dtype', + 'isdense', + 'isscalarlike', + 'issparse', + 'isspmatrix', + 'spmatrix', + 'validateaxis', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="base", + private_modules=["_base"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/bsr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/bsr.py new file mode 100644 index 0000000000000000000000000000000000000000..c686301a78fc3e2221600eb06035a5cb12898cdb --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/bsr.py @@ -0,0 +1,36 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'bsr_matmat', + 'bsr_matrix', + 'bsr_matvec', + 'bsr_matvecs', + 'bsr_sort_indices', + 'bsr_tocsr', + 'bsr_transpose', + 'check_shape', + 'csr_matmat_maxnnz', + 'getdata', + 'getdtype', + 'isshape', + 'isspmatrix_bsr', + 'spmatrix', + 'to_native', + 'upcast', + 'warn', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="bsr", + private_modules=["_bsr"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/compressed.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/compressed.py new file mode 100644 index 0000000000000000000000000000000000000000..e6dc8a73e5ab527cfe0b73d558dae25047cfb98b --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/compressed.py @@ -0,0 +1,43 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'IndexMixin', + 'SparseEfficiencyWarning', + 'check_shape', + 'csr_column_index1', + 'csr_column_index2', + 'csr_row_index', + 'csr_row_slice', + 'csr_sample_offsets', + 'csr_sample_values', + 'csr_todense', + 'downcast_intp_index', + 'get_csr_submatrix', + 'get_sum_dtype', + 'getdtype', + 'is_pydata_spmatrix', + 'isdense', + 'isintlike', + 'isscalarlike', + 'isshape', + 'operator', + 'to_native', + 'upcast', + 'upcast_char', + 'warn', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="compressed", + private_modules=["_compressed"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/construct.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/construct.py new file mode 100644 index 0000000000000000000000000000000000000000..c3d34d2fd38887877980727bceaaa215129bf283 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/construct.py @@ -0,0 +1,44 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'block_diag', + 'bmat', + 'bsr_matrix', + 'check_random_state', + 'coo_matrix', + 'csc_matrix', + 'csr_hstack', + 'csr_matrix', + 'dia_matrix', + 'diags', + 'eye', + 'get_index_dtype', + 'hstack', + 'identity', + 'isscalarlike', + 'issparse', + 'kron', + 'kronsum', + 'numbers', + 'rand', + 'random', + 'rng_integers', + 'spdiags', + 'upcast', + 'vstack', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="construct", + private_modules=["_construct"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/coo.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/coo.py new file mode 100644 index 0000000000000000000000000000000000000000..bda2da3d09a676ab79739331a21ba26102bb90ae --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/coo.py @@ -0,0 +1,37 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'SparseEfficiencyWarning', + 'check_reshape_kwargs', + 'check_shape', + 'coo_matrix', + 'coo_matvec', + 'coo_tocsr', + 'coo_todense', + 'downcast_intp_index', + 'getdata', + 'getdtype', + 'isshape', + 'isspmatrix_coo', + 'operator', + 'spmatrix', + 'to_native', + 'upcast', + 'upcast_char', + 'warn', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="coo", + private_modules=["_coo"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csc.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csc.py new file mode 100644 index 0000000000000000000000000000000000000000..d140b841e0724155f8602a4215836e2c8a7fad72 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csc.py @@ -0,0 +1,25 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'csc_matrix', + 'csc_tocsr', + 'expandptr', + 'isspmatrix_csc', + 'spmatrix', + 'upcast', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="csc", + private_modules=["_csc"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2fcb5bc7206fa61c862b1ead6754dbaf541c5687 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__init__.py @@ -0,0 +1,210 @@ +r""" +Compressed sparse graph routines (:mod:`scipy.sparse.csgraph`) +============================================================== + +.. currentmodule:: scipy.sparse.csgraph + +Fast graph algorithms based on sparse matrix representations. + +Contents +-------- + +.. autosummary:: + :toctree: generated/ + + connected_components -- determine connected components of a graph + laplacian -- compute the laplacian of a graph + shortest_path -- compute the shortest path between points on a positive graph + dijkstra -- use Dijkstra's algorithm for shortest path + floyd_warshall -- use the Floyd-Warshall algorithm for shortest path + bellman_ford -- use the Bellman-Ford algorithm for shortest path + johnson -- use Johnson's algorithm for shortest path + yen -- use Yen's algorithm for K-shortest paths between to nodes. + breadth_first_order -- compute a breadth-first order of nodes + depth_first_order -- compute a depth-first order of nodes + breadth_first_tree -- construct the breadth-first tree from a given node + depth_first_tree -- construct a depth-first tree from a given node + minimum_spanning_tree -- construct the minimum spanning tree of a graph + reverse_cuthill_mckee -- compute permutation for reverse Cuthill-McKee ordering + maximum_flow -- solve the maximum flow problem for a graph + maximum_bipartite_matching -- compute a maximum matching of a bipartite graph + min_weight_full_bipartite_matching - compute a minimum weight full matching of a bipartite graph + structural_rank -- compute the structural rank of a graph + NegativeCycleError + +.. autosummary:: + :toctree: generated/ + + construct_dist_matrix + csgraph_from_dense + csgraph_from_masked + csgraph_masked_from_dense + csgraph_to_dense + csgraph_to_masked + reconstruct_path + +Graph Representations +--------------------- +This module uses graphs which are stored in a matrix format. A +graph with N nodes can be represented by an (N x N) adjacency matrix G. +If there is a connection from node i to node j, then G[i, j] = w, where +w is the weight of the connection. For nodes i and j which are +not connected, the value depends on the representation: + +- for dense array representations, non-edges are represented by + G[i, j] = 0, infinity, or NaN. + +- for dense masked representations (of type np.ma.MaskedArray), non-edges + are represented by masked values. This can be useful when graphs with + zero-weight edges are desired. + +- for sparse array representations, non-edges are represented by + non-entries in the matrix. This sort of sparse representation also + allows for edges with zero weights. + +As a concrete example, imagine that you would like to represent the following +undirected graph:: + + G + + (0) + / \ + 1 2 + / \ + (2) (1) + +This graph has three nodes, where node 0 and 1 are connected by an edge of +weight 2, and nodes 0 and 2 are connected by an edge of weight 1. +We can construct the dense, masked, and sparse representations as follows, +keeping in mind that an undirected graph is represented by a symmetric matrix:: + + >>> import numpy as np + >>> G_dense = np.array([[0, 2, 1], + ... [2, 0, 0], + ... [1, 0, 0]]) + >>> G_masked = np.ma.masked_values(G_dense, 0) + >>> from scipy.sparse import csr_matrix + >>> G_sparse = csr_matrix(G_dense) + +This becomes more difficult when zero edges are significant. For example, +consider the situation when we slightly modify the above graph:: + + G2 + + (0) + / \ + 0 2 + / \ + (2) (1) + +This is identical to the previous graph, except nodes 0 and 2 are connected +by an edge of zero weight. In this case, the dense representation above +leads to ambiguities: how can non-edges be represented if zero is a meaningful +value? In this case, either a masked or sparse representation must be used +to eliminate the ambiguity:: + + >>> import numpy as np + >>> G2_data = np.array([[np.inf, 2, 0 ], + ... [2, np.inf, np.inf], + ... [0, np.inf, np.inf]]) + >>> G2_masked = np.ma.masked_invalid(G2_data) + >>> from scipy.sparse.csgraph import csgraph_from_dense + >>> # G2_sparse = csr_matrix(G2_data) would give the wrong result + >>> G2_sparse = csgraph_from_dense(G2_data, null_value=np.inf) + >>> G2_sparse.data + array([ 2., 0., 2., 0.]) + +Here we have used a utility routine from the csgraph submodule in order to +convert the dense representation to a sparse representation which can be +understood by the algorithms in submodule. By viewing the data array, we +can see that the zero values are explicitly encoded in the graph. + +Directed vs. undirected +^^^^^^^^^^^^^^^^^^^^^^^ +Matrices may represent either directed or undirected graphs. This is +specified throughout the csgraph module by a boolean keyword. Graphs are +assumed to be directed by default. In a directed graph, traversal from node +i to node j can be accomplished over the edge G[i, j], but not the edge +G[j, i]. Consider the following dense graph:: + + >>> import numpy as np + >>> G_dense = np.array([[0, 1, 0], + ... [2, 0, 3], + ... [0, 4, 0]]) + +When ``directed=True`` we get the graph:: + + ---1--> ---3--> + (0) (1) (2) + <--2--- <--4--- + +In a non-directed graph, traversal from node i to node j can be +accomplished over either G[i, j] or G[j, i]. If both edges are not null, +and the two have unequal weights, then the smaller of the two is used. + +So for the same graph, when ``directed=False`` we get the graph:: + + (0)--1--(1)--3--(2) + +Note that a symmetric matrix will represent an undirected graph, regardless +of whether the 'directed' keyword is set to True or False. In this case, +using ``directed=True`` generally leads to more efficient computation. + +The routines in this module accept as input either scipy.sparse representations +(csr, csc, or lil format), masked representations, or dense representations +with non-edges indicated by zeros, infinities, and NaN entries. +""" # noqa: E501 + +__docformat__ = "restructuredtext en" + +__all__ = ['connected_components', + 'laplacian', + 'shortest_path', + 'floyd_warshall', + 'dijkstra', + 'bellman_ford', + 'johnson', + 'yen', + 'breadth_first_order', + 'depth_first_order', + 'breadth_first_tree', + 'depth_first_tree', + 'minimum_spanning_tree', + 'reverse_cuthill_mckee', + 'maximum_flow', + 'maximum_bipartite_matching', + 'min_weight_full_bipartite_matching', + 'structural_rank', + 'construct_dist_matrix', + 'reconstruct_path', + 'csgraph_masked_from_dense', + 'csgraph_from_dense', + 'csgraph_from_masked', + 'csgraph_to_dense', + 'csgraph_to_masked', + 'NegativeCycleError'] + +from ._laplacian import laplacian +from ._shortest_path import ( + shortest_path, floyd_warshall, dijkstra, bellman_ford, johnson, yen, + NegativeCycleError +) +from ._traversal import ( + breadth_first_order, depth_first_order, breadth_first_tree, + depth_first_tree, connected_components +) +from ._min_spanning_tree import minimum_spanning_tree +from ._flow import maximum_flow +from ._matching import ( + maximum_bipartite_matching, min_weight_full_bipartite_matching +) +from ._reordering import reverse_cuthill_mckee, structural_rank +from ._tools import ( + construct_dist_matrix, reconstruct_path, csgraph_from_dense, + csgraph_to_dense, csgraph_masked_from_dense, csgraph_from_masked, + csgraph_to_masked +) + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b95c75747583b372503714bf016da81d0330c5f2 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/_laplacian.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/_laplacian.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbf87916de6fcd85b3cc5187677238b2c16feba2 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/_laplacian.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/_validation.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/_validation.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf8663f0797820bd47e017b7e4df678abcc0cb13 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/__pycache__/_validation.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/_laplacian.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/_laplacian.py new file mode 100644 index 0000000000000000000000000000000000000000..8a50cd491069e94b08bef8191a44e4439854e2a2 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/_laplacian.py @@ -0,0 +1,562 @@ +""" +Laplacian of a compressed-sparse graph +""" + +import numpy as np +from scipy.sparse import issparse +from scipy.sparse.linalg import LinearOperator +from scipy.sparse._sputils import convert_pydata_sparse_to_scipy, is_pydata_spmatrix + + +############################################################################### +# Graph laplacian +def laplacian( + csgraph, + normed=False, + return_diag=False, + use_out_degree=False, + *, + copy=True, + form="array", + dtype=None, + symmetrized=False, +): + """ + Return the Laplacian of a directed graph. + + Parameters + ---------- + csgraph : array_like or sparse matrix, 2 dimensions + compressed-sparse graph, with shape (N, N). + normed : bool, optional + If True, then compute symmetrically normalized Laplacian. + Default: False. + return_diag : bool, optional + If True, then also return an array related to vertex degrees. + Default: False. + use_out_degree : bool, optional + If True, then use out-degree instead of in-degree. + This distinction matters only if the graph is asymmetric. + Default: False. + copy: bool, optional + If False, then change `csgraph` in place if possible, + avoiding doubling the memory use. + Default: True, for backward compatibility. + form: 'array', or 'function', or 'lo' + Determines the format of the output Laplacian: + + * 'array' is a numpy array; + * 'function' is a pointer to evaluating the Laplacian-vector + or Laplacian-matrix product; + * 'lo' results in the format of the `LinearOperator`. + + Choosing 'function' or 'lo' always avoids doubling + the memory use, ignoring `copy` value. + Default: 'array', for backward compatibility. + dtype: None or one of numeric numpy dtypes, optional + The dtype of the output. If ``dtype=None``, the dtype of the + output matches the dtype of the input csgraph, except for + the case ``normed=True`` and integer-like csgraph, where + the output dtype is 'float' allowing accurate normalization, + but dramatically increasing the memory use. + Default: None, for backward compatibility. + symmetrized: bool, optional + If True, then the output Laplacian is symmetric/Hermitian. + The symmetrization is done by ``csgraph + csgraph.T.conj`` + without dividing by 2 to preserve integer dtypes if possible + prior to the construction of the Laplacian. + The symmetrization will increase the memory footprint of + sparse matrices unless the sparsity pattern is symmetric or + `form` is 'function' or 'lo'. + Default: False, for backward compatibility. + + Returns + ------- + lap : ndarray, or sparse matrix, or `LinearOperator` + The N x N Laplacian of csgraph. It will be a NumPy array (dense) + if the input was dense, or a sparse matrix otherwise, or + the format of a function or `LinearOperator` if + `form` equals 'function' or 'lo', respectively. + diag : ndarray, optional + The length-N main diagonal of the Laplacian matrix. + For the normalized Laplacian, this is the array of square roots + of vertex degrees or 1 if the degree is zero. + + Notes + ----- + The Laplacian matrix of a graph is sometimes referred to as the + "Kirchhoff matrix" or just the "Laplacian", and is useful in many + parts of spectral graph theory. + In particular, the eigen-decomposition of the Laplacian can give + insight into many properties of the graph, e.g., + is commonly used for spectral data embedding and clustering. + + The constructed Laplacian doubles the memory use if ``copy=True`` and + ``form="array"`` which is the default. + Choosing ``copy=False`` has no effect unless ``form="array"`` + or the matrix is sparse in the ``coo`` format, or dense array, except + for the integer input with ``normed=True`` that forces the float output. + + Sparse input is reformatted into ``coo`` if ``form="array"``, + which is the default. + + If the input adjacency matrix is not symmetric, the Laplacian is + also non-symmetric unless ``symmetrized=True`` is used. + + Diagonal entries of the input adjacency matrix are ignored and + replaced with zeros for the purpose of normalization where ``normed=True``. + The normalization uses the inverse square roots of row-sums of the input + adjacency matrix, and thus may fail if the row-sums contain + negative or complex with a non-zero imaginary part values. + + The normalization is symmetric, making the normalized Laplacian also + symmetric if the input csgraph was symmetric. + + References + ---------- + .. [1] Laplacian matrix. https://en.wikipedia.org/wiki/Laplacian_matrix + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csgraph + + Our first illustration is the symmetric graph + + >>> G = np.arange(4) * np.arange(4)[:, np.newaxis] + >>> G + array([[0, 0, 0, 0], + [0, 1, 2, 3], + [0, 2, 4, 6], + [0, 3, 6, 9]]) + + and its symmetric Laplacian matrix + + >>> csgraph.laplacian(G) + array([[ 0, 0, 0, 0], + [ 0, 5, -2, -3], + [ 0, -2, 8, -6], + [ 0, -3, -6, 9]]) + + The non-symmetric graph + + >>> G = np.arange(9).reshape(3, 3) + >>> G + array([[0, 1, 2], + [3, 4, 5], + [6, 7, 8]]) + + has different row- and column sums, resulting in two varieties + of the Laplacian matrix, using an in-degree, which is the default + + >>> L_in_degree = csgraph.laplacian(G) + >>> L_in_degree + array([[ 9, -1, -2], + [-3, 8, -5], + [-6, -7, 7]]) + + or alternatively an out-degree + + >>> L_out_degree = csgraph.laplacian(G, use_out_degree=True) + >>> L_out_degree + array([[ 3, -1, -2], + [-3, 8, -5], + [-6, -7, 13]]) + + Constructing a symmetric Laplacian matrix, one can add the two as + + >>> L_in_degree + L_out_degree.T + array([[ 12, -4, -8], + [ -4, 16, -12], + [ -8, -12, 20]]) + + or use the ``symmetrized=True`` option + + >>> csgraph.laplacian(G, symmetrized=True) + array([[ 12, -4, -8], + [ -4, 16, -12], + [ -8, -12, 20]]) + + that is equivalent to symmetrizing the original graph + + >>> csgraph.laplacian(G + G.T) + array([[ 12, -4, -8], + [ -4, 16, -12], + [ -8, -12, 20]]) + + The goal of normalization is to make the non-zero diagonal entries + of the Laplacian matrix to be all unit, also scaling off-diagonal + entries correspondingly. The normalization can be done manually, e.g., + + >>> G = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]) + >>> L, d = csgraph.laplacian(G, return_diag=True) + >>> L + array([[ 2, -1, -1], + [-1, 2, -1], + [-1, -1, 2]]) + >>> d + array([2, 2, 2]) + >>> scaling = np.sqrt(d) + >>> scaling + array([1.41421356, 1.41421356, 1.41421356]) + >>> (1/scaling)*L*(1/scaling) + array([[ 1. , -0.5, -0.5], + [-0.5, 1. , -0.5], + [-0.5, -0.5, 1. ]]) + + Or using ``normed=True`` option + + >>> L, d = csgraph.laplacian(G, return_diag=True, normed=True) + >>> L + array([[ 1. , -0.5, -0.5], + [-0.5, 1. , -0.5], + [-0.5, -0.5, 1. ]]) + + which now instead of the diagonal returns the scaling coefficients + + >>> d + array([1.41421356, 1.41421356, 1.41421356]) + + Zero scaling coefficients are substituted with 1s, where scaling + has thus no effect, e.g., + + >>> G = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 0]]) + >>> G + array([[0, 0, 0], + [0, 0, 1], + [0, 1, 0]]) + >>> L, d = csgraph.laplacian(G, return_diag=True, normed=True) + >>> L + array([[ 0., -0., -0.], + [-0., 1., -1.], + [-0., -1., 1.]]) + >>> d + array([1., 1., 1.]) + + Only the symmetric normalization is implemented, resulting + in a symmetric Laplacian matrix if and only if its graph is symmetric + and has all non-negative degrees, like in the examples above. + + The output Laplacian matrix is by default a dense array or a sparse matrix + inferring its shape, format, and dtype from the input graph matrix: + + >>> G = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]).astype(np.float32) + >>> G + array([[0., 1., 1.], + [1., 0., 1.], + [1., 1., 0.]], dtype=float32) + >>> csgraph.laplacian(G) + array([[ 2., -1., -1.], + [-1., 2., -1.], + [-1., -1., 2.]], dtype=float32) + + but can alternatively be generated matrix-free as a LinearOperator: + + >>> L = csgraph.laplacian(G, form="lo") + >>> L + <3x3 _CustomLinearOperator with dtype=float32> + >>> L(np.eye(3)) + array([[ 2., -1., -1.], + [-1., 2., -1.], + [-1., -1., 2.]]) + + or as a lambda-function: + + >>> L = csgraph.laplacian(G, form="function") + >>> L + . at 0x0000012AE6F5A598> + >>> L(np.eye(3)) + array([[ 2., -1., -1.], + [-1., 2., -1.], + [-1., -1., 2.]]) + + The Laplacian matrix is used for + spectral data clustering and embedding + as well as for spectral graph partitioning. + Our final example illustrates the latter + for a noisy directed linear graph. + + >>> from scipy.sparse import diags, random + >>> from scipy.sparse.linalg import lobpcg + + Create a directed linear graph with ``N=35`` vertices + using a sparse adjacency matrix ``G``: + + >>> N = 35 + >>> G = diags(np.ones(N-1), 1, format="csr") + + Fix a random seed ``rng`` and add a random sparse noise to the graph ``G``: + + >>> rng = np.random.default_rng() + >>> G += 1e-2 * random(N, N, density=0.1, random_state=rng) + + Set initial approximations for eigenvectors: + + >>> X = rng.random((N, 2)) + + The constant vector of ones is always a trivial eigenvector + of the non-normalized Laplacian to be filtered out: + + >>> Y = np.ones((N, 1)) + + Alternating (1) the sign of the graph weights allows determining + labels for spectral max- and min- cuts in a single loop. + Since the graph is undirected, the option ``symmetrized=True`` + must be used in the construction of the Laplacian. + The option ``normed=True`` cannot be used in (2) for the negative weights + here as the symmetric normalization evaluates square roots. + The option ``form="lo"`` in (2) is matrix-free, i.e., guarantees + a fixed memory footprint and read-only access to the graph. + Calling the eigenvalue solver ``lobpcg`` (3) computes the Fiedler vector + that determines the labels as the signs of its components in (5). + Since the sign in an eigenvector is not deterministic and can flip, + we fix the sign of the first component to be always +1 in (4). + + >>> for cut in ["max", "min"]: + ... G = -G # 1. + ... L = csgraph.laplacian(G, symmetrized=True, form="lo") # 2. + ... _, eves = lobpcg(L, X, Y=Y, largest=False, tol=1e-2) # 3. + ... eves *= np.sign(eves[0, 0]) # 4. + ... print(cut + "-cut labels:\\n", 1 * (eves[:, 0]>0)) # 5. + max-cut labels: + [1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1] + min-cut labels: + [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] + + As anticipated for a (slightly noisy) linear graph, + the max-cut strips all the edges of the graph coloring all + odd vertices into one color and all even vertices into another one, + while the balanced min-cut partitions the graph + in the middle by deleting a single edge. + Both determined partitions are optimal. + """ + is_pydata_sparse = is_pydata_spmatrix(csgraph) + if is_pydata_sparse: + pydata_sparse_cls = csgraph.__class__ + csgraph = convert_pydata_sparse_to_scipy(csgraph) + if csgraph.ndim != 2 or csgraph.shape[0] != csgraph.shape[1]: + raise ValueError('csgraph must be a square matrix or array') + + if normed and ( + np.issubdtype(csgraph.dtype, np.signedinteger) + or np.issubdtype(csgraph.dtype, np.uint) + ): + csgraph = csgraph.astype(np.float64) + + if form == "array": + create_lap = ( + _laplacian_sparse if issparse(csgraph) else _laplacian_dense + ) + else: + create_lap = ( + _laplacian_sparse_flo + if issparse(csgraph) + else _laplacian_dense_flo + ) + + degree_axis = 1 if use_out_degree else 0 + + lap, d = create_lap( + csgraph, + normed=normed, + axis=degree_axis, + copy=copy, + form=form, + dtype=dtype, + symmetrized=symmetrized, + ) + if is_pydata_sparse: + lap = pydata_sparse_cls.from_scipy_sparse(lap) + if return_diag: + return lap, d + return lap + + +def _setdiag_dense(m, d): + step = len(d) + 1 + m.flat[::step] = d + + +def _laplace(m, d): + return lambda v: v * d[:, np.newaxis] - m @ v + + +def _laplace_normed(m, d, nd): + laplace = _laplace(m, d) + return lambda v: nd[:, np.newaxis] * laplace(v * nd[:, np.newaxis]) + + +def _laplace_sym(m, d): + return ( + lambda v: v * d[:, np.newaxis] + - m @ v + - np.transpose(np.conjugate(np.transpose(np.conjugate(v)) @ m)) + ) + + +def _laplace_normed_sym(m, d, nd): + laplace_sym = _laplace_sym(m, d) + return lambda v: nd[:, np.newaxis] * laplace_sym(v * nd[:, np.newaxis]) + + +def _linearoperator(mv, shape, dtype): + return LinearOperator(matvec=mv, matmat=mv, shape=shape, dtype=dtype) + + +def _laplacian_sparse_flo(graph, normed, axis, copy, form, dtype, symmetrized): + # The keyword argument `copy` is unused and has no effect here. + del copy + + if dtype is None: + dtype = graph.dtype + + graph_sum = np.asarray(graph.sum(axis=axis)).ravel() + graph_diagonal = graph.diagonal() + diag = graph_sum - graph_diagonal + if symmetrized: + graph_sum += np.asarray(graph.sum(axis=1 - axis)).ravel() + diag = graph_sum - graph_diagonal - graph_diagonal + + if normed: + isolated_node_mask = diag == 0 + w = np.where(isolated_node_mask, 1, np.sqrt(diag)) + if symmetrized: + md = _laplace_normed_sym(graph, graph_sum, 1.0 / w) + else: + md = _laplace_normed(graph, graph_sum, 1.0 / w) + if form == "function": + return md, w.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, w.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + else: + if symmetrized: + md = _laplace_sym(graph, graph_sum) + else: + md = _laplace(graph, graph_sum) + if form == "function": + return md, diag.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, diag.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + + +def _laplacian_sparse(graph, normed, axis, copy, form, dtype, symmetrized): + # The keyword argument `form` is unused and has no effect here. + del form + + if dtype is None: + dtype = graph.dtype + + needs_copy = False + if graph.format in ('lil', 'dok'): + m = graph.tocoo() + else: + m = graph + if copy: + needs_copy = True + + if symmetrized: + m += m.T.conj() + + w = np.asarray(m.sum(axis=axis)).ravel() - m.diagonal() + if normed: + m = m.tocoo(copy=needs_copy) + isolated_node_mask = (w == 0) + w = np.where(isolated_node_mask, 1, np.sqrt(w)) + m.data /= w[m.row] + m.data /= w[m.col] + m.data *= -1 + m.setdiag(1 - isolated_node_mask) + else: + if m.format == 'dia': + m = m.copy() + else: + m = m.tocoo(copy=needs_copy) + m.data *= -1 + m.setdiag(w) + + return m.astype(dtype, copy=False), w.astype(dtype) + + +def _laplacian_dense_flo(graph, normed, axis, copy, form, dtype, symmetrized): + + if copy: + m = np.array(graph) + else: + m = np.asarray(graph) + + if dtype is None: + dtype = m.dtype + + graph_sum = m.sum(axis=axis) + graph_diagonal = m.diagonal() + diag = graph_sum - graph_diagonal + if symmetrized: + graph_sum += m.sum(axis=1 - axis) + diag = graph_sum - graph_diagonal - graph_diagonal + + if normed: + isolated_node_mask = diag == 0 + w = np.where(isolated_node_mask, 1, np.sqrt(diag)) + if symmetrized: + md = _laplace_normed_sym(m, graph_sum, 1.0 / w) + else: + md = _laplace_normed(m, graph_sum, 1.0 / w) + if form == "function": + return md, w.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, w.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + else: + if symmetrized: + md = _laplace_sym(m, graph_sum) + else: + md = _laplace(m, graph_sum) + if form == "function": + return md, diag.astype(dtype, copy=False) + elif form == "lo": + m = _linearoperator(md, shape=graph.shape, dtype=dtype) + return m, diag.astype(dtype, copy=False) + else: + raise ValueError(f"Invalid form: {form!r}") + + +def _laplacian_dense(graph, normed, axis, copy, form, dtype, symmetrized): + + if form != "array": + raise ValueError(f'{form!r} must be "array"') + + if dtype is None: + dtype = graph.dtype + + if copy: + m = np.array(graph) + else: + m = np.asarray(graph) + + if dtype is None: + dtype = m.dtype + + if symmetrized: + m += m.T.conj() + np.fill_diagonal(m, 0) + w = m.sum(axis=axis) + if normed: + isolated_node_mask = (w == 0) + w = np.where(isolated_node_mask, 1, np.sqrt(w)) + m /= w + m /= w[:, np.newaxis] + m *= -1 + _setdiag_dense(m, 1 - isolated_node_mask) + else: + m *= -1 + _setdiag_dense(m, w) + + return m.astype(dtype, copy=False), w.astype(dtype, copy=False) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/_validation.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/_validation.py new file mode 100644 index 0000000000000000000000000000000000000000..e160cf5e7b0e9fd94772e5e150e32c8b0d0be7c6 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/_validation.py @@ -0,0 +1,61 @@ +import numpy as np +from scipy.sparse import csr_matrix, issparse +from scipy.sparse._sputils import convert_pydata_sparse_to_scipy +from scipy.sparse.csgraph._tools import ( + csgraph_to_dense, csgraph_from_dense, + csgraph_masked_from_dense, csgraph_from_masked +) + +DTYPE = np.float64 + + +def validate_graph(csgraph, directed, dtype=DTYPE, + csr_output=True, dense_output=True, + copy_if_dense=False, copy_if_sparse=False, + null_value_in=0, null_value_out=np.inf, + infinity_null=True, nan_null=True): + """Routine for validation and conversion of csgraph inputs""" + if not (csr_output or dense_output): + raise ValueError("Internal: dense or csr output must be true") + + csgraph = convert_pydata_sparse_to_scipy(csgraph) + + # if undirected and csc storage, then transposing in-place + # is quicker than later converting to csr. + if (not directed) and issparse(csgraph) and csgraph.format == "csc": + csgraph = csgraph.T + + if issparse(csgraph): + if csr_output: + csgraph = csr_matrix(csgraph, dtype=DTYPE, copy=copy_if_sparse) + else: + csgraph = csgraph_to_dense(csgraph, null_value=null_value_out) + elif np.ma.isMaskedArray(csgraph): + if dense_output: + mask = csgraph.mask + csgraph = np.array(csgraph.data, dtype=DTYPE, copy=copy_if_dense) + csgraph[mask] = null_value_out + else: + csgraph = csgraph_from_masked(csgraph) + else: + if dense_output: + csgraph = csgraph_masked_from_dense(csgraph, + copy=copy_if_dense, + null_value=null_value_in, + nan_null=nan_null, + infinity_null=infinity_null) + mask = csgraph.mask + csgraph = np.asarray(csgraph.data, dtype=DTYPE) + csgraph[mask] = null_value_out + else: + csgraph = csgraph_from_dense(csgraph, null_value=null_value_in, + infinity_null=infinity_null, + nan_null=nan_null) + + if csgraph.ndim != 2: + raise ValueError("compressed-sparse graph must be 2-D") + + if csgraph.shape[0] != csgraph.shape[1]: + raise ValueError("compressed-sparse graph must be shape (N, N)") + + return csgraph diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00cced219fefad2ad73fd770e40ef03d4062b4c9 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_connected_components.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_connected_components.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be4d2a436117d63bbd6a7b102ec2e9990c5284a2 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_connected_components.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_conversions.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_conversions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ad0ce639abaecb7b183d33a9634a33d6416984b Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_conversions.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_flow.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_flow.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a079f5eecef1f816f67ca970d9d8c8a7adcb5237 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_flow.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_graph_laplacian.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_graph_laplacian.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0a16c53cb67688eb5301af8b5c964987197c7d6 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_graph_laplacian.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_matching.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_matching.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a206c853c40fa5ba8996fbe49488b90c509ba1bf Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_matching.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_pydata_sparse.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_pydata_sparse.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6215ab7d1419677ab6259b859d9517630af877b Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_pydata_sparse.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_reordering.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_reordering.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa85b6682eb33f6058410088407e41f4eb6caf1e Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_reordering.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_shortest_path.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_shortest_path.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f183ce1cd398b30e8409e4acccb4a38e04272bc Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_shortest_path.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_spanning_tree.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_spanning_tree.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44a6ee568cedc34560f23b32d5bd9a1be0798968 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_spanning_tree.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_traversal.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_traversal.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4fac6fce029a6dd3835a1d10da408f8163928ec6 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/__pycache__/test_traversal.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_connected_components.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_connected_components.py new file mode 100644 index 0000000000000000000000000000000000000000..0b190a24deb9f2818893a120f8ea376fbfb8d6fe --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_connected_components.py @@ -0,0 +1,119 @@ +import numpy as np +from numpy.testing import assert_equal, assert_array_almost_equal +from scipy.sparse import csgraph, csr_array + + +def test_weak_connections(): + Xde = np.array([[0, 1, 0], + [0, 0, 0], + [0, 0, 0]]) + + Xsp = csgraph.csgraph_from_dense(Xde, null_value=0) + + for X in Xsp, Xde: + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='weak') + + assert_equal(n_components, 2) + assert_array_almost_equal(labels, [0, 0, 1]) + + +def test_strong_connections(): + X1de = np.array([[0, 1, 0], + [0, 0, 0], + [0, 0, 0]]) + X2de = X1de + X1de.T + + X1sp = csgraph.csgraph_from_dense(X1de, null_value=0) + X2sp = csgraph.csgraph_from_dense(X2de, null_value=0) + + for X in X1sp, X1de: + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='strong') + + assert_equal(n_components, 3) + labels.sort() + assert_array_almost_equal(labels, [0, 1, 2]) + + for X in X2sp, X2de: + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='strong') + + assert_equal(n_components, 2) + labels.sort() + assert_array_almost_equal(labels, [0, 0, 1]) + + +def test_strong_connections2(): + X = np.array([[0, 0, 0, 0, 0, 0], + [1, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0]]) + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='strong') + assert_equal(n_components, 5) + labels.sort() + assert_array_almost_equal(labels, [0, 1, 2, 2, 3, 4]) + + +def test_weak_connections2(): + X = np.array([[0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0]]) + n_components, labels =\ + csgraph.connected_components(X, directed=True, + connection='weak') + assert_equal(n_components, 2) + labels.sort() + assert_array_almost_equal(labels, [0, 0, 1, 1, 1, 1]) + + +def test_ticket1876(): + # Regression test: this failed in the original implementation + # There should be two strongly-connected components; previously gave one + g = np.array([[0, 1, 1, 0], + [1, 0, 0, 1], + [0, 0, 0, 1], + [0, 0, 1, 0]]) + n_components, labels = csgraph.connected_components(g, connection='strong') + + assert_equal(n_components, 2) + assert_equal(labels[0], labels[1]) + assert_equal(labels[2], labels[3]) + + +def test_fully_connected_graph(): + # Fully connected dense matrices raised an exception. + # https://github.com/scipy/scipy/issues/3818 + g = np.ones((4, 4)) + n_components, labels = csgraph.connected_components(g) + assert_equal(n_components, 1) + + +def test_int64_indices_undirected(): + # See https://github.com/scipy/scipy/issues/18716 + g = csr_array(([1], np.array([[0], [1]], dtype=np.int64)), shape=(2, 2)) + assert g.indices.dtype == np.int64 + n, labels = csgraph.connected_components(g, directed=False) + assert n == 1 + assert_array_almost_equal(labels, [0, 0]) + + +def test_int64_indices_directed(): + # See https://github.com/scipy/scipy/issues/18716 + g = csr_array(([1], np.array([[0], [1]], dtype=np.int64)), shape=(2, 2)) + assert g.indices.dtype == np.int64 + n, labels = csgraph.connected_components(g, directed=True, + connection='strong') + assert n == 2 + assert_array_almost_equal(labels, [1, 0]) + diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_conversions.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_conversions.py new file mode 100644 index 0000000000000000000000000000000000000000..e7900d67b543187e6a34b76ee5c9511cfcccae9e --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_conversions.py @@ -0,0 +1,61 @@ +import numpy as np +from numpy.testing import assert_array_almost_equal +from scipy.sparse import csr_matrix +from scipy.sparse.csgraph import csgraph_from_dense, csgraph_to_dense + + +def test_csgraph_from_dense(): + np.random.seed(1234) + G = np.random.random((10, 10)) + some_nulls = (G < 0.4) + all_nulls = (G < 0.8) + + for null_value in [0, np.nan, np.inf]: + G[all_nulls] = null_value + with np.errstate(invalid="ignore"): + G_csr = csgraph_from_dense(G, null_value=0) + + G[all_nulls] = 0 + assert_array_almost_equal(G, G_csr.toarray()) + + for null_value in [np.nan, np.inf]: + G[all_nulls] = 0 + G[some_nulls] = null_value + with np.errstate(invalid="ignore"): + G_csr = csgraph_from_dense(G, null_value=0) + + G[all_nulls] = 0 + assert_array_almost_equal(G, G_csr.toarray()) + + +def test_csgraph_to_dense(): + np.random.seed(1234) + G = np.random.random((10, 10)) + nulls = (G < 0.8) + G[nulls] = np.inf + + G_csr = csgraph_from_dense(G) + + for null_value in [0, 10, -np.inf, np.inf]: + G[nulls] = null_value + assert_array_almost_equal(G, csgraph_to_dense(G_csr, null_value)) + + +def test_multiple_edges(): + # create a random square matrix with an even number of elements + np.random.seed(1234) + X = np.random.random((10, 10)) + Xcsr = csr_matrix(X) + + # now double-up every other column + Xcsr.indices[::2] = Xcsr.indices[1::2] + + # normal sparse toarray() will sum the duplicated edges + Xdense = Xcsr.toarray() + assert_array_almost_equal(Xdense[:, 1::2], + X[:, ::2] + X[:, 1::2]) + + # csgraph_to_dense chooses the minimum of each duplicated edge + Xdense = csgraph_to_dense(Xcsr) + assert_array_almost_equal(Xdense[:, 1::2], + np.minimum(X[:, ::2], X[:, 1::2])) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_flow.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_flow.py new file mode 100644 index 0000000000000000000000000000000000000000..8bb129a572dd3abedb2afd896d04fa53e8c096bc --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_flow.py @@ -0,0 +1,201 @@ +import numpy as np +from numpy.testing import assert_array_equal +import pytest + +from scipy.sparse import csr_matrix, csc_matrix +from scipy.sparse.csgraph import maximum_flow +from scipy.sparse.csgraph._flow import ( + _add_reverse_edges, _make_edge_pointers, _make_tails +) + +methods = ['edmonds_karp', 'dinic'] + +def test_raises_on_dense_input(): + with pytest.raises(TypeError): + graph = np.array([[0, 1], [0, 0]]) + maximum_flow(graph, 0, 1) + maximum_flow(graph, 0, 1, method='edmonds_karp') + + +def test_raises_on_csc_input(): + with pytest.raises(TypeError): + graph = csc_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, 0, 1) + maximum_flow(graph, 0, 1, method='edmonds_karp') + + +def test_raises_on_floating_point_input(): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1.5], [0, 0]], dtype=np.float64) + maximum_flow(graph, 0, 1) + maximum_flow(graph, 0, 1, method='edmonds_karp') + + +def test_raises_on_non_square_input(): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1, 2], [2, 1, 0]]) + maximum_flow(graph, 0, 1) + + +def test_raises_when_source_is_sink(): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, 0, 0) + maximum_flow(graph, 0, 0, method='edmonds_karp') + + +@pytest.mark.parametrize('method', methods) +@pytest.mark.parametrize('source', [-1, 2, 3]) +def test_raises_when_source_is_out_of_bounds(source, method): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, source, 1, method=method) + + +@pytest.mark.parametrize('method', methods) +@pytest.mark.parametrize('sink', [-1, 2, 3]) +def test_raises_when_sink_is_out_of_bounds(sink, method): + with pytest.raises(ValueError): + graph = csr_matrix([[0, 1], [0, 0]]) + maximum_flow(graph, 0, sink, method=method) + + +@pytest.mark.parametrize('method', methods) +def test_simple_graph(method): + # This graph looks as follows: + # (0) --5--> (1) + graph = csr_matrix([[0, 5], [0, 0]]) + res = maximum_flow(graph, 0, 1, method=method) + assert res.flow_value == 5 + expected_flow = np.array([[0, 5], [-5, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_bottle_neck_graph(method): + # This graph cannot use the full capacity between 0 and 1: + # (0) --5--> (1) --3--> (2) + graph = csr_matrix([[0, 5, 0], [0, 0, 3], [0, 0, 0]]) + res = maximum_flow(graph, 0, 2, method=method) + assert res.flow_value == 3 + expected_flow = np.array([[0, 3, 0], [-3, 0, 3], [0, -3, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_backwards_flow(method): + # This example causes backwards flow between vertices 3 and 4, + # and so this test ensures that we handle that accordingly. See + # https://stackoverflow.com/q/38843963/5085211 + # for more information. + graph = csr_matrix([[0, 10, 0, 0, 10, 0, 0, 0], + [0, 0, 10, 0, 0, 0, 0, 0], + [0, 0, 0, 10, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 10], + [0, 0, 0, 10, 0, 10, 0, 0], + [0, 0, 0, 0, 0, 0, 10, 0], + [0, 0, 0, 0, 0, 0, 0, 10], + [0, 0, 0, 0, 0, 0, 0, 0]]) + res = maximum_flow(graph, 0, 7, method=method) + assert res.flow_value == 20 + expected_flow = np.array([[0, 10, 0, 0, 10, 0, 0, 0], + [-10, 0, 10, 0, 0, 0, 0, 0], + [0, -10, 0, 10, 0, 0, 0, 0], + [0, 0, -10, 0, 0, 0, 0, 10], + [-10, 0, 0, 0, 0, 10, 0, 0], + [0, 0, 0, 0, -10, 0, 10, 0], + [0, 0, 0, 0, 0, -10, 0, 10], + [0, 0, 0, -10, 0, 0, -10, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_example_from_clrs_chapter_26_1(method): + # See page 659 in CLRS second edition, but note that the maximum flow + # we find is slightly different than the one in CLRS; we push a flow of + # 12 to v_1 instead of v_2. + graph = csr_matrix([[0, 16, 13, 0, 0, 0], + [0, 0, 10, 12, 0, 0], + [0, 4, 0, 0, 14, 0], + [0, 0, 9, 0, 0, 20], + [0, 0, 0, 7, 0, 4], + [0, 0, 0, 0, 0, 0]]) + res = maximum_flow(graph, 0, 5, method=method) + assert res.flow_value == 23 + expected_flow = np.array([[0, 12, 11, 0, 0, 0], + [-12, 0, 0, 12, 0, 0], + [-11, 0, 0, 0, 11, 0], + [0, -12, 0, 0, -7, 19], + [0, 0, -11, 7, 0, 4], + [0, 0, 0, -19, -4, 0]]) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_disconnected_graph(method): + # This tests the following disconnected graph: + # (0) --5--> (1) (2) --3--> (3) + graph = csr_matrix([[0, 5, 0, 0], + [0, 0, 0, 0], + [0, 0, 9, 3], + [0, 0, 0, 0]]) + res = maximum_flow(graph, 0, 3, method=method) + assert res.flow_value == 0 + expected_flow = np.zeros((4, 4), dtype=np.int32) + assert_array_equal(res.flow.toarray(), expected_flow) + + +@pytest.mark.parametrize('method', methods) +def test_add_reverse_edges_large_graph(method): + # Regression test for https://github.com/scipy/scipy/issues/14385 + n = 100_000 + indices = np.arange(1, n) + indptr = np.array(list(range(n)) + [n - 1]) + data = np.ones(n - 1, dtype=np.int32) + graph = csr_matrix((data, indices, indptr), shape=(n, n)) + res = maximum_flow(graph, 0, n - 1, method=method) + assert res.flow_value == 1 + expected_flow = graph - graph.transpose() + assert_array_equal(res.flow.data, expected_flow.data) + assert_array_equal(res.flow.indices, expected_flow.indices) + assert_array_equal(res.flow.indptr, expected_flow.indptr) + + +@pytest.mark.parametrize("a,b_data_expected", [ + ([[]], []), + ([[0], [0]], []), + ([[1, 0, 2], [0, 0, 0], [0, 3, 0]], [1, 2, 0, 0, 3]), + ([[9, 8, 7], [4, 5, 6], [0, 0, 0]], [9, 8, 7, 4, 5, 6, 0, 0])]) +def test_add_reverse_edges(a, b_data_expected): + """Test that the reversal of the edges of the input graph works + as expected. + """ + a = csr_matrix(a, dtype=np.int32, shape=(len(a), len(a))) + b = _add_reverse_edges(a) + assert_array_equal(b.data, b_data_expected) + + +@pytest.mark.parametrize("a,expected", [ + ([[]], []), + ([[0]], []), + ([[1]], [0]), + ([[0, 1], [10, 0]], [1, 0]), + ([[1, 0, 2], [0, 0, 3], [4, 5, 0]], [0, 3, 4, 1, 2]) +]) +def test_make_edge_pointers(a, expected): + a = csr_matrix(a, dtype=np.int32) + rev_edge_ptr = _make_edge_pointers(a) + assert_array_equal(rev_edge_ptr, expected) + + +@pytest.mark.parametrize("a,expected", [ + ([[]], []), + ([[0]], []), + ([[1]], [0]), + ([[0, 1], [10, 0]], [0, 1]), + ([[1, 0, 2], [0, 0, 3], [4, 5, 0]], [0, 0, 1, 2, 2]) +]) +def test_make_tails(a, expected): + a = csr_matrix(a, dtype=np.int32) + tails = _make_tails(a) + assert_array_equal(tails, expected) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_graph_laplacian.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_graph_laplacian.py new file mode 100644 index 0000000000000000000000000000000000000000..4a4213dcbec63530466078437d01ca7765494514 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_graph_laplacian.py @@ -0,0 +1,369 @@ +import pytest +import numpy as np +from numpy.testing import assert_allclose +from pytest import raises as assert_raises +from scipy import sparse + +from scipy.sparse import csgraph +from scipy._lib._util import np_long, np_ulong + + +def check_int_type(mat): + return np.issubdtype(mat.dtype, np.signedinteger) or np.issubdtype( + mat.dtype, np_ulong + ) + + +def test_laplacian_value_error(): + for t in int, float, complex: + for m in ([1, 1], + [[[1]]], + [[1, 2, 3], [4, 5, 6]], + [[1, 2], [3, 4], [5, 5]]): + A = np.array(m, dtype=t) + assert_raises(ValueError, csgraph.laplacian, A) + + +def _explicit_laplacian(x, normed=False): + if sparse.issparse(x): + x = x.toarray() + x = np.asarray(x) + y = -1.0 * x + for j in range(y.shape[0]): + y[j,j] = x[j,j+1:].sum() + x[j,:j].sum() + if normed: + d = np.diag(y).copy() + d[d == 0] = 1.0 + y /= d[:,None]**.5 + y /= d[None,:]**.5 + return y + + +def _check_symmetric_graph_laplacian(mat, normed, copy=True): + if not hasattr(mat, 'shape'): + mat = eval(mat, dict(np=np, sparse=sparse)) + + if sparse.issparse(mat): + sp_mat = mat + mat = sp_mat.toarray() + else: + sp_mat = sparse.csr_matrix(mat) + + mat_copy = np.copy(mat) + sp_mat_copy = sparse.csr_matrix(sp_mat, copy=True) + + n_nodes = mat.shape[0] + explicit_laplacian = _explicit_laplacian(mat, normed=normed) + laplacian = csgraph.laplacian(mat, normed=normed, copy=copy) + sp_laplacian = csgraph.laplacian(sp_mat, normed=normed, + copy=copy) + + if copy: + assert_allclose(mat, mat_copy) + _assert_allclose_sparse(sp_mat, sp_mat_copy) + else: + if not (normed and check_int_type(mat)): + assert_allclose(laplacian, mat) + if sp_mat.format == 'coo': + _assert_allclose_sparse(sp_laplacian, sp_mat) + + assert_allclose(laplacian, sp_laplacian.toarray()) + + for tested in [laplacian, sp_laplacian.toarray()]: + if not normed: + assert_allclose(tested.sum(axis=0), np.zeros(n_nodes)) + assert_allclose(tested.T, tested) + assert_allclose(tested, explicit_laplacian) + + +def test_symmetric_graph_laplacian(): + symmetric_mats = ( + 'np.arange(10) * np.arange(10)[:, np.newaxis]', + 'np.ones((7, 7))', + 'np.eye(19)', + 'sparse.diags([1, 1], [-1, 1], shape=(4, 4))', + 'sparse.diags([1, 1], [-1, 1], shape=(4, 4)).toarray()', + 'sparse.diags([1, 1], [-1, 1], shape=(4, 4)).todense()', + 'np.vander(np.arange(4)) + np.vander(np.arange(4)).T' + ) + for mat in symmetric_mats: + for normed in True, False: + for copy in True, False: + _check_symmetric_graph_laplacian(mat, normed, copy) + + +def _assert_allclose_sparse(a, b, **kwargs): + # helper function that can deal with sparse matrices + if sparse.issparse(a): + a = a.toarray() + if sparse.issparse(b): + b = b.toarray() + assert_allclose(a, b, **kwargs) + + +def _check_laplacian_dtype_none( + A, desired_L, desired_d, normed, use_out_degree, copy, dtype, arr_type +): + mat = arr_type(A, dtype=dtype) + L, d = csgraph.laplacian( + mat, + normed=normed, + return_diag=True, + use_out_degree=use_out_degree, + copy=copy, + dtype=None, + ) + if normed and check_int_type(mat): + assert L.dtype == np.float64 + assert d.dtype == np.float64 + _assert_allclose_sparse(L, desired_L, atol=1e-12) + _assert_allclose_sparse(d, desired_d, atol=1e-12) + else: + assert L.dtype == dtype + assert d.dtype == dtype + desired_L = np.asarray(desired_L).astype(dtype) + desired_d = np.asarray(desired_d).astype(dtype) + _assert_allclose_sparse(L, desired_L, atol=1e-12) + _assert_allclose_sparse(d, desired_d, atol=1e-12) + + if not copy: + if not (normed and check_int_type(mat)): + if type(mat) is np.ndarray: + assert_allclose(L, mat) + elif mat.format == "coo": + _assert_allclose_sparse(L, mat) + + +def _check_laplacian_dtype( + A, desired_L, desired_d, normed, use_out_degree, copy, dtype, arr_type +): + mat = arr_type(A, dtype=dtype) + L, d = csgraph.laplacian( + mat, + normed=normed, + return_diag=True, + use_out_degree=use_out_degree, + copy=copy, + dtype=dtype, + ) + assert L.dtype == dtype + assert d.dtype == dtype + desired_L = np.asarray(desired_L).astype(dtype) + desired_d = np.asarray(desired_d).astype(dtype) + _assert_allclose_sparse(L, desired_L, atol=1e-12) + _assert_allclose_sparse(d, desired_d, atol=1e-12) + + if not copy: + if not (normed and check_int_type(mat)): + if type(mat) is np.ndarray: + assert_allclose(L, mat) + elif mat.format == 'coo': + _assert_allclose_sparse(L, mat) + + +INT_DTYPES = {np.intc, np_long, np.longlong} +REAL_DTYPES = {np.float32, np.float64, np.longdouble} +COMPLEX_DTYPES = {np.complex64, np.complex128, np.clongdouble} +# use sorted list to ensure fixed order of tests +DTYPES = sorted(INT_DTYPES ^ REAL_DTYPES ^ COMPLEX_DTYPES, key=str) + + +@pytest.mark.parametrize("dtype", DTYPES) +@pytest.mark.parametrize("arr_type", [np.array, + sparse.csr_matrix, + sparse.coo_matrix, + sparse.csr_array, + sparse.coo_array]) +@pytest.mark.parametrize("copy", [True, False]) +@pytest.mark.parametrize("normed", [True, False]) +@pytest.mark.parametrize("use_out_degree", [True, False]) +def test_asymmetric_laplacian(use_out_degree, normed, + copy, dtype, arr_type): + # adjacency matrix + A = [[0, 1, 0], + [4, 2, 0], + [0, 0, 0]] + A = arr_type(np.array(A), dtype=dtype) + A_copy = A.copy() + + if not normed and use_out_degree: + # Laplacian matrix using out-degree + L = [[1, -1, 0], + [-4, 4, 0], + [0, 0, 0]] + d = [1, 4, 0] + + if normed and use_out_degree: + # normalized Laplacian matrix using out-degree + L = [[1, -0.5, 0], + [-2, 1, 0], + [0, 0, 0]] + d = [1, 2, 1] + + if not normed and not use_out_degree: + # Laplacian matrix using in-degree + L = [[4, -1, 0], + [-4, 1, 0], + [0, 0, 0]] + d = [4, 1, 0] + + if normed and not use_out_degree: + # normalized Laplacian matrix using in-degree + L = [[1, -0.5, 0], + [-2, 1, 0], + [0, 0, 0]] + d = [2, 1, 1] + + _check_laplacian_dtype_none( + A, + L, + d, + normed=normed, + use_out_degree=use_out_degree, + copy=copy, + dtype=dtype, + arr_type=arr_type, + ) + + _check_laplacian_dtype( + A_copy, + L, + d, + normed=normed, + use_out_degree=use_out_degree, + copy=copy, + dtype=dtype, + arr_type=arr_type, + ) + + +@pytest.mark.parametrize("fmt", ['csr', 'csc', 'coo', 'lil', + 'dok', 'dia', 'bsr']) +@pytest.mark.parametrize("normed", [True, False]) +@pytest.mark.parametrize("copy", [True, False]) +def test_sparse_formats(fmt, normed, copy): + mat = sparse.diags([1, 1], [-1, 1], shape=(4, 4), format=fmt) + _check_symmetric_graph_laplacian(mat, normed, copy) + + +@pytest.mark.parametrize( + "arr_type", [np.asarray, + sparse.csr_matrix, + sparse.coo_matrix, + sparse.csr_array, + sparse.coo_array] +) +@pytest.mark.parametrize("form", ["array", "function", "lo"]) +def test_laplacian_symmetrized(arr_type, form): + # adjacency matrix + n = 3 + mat = arr_type(np.arange(n * n).reshape(n, n)) + L_in, d_in = csgraph.laplacian( + mat, + return_diag=True, + form=form, + ) + L_out, d_out = csgraph.laplacian( + mat, + return_diag=True, + use_out_degree=True, + form=form, + ) + Ls, ds = csgraph.laplacian( + mat, + return_diag=True, + symmetrized=True, + form=form, + ) + Ls_normed, ds_normed = csgraph.laplacian( + mat, + return_diag=True, + symmetrized=True, + normed=True, + form=form, + ) + mat += mat.T + Lss, dss = csgraph.laplacian(mat, return_diag=True, form=form) + Lss_normed, dss_normed = csgraph.laplacian( + mat, + return_diag=True, + normed=True, + form=form, + ) + + assert_allclose(ds, d_in + d_out) + assert_allclose(ds, dss) + assert_allclose(ds_normed, dss_normed) + + d = {} + for L in ["L_in", "L_out", "Ls", "Ls_normed", "Lss", "Lss_normed"]: + if form == "array": + d[L] = eval(L) + else: + d[L] = eval(L)(np.eye(n, dtype=mat.dtype)) + + _assert_allclose_sparse(d["Ls"], d["L_in"] + d["L_out"].T) + _assert_allclose_sparse(d["Ls"], d["Lss"]) + _assert_allclose_sparse(d["Ls_normed"], d["Lss_normed"]) + + +@pytest.mark.parametrize( + "arr_type", [np.asarray, + sparse.csr_matrix, + sparse.coo_matrix, + sparse.csr_array, + sparse.coo_array] +) +@pytest.mark.parametrize("dtype", DTYPES) +@pytest.mark.parametrize("normed", [True, False]) +@pytest.mark.parametrize("symmetrized", [True, False]) +@pytest.mark.parametrize("use_out_degree", [True, False]) +@pytest.mark.parametrize("form", ["function", "lo"]) +def test_format(dtype, arr_type, normed, symmetrized, use_out_degree, form): + n = 3 + mat = [[0, 1, 0], [4, 2, 0], [0, 0, 0]] + mat = arr_type(np.array(mat), dtype=dtype) + Lo, do = csgraph.laplacian( + mat, + return_diag=True, + normed=normed, + symmetrized=symmetrized, + use_out_degree=use_out_degree, + dtype=dtype, + ) + La, da = csgraph.laplacian( + mat, + return_diag=True, + normed=normed, + symmetrized=symmetrized, + use_out_degree=use_out_degree, + dtype=dtype, + form="array", + ) + assert_allclose(do, da) + _assert_allclose_sparse(Lo, La) + + L, d = csgraph.laplacian( + mat, + return_diag=True, + normed=normed, + symmetrized=symmetrized, + use_out_degree=use_out_degree, + dtype=dtype, + form=form, + ) + assert_allclose(d, do) + assert d.dtype == dtype + Lm = L(np.eye(n, dtype=mat.dtype)).astype(dtype) + _assert_allclose_sparse(Lm, Lo, rtol=2e-7, atol=2e-7) + x = np.arange(6).reshape(3, 2) + if not (normed and dtype in INT_DTYPES): + assert_allclose(L(x), Lo @ x) + else: + # Normalized Lo is casted to integer, but L() is not + pass + + +def test_format_error_message(): + with pytest.raises(ValueError, match="Invalid form: 'toto'"): + _ = csgraph.laplacian(np.eye(1), form='toto') diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_matching.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_matching.py new file mode 100644 index 0000000000000000000000000000000000000000..87e2920fe971d22a16b473d543e2ad26ac8e777d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_matching.py @@ -0,0 +1,294 @@ +from itertools import product + +import numpy as np +from numpy.testing import assert_array_equal, assert_equal +import pytest + +from scipy.sparse import csr_matrix, coo_matrix, diags +from scipy.sparse.csgraph import ( + maximum_bipartite_matching, min_weight_full_bipartite_matching +) + + +def test_maximum_bipartite_matching_raises_on_dense_input(): + with pytest.raises(TypeError): + graph = np.array([[0, 1], [0, 0]]) + maximum_bipartite_matching(graph) + + +def test_maximum_bipartite_matching_empty_graph(): + graph = csr_matrix((0, 0)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + expected_matching = np.array([]) + assert_array_equal(expected_matching, x) + assert_array_equal(expected_matching, y) + + +def test_maximum_bipartite_matching_empty_left_partition(): + graph = csr_matrix((2, 0)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert_array_equal(np.array([]), x) + assert_array_equal(np.array([-1, -1]), y) + + +def test_maximum_bipartite_matching_empty_right_partition(): + graph = csr_matrix((0, 3)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert_array_equal(np.array([-1, -1, -1]), x) + assert_array_equal(np.array([]), y) + + +def test_maximum_bipartite_matching_graph_with_no_edges(): + graph = csr_matrix((2, 2)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert_array_equal(np.array([-1, -1]), x) + assert_array_equal(np.array([-1, -1]), y) + + +def test_maximum_bipartite_matching_graph_that_causes_augmentation(): + # In this graph, column 1 is initially assigned to row 1, but it should be + # reassigned to make room for row 2. + graph = csr_matrix([[1, 1], [1, 0]]) + x = maximum_bipartite_matching(graph, perm_type='column') + y = maximum_bipartite_matching(graph, perm_type='row') + expected_matching = np.array([1, 0]) + assert_array_equal(expected_matching, x) + assert_array_equal(expected_matching, y) + + +def test_maximum_bipartite_matching_graph_with_more_rows_than_columns(): + graph = csr_matrix([[1, 1], [1, 0], [0, 1]]) + x = maximum_bipartite_matching(graph, perm_type='column') + y = maximum_bipartite_matching(graph, perm_type='row') + assert_array_equal(np.array([0, -1, 1]), x) + assert_array_equal(np.array([0, 2]), y) + + +def test_maximum_bipartite_matching_graph_with_more_columns_than_rows(): + graph = csr_matrix([[1, 1, 0], [0, 0, 1]]) + x = maximum_bipartite_matching(graph, perm_type='column') + y = maximum_bipartite_matching(graph, perm_type='row') + assert_array_equal(np.array([0, 2]), x) + assert_array_equal(np.array([0, -1, 1]), y) + + +def test_maximum_bipartite_matching_explicit_zeros_count_as_edges(): + data = [0, 0] + indices = [1, 0] + indptr = [0, 1, 2] + graph = csr_matrix((data, indices, indptr), shape=(2, 2)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + expected_matching = np.array([1, 0]) + assert_array_equal(expected_matching, x) + assert_array_equal(expected_matching, y) + + +def test_maximum_bipartite_matching_feasibility_of_result(): + # This is a regression test for GitHub issue #11458 + data = np.ones(50, dtype=int) + indices = [11, 12, 19, 22, 23, 5, 22, 3, 8, 10, 5, 6, 11, 12, 13, 5, 13, + 14, 20, 22, 3, 15, 3, 13, 14, 11, 12, 19, 22, 23, 5, 22, 3, 8, + 10, 5, 6, 11, 12, 13, 5, 13, 14, 20, 22, 3, 15, 3, 13, 14] + indptr = [0, 5, 7, 10, 10, 15, 20, 22, 22, 23, 25, 30, 32, 35, 35, 40, 45, + 47, 47, 48, 50] + graph = csr_matrix((data, indices, indptr), shape=(20, 25)) + x = maximum_bipartite_matching(graph, perm_type='row') + y = maximum_bipartite_matching(graph, perm_type='column') + assert (x != -1).sum() == 13 + assert (y != -1).sum() == 13 + # Ensure that each element of the matching is in fact an edge in the graph. + for u, v in zip(range(graph.shape[0]), y): + if v != -1: + assert graph[u, v] + for u, v in zip(x, range(graph.shape[1])): + if u != -1: + assert graph[u, v] + + +def test_matching_large_random_graph_with_one_edge_incident_to_each_vertex(): + np.random.seed(42) + A = diags(np.ones(25), offsets=0, format='csr') + rand_perm = np.random.permutation(25) + rand_perm2 = np.random.permutation(25) + + Rrow = np.arange(25) + Rcol = rand_perm + Rdata = np.ones(25, dtype=int) + Rmat = coo_matrix((Rdata, (Rrow, Rcol))).tocsr() + + Crow = rand_perm2 + Ccol = np.arange(25) + Cdata = np.ones(25, dtype=int) + Cmat = coo_matrix((Cdata, (Crow, Ccol))).tocsr() + # Randomly permute identity matrix + B = Rmat * A * Cmat + + # Row permute + perm = maximum_bipartite_matching(B, perm_type='row') + Rrow = np.arange(25) + Rcol = perm + Rdata = np.ones(25, dtype=int) + Rmat = coo_matrix((Rdata, (Rrow, Rcol))).tocsr() + C1 = Rmat * B + + # Column permute + perm2 = maximum_bipartite_matching(B, perm_type='column') + Crow = perm2 + Ccol = np.arange(25) + Cdata = np.ones(25, dtype=int) + Cmat = coo_matrix((Cdata, (Crow, Ccol))).tocsr() + C2 = B * Cmat + + # Should get identity matrix back + assert_equal(any(C1.diagonal() == 0), False) + assert_equal(any(C2.diagonal() == 0), False) + + +@pytest.mark.parametrize('num_rows,num_cols', [(0, 0), (2, 0), (0, 3)]) +def test_min_weight_full_matching_trivial_graph(num_rows, num_cols): + biadjacency_matrix = csr_matrix((num_cols, num_rows)) + row_ind, col_ind = min_weight_full_bipartite_matching(biadjacency_matrix) + assert len(row_ind) == 0 + assert len(col_ind) == 0 + + +@pytest.mark.parametrize('biadjacency_matrix', + [ + [[1, 1, 1], [1, 0, 0], [1, 0, 0]], + [[1, 1, 1], [0, 0, 1], [0, 0, 1]], + [[1, 0, 0, 1], [1, 1, 0, 1], [0, 0, 0, 0]], + [[1, 0, 0], [2, 0, 0]], + [[0, 1, 0], [0, 2, 0]], + [[1, 0], [2, 0], [5, 0]] + ]) +def test_min_weight_full_matching_infeasible_problems(biadjacency_matrix): + with pytest.raises(ValueError): + min_weight_full_bipartite_matching(csr_matrix(biadjacency_matrix)) + + +def test_min_weight_full_matching_large_infeasible(): + # Regression test for GitHub issue #17269 + a = np.asarray([ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], + [0.0, 0.11687445, 0.0, 0.0, 0.01319788, 0.07509257, 0.0, + 0.0, 0.0, 0.74228317, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.81087935, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.8408466, 0.0, 0.0, 0.0, 0.0, 0.01194389, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.82994211, 0.0, 0.0, 0.0, 0.11468516, 0.0, 0.0, 0.0, + 0.11173505, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0], + [0.18796507, 0.0, 0.04002318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.75883335, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.71545464, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02748488, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.78470564, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.14829198, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.10870609, 0.0, 0.0, 0.0, 0.8918677, 0.0, 0.0, 0.0, 0.06306644, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.63844085, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.7442354, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09850549, 0.0, 0.0, 0.18638258, + 0.2769244, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.73182464, 0.0, 0.0, 0.46443561, + 0.38589284, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.29510278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09666032, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + ]) + with pytest.raises(ValueError, match='no full matching exists'): + min_weight_full_bipartite_matching(csr_matrix(a)) + + +def test_explicit_zero_causes_warning(): + with pytest.warns(UserWarning): + biadjacency_matrix = csr_matrix(((2, 0, 3), (0, 1, 1), (0, 2, 3))) + min_weight_full_bipartite_matching(biadjacency_matrix) + + +# General test for linear sum assignment solvers to make it possible to rely +# on the same tests for scipy.optimize.linear_sum_assignment. +def linear_sum_assignment_assertions( + solver, array_type, sign, test_case +): + cost_matrix, expected_cost = test_case + maximize = sign == -1 + cost_matrix = sign * array_type(cost_matrix) + expected_cost = sign * np.array(expected_cost) + + row_ind, col_ind = solver(cost_matrix, maximize=maximize) + assert_array_equal(row_ind, np.sort(row_ind)) + assert_array_equal(expected_cost, + np.array(cost_matrix[row_ind, col_ind]).flatten()) + + cost_matrix = cost_matrix.T + row_ind, col_ind = solver(cost_matrix, maximize=maximize) + assert_array_equal(row_ind, np.sort(row_ind)) + assert_array_equal(np.sort(expected_cost), + np.sort(np.array( + cost_matrix[row_ind, col_ind])).flatten()) + + +linear_sum_assignment_test_cases = product( + [-1, 1], + [ + # Square + ([[400, 150, 400], + [400, 450, 600], + [300, 225, 300]], + [150, 400, 300]), + + # Rectangular variant + ([[400, 150, 400, 1], + [400, 450, 600, 2], + [300, 225, 300, 3]], + [150, 2, 300]), + + ([[10, 10, 8], + [9, 8, 1], + [9, 7, 4]], + [10, 1, 7]), + + # Square + ([[10, 10, 8, 11], + [9, 8, 1, 1], + [9, 7, 4, 10]], + [10, 1, 4]), + + # Rectangular variant + ([[10, float("inf"), float("inf")], + [float("inf"), float("inf"), 1], + [float("inf"), 7, float("inf")]], + [10, 1, 7]) + ]) + + +@pytest.mark.parametrize('sign,test_case', linear_sum_assignment_test_cases) +def test_min_weight_full_matching_small_inputs(sign, test_case): + linear_sum_assignment_assertions( + min_weight_full_bipartite_matching, csr_matrix, sign, test_case) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_pydata_sparse.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_pydata_sparse.py new file mode 100644 index 0000000000000000000000000000000000000000..63ed5f61a430e4291c40284f1bbfff3165421013 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_pydata_sparse.py @@ -0,0 +1,149 @@ +import pytest + +import numpy as np +import scipy.sparse as sp +import scipy.sparse.csgraph as spgraph + +from numpy.testing import assert_equal + +try: + import sparse +except Exception: + sparse = None + +pytestmark = pytest.mark.skipif(sparse is None, + reason="pydata/sparse not installed") + + +msg = "pydata/sparse (0.15.1) does not implement necessary operations" + + +sparse_params = (pytest.param("COO"), + pytest.param("DOK", marks=[pytest.mark.xfail(reason=msg)])) + + +@pytest.fixture(params=sparse_params) +def sparse_cls(request): + return getattr(sparse, request.param) + + +@pytest.fixture +def graphs(sparse_cls): + graph = [ + [0, 1, 1, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 1], + [0, 0, 0, 0, 0], + ] + A_dense = np.array(graph) + A_sparse = sparse_cls(A_dense) + return A_dense, A_sparse + + +@pytest.mark.parametrize( + "func", + [ + spgraph.shortest_path, + spgraph.dijkstra, + spgraph.floyd_warshall, + spgraph.bellman_ford, + spgraph.johnson, + spgraph.reverse_cuthill_mckee, + spgraph.maximum_bipartite_matching, + spgraph.structural_rank, + ] +) +def test_csgraph_equiv(func, graphs): + A_dense, A_sparse = graphs + actual = func(A_sparse) + desired = func(sp.csc_matrix(A_dense)) + assert_equal(actual, desired) + + +def test_connected_components(graphs): + A_dense, A_sparse = graphs + func = spgraph.connected_components + + actual_comp, actual_labels = func(A_sparse) + desired_comp, desired_labels, = func(sp.csc_matrix(A_dense)) + + assert actual_comp == desired_comp + assert_equal(actual_labels, desired_labels) + + +def test_laplacian(graphs): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + func = spgraph.laplacian + + actual = func(A_sparse) + desired = func(sp.csc_matrix(A_dense)) + + assert isinstance(actual, sparse_cls) + + assert_equal(actual.todense(), desired.todense()) + + +@pytest.mark.parametrize( + "func", [spgraph.breadth_first_order, spgraph.depth_first_order] +) +def test_order_search(graphs, func): + A_dense, A_sparse = graphs + + actual = func(A_sparse, 0) + desired = func(sp.csc_matrix(A_dense), 0) + + assert_equal(actual, desired) + + +@pytest.mark.parametrize( + "func", [spgraph.breadth_first_tree, spgraph.depth_first_tree] +) +def test_tree_search(graphs, func): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + + actual = func(A_sparse, 0) + desired = func(sp.csc_matrix(A_dense), 0) + + assert isinstance(actual, sparse_cls) + + assert_equal(actual.todense(), desired.todense()) + + +def test_minimum_spanning_tree(graphs): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + func = spgraph.minimum_spanning_tree + + actual = func(A_sparse) + desired = func(sp.csc_matrix(A_dense)) + + assert isinstance(actual, sparse_cls) + + assert_equal(actual.todense(), desired.todense()) + + +def test_maximum_flow(graphs): + A_dense, A_sparse = graphs + sparse_cls = type(A_sparse) + func = spgraph.maximum_flow + + actual = func(A_sparse, 0, 2) + desired = func(sp.csr_matrix(A_dense), 0, 2) + + assert actual.flow_value == desired.flow_value + assert isinstance(actual.flow, sparse_cls) + + assert_equal(actual.flow.todense(), desired.flow.todense()) + + +def test_min_weight_full_bipartite_matching(graphs): + A_dense, A_sparse = graphs + func = spgraph.min_weight_full_bipartite_matching + + actual = func(A_sparse[0:2, 1:3]) + desired = func(sp.csc_matrix(A_dense)[0:2, 1:3]) + + assert_equal(actual, desired) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_reordering.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_reordering.py new file mode 100644 index 0000000000000000000000000000000000000000..cb4c002fa303e7196278367afd316d47b3473cbb --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_reordering.py @@ -0,0 +1,70 @@ +import numpy as np +from numpy.testing import assert_equal +from scipy.sparse.csgraph import reverse_cuthill_mckee, structural_rank +from scipy.sparse import csc_matrix, csr_matrix, coo_matrix + + +def test_graph_reverse_cuthill_mckee(): + A = np.array([[1, 0, 0, 0, 1, 0, 0, 0], + [0, 1, 1, 0, 0, 1, 0, 1], + [0, 1, 1, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 1, 0], + [1, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 1], + [0, 0, 0, 1, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 1, 0, 1]], dtype=int) + + graph = csr_matrix(A) + perm = reverse_cuthill_mckee(graph) + correct_perm = np.array([6, 3, 7, 5, 1, 2, 4, 0]) + assert_equal(perm, correct_perm) + + # Test int64 indices input + graph.indices = graph.indices.astype('int64') + graph.indptr = graph.indptr.astype('int64') + perm = reverse_cuthill_mckee(graph, True) + assert_equal(perm, correct_perm) + + +def test_graph_reverse_cuthill_mckee_ordering(): + data = np.ones(63,dtype=int) + rows = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, + 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, + 14, 15, 15, 15, 15, 15]) + cols = np.array([0, 2, 5, 8, 10, 1, 3, 9, 11, 0, 2, + 7, 10, 1, 3, 11, 4, 6, 12, 14, 0, 7, 13, + 15, 4, 6, 14, 2, 5, 7, 15, 0, 8, 10, 13, + 1, 9, 11, 0, 2, 8, 10, 15, 1, 3, 9, 11, + 4, 12, 14, 5, 8, 13, 15, 4, 6, 12, 14, + 5, 7, 10, 13, 15]) + graph = coo_matrix((data, (rows,cols))).tocsr() + perm = reverse_cuthill_mckee(graph) + correct_perm = np.array([12, 14, 4, 6, 10, 8, 2, 15, + 0, 13, 7, 5, 9, 11, 1, 3]) + assert_equal(perm, correct_perm) + + +def test_graph_structural_rank(): + # Test square matrix #1 + A = csc_matrix([[1, 1, 0], + [1, 0, 1], + [0, 1, 0]]) + assert_equal(structural_rank(A), 3) + + # Test square matrix #2 + rows = np.array([0,0,0,0,0,1,1,2,2,3,3,3,3,3,3,4,4,5,5,6,6,7,7]) + cols = np.array([0,1,2,3,4,2,5,2,6,0,1,3,5,6,7,4,5,5,6,2,6,2,4]) + data = np.ones_like(rows) + B = coo_matrix((data,(rows,cols)), shape=(8,8)) + assert_equal(structural_rank(B), 6) + + #Test non-square matrix + C = csc_matrix([[1, 0, 2, 0], + [2, 0, 4, 0]]) + assert_equal(structural_rank(C), 2) + + #Test tall matrix + assert_equal(structural_rank(C.T), 2) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_shortest_path.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_shortest_path.py new file mode 100644 index 0000000000000000000000000000000000000000..046df018694881f97cb7569f6d4a3edf5af875a3 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_shortest_path.py @@ -0,0 +1,454 @@ +from io import StringIO +import warnings +import numpy as np +from numpy.testing import assert_array_almost_equal, assert_array_equal, assert_allclose +from pytest import raises as assert_raises +from scipy.sparse.csgraph import (shortest_path, dijkstra, johnson, + bellman_ford, construct_dist_matrix, yen, + NegativeCycleError) +import scipy.sparse +from scipy.io import mmread +import pytest + +directed_G = np.array([[0, 3, 3, 0, 0], + [0, 0, 0, 2, 4], + [0, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [2, 0, 0, 2, 0]], dtype=float) + +undirected_G = np.array([[0, 3, 3, 1, 2], + [3, 0, 0, 2, 4], + [3, 0, 0, 0, 0], + [1, 2, 0, 0, 2], + [2, 4, 0, 2, 0]], dtype=float) + +unweighted_G = (directed_G > 0).astype(float) + +directed_SP = [[0, 3, 3, 5, 7], + [3, 0, 6, 2, 4], + [np.inf, np.inf, 0, np.inf, np.inf], + [1, 4, 4, 0, 8], + [2, 5, 5, 2, 0]] + +directed_2SP_0_to_3 = [[-9999, 0, -9999, 1, -9999], + [-9999, 0, -9999, 4, 1]] + +directed_sparse_zero_G = scipy.sparse.csr_matrix(([0, 1, 2, 3, 1], + ([0, 1, 2, 3, 4], + [1, 2, 0, 4, 3])), + shape = (5, 5)) + +directed_sparse_zero_SP = [[0, 0, 1, np.inf, np.inf], + [3, 0, 1, np.inf, np.inf], + [2, 2, 0, np.inf, np.inf], + [np.inf, np.inf, np.inf, 0, 3], + [np.inf, np.inf, np.inf, 1, 0]] + +undirected_sparse_zero_G = scipy.sparse.csr_matrix(([0, 0, 1, 1, 2, 2, 1, 1], + ([0, 1, 1, 2, 2, 0, 3, 4], + [1, 0, 2, 1, 0, 2, 4, 3])), + shape = (5, 5)) + +undirected_sparse_zero_SP = [[0, 0, 1, np.inf, np.inf], + [0, 0, 1, np.inf, np.inf], + [1, 1, 0, np.inf, np.inf], + [np.inf, np.inf, np.inf, 0, 1], + [np.inf, np.inf, np.inf, 1, 0]] + +directed_pred = np.array([[-9999, 0, 0, 1, 1], + [3, -9999, 0, 1, 1], + [-9999, -9999, -9999, -9999, -9999], + [3, 0, 0, -9999, 1], + [4, 0, 0, 4, -9999]], dtype=float) + +undirected_SP = np.array([[0, 3, 3, 1, 2], + [3, 0, 6, 2, 4], + [3, 6, 0, 4, 5], + [1, 2, 4, 0, 2], + [2, 4, 5, 2, 0]], dtype=float) + +undirected_SP_limit_2 = np.array([[0, np.inf, np.inf, 1, 2], + [np.inf, 0, np.inf, 2, np.inf], + [np.inf, np.inf, 0, np.inf, np.inf], + [1, 2, np.inf, 0, 2], + [2, np.inf, np.inf, 2, 0]], dtype=float) + +undirected_SP_limit_0 = np.ones((5, 5), dtype=float) - np.eye(5) +undirected_SP_limit_0[undirected_SP_limit_0 > 0] = np.inf + +undirected_pred = np.array([[-9999, 0, 0, 0, 0], + [1, -9999, 0, 1, 1], + [2, 0, -9999, 0, 0], + [3, 3, 0, -9999, 3], + [4, 4, 0, 4, -9999]], dtype=float) + +directed_negative_weighted_G = np.array([[0, 0, 0], + [-1, 0, 0], + [0, -1, 0]], dtype=float) + +directed_negative_weighted_SP = np.array([[0, np.inf, np.inf], + [-1, 0, np.inf], + [-2, -1, 0]], dtype=float) + +methods = ['auto', 'FW', 'D', 'BF', 'J'] + + +def test_dijkstra_limit(): + limits = [0, 2, np.inf] + results = [undirected_SP_limit_0, + undirected_SP_limit_2, + undirected_SP] + + def check(limit, result): + SP = dijkstra(undirected_G, directed=False, limit=limit) + assert_array_almost_equal(SP, result) + + for limit, result in zip(limits, results): + check(limit, result) + + +def test_directed(): + def check(method): + SP = shortest_path(directed_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP, directed_SP) + + for method in methods: + check(method) + + +def test_undirected(): + def check(method, directed_in): + if directed_in: + SP1 = shortest_path(directed_G, method=method, directed=False, + overwrite=False) + assert_array_almost_equal(SP1, undirected_SP) + else: + SP2 = shortest_path(undirected_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP2, undirected_SP) + + for method in methods: + for directed_in in (True, False): + check(method, directed_in) + + +def test_directed_sparse_zero(): + # test directed sparse graph with zero-weight edge and two connected components + def check(method): + SP = shortest_path(directed_sparse_zero_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP, directed_sparse_zero_SP) + + for method in methods: + check(method) + + +def test_undirected_sparse_zero(): + def check(method, directed_in): + if directed_in: + SP1 = shortest_path(directed_sparse_zero_G, method=method, directed=False, + overwrite=False) + assert_array_almost_equal(SP1, undirected_sparse_zero_SP) + else: + SP2 = shortest_path(undirected_sparse_zero_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP2, undirected_sparse_zero_SP) + + for method in methods: + for directed_in in (True, False): + check(method, directed_in) + + +@pytest.mark.parametrize('directed, SP_ans', + ((True, directed_SP), + (False, undirected_SP))) +@pytest.mark.parametrize('indices', ([0, 2, 4], [0, 4], [3, 4], [0, 0])) +def test_dijkstra_indices_min_only(directed, SP_ans, indices): + SP_ans = np.array(SP_ans) + indices = np.array(indices, dtype=np.int64) + min_ind_ans = indices[np.argmin(SP_ans[indices, :], axis=0)] + min_d_ans = np.zeros(SP_ans.shape[0], SP_ans.dtype) + for k in range(SP_ans.shape[0]): + min_d_ans[k] = SP_ans[min_ind_ans[k], k] + min_ind_ans[np.isinf(min_d_ans)] = -9999 + + SP, pred, sources = dijkstra(directed_G, + directed=directed, + indices=indices, + min_only=True, + return_predecessors=True) + assert_array_almost_equal(SP, min_d_ans) + assert_array_equal(min_ind_ans, sources) + SP = dijkstra(directed_G, + directed=directed, + indices=indices, + min_only=True, + return_predecessors=False) + assert_array_almost_equal(SP, min_d_ans) + + +@pytest.mark.parametrize('n', (10, 100, 1000)) +def test_dijkstra_min_only_random(n): + np.random.seed(1234) + data = scipy.sparse.rand(n, n, density=0.5, format='lil', + random_state=42, dtype=np.float64) + data.setdiag(np.zeros(n, dtype=np.bool_)) + # choose some random vertices + v = np.arange(n) + np.random.shuffle(v) + indices = v[:int(n*.1)] + ds, pred, sources = dijkstra(data, + directed=True, + indices=indices, + min_only=True, + return_predecessors=True) + for k in range(n): + p = pred[k] + s = sources[k] + while p != -9999: + assert sources[p] == s + p = pred[p] + + +def test_dijkstra_random(): + # reproduces the hang observed in gh-17782 + n = 10 + indices = [0, 4, 4, 5, 7, 9, 0, 6, 2, 3, 7, 9, 1, 2, 9, 2, 5, 6] + indptr = [0, 0, 2, 5, 6, 7, 8, 12, 15, 18, 18] + data = [0.33629, 0.40458, 0.47493, 0.42757, 0.11497, 0.91653, 0.69084, + 0.64979, 0.62555, 0.743, 0.01724, 0.99945, 0.31095, 0.15557, + 0.02439, 0.65814, 0.23478, 0.24072] + graph = scipy.sparse.csr_matrix((data, indices, indptr), shape=(n, n)) + dijkstra(graph, directed=True, return_predecessors=True) + + +def test_gh_17782_segfault(): + text = """%%MatrixMarket matrix coordinate real general + 84 84 22 + 2 1 4.699999809265137e+00 + 6 14 1.199999973177910e-01 + 9 6 1.199999973177910e-01 + 10 16 2.012000083923340e+01 + 11 10 1.422000026702881e+01 + 12 1 9.645999908447266e+01 + 13 18 2.012000083923340e+01 + 14 13 4.679999828338623e+00 + 15 11 1.199999973177910e-01 + 16 12 1.199999973177910e-01 + 18 15 1.199999973177910e-01 + 32 2 2.299999952316284e+00 + 33 20 6.000000000000000e+00 + 33 32 5.000000000000000e+00 + 36 9 3.720000028610229e+00 + 36 37 3.720000028610229e+00 + 36 38 3.720000028610229e+00 + 37 44 8.159999847412109e+00 + 38 32 7.903999328613281e+01 + 43 20 2.400000000000000e+01 + 43 33 4.000000000000000e+00 + 44 43 6.028000259399414e+01 + """ + data = mmread(StringIO(text)) + dijkstra(data, directed=True, return_predecessors=True) + + +def test_shortest_path_indices(): + indices = np.arange(4) + + def check(func, indshape): + outshape = indshape + (5,) + SP = func(directed_G, directed=False, + indices=indices.reshape(indshape)) + assert_array_almost_equal(SP, undirected_SP[indices].reshape(outshape)) + + for indshape in [(4,), (4, 1), (2, 2)]: + for func in (dijkstra, bellman_ford, johnson, shortest_path): + check(func, indshape) + + assert_raises(ValueError, shortest_path, directed_G, method='FW', + indices=indices) + + +def test_predecessors(): + SP_res = {True: directed_SP, + False: undirected_SP} + pred_res = {True: directed_pred, + False: undirected_pred} + + def check(method, directed): + SP, pred = shortest_path(directed_G, method, directed=directed, + overwrite=False, + return_predecessors=True) + assert_array_almost_equal(SP, SP_res[directed]) + assert_array_almost_equal(pred, pred_res[directed]) + + for method in methods: + for directed in (True, False): + check(method, directed) + + +def test_construct_shortest_path(): + def check(method, directed): + SP1, pred = shortest_path(directed_G, + directed=directed, + overwrite=False, + return_predecessors=True) + SP2 = construct_dist_matrix(directed_G, pred, directed=directed) + assert_array_almost_equal(SP1, SP2) + + for method in methods: + for directed in (True, False): + check(method, directed) + + +def test_unweighted_path(): + def check(method, directed): + SP1 = shortest_path(directed_G, + directed=directed, + overwrite=False, + unweighted=True) + SP2 = shortest_path(unweighted_G, + directed=directed, + overwrite=False, + unweighted=False) + assert_array_almost_equal(SP1, SP2) + + for method in methods: + for directed in (True, False): + check(method, directed) + + +def test_negative_cycles(): + # create a small graph with a negative cycle + graph = np.ones([5, 5]) + graph.flat[::6] = 0 + graph[1, 2] = -2 + + def check(method, directed): + assert_raises(NegativeCycleError, shortest_path, graph, method, + directed) + + for directed in (True, False): + for method in ['FW', 'J', 'BF']: + check(method, directed) + + assert_raises(NegativeCycleError, yen, graph, 0, 1, 1, + directed=directed) + + +@pytest.mark.parametrize("method", ['FW', 'J', 'BF']) +def test_negative_weights(method): + SP = shortest_path(directed_negative_weighted_G, method, directed=True) + assert_allclose(SP, directed_negative_weighted_SP, atol=1e-10) + + +def test_masked_input(): + np.ma.masked_equal(directed_G, 0) + + def check(method): + SP = shortest_path(directed_G, method=method, directed=True, + overwrite=False) + assert_array_almost_equal(SP, directed_SP) + + for method in methods: + check(method) + + +def test_overwrite(): + G = np.array([[0, 3, 3, 1, 2], + [3, 0, 0, 2, 4], + [3, 0, 0, 0, 0], + [1, 2, 0, 0, 2], + [2, 4, 0, 2, 0]], dtype=float) + foo = G.copy() + shortest_path(foo, overwrite=False) + assert_array_equal(foo, G) + + +@pytest.mark.parametrize('method', methods) +def test_buffer(method): + # Smoke test that sparse matrices with read-only buffers (e.g., those from + # joblib workers) do not cause:: + # + # ValueError: buffer source array is read-only + # + G = scipy.sparse.csr_matrix([[1.]]) + G.data.flags['WRITEABLE'] = False + shortest_path(G, method=method) + + +def test_NaN_warnings(): + with warnings.catch_warnings(record=True) as record: + shortest_path(np.array([[0, 1], [np.nan, 0]])) + for r in record: + assert r.category is not RuntimeWarning + + +def test_sparse_matrices(): + # Test that using lil,csr and csc sparse matrix do not cause error + G_dense = np.array([[0, 3, 0, 0, 0], + [0, 0, -1, 0, 0], + [0, 0, 0, 2, 0], + [0, 0, 0, 0, 4], + [0, 0, 0, 0, 0]], dtype=float) + SP = shortest_path(G_dense) + G_csr = scipy.sparse.csr_matrix(G_dense) + G_csc = scipy.sparse.csc_matrix(G_dense) + G_lil = scipy.sparse.lil_matrix(G_dense) + assert_array_almost_equal(SP, shortest_path(G_csr)) + assert_array_almost_equal(SP, shortest_path(G_csc)) + assert_array_almost_equal(SP, shortest_path(G_lil)) + + +def test_yen_directed(): + distances, predecessors = yen( + directed_G, + source=0, + sink=3, + K=2, + return_predecessors=True + ) + assert_allclose(distances, [5., 9.]) + assert_allclose(predecessors, directed_2SP_0_to_3) + + +def test_yen_undirected(): + distances = yen( + undirected_G, + source=0, + sink=3, + K=4, + ) + assert_allclose(distances, [1., 4., 5., 8.]) + +def test_yen_unweighted(): + # Ask for more paths than there are, verify only the available paths are returned + distances, predecessors = yen( + directed_G, + source=0, + sink=3, + K=4, + unweighted=True, + return_predecessors=True, + ) + assert_allclose(distances, [2., 3.]) + assert_allclose(predecessors, directed_2SP_0_to_3) + +def test_yen_no_paths(): + distances = yen( + directed_G, + source=2, + sink=3, + K=1, + ) + assert distances.size == 0 + +def test_yen_negative_weights(): + distances = yen( + directed_negative_weighted_G, + source=2, + sink=0, + K=1, + ) + assert_allclose(distances, [-2.]) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_spanning_tree.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_spanning_tree.py new file mode 100644 index 0000000000000000000000000000000000000000..90ef6d1b1ba86170b0264f76e6a63e21749acdc8 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_spanning_tree.py @@ -0,0 +1,66 @@ +"""Test the minimum spanning tree function""" +import numpy as np +from numpy.testing import assert_ +import numpy.testing as npt +from scipy.sparse import csr_matrix +from scipy.sparse.csgraph import minimum_spanning_tree + + +def test_minimum_spanning_tree(): + + # Create a graph with two connected components. + graph = [[0,1,0,0,0], + [1,0,0,0,0], + [0,0,0,8,5], + [0,0,8,0,1], + [0,0,5,1,0]] + graph = np.asarray(graph) + + # Create the expected spanning tree. + expected = [[0,1,0,0,0], + [0,0,0,0,0], + [0,0,0,0,5], + [0,0,0,0,1], + [0,0,0,0,0]] + expected = np.asarray(expected) + + # Ensure minimum spanning tree code gives this expected output. + csgraph = csr_matrix(graph) + mintree = minimum_spanning_tree(csgraph) + mintree_array = mintree.toarray() + npt.assert_array_equal(mintree_array, expected, + 'Incorrect spanning tree found.') + + # Ensure that the original graph was not modified. + npt.assert_array_equal(csgraph.toarray(), graph, + 'Original graph was modified.') + + # Now let the algorithm modify the csgraph in place. + mintree = minimum_spanning_tree(csgraph, overwrite=True) + npt.assert_array_equal(mintree.toarray(), expected, + 'Graph was not properly modified to contain MST.') + + np.random.seed(1234) + for N in (5, 10, 15, 20): + + # Create a random graph. + graph = 3 + np.random.random((N, N)) + csgraph = csr_matrix(graph) + + # The spanning tree has at most N - 1 edges. + mintree = minimum_spanning_tree(csgraph) + assert_(mintree.nnz < N) + + # Set the sub diagonal to 1 to create a known spanning tree. + idx = np.arange(N-1) + graph[idx,idx+1] = 1 + csgraph = csr_matrix(graph) + mintree = minimum_spanning_tree(csgraph) + + # We expect to see this pattern in the spanning tree and otherwise + # have this zero. + expected = np.zeros((N, N)) + expected[idx, idx+1] = 1 + + npt.assert_array_equal(mintree.toarray(), expected, + 'Incorrect spanning tree found.') diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_traversal.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_traversal.py new file mode 100644 index 0000000000000000000000000000000000000000..414e2d14864da8613eaf85f41a0b391ce1ae916d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csgraph/tests/test_traversal.py @@ -0,0 +1,81 @@ +import numpy as np +import pytest +from numpy.testing import assert_array_almost_equal +from scipy.sparse import csr_array +from scipy.sparse.csgraph import (breadth_first_tree, depth_first_tree, + csgraph_to_dense, csgraph_from_dense) + + +def test_graph_breadth_first(): + csgraph = np.array([[0, 1, 2, 0, 0], + [1, 0, 0, 0, 3], + [2, 0, 0, 7, 0], + [0, 0, 7, 0, 1], + [0, 3, 0, 1, 0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + bfirst = np.array([[0, 1, 2, 0, 0], + [0, 0, 0, 0, 3], + [0, 0, 0, 7, 0], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]]) + + for directed in [True, False]: + bfirst_test = breadth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(bfirst_test), + bfirst) + + +def test_graph_depth_first(): + csgraph = np.array([[0, 1, 2, 0, 0], + [1, 0, 0, 0, 3], + [2, 0, 0, 7, 0], + [0, 0, 7, 0, 1], + [0, 3, 0, 1, 0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + dfirst = np.array([[0, 1, 0, 0, 0], + [0, 0, 0, 0, 3], + [0, 0, 0, 0, 0], + [0, 0, 7, 0, 0], + [0, 0, 0, 1, 0]]) + + for directed in [True, False]: + dfirst_test = depth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(dfirst_test), + dfirst) + + +def test_graph_breadth_first_trivial_graph(): + csgraph = np.array([[0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + bfirst = np.array([[0]]) + + for directed in [True, False]: + bfirst_test = breadth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(bfirst_test), + bfirst) + + +def test_graph_depth_first_trivial_graph(): + csgraph = np.array([[0]]) + csgraph = csgraph_from_dense(csgraph, null_value=0) + + bfirst = np.array([[0]]) + + for directed in [True, False]: + bfirst_test = depth_first_tree(csgraph, 0, directed) + assert_array_almost_equal(csgraph_to_dense(bfirst_test), + bfirst) + + +@pytest.mark.parametrize('directed', [True, False]) +@pytest.mark.parametrize('tree_func', [breadth_first_tree, depth_first_tree]) +def test_int64_indices(tree_func, directed): + # See https://github.com/scipy/scipy/issues/18716 + g = csr_array(([1], np.array([[0], [1]], dtype=np.int64)), shape=(2, 2)) + assert g.indices.dtype == np.int64 + tree = tree_func(g, 0, directed=directed) + assert_array_almost_equal(csgraph_to_dense(tree), [[0, 1], [0, 0]]) + diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csr.py new file mode 100644 index 0000000000000000000000000000000000000000..86bb1e072ebe4480e9dcb01f2d36f7387872b898 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/csr.py @@ -0,0 +1,27 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'csr_count_blocks', + 'csr_matrix', + 'csr_tobsr', + 'csr_tocsc', + 'get_csr_submatrix', + 'isspmatrix_csr', + 'spmatrix', + 'upcast', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="csr", + private_modules=["_csr"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/data.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/data.py new file mode 100644 index 0000000000000000000000000000000000000000..a9958bcda6dd35ac0779514d79b7f1c494c1b01a --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/data.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'isscalarlike', + 'name', + 'npfunc', + 'validateaxis', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="data", + private_modules=["_data"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/dia.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/dia.py new file mode 100644 index 0000000000000000000000000000000000000000..f79abd39f114b23df8ceb6eafb7fcc1c07218dcb --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/dia.py @@ -0,0 +1,29 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'check_shape', + 'dia_matrix', + 'dia_matvec', + 'get_sum_dtype', + 'getdtype', + 'isshape', + 'isspmatrix_dia', + 'spmatrix', + 'upcast_char', + 'validateaxis', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="dia", + private_modules=["_dia"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/dok.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/dok.py new file mode 100644 index 0000000000000000000000000000000000000000..847824456eaa3145d5ecb078e30251875168775b --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/dok.py @@ -0,0 +1,32 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'IndexMixin', + 'check_shape', + 'dok_matrix', + 'getdtype', + 'isdense', + 'isintlike', + 'isscalarlike', + 'isshape', + 'isspmatrix_dok', + 'itertools', + 'spmatrix', + 'upcast', + 'upcast_scalar', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="dok", + private_modules=["_dok"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/extract.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/extract.py new file mode 100644 index 0000000000000000000000000000000000000000..be5e161b6f99e57e2b2a6b3d4f1ef6427c07658d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/extract.py @@ -0,0 +1,23 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'coo_matrix', + 'find', + 'tril', + 'triu', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="extract", + private_modules=["_extract"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/lil.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/lil.py new file mode 100644 index 0000000000000000000000000000000000000000..5f7bf8eb03bb36a1b2fa77c5fc0840e532ab64fd --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/lil.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'isspmatrix_lil', + 'lil_array', + 'lil_matrix', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="lil", + private_modules=["_lil"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0d20b194dcbac5c2f48947d37e6b233edc2baf2b --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__init__.py @@ -0,0 +1,146 @@ +""" +Sparse linear algebra (:mod:`scipy.sparse.linalg`) +================================================== + +.. currentmodule:: scipy.sparse.linalg + +Abstract linear operators +------------------------- + +.. autosummary:: + :toctree: generated/ + + LinearOperator -- abstract representation of a linear operator + aslinearoperator -- convert an object to an abstract linear operator + +Matrix Operations +----------------- + +.. autosummary:: + :toctree: generated/ + + inv -- compute the sparse matrix inverse + expm -- compute the sparse matrix exponential + expm_multiply -- compute the product of a matrix exponential and a matrix + matrix_power -- compute the matrix power by raising a matrix to an exponent + +Matrix norms +------------ + +.. autosummary:: + :toctree: generated/ + + norm -- Norm of a sparse matrix + onenormest -- Estimate the 1-norm of a sparse matrix + +Solving linear problems +----------------------- + +Direct methods for linear equation systems: + +.. autosummary:: + :toctree: generated/ + + spsolve -- Solve the sparse linear system Ax=b + spsolve_triangular -- Solve sparse linear system Ax=b for a triangular A. + factorized -- Pre-factorize matrix to a function solving a linear system + MatrixRankWarning -- Warning on exactly singular matrices + use_solver -- Select direct solver to use + +Iterative methods for linear equation systems: + +.. autosummary:: + :toctree: generated/ + + bicg -- Use BIConjugate Gradient iteration to solve Ax = b + bicgstab -- Use BIConjugate Gradient STABilized iteration to solve Ax = b + cg -- Use Conjugate Gradient iteration to solve Ax = b + cgs -- Use Conjugate Gradient Squared iteration to solve Ax = b + gmres -- Use Generalized Minimal RESidual iteration to solve Ax = b + lgmres -- Solve a matrix equation using the LGMRES algorithm + minres -- Use MINimum RESidual iteration to solve Ax = b + qmr -- Use Quasi-Minimal Residual iteration to solve Ax = b + gcrotmk -- Solve a matrix equation using the GCROT(m,k) algorithm + tfqmr -- Use Transpose-Free Quasi-Minimal Residual iteration to solve Ax = b + +Iterative methods for least-squares problems: + +.. autosummary:: + :toctree: generated/ + + lsqr -- Find the least-squares solution to a sparse linear equation system + lsmr -- Find the least-squares solution to a sparse linear equation system + +Matrix factorizations +--------------------- + +Eigenvalue problems: + +.. autosummary:: + :toctree: generated/ + + eigs -- Find k eigenvalues and eigenvectors of the square matrix A + eigsh -- Find k eigenvalues and eigenvectors of a symmetric matrix + lobpcg -- Solve symmetric partial eigenproblems with optional preconditioning + +Singular values problems: + +.. autosummary:: + :toctree: generated/ + + svds -- Compute k singular values/vectors for a sparse matrix + +The `svds` function supports the following solvers: + +.. toctree:: + + sparse.linalg.svds-arpack + sparse.linalg.svds-lobpcg + sparse.linalg.svds-propack + +Complete or incomplete LU factorizations + +.. autosummary:: + :toctree: generated/ + + splu -- Compute a LU decomposition for a sparse matrix + spilu -- Compute an incomplete LU decomposition for a sparse matrix + SuperLU -- Object representing an LU factorization + +Sparse arrays with structure +---------------------------- + +.. autosummary:: + :toctree: generated/ + + LaplacianNd -- Laplacian on a uniform rectangular grid in ``N`` dimensions + +Exceptions +---------- + +.. autosummary:: + :toctree: generated/ + + ArpackNoConvergence + ArpackError + +""" + +from ._isolve import * +from ._dsolve import * +from ._interface import * +from ._eigen import * +from ._matfuncs import * +from ._onenormest import * +from ._norm import * +from ._expm_multiply import * +from ._special_sparse_arrays import * + +# Deprecated namespaces, to be removed in v2.0.0 +from . import isolve, dsolve, interface, eigen, matfuncs + +__all__ = [s for s in dir() if not s.startswith('_')] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34caab0a29531155ce321046796d897935e3cfe1 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_expm_multiply.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_expm_multiply.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fe73069561a44bd5df5811527ec48c9bf6eebc1 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_expm_multiply.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_interface.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_interface.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bffd7629d8eac3898bc41495fb00636897ba3e0e Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_interface.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_matfuncs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_matfuncs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd6829cb045971a44cad9a7e7391c46a7ea0c522 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_matfuncs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_norm.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_norm.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c657039e5a5c550980a11768545f164999263fc9 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_norm.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_onenormest.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_onenormest.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f95ad087cc0ebc777f4fa0a6ecd74a4a4f20315e Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_onenormest.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_special_sparse_arrays.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_special_sparse_arrays.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5da68e84db225fd77d229f815ac138bb9f0e0a8c Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_special_sparse_arrays.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_svdp.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_svdp.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..012f88ba8c2aae253ca24f0c32af8e403d495a9e Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/_svdp.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/dsolve.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/dsolve.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..280161b78a086a5926fe117b2b432aacdc01739a Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/dsolve.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/eigen.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/eigen.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1de1e3921f4aad836cb7f7e5973c8df1de53f842 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/eigen.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/interface.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/interface.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..408724ef44d23cac5b463c7cb36bcacaf30ee16a Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/interface.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/isolve.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/isolve.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afbe28f5174f7940be2a2875ed8173213eb5ecad Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/isolve.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/matfuncs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/matfuncs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cfd34efcfbd4d47374773b315c8fa752b3aff2c9 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/__pycache__/matfuncs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..25278d34ecd3353d409a25f7a94797902fe6ef93 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__init__.py @@ -0,0 +1,22 @@ +""" +Sparse Eigenvalue Solvers +------------------------- + +The submodules of sparse.linalg._eigen: + 1. lobpcg: Locally Optimal Block Preconditioned Conjugate Gradient Method + +""" +from .arpack import * +from .lobpcg import * +from ._svds import svds + +from . import arpack + +__all__ = [ + 'ArpackError', 'ArpackNoConvergence', + 'eigs', 'eigsh', 'lobpcg', 'svds' +] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2ba5cd9ab1832f8807546c0b772428d3ac4a1b3 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/_svds.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/_svds.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7339c44e79a117aaaa3f5b0e56d10035df937453 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/_svds.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/_svds_doc.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/_svds_doc.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..333e1899369405158d1f53285ee07a8cef154379 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/__pycache__/_svds_doc.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/_svds.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/_svds.py new file mode 100644 index 0000000000000000000000000000000000000000..60f7c3cde855289276f5dbf89e726ad06e087977 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/_svds.py @@ -0,0 +1,546 @@ +import math +import numpy as np + +from .arpack import _arpack # type: ignore[attr-defined] +from . import eigsh + +from scipy._lib._util import check_random_state +from scipy.sparse.linalg._interface import LinearOperator, aslinearoperator +from scipy.sparse.linalg._eigen.lobpcg import lobpcg # type: ignore[no-redef] +from scipy.sparse.linalg._svdp import _svdp +from scipy.linalg import svd + +arpack_int = _arpack.timing.nbx.dtype +__all__ = ['svds'] + + +def _herm(x): + return x.T.conj() + + +def _iv(A, k, ncv, tol, which, v0, maxiter, + return_singular, solver, random_state): + + # input validation/standardization for `solver` + # out of order because it's needed for other parameters + solver = str(solver).lower() + solvers = {"arpack", "lobpcg", "propack"} + if solver not in solvers: + raise ValueError(f"solver must be one of {solvers}.") + + # input validation/standardization for `A` + A = aslinearoperator(A) # this takes care of some input validation + if not (np.issubdtype(A.dtype, np.complexfloating) + or np.issubdtype(A.dtype, np.floating)): + message = "`A` must be of floating or complex floating data type." + raise ValueError(message) + if math.prod(A.shape) == 0: + message = "`A` must not be empty." + raise ValueError(message) + + # input validation/standardization for `k` + kmax = min(A.shape) if solver == 'propack' else min(A.shape) - 1 + if int(k) != k or not (0 < k <= kmax): + message = "`k` must be an integer satisfying `0 < k < min(A.shape)`." + raise ValueError(message) + k = int(k) + + # input validation/standardization for `ncv` + if solver == "arpack" and ncv is not None: + if int(ncv) != ncv or not (k < ncv < min(A.shape)): + message = ("`ncv` must be an integer satisfying " + "`k < ncv < min(A.shape)`.") + raise ValueError(message) + ncv = int(ncv) + + # input validation/standardization for `tol` + if tol < 0 or not np.isfinite(tol): + message = "`tol` must be a non-negative floating point value." + raise ValueError(message) + tol = float(tol) + + # input validation/standardization for `which` + which = str(which).upper() + whichs = {'LM', 'SM'} + if which not in whichs: + raise ValueError(f"`which` must be in {whichs}.") + + # input validation/standardization for `v0` + if v0 is not None: + v0 = np.atleast_1d(v0) + if not (np.issubdtype(v0.dtype, np.complexfloating) + or np.issubdtype(v0.dtype, np.floating)): + message = ("`v0` must be of floating or complex floating " + "data type.") + raise ValueError(message) + + shape = (A.shape[0],) if solver == 'propack' else (min(A.shape),) + if v0.shape != shape: + message = f"`v0` must have shape {shape}." + raise ValueError(message) + + # input validation/standardization for `maxiter` + if maxiter is not None and (int(maxiter) != maxiter or maxiter <= 0): + message = "`maxiter` must be a positive integer." + raise ValueError(message) + maxiter = int(maxiter) if maxiter is not None else maxiter + + # input validation/standardization for `return_singular_vectors` + # not going to be flexible with this; too complicated for little gain + rs_options = {True, False, "vh", "u"} + if return_singular not in rs_options: + raise ValueError(f"`return_singular_vectors` must be in {rs_options}.") + + random_state = check_random_state(random_state) + + return (A, k, ncv, tol, which, v0, maxiter, + return_singular, solver, random_state) + + +def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='arpack', random_state=None, options=None): + """ + Partial singular value decomposition of a sparse matrix. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : ndarray, sparse matrix, or LinearOperator + Matrix to decompose of a floating point numeric dtype. + k : int, default: 6 + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= kmax``, where ``kmax=min(M, N)`` for + ``solver='propack'`` and ``kmax=min(M, N) - 1`` otherwise. + ncv : int, optional + When ``solver='arpack'``, this is the number of Lanczos vectors + generated. See :ref:`'arpack' ` for details. + When ``solver='lobpcg'`` or ``solver='propack'``, this parameter is + ignored. + tol : float, optional + Tolerance for singular values. Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. + v0 : ndarray, optional + The starting vector for iteration; see method-specific + documentation (:ref:`'arpack' `, + :ref:`'lobpcg' `), or + :ref:`'propack' ` for details. + maxiter : int, optional + Maximum number of iterations; see method-specific + documentation (:ref:`'arpack' `, + :ref:`'lobpcg' `), or + :ref:`'propack' ` for details. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: if ``M <= N``, compute only the left singular vectors and + return ``None`` for the right singular vectors. Otherwise, compute + all singular vectors. + - ``"vh"``: if ``M > N``, compute only the right singular vectors and + return ``None`` for the left singular vectors. Otherwise, compute + all singular vectors. + + If ``solver='propack'``, the option is respected regardless of the + matrix shape. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + The solver used. + :ref:`'arpack' `, + :ref:`'lobpcg' `, and + :ref:`'propack' ` are supported. + Default: `'arpack'`. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is a naive implementation using ARPACK or LOBPCG as an eigensolver + on the matrix ``A.conj().T @ A`` or ``A @ A.conj().T``, depending on + which one is smaller size, followed by the Rayleigh-Ritz method + as postprocessing; see + Using the normal matrix, in Rayleigh-Ritz method, (2022, Nov. 19), + Wikipedia, https://w.wiki/4zms. + + Alternatively, the PROPACK solver can be called. + + Choices of the input matrix `A` numeric dtype may be limited. + Only ``solver="lobpcg"`` supports all floating point dtypes + real: 'np.float32', 'np.float64', 'np.longdouble' and + complex: 'np.complex64', 'np.complex128', 'np.clongdouble'. + The ``solver="arpack"`` supports only + 'np.float32', 'np.float64', and 'np.complex128'. + + Examples + -------- + Construct a matrix `A` from singular values and vectors. + + >>> import numpy as np + >>> from scipy import sparse, linalg, stats + >>> from scipy.sparse.linalg import svds, aslinearoperator, LinearOperator + + Construct a dense matrix `A` from singular values and vectors. + + >>> rng = np.random.default_rng(258265244568965474821194062361901728911) + >>> orthogonal = stats.ortho_group.rvs(10, random_state=rng) + >>> s = [1e-3, 1, 2, 3, 4] # non-zero singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ np.diag(s) @ vT + + With only four singular values/vectors, the SVD approximates the original + matrix. + + >>> u4, s4, vT4 = svds(A, k=4) + >>> A4 = u4 @ np.diag(s4) @ vT4 + >>> np.allclose(A4, A, atol=1e-3) + True + + With all five non-zero singular values/vectors, we can reproduce + the original matrix more accurately. + + >>> u5, s5, vT5 = svds(A, k=5) + >>> A5 = u5 @ np.diag(s5) @ vT5 + >>> np.allclose(A5, A) + True + + The singular values match the expected singular values. + + >>> np.allclose(s5, s) + True + + Since the singular values are not close to each other in this example, + every singular vector matches as expected up to a difference in sign. + + >>> (np.allclose(np.abs(u5), np.abs(u)) and + ... np.allclose(np.abs(vT5), np.abs(vT))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u5.T @ u5, np.eye(5)) and + ... np.allclose(vT5 @ vT5.T, np.eye(5))) + True + + If there are (nearly) multiple singular values, the corresponding + individual singular vectors may be unstable, but the whole invariant + subspace containing all such singular vectors is computed accurately + as can be measured by angles between subspaces via 'subspace_angles'. + + >>> rng = np.random.default_rng(178686584221410808734965903901790843963) + >>> s = [1, 1 + 1e-6] # non-zero singular values + >>> u, _ = np.linalg.qr(rng.standard_normal((99, 2))) + >>> v, _ = np.linalg.qr(rng.standard_normal((99, 2))) + >>> vT = v.T + >>> A = u @ np.diag(s) @ vT + >>> A = A.astype(np.float32) + >>> u2, s2, vT2 = svds(A, k=2, random_state=rng) + >>> np.allclose(s2, s) + True + + The angles between the individual exact and computed singular vectors + may not be so small. To check use: + + >>> (linalg.subspace_angles(u2[:, :1], u[:, :1]) + + ... linalg.subspace_angles(u2[:, 1:], u[:, 1:])) + array([0.06562513]) # may vary + >>> (linalg.subspace_angles(vT2[:1, :].T, vT[:1, :].T) + + ... linalg.subspace_angles(vT2[1:, :].T, vT[1:, :].T)) + array([0.06562507]) # may vary + + As opposed to the angles between the 2-dimensional invariant subspaces + that these vectors span, which are small for rights singular vectors + + >>> linalg.subspace_angles(u2, u).sum() < 1e-6 + True + + as well as for left singular vectors. + + >>> linalg.subspace_angles(vT2.T, vT.T).sum() < 1e-6 + True + + The next example follows that of 'sklearn.decomposition.TruncatedSVD'. + + >>> rng = np.random.RandomState(0) + >>> X_dense = rng.random(size=(100, 100)) + >>> X_dense[:, 2 * np.arange(50)] = 0 + >>> X = sparse.csr_matrix(X_dense) + >>> _, singular_values, _ = svds(X, k=5, random_state=rng) + >>> print(singular_values) + [ 4.3293... 4.4491... 4.5420... 4.5987... 35.2410...] + + The function can be called without the transpose of the input matrix + ever explicitly constructed. + + >>> rng = np.random.default_rng(102524723947864966825913730119128190974) + >>> G = sparse.rand(8, 9, density=0.5, random_state=rng) + >>> Glo = aslinearoperator(G) + >>> _, singular_values_svds, _ = svds(Glo, k=5, random_state=rng) + >>> _, singular_values_svd, _ = linalg.svd(G.toarray()) + >>> np.allclose(singular_values_svds, singular_values_svd[-4::-1]) + True + + The most memory efficient scenario is where neither + the original matrix, nor its transpose, is explicitly constructed. + Our example computes the smallest singular values and vectors + of 'LinearOperator' constructed from the numpy function 'np.diff' used + column-wise to be consistent with 'LinearOperator' operating on columns. + + >>> diff0 = lambda a: np.diff(a, axis=0) + + Let us create the matrix from 'diff0' to be used for validation only. + + >>> n = 5 # The dimension of the space. + >>> M_from_diff0 = diff0(np.eye(n)) + >>> print(M_from_diff0.astype(int)) + [[-1 1 0 0 0] + [ 0 -1 1 0 0] + [ 0 0 -1 1 0] + [ 0 0 0 -1 1]] + + The matrix 'M_from_diff0' is bi-diagonal and could be alternatively + created directly by + + >>> M = - np.eye(n - 1, n, dtype=int) + >>> np.fill_diagonal(M[:,1:], 1) + >>> np.allclose(M, M_from_diff0) + True + + Its transpose + + >>> print(M.T) + [[-1 0 0 0] + [ 1 -1 0 0] + [ 0 1 -1 0] + [ 0 0 1 -1] + [ 0 0 0 1]] + + can be viewed as the incidence matrix; see + Incidence matrix, (2022, Nov. 19), Wikipedia, https://w.wiki/5YXU, + of a linear graph with 5 vertices and 4 edges. The 5x5 normal matrix + ``M.T @ M`` thus is + + >>> print(M.T @ M) + [[ 1 -1 0 0 0] + [-1 2 -1 0 0] + [ 0 -1 2 -1 0] + [ 0 0 -1 2 -1] + [ 0 0 0 -1 1]] + + the graph Laplacian, while the actually used in 'svds' smaller size + 4x4 normal matrix ``M @ M.T`` + + >>> print(M @ M.T) + [[ 2 -1 0 0] + [-1 2 -1 0] + [ 0 -1 2 -1] + [ 0 0 -1 2]] + + is the so-called edge-based Laplacian; see + Symmetric Laplacian via the incidence matrix, in Laplacian matrix, + (2022, Nov. 19), Wikipedia, https://w.wiki/5YXW. + + The 'LinearOperator' setup needs the options 'rmatvec' and 'rmatmat' + of multiplication by the matrix transpose ``M.T``, but we want to be + matrix-free to save memory, so knowing how ``M.T`` looks like, we + manually construct the following function to be + used in ``rmatmat=diff0t``. + + >>> def diff0t(a): + ... if a.ndim == 1: + ... a = a[:,np.newaxis] # Turn 1D into 2D array + ... d = np.zeros((a.shape[0] + 1, a.shape[1]), dtype=a.dtype) + ... d[0, :] = - a[0, :] + ... d[1:-1, :] = a[0:-1, :] - a[1:, :] + ... d[-1, :] = a[-1, :] + ... return d + + We check that our function 'diff0t' for the matrix transpose is valid. + + >>> np.allclose(M.T, diff0t(np.eye(n-1))) + True + + Now we setup our matrix-free 'LinearOperator' called 'diff0_func_aslo' + and for validation the matrix-based 'diff0_matrix_aslo'. + + >>> def diff0_func_aslo_def(n): + ... return LinearOperator(matvec=diff0, + ... matmat=diff0, + ... rmatvec=diff0t, + ... rmatmat=diff0t, + ... shape=(n - 1, n)) + >>> diff0_func_aslo = diff0_func_aslo_def(n) + >>> diff0_matrix_aslo = aslinearoperator(M_from_diff0) + + And validate both the matrix and its transpose in 'LinearOperator'. + + >>> np.allclose(diff0_func_aslo(np.eye(n)), + ... diff0_matrix_aslo(np.eye(n))) + True + >>> np.allclose(diff0_func_aslo.T(np.eye(n-1)), + ... diff0_matrix_aslo.T(np.eye(n-1))) + True + + Having the 'LinearOperator' setup validated, we run the solver. + + >>> n = 100 + >>> diff0_func_aslo = diff0_func_aslo_def(n) + >>> u, s, vT = svds(diff0_func_aslo, k=3, which='SM') + + The singular values squared and the singular vectors are known + explicitly; see + Pure Dirichlet boundary conditions, in + Eigenvalues and eigenvectors of the second derivative, + (2022, Nov. 19), Wikipedia, https://w.wiki/5YX6, + since 'diff' corresponds to first + derivative, and its smaller size n-1 x n-1 normal matrix + ``M @ M.T`` represent the discrete second derivative with the Dirichlet + boundary conditions. We use these analytic expressions for validation. + + >>> se = 2. * np.sin(np.pi * np.arange(1, 4) / (2. * n)) + >>> ue = np.sqrt(2 / n) * np.sin(np.pi * np.outer(np.arange(1, n), + ... np.arange(1, 4)) / n) + >>> np.allclose(s, se, atol=1e-3) + True + >>> print(np.allclose(np.abs(u), np.abs(ue), atol=1e-6)) + True + + """ + args = _iv(A, k, ncv, tol, which, v0, maxiter, return_singular_vectors, + solver, random_state) + (A, k, ncv, tol, which, v0, maxiter, + return_singular_vectors, solver, random_state) = args + + largest = (which == 'LM') + n, m = A.shape + + if n >= m: + X_dot = A.matvec + X_matmat = A.matmat + XH_dot = A.rmatvec + XH_mat = A.rmatmat + transpose = False + else: + X_dot = A.rmatvec + X_matmat = A.rmatmat + XH_dot = A.matvec + XH_mat = A.matmat + transpose = True + + dtype = getattr(A, 'dtype', None) + if dtype is None: + dtype = A.dot(np.zeros([m, 1])).dtype + + def matvec_XH_X(x): + return XH_dot(X_dot(x)) + + def matmat_XH_X(x): + return XH_mat(X_matmat(x)) + + XH_X = LinearOperator(matvec=matvec_XH_X, dtype=A.dtype, + matmat=matmat_XH_X, + shape=(min(A.shape), min(A.shape))) + + # Get a low rank approximation of the implicitly defined gramian matrix. + # This is not a stable way to approach the problem. + if solver == 'lobpcg': + + if k == 1 and v0 is not None: + X = np.reshape(v0, (-1, 1)) + else: + X = random_state.standard_normal(size=(min(A.shape), k)) + + _, eigvec = lobpcg(XH_X, X, tol=tol ** 2, maxiter=maxiter, + largest=largest) + + elif solver == 'propack': + jobu = return_singular_vectors in {True, 'u'} + jobv = return_singular_vectors in {True, 'vh'} + irl_mode = (which == 'SM') + res = _svdp(A, k=k, tol=tol**2, which=which, maxiter=None, + compute_u=jobu, compute_v=jobv, irl_mode=irl_mode, + kmax=maxiter, v0=v0, random_state=random_state) + + u, s, vh, _ = res # but we'll ignore bnd, the last output + + # PROPACK order appears to be largest first. `svds` output order is not + # guaranteed, according to documentation, but for ARPACK and LOBPCG + # they actually are ordered smallest to largest, so reverse for + # consistency. + s = s[::-1] + u = u[:, ::-1] + vh = vh[::-1] + + u = u if jobu else None + vh = vh if jobv else None + + if return_singular_vectors: + return u, s, vh + else: + return s + + elif solver == 'arpack' or solver is None: + if v0 is None: + v0 = random_state.standard_normal(size=(min(A.shape),)) + _, eigvec = eigsh(XH_X, k=k, tol=tol ** 2, maxiter=maxiter, + ncv=ncv, which=which, v0=v0) + # arpack do not guarantee exactly orthonormal eigenvectors + # for clustered eigenvalues, especially in complex arithmetic + eigvec, _ = np.linalg.qr(eigvec) + + # the eigenvectors eigvec must be orthonomal here; see gh-16712 + Av = X_matmat(eigvec) + if not return_singular_vectors: + s = svd(Av, compute_uv=False, overwrite_a=True) + return s[::-1] + + # compute the left singular vectors of X and update the right ones + # accordingly + u, s, vh = svd(Av, full_matrices=False, overwrite_a=True) + u = u[:, ::-1] + s = s[::-1] + vh = vh[::-1] + + jobu = return_singular_vectors in {True, 'u'} + jobv = return_singular_vectors in {True, 'vh'} + + if transpose: + u_tmp = eigvec @ _herm(vh) if jobu else None + vh = _herm(u) if jobv else None + u = u_tmp + else: + if not jobu: + u = None + vh = vh @ _herm(eigvec) if jobv else None + + return u, s, vh diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/_svds_doc.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/_svds_doc.py new file mode 100644 index 0000000000000000000000000000000000000000..3de1b76d6d4dc437bfbee8faf8171f2dfa2377fa --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/_svds_doc.py @@ -0,0 +1,400 @@ +def _svds_arpack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='arpack', random_state=None): + """ + Partial singular value decomposition of a sparse matrix using ARPACK. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : sparse matrix or LinearOperator + Matrix to decompose. + k : int, optional + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= min(M, N) - 1``. + Default is 6. + ncv : int, optional + The number of Lanczos vectors generated. + The default is ``min(n, max(2*k + 1, 20))``. + If specified, must satistify ``k + 1 < ncv < min(M, N)``; ``ncv > 2*k`` + is recommended. + tol : float, optional + Tolerance for singular values. Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. + v0 : ndarray, optional + The starting vector for iteration: + an (approximate) left singular vector if ``N > M`` and a right singular + vector otherwise. Must be of length ``min(M, N)``. + Default: random + maxiter : int, optional + Maximum number of Arnoldi update iterations allowed; + default is ``min(M, N) * 10``. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: if ``M <= N``, compute only the left singular vectors and + return ``None`` for the right singular vectors. Otherwise, compute + all singular vectors. + - ``"vh"``: if ``M > N``, compute only the right singular vectors and + return ``None`` for the left singular vectors. Otherwise, compute + all singular vectors. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + This is the solver-specific documentation for ``solver='arpack'``. + :ref:`'lobpcg' ` and + :ref:`'propack' ` + are also supported. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is a naive implementation using ARPACK as an eigensolver + on ``A.conj().T @ A`` or ``A @ A.conj().T``, depending on which one is more + efficient. + + Examples + -------- + Construct a matrix ``A`` from singular values and vectors. + + >>> import numpy as np + >>> from scipy.stats import ortho_group + >>> from scipy.sparse import csc_matrix, diags + >>> from scipy.sparse.linalg import svds + >>> rng = np.random.default_rng() + >>> orthogonal = csc_matrix(ortho_group.rvs(10, random_state=rng)) + >>> s = [0.0001, 0.001, 3, 4, 5] # singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ diags(s) @ vT + + With only three singular values/vectors, the SVD approximates the original + matrix. + + >>> u2, s2, vT2 = svds(A, k=3, solver='arpack') + >>> A2 = u2 @ np.diag(s2) @ vT2 + >>> np.allclose(A2, A.toarray(), atol=1e-3) + True + + With all five singular values/vectors, we can reproduce the original + matrix. + + >>> u3, s3, vT3 = svds(A, k=5, solver='arpack') + >>> A3 = u3 @ np.diag(s3) @ vT3 + >>> np.allclose(A3, A.toarray()) + True + + The singular values match the expected singular values, and the singular + vectors are as expected up to a difference in sign. + + >>> (np.allclose(s3, s) and + ... np.allclose(np.abs(u3), np.abs(u.toarray())) and + ... np.allclose(np.abs(vT3), np.abs(vT.toarray()))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u3.T @ u3, np.eye(5)) and + ... np.allclose(vT3 @ vT3.T, np.eye(5))) + True + """ + pass + + +def _svds_lobpcg_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='lobpcg', random_state=None): + """ + Partial singular value decomposition of a sparse matrix using LOBPCG. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : sparse matrix or LinearOperator + Matrix to decompose. + k : int, default: 6 + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= min(M, N) - 1``. + ncv : int, optional + Ignored. + tol : float, optional + Tolerance for singular values. Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. + v0 : ndarray, optional + If `k` is 1, the starting vector for iteration: + an (approximate) left singular vector if ``N > M`` and a right singular + vector otherwise. Must be of length ``min(M, N)``. + Ignored otherwise. + Default: random + maxiter : int, default: 20 + Maximum number of iterations. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: if ``M <= N``, compute only the left singular vectors and + return ``None`` for the right singular vectors. Otherwise, compute + all singular vectors. + - ``"vh"``: if ``M > N``, compute only the right singular vectors and + return ``None`` for the left singular vectors. Otherwise, compute + all singular vectors. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + This is the solver-specific documentation for ``solver='lobpcg'``. + :ref:`'arpack' ` and + :ref:`'propack' ` + are also supported. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is a naive implementation using LOBPCG as an eigensolver + on ``A.conj().T @ A`` or ``A @ A.conj().T``, depending on which one is more + efficient. + + Examples + -------- + Construct a matrix ``A`` from singular values and vectors. + + >>> import numpy as np + >>> from scipy.stats import ortho_group + >>> from scipy.sparse import csc_matrix, diags + >>> from scipy.sparse.linalg import svds + >>> rng = np.random.default_rng() + >>> orthogonal = csc_matrix(ortho_group.rvs(10, random_state=rng)) + >>> s = [0.0001, 0.001, 3, 4, 5] # singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ diags(s) @ vT + + With only three singular values/vectors, the SVD approximates the original + matrix. + + >>> u2, s2, vT2 = svds(A, k=3, solver='lobpcg') + >>> A2 = u2 @ np.diag(s2) @ vT2 + >>> np.allclose(A2, A.toarray(), atol=1e-3) + True + + With all five singular values/vectors, we can reproduce the original + matrix. + + >>> u3, s3, vT3 = svds(A, k=5, solver='lobpcg') + >>> A3 = u3 @ np.diag(s3) @ vT3 + >>> np.allclose(A3, A.toarray()) + True + + The singular values match the expected singular values, and the singular + vectors are as expected up to a difference in sign. + + >>> (np.allclose(s3, s) and + ... np.allclose(np.abs(u3), np.abs(u.todense())) and + ... np.allclose(np.abs(vT3), np.abs(vT.todense()))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u3.T @ u3, np.eye(5)) and + ... np.allclose(vT3 @ vT3.T, np.eye(5))) + True + + """ + pass + + +def _svds_propack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None, + maxiter=None, return_singular_vectors=True, + solver='propack', random_state=None): + """ + Partial singular value decomposition of a sparse matrix using PROPACK. + + Compute the largest or smallest `k` singular values and corresponding + singular vectors of a sparse matrix `A`. The order in which the singular + values are returned is not guaranteed. + + In the descriptions below, let ``M, N = A.shape``. + + Parameters + ---------- + A : sparse matrix or LinearOperator + Matrix to decompose. If `A` is a ``LinearOperator`` + object, it must define both ``matvec`` and ``rmatvec`` methods. + k : int, default: 6 + Number of singular values and singular vectors to compute. + Must satisfy ``1 <= k <= min(M, N)``. + ncv : int, optional + Ignored. + tol : float, optional + The desired relative accuracy for computed singular values. + Zero (default) means machine precision. + which : {'LM', 'SM'} + Which `k` singular values to find: either the largest magnitude ('LM') + or smallest magnitude ('SM') singular values. Note that choosing + ``which='SM'`` will force the ``irl`` option to be set ``True``. + v0 : ndarray, optional + Starting vector for iterations: must be of length ``A.shape[0]``. + If not specified, PROPACK will generate a starting vector. + maxiter : int, optional + Maximum number of iterations / maximal dimension of the Krylov + subspace. Default is ``10 * k``. + return_singular_vectors : {True, False, "u", "vh"} + Singular values are always computed and returned; this parameter + controls the computation and return of singular vectors. + + - ``True``: return singular vectors. + - ``False``: do not return singular vectors. + - ``"u"``: compute only the left singular vectors; return ``None`` for + the right singular vectors. + - ``"vh"``: compute only the right singular vectors; return ``None`` + for the left singular vectors. + + solver : {'arpack', 'propack', 'lobpcg'}, optional + This is the solver-specific documentation for ``solver='propack'``. + :ref:`'arpack' ` and + :ref:`'lobpcg' ` + are also supported. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + options : dict, optional + A dictionary of solver-specific options. No solver-specific options + are currently supported; this parameter is reserved for future use. + + Returns + ------- + u : ndarray, shape=(M, k) + Unitary matrix having left singular vectors as columns. + s : ndarray, shape=(k,) + The singular values. + vh : ndarray, shape=(k, N) + Unitary matrix having right singular vectors as rows. + + Notes + ----- + This is an interface to the Fortran library PROPACK [1]_. + The current default is to run with IRL mode disabled unless seeking the + smallest singular values/vectors (``which='SM'``). + + References + ---------- + + .. [1] Larsen, Rasmus Munk. "PROPACK-Software for large and sparse SVD + calculations." Available online. URL + http://sun.stanford.edu/~rmunk/PROPACK (2004): 2008-2009. + + Examples + -------- + Construct a matrix ``A`` from singular values and vectors. + + >>> import numpy as np + >>> from scipy.stats import ortho_group + >>> from scipy.sparse import csc_matrix, diags + >>> from scipy.sparse.linalg import svds + >>> rng = np.random.default_rng() + >>> orthogonal = csc_matrix(ortho_group.rvs(10, random_state=rng)) + >>> s = [0.0001, 0.001, 3, 4, 5] # singular values + >>> u = orthogonal[:, :5] # left singular vectors + >>> vT = orthogonal[:, 5:].T # right singular vectors + >>> A = u @ diags(s) @ vT + + With only three singular values/vectors, the SVD approximates the original + matrix. + + >>> u2, s2, vT2 = svds(A, k=3, solver='propack') + >>> A2 = u2 @ np.diag(s2) @ vT2 + >>> np.allclose(A2, A.todense(), atol=1e-3) + True + + With all five singular values/vectors, we can reproduce the original + matrix. + + >>> u3, s3, vT3 = svds(A, k=5, solver='propack') + >>> A3 = u3 @ np.diag(s3) @ vT3 + >>> np.allclose(A3, A.todense()) + True + + The singular values match the expected singular values, and the singular + vectors are as expected up to a difference in sign. + + >>> (np.allclose(s3, s) and + ... np.allclose(np.abs(u3), np.abs(u.toarray())) and + ... np.allclose(np.abs(vT3), np.abs(vT.toarray()))) + True + + The singular vectors are also orthogonal. + + >>> (np.allclose(u3.T @ u3, np.eye(5)) and + ... np.allclose(vT3 @ vT3.T, np.eye(5))) + True + + """ + pass diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/COPYING b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..e87667e1b8c178e53c6a7c6268ebc09ab4b0476c --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/COPYING @@ -0,0 +1,45 @@ + +BSD Software License + +Pertains to ARPACK and P_ARPACK + +Copyright (c) 1996-2008 Rice University. +Developed by D.C. Sorensen, R.B. Lehoucq, C. Yang, and K. Maschhoff. +All rights reserved. + +Arpack has been renamed to arpack-ng. + +Copyright (c) 2001-2011 - Scilab Enterprises +Updated by Allan Cornet, Sylvestre Ledru. + +Copyright (c) 2010 - Jordi Gutiérrez Hermoso (Octave patch) + +Copyright (c) 2007 - Sébastien Fabbro (gentoo patch) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + +- Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..679b94480d7ff5a11e037ffb758f2214c6e5097f --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__init__.py @@ -0,0 +1,20 @@ +""" +Eigenvalue solver using iterative methods. + +Find k eigenvectors and eigenvalues of a matrix A using the +Arnoldi/Lanczos iterative methods from ARPACK [1]_,[2]_. + +These methods are most useful for large sparse matrices. + + - eigs(A,k) + - eigsh(A,k) + +References +---------- +.. [1] ARPACK Software, http://www.caam.rice.edu/software/ARPACK/ +.. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: + Solution of Large Scale Eigenvalue Problems by Implicitly Restarted + Arnoldi Methods. SIAM, Philadelphia, PA, 1998. + +""" +from .arpack import * diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8067bd2665833f1adfa409f32219cf378d4e6489 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__pycache__/arpack.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__pycache__/arpack.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8e4b11792bf92393294e18289a7fbdc81aa50a3 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/__pycache__/arpack.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/arpack.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/arpack.py new file mode 100644 index 0000000000000000000000000000000000000000..f7a6fa218ca462212f1ed8a2fc0bd0037cf9d44b --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/arpack.py @@ -0,0 +1,1702 @@ +""" +Find a few eigenvectors and eigenvalues of a matrix. + + +Uses ARPACK: https://github.com/opencollab/arpack-ng + +""" +# Wrapper implementation notes +# +# ARPACK Entry Points +# ------------------- +# The entry points to ARPACK are +# - (s,d)seupd : single and double precision symmetric matrix +# - (s,d,c,z)neupd: single,double,complex,double complex general matrix +# This wrapper puts the *neupd (general matrix) interfaces in eigs() +# and the *seupd (symmetric matrix) in eigsh(). +# There is no specialized interface for complex Hermitian matrices. +# To find eigenvalues of a complex Hermitian matrix you +# may use eigsh(), but eigsh() will simply call eigs() +# and return the real part of the eigenvalues thus obtained. + +# Number of eigenvalues returned and complex eigenvalues +# ------------------------------------------------------ +# The ARPACK nonsymmetric real and double interface (s,d)naupd return +# eigenvalues and eigenvectors in real (float,double) arrays. +# Since the eigenvalues and eigenvectors are, in general, complex +# ARPACK puts the real and imaginary parts in consecutive entries +# in real-valued arrays. This wrapper puts the real entries +# into complex data types and attempts to return the requested eigenvalues +# and eigenvectors. + + +# Solver modes +# ------------ +# ARPACK and handle shifted and shift-inverse computations +# for eigenvalues by providing a shift (sigma) and a solver. + +import numpy as np +import warnings +from scipy.sparse.linalg._interface import aslinearoperator, LinearOperator +from scipy.sparse import eye, issparse +from scipy.linalg import eig, eigh, lu_factor, lu_solve +from scipy.sparse._sputils import isdense, is_pydata_spmatrix +from scipy.sparse.linalg import gmres, splu +from scipy._lib._util import _aligned_zeros +from scipy._lib._threadsafety import ReentrancyLock + +from . import _arpack +arpack_int = _arpack.timing.nbx.dtype + +__docformat__ = "restructuredtext en" + +__all__ = ['eigs', 'eigsh', 'ArpackError', 'ArpackNoConvergence'] + + +_type_conv = {'f': 's', 'd': 'd', 'F': 'c', 'D': 'z'} +_ndigits = {'f': 5, 'd': 12, 'F': 5, 'D': 12} + +DNAUPD_ERRORS = { + 0: "Normal exit.", + 1: "Maximum number of iterations taken. " + "All possible eigenvalues of OP has been found. IPARAM(5) " + "returns the number of wanted converged Ritz values.", + 2: "No longer an informational error. Deprecated starting " + "with release 2 of ARPACK.", + 3: "No shifts could be applied during a cycle of the " + "Implicitly restarted Arnoldi iteration. One possibility " + "is to increase the size of NCV relative to NEV. ", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV-NEV >= 2 and less than or equal to N.", + -4: "The maximum number of Arnoldi update iterations allowed " + "must be greater than zero.", + -5: " WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work array WORKL is not sufficient.", + -8: "Error return from LAPACK eigenvalue calculation;", + -9: "Starting vector is zero.", + -10: "IPARAM(7) must be 1,2,3,4.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "IPARAM(1) must be equal to 0 or 1.", + -13: "NEV and WHICH = 'BE' are incompatible.", + -9999: "Could not build an Arnoldi factorization. " + "IPARAM(5) returns the size of the current Arnoldi " + "factorization. The user is advised to check that " + "enough workspace and array storage has been allocated." +} + +SNAUPD_ERRORS = DNAUPD_ERRORS + +ZNAUPD_ERRORS = DNAUPD_ERRORS.copy() +ZNAUPD_ERRORS[-10] = "IPARAM(7) must be 1,2,3." + +CNAUPD_ERRORS = ZNAUPD_ERRORS + +DSAUPD_ERRORS = { + 0: "Normal exit.", + 1: "Maximum number of iterations taken. " + "All possible eigenvalues of OP has been found.", + 2: "No longer an informational error. Deprecated starting with " + "release 2 of ARPACK.", + 3: "No shifts could be applied during a cycle of the Implicitly " + "restarted Arnoldi iteration. One possibility is to increase " + "the size of NCV relative to NEV. ", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV must be greater than NEV and less than or equal to N.", + -4: "The maximum number of Arnoldi update iterations allowed " + "must be greater than zero.", + -5: "WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work array WORKL is not sufficient.", + -8: "Error return from trid. eigenvalue calculation; " + "Informational error from LAPACK routine dsteqr .", + -9: "Starting vector is zero.", + -10: "IPARAM(7) must be 1,2,3,4,5.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "IPARAM(1) must be equal to 0 or 1.", + -13: "NEV and WHICH = 'BE' are incompatible. ", + -9999: "Could not build an Arnoldi factorization. " + "IPARAM(5) returns the size of the current Arnoldi " + "factorization. The user is advised to check that " + "enough workspace and array storage has been allocated.", +} + +SSAUPD_ERRORS = DSAUPD_ERRORS + +DNEUPD_ERRORS = { + 0: "Normal exit.", + 1: "The Schur form computed by LAPACK routine dlahqr " + "could not be reordered by LAPACK routine dtrsen. " + "Re-enter subroutine dneupd with IPARAM(5)NCV and " + "increase the size of the arrays DR and DI to have " + "dimension at least dimension NCV and allocate at least NCV " + "columns for Z. NOTE: Not necessary if Z and V share " + "the same space. Please notify the authors if this error" + "occurs.", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV-NEV >= 2 and less than or equal to N.", + -5: "WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work WORKL array is not sufficient.", + -8: "Error return from calculation of a real Schur form. " + "Informational error from LAPACK routine dlahqr .", + -9: "Error return from calculation of eigenvectors. " + "Informational error from LAPACK routine dtrevc.", + -10: "IPARAM(7) must be 1,2,3,4.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "HOWMNY = 'S' not yet implemented", + -13: "HOWMNY must be one of 'A' or 'P' if RVEC = .true.", + -14: "DNAUPD did not find any eigenvalues to sufficient " + "accuracy.", + -15: "DNEUPD got a different count of the number of converged " + "Ritz values than DNAUPD got. This indicates the user " + "probably made an error in passing data from DNAUPD to " + "DNEUPD or that the data was modified before entering " + "DNEUPD", +} + +SNEUPD_ERRORS = DNEUPD_ERRORS.copy() +SNEUPD_ERRORS[1] = ("The Schur form computed by LAPACK routine slahqr " + "could not be reordered by LAPACK routine strsen . " + "Re-enter subroutine dneupd with IPARAM(5)=NCV and " + "increase the size of the arrays DR and DI to have " + "dimension at least dimension NCV and allocate at least " + "NCV columns for Z. NOTE: Not necessary if Z and V share " + "the same space. Please notify the authors if this error " + "occurs.") +SNEUPD_ERRORS[-14] = ("SNAUPD did not find any eigenvalues to sufficient " + "accuracy.") +SNEUPD_ERRORS[-15] = ("SNEUPD got a different count of the number of " + "converged Ritz values than SNAUPD got. This indicates " + "the user probably made an error in passing data from " + "SNAUPD to SNEUPD or that the data was modified before " + "entering SNEUPD") + +ZNEUPD_ERRORS = {0: "Normal exit.", + 1: "The Schur form computed by LAPACK routine csheqr " + "could not be reordered by LAPACK routine ztrsen. " + "Re-enter subroutine zneupd with IPARAM(5)=NCV and " + "increase the size of the array D to have " + "dimension at least dimension NCV and allocate at least " + "NCV columns for Z. NOTE: Not necessary if Z and V share " + "the same space. Please notify the authors if this error " + "occurs.", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV-NEV >= 1 and less than or equal to N.", + -5: "WHICH must be one of 'LM', 'SM', 'LR', 'SR', 'LI', 'SI'", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work WORKL array is not sufficient.", + -8: "Error return from LAPACK eigenvalue calculation. " + "This should never happened.", + -9: "Error return from calculation of eigenvectors. " + "Informational error from LAPACK routine ztrevc.", + -10: "IPARAM(7) must be 1,2,3", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "HOWMNY = 'S' not yet implemented", + -13: "HOWMNY must be one of 'A' or 'P' if RVEC = .true.", + -14: "ZNAUPD did not find any eigenvalues to sufficient " + "accuracy.", + -15: "ZNEUPD got a different count of the number of " + "converged Ritz values than ZNAUPD got. This " + "indicates the user probably made an error in passing " + "data from ZNAUPD to ZNEUPD or that the data was " + "modified before entering ZNEUPD" + } + +CNEUPD_ERRORS = ZNEUPD_ERRORS.copy() +CNEUPD_ERRORS[-14] = ("CNAUPD did not find any eigenvalues to sufficient " + "accuracy.") +CNEUPD_ERRORS[-15] = ("CNEUPD got a different count of the number of " + "converged Ritz values than CNAUPD got. This indicates " + "the user probably made an error in passing data from " + "CNAUPD to CNEUPD or that the data was modified before " + "entering CNEUPD") + +DSEUPD_ERRORS = { + 0: "Normal exit.", + -1: "N must be positive.", + -2: "NEV must be positive.", + -3: "NCV must be greater than NEV and less than or equal to N.", + -5: "WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.", + -6: "BMAT must be one of 'I' or 'G'.", + -7: "Length of private work WORKL array is not sufficient.", + -8: ("Error return from trid. eigenvalue calculation; " + "Information error from LAPACK routine dsteqr."), + -9: "Starting vector is zero.", + -10: "IPARAM(7) must be 1,2,3,4,5.", + -11: "IPARAM(7) = 1 and BMAT = 'G' are incompatible.", + -12: "NEV and WHICH = 'BE' are incompatible.", + -14: "DSAUPD did not find any eigenvalues to sufficient accuracy.", + -15: "HOWMNY must be one of 'A' or 'S' if RVEC = .true.", + -16: "HOWMNY = 'S' not yet implemented", + -17: ("DSEUPD got a different count of the number of converged " + "Ritz values than DSAUPD got. This indicates the user " + "probably made an error in passing data from DSAUPD to " + "DSEUPD or that the data was modified before entering " + "DSEUPD.") +} + +SSEUPD_ERRORS = DSEUPD_ERRORS.copy() +SSEUPD_ERRORS[-14] = ("SSAUPD did not find any eigenvalues " + "to sufficient accuracy.") +SSEUPD_ERRORS[-17] = ("SSEUPD got a different count of the number of " + "converged " + "Ritz values than SSAUPD got. This indicates the user " + "probably made an error in passing data from SSAUPD to " + "SSEUPD or that the data was modified before entering " + "SSEUPD.") + +_SAUPD_ERRORS = {'d': DSAUPD_ERRORS, + 's': SSAUPD_ERRORS} +_NAUPD_ERRORS = {'d': DNAUPD_ERRORS, + 's': SNAUPD_ERRORS, + 'z': ZNAUPD_ERRORS, + 'c': CNAUPD_ERRORS} +_SEUPD_ERRORS = {'d': DSEUPD_ERRORS, + 's': SSEUPD_ERRORS} +_NEUPD_ERRORS = {'d': DNEUPD_ERRORS, + 's': SNEUPD_ERRORS, + 'z': ZNEUPD_ERRORS, + 'c': CNEUPD_ERRORS} + +# accepted values of parameter WHICH in _SEUPD +_SEUPD_WHICH = ['LM', 'SM', 'LA', 'SA', 'BE'] + +# accepted values of parameter WHICH in _NAUPD +_NEUPD_WHICH = ['LM', 'SM', 'LR', 'SR', 'LI', 'SI'] + + +class ArpackError(RuntimeError): + """ + ARPACK error + """ + + def __init__(self, info, infodict=_NAUPD_ERRORS): + msg = infodict.get(info, "Unknown error") + RuntimeError.__init__(self, "ARPACK error %d: %s" % (info, msg)) + + +class ArpackNoConvergence(ArpackError): + """ + ARPACK iteration did not converge + + Attributes + ---------- + eigenvalues : ndarray + Partial result. Converged eigenvalues. + eigenvectors : ndarray + Partial result. Converged eigenvectors. + + """ + + def __init__(self, msg, eigenvalues, eigenvectors): + ArpackError.__init__(self, -1, {-1: msg}) + self.eigenvalues = eigenvalues + self.eigenvectors = eigenvectors + + +def choose_ncv(k): + """ + Choose number of lanczos vectors based on target number + of singular/eigen values and vectors to compute, k. + """ + return max(2 * k + 1, 20) + + +class _ArpackParams: + def __init__(self, n, k, tp, mode=1, sigma=None, + ncv=None, v0=None, maxiter=None, which="LM", tol=0): + if k <= 0: + raise ValueError("k must be positive, k=%d" % k) + + if maxiter is None: + maxiter = n * 10 + if maxiter <= 0: + raise ValueError("maxiter must be positive, maxiter=%d" % maxiter) + + if tp not in 'fdFD': + raise ValueError("matrix type must be 'f', 'd', 'F', or 'D'") + + if v0 is not None: + # ARPACK overwrites its initial resid, make a copy + self.resid = np.array(v0, copy=True) + info = 1 + else: + # ARPACK will use a random initial vector. + self.resid = np.zeros(n, tp) + info = 0 + + if sigma is None: + #sigma not used + self.sigma = 0 + else: + self.sigma = sigma + + if ncv is None: + ncv = choose_ncv(k) + ncv = min(ncv, n) + + self.v = np.zeros((n, ncv), tp) # holds Ritz vectors + self.iparam = np.zeros(11, arpack_int) + + # set solver mode and parameters + ishfts = 1 + self.mode = mode + self.iparam[0] = ishfts + self.iparam[2] = maxiter + self.iparam[3] = 1 + self.iparam[6] = mode + + self.n = n + self.tol = tol + self.k = k + self.maxiter = maxiter + self.ncv = ncv + self.which = which + self.tp = tp + self.info = info + + self.converged = False + self.ido = 0 + + def _raise_no_convergence(self): + msg = "No convergence (%d iterations, %d/%d eigenvectors converged)" + k_ok = self.iparam[4] + num_iter = self.iparam[2] + try: + ev, vec = self.extract(True) + except ArpackError as err: + msg = f"{msg} [{err}]" + ev = np.zeros((0,)) + vec = np.zeros((self.n, 0)) + k_ok = 0 + raise ArpackNoConvergence(msg % (num_iter, k_ok, self.k), ev, vec) + + +class _SymmetricArpackParams(_ArpackParams): + def __init__(self, n, k, tp, matvec, mode=1, M_matvec=None, + Minv_matvec=None, sigma=None, + ncv=None, v0=None, maxiter=None, which="LM", tol=0): + # The following modes are supported: + # mode = 1: + # Solve the standard eigenvalue problem: + # A*x = lambda*x : + # A - symmetric + # Arguments should be + # matvec = left multiplication by A + # M_matvec = None [not used] + # Minv_matvec = None [not used] + # + # mode = 2: + # Solve the general eigenvalue problem: + # A*x = lambda*M*x + # A - symmetric + # M - symmetric positive definite + # Arguments should be + # matvec = left multiplication by A + # M_matvec = left multiplication by M + # Minv_matvec = left multiplication by M^-1 + # + # mode = 3: + # Solve the general eigenvalue problem in shift-invert mode: + # A*x = lambda*M*x + # A - symmetric + # M - symmetric positive semi-definite + # Arguments should be + # matvec = None [not used] + # M_matvec = left multiplication by M + # or None, if M is the identity + # Minv_matvec = left multiplication by [A-sigma*M]^-1 + # + # mode = 4: + # Solve the general eigenvalue problem in Buckling mode: + # A*x = lambda*AG*x + # A - symmetric positive semi-definite + # AG - symmetric indefinite + # Arguments should be + # matvec = left multiplication by A + # M_matvec = None [not used] + # Minv_matvec = left multiplication by [A-sigma*AG]^-1 + # + # mode = 5: + # Solve the general eigenvalue problem in Cayley-transformed mode: + # A*x = lambda*M*x + # A - symmetric + # M - symmetric positive semi-definite + # Arguments should be + # matvec = left multiplication by A + # M_matvec = left multiplication by M + # or None, if M is the identity + # Minv_matvec = left multiplication by [A-sigma*M]^-1 + if mode == 1: + if matvec is None: + raise ValueError("matvec must be specified for mode=1") + if M_matvec is not None: + raise ValueError("M_matvec cannot be specified for mode=1") + if Minv_matvec is not None: + raise ValueError("Minv_matvec cannot be specified for mode=1") + + self.OP = matvec + self.B = lambda x: x + self.bmat = 'I' + elif mode == 2: + if matvec is None: + raise ValueError("matvec must be specified for mode=2") + if M_matvec is None: + raise ValueError("M_matvec must be specified for mode=2") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=2") + + self.OP = lambda x: Minv_matvec(matvec(x)) + self.OPa = Minv_matvec + self.OPb = matvec + self.B = M_matvec + self.bmat = 'G' + elif mode == 3: + if matvec is not None: + raise ValueError("matvec must not be specified for mode=3") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=3") + + if M_matvec is None: + self.OP = Minv_matvec + self.OPa = Minv_matvec + self.B = lambda x: x + self.bmat = 'I' + else: + self.OP = lambda x: Minv_matvec(M_matvec(x)) + self.OPa = Minv_matvec + self.B = M_matvec + self.bmat = 'G' + elif mode == 4: + if matvec is None: + raise ValueError("matvec must be specified for mode=4") + if M_matvec is not None: + raise ValueError("M_matvec must not be specified for mode=4") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=4") + self.OPa = Minv_matvec + self.OP = lambda x: self.OPa(matvec(x)) + self.B = matvec + self.bmat = 'G' + elif mode == 5: + if matvec is None: + raise ValueError("matvec must be specified for mode=5") + if Minv_matvec is None: + raise ValueError("Minv_matvec must be specified for mode=5") + + self.OPa = Minv_matvec + self.A_matvec = matvec + + if M_matvec is None: + self.OP = lambda x: Minv_matvec(matvec(x) + sigma * x) + self.B = lambda x: x + self.bmat = 'I' + else: + self.OP = lambda x: Minv_matvec(matvec(x) + + sigma * M_matvec(x)) + self.B = M_matvec + self.bmat = 'G' + else: + raise ValueError("mode=%i not implemented" % mode) + + if which not in _SEUPD_WHICH: + raise ValueError("which must be one of %s" + % ' '.join(_SEUPD_WHICH)) + if k >= n: + raise ValueError("k must be less than ndim(A), k=%d" % k) + + _ArpackParams.__init__(self, n, k, tp, mode, sigma, + ncv, v0, maxiter, which, tol) + + if self.ncv > n or self.ncv <= k: + raise ValueError("ncv must be k= n - 1: + raise ValueError("k must be less than ndim(A)-1, k=%d" % k) + + _ArpackParams.__init__(self, n, k, tp, mode, sigma, + ncv, v0, maxiter, which, tol) + + if self.ncv > n or self.ncv <= k + 1: + raise ValueError("ncv must be k+1 k, so we'll + # throw out this case. + nreturned -= 1 + i += 1 + + else: + # real matrix, mode 3 or 4, imag(sigma) is nonzero: + # see remark 3 in neupd.f + # Build complex eigenvalues from real and imaginary parts + i = 0 + while i <= k: + if abs(d[i].imag) == 0: + d[i] = np.dot(zr[:, i], self.matvec(zr[:, i])) + else: + if i < k: + z[:, i] = zr[:, i] + 1.0j * zr[:, i + 1] + z[:, i + 1] = z[:, i].conjugate() + d[i] = ((np.dot(zr[:, i], + self.matvec(zr[:, i])) + + np.dot(zr[:, i + 1], + self.matvec(zr[:, i + 1]))) + + 1j * (np.dot(zr[:, i], + self.matvec(zr[:, i + 1])) + - np.dot(zr[:, i + 1], + self.matvec(zr[:, i])))) + d[i + 1] = d[i].conj() + i += 1 + else: + #last eigenvalue is complex: the imaginary part of + # the eigenvector has not been returned + #this can only happen if nreturned > k, so we'll + # throw out this case. + nreturned -= 1 + i += 1 + + # Now we have k+1 possible eigenvalues and eigenvectors + # Return the ones specified by the keyword "which" + + if nreturned <= k: + # we got less or equal as many eigenvalues we wanted + d = d[:nreturned] + z = z[:, :nreturned] + else: + # we got one extra eigenvalue (likely a cc pair, but which?) + if self.mode in (1, 2): + rd = d + elif self.mode in (3, 4): + rd = 1 / (d - self.sigma) + + if self.which in ['LR', 'SR']: + ind = np.argsort(rd.real) + elif self.which in ['LI', 'SI']: + # for LI,SI ARPACK returns largest,smallest + # abs(imaginary) (complex pairs come together) + ind = np.argsort(abs(rd.imag)) + else: + ind = np.argsort(abs(rd)) + + if self.which in ['LR', 'LM', 'LI']: + ind = ind[-k:][::-1] + elif self.which in ['SR', 'SM', 'SI']: + ind = ind[:k] + + d = d[ind] + z = z[:, ind] + else: + # complex is so much simpler... + d, z, ierr =\ + self._arpack_extract(return_eigenvectors, + howmny, sselect, self.sigma, workev, + self.bmat, self.which, k, self.tol, self.resid, + self.v, self.iparam, self.ipntr, + self.workd, self.workl, self.rwork, ierr) + + if ierr != 0: + raise ArpackError(ierr, infodict=self.extract_infodict) + + k_ok = self.iparam[4] + d = d[:k_ok] + z = z[:, :k_ok] + + if return_eigenvectors: + return d, z + else: + return d + + +def _aslinearoperator_with_dtype(m): + m = aslinearoperator(m) + if not hasattr(m, 'dtype'): + x = np.zeros(m.shape[1]) + m.dtype = (m * x).dtype + return m + + +class SpLuInv(LinearOperator): + """ + SpLuInv: + helper class to repeatedly solve M*x=b + using a sparse LU-decomposition of M + """ + + def __init__(self, M): + self.M_lu = splu(M) + self.shape = M.shape + self.dtype = M.dtype + self.isreal = not np.issubdtype(self.dtype, np.complexfloating) + + def _matvec(self, x): + # careful here: splu.solve will throw away imaginary + # part of x if M is real + x = np.asarray(x) + if self.isreal and np.issubdtype(x.dtype, np.complexfloating): + return (self.M_lu.solve(np.real(x).astype(self.dtype)) + + 1j * self.M_lu.solve(np.imag(x).astype(self.dtype))) + else: + return self.M_lu.solve(x.astype(self.dtype)) + + +class LuInv(LinearOperator): + """ + LuInv: + helper class to repeatedly solve M*x=b + using an LU-decomposition of M + """ + + def __init__(self, M): + self.M_lu = lu_factor(M) + self.shape = M.shape + self.dtype = M.dtype + + def _matvec(self, x): + return lu_solve(self.M_lu, x) + + +def gmres_loose(A, b, tol): + """ + gmres with looser termination condition. + """ + b = np.asarray(b) + min_tol = 1000 * np.sqrt(b.size) * np.finfo(b.dtype).eps + return gmres(A, b, rtol=max(tol, min_tol), atol=0) + + +class IterInv(LinearOperator): + """ + IterInv: + helper class to repeatedly solve M*x=b + using an iterative method. + """ + + def __init__(self, M, ifunc=gmres_loose, tol=0): + self.M = M + if hasattr(M, 'dtype'): + self.dtype = M.dtype + else: + x = np.zeros(M.shape[1]) + self.dtype = (M * x).dtype + self.shape = M.shape + + if tol <= 0: + # when tol=0, ARPACK uses machine tolerance as calculated + # by LAPACK's _LAMCH function. We should match this + tol = 2 * np.finfo(self.dtype).eps + self.ifunc = ifunc + self.tol = tol + + def _matvec(self, x): + b, info = self.ifunc(self.M, x, tol=self.tol) + if info != 0: + raise ValueError("Error in inverting M: function " + "%s did not converge (info = %i)." + % (self.ifunc.__name__, info)) + return b + + +class IterOpInv(LinearOperator): + """ + IterOpInv: + helper class to repeatedly solve [A-sigma*M]*x = b + using an iterative method + """ + + def __init__(self, A, M, sigma, ifunc=gmres_loose, tol=0): + self.A = A + self.M = M + self.sigma = sigma + + def mult_func(x): + return A.matvec(x) - sigma * M.matvec(x) + + def mult_func_M_None(x): + return A.matvec(x) - sigma * x + + x = np.zeros(A.shape[1]) + if M is None: + dtype = mult_func_M_None(x).dtype + self.OP = LinearOperator(self.A.shape, + mult_func_M_None, + dtype=dtype) + else: + dtype = mult_func(x).dtype + self.OP = LinearOperator(self.A.shape, + mult_func, + dtype=dtype) + self.shape = A.shape + + if tol <= 0: + # when tol=0, ARPACK uses machine tolerance as calculated + # by LAPACK's _LAMCH function. We should match this + tol = 2 * np.finfo(self.OP.dtype).eps + self.ifunc = ifunc + self.tol = tol + + def _matvec(self, x): + b, info = self.ifunc(self.OP, x, tol=self.tol) + if info != 0: + raise ValueError("Error in inverting [A-sigma*M]: function " + "%s did not converge (info = %i)." + % (self.ifunc.__name__, info)) + return b + + @property + def dtype(self): + return self.OP.dtype + + +def _fast_spmatrix_to_csc(A, hermitian=False): + """Convert sparse matrix to CSC (by transposing, if possible)""" + if (A.format == "csr" and hermitian + and not np.issubdtype(A.dtype, np.complexfloating)): + return A.T + elif is_pydata_spmatrix(A): + # No need to convert + return A + else: + return A.tocsc() + + +def get_inv_matvec(M, hermitian=False, tol=0): + if isdense(M): + return LuInv(M).matvec + elif issparse(M) or is_pydata_spmatrix(M): + M = _fast_spmatrix_to_csc(M, hermitian=hermitian) + return SpLuInv(M).matvec + else: + return IterInv(M, tol=tol).matvec + + +def get_OPinv_matvec(A, M, sigma, hermitian=False, tol=0): + if sigma == 0: + return get_inv_matvec(A, hermitian=hermitian, tol=tol) + + if M is None: + #M is the identity matrix + if isdense(A): + if (np.issubdtype(A.dtype, np.complexfloating) + or np.imag(sigma) == 0): + A = np.copy(A) + else: + A = A + 0j + A.flat[::A.shape[1] + 1] -= sigma + return LuInv(A).matvec + elif issparse(A) or is_pydata_spmatrix(A): + A = A - sigma * eye(A.shape[0]) + A = _fast_spmatrix_to_csc(A, hermitian=hermitian) + return SpLuInv(A).matvec + else: + return IterOpInv(_aslinearoperator_with_dtype(A), + M, sigma, tol=tol).matvec + else: + if ((not isdense(A) and not issparse(A) and not is_pydata_spmatrix(A)) or + (not isdense(M) and not issparse(M) and not is_pydata_spmatrix(A))): + return IterOpInv(_aslinearoperator_with_dtype(A), + _aslinearoperator_with_dtype(M), + sigma, tol=tol).matvec + elif isdense(A) or isdense(M): + return LuInv(A - sigma * M).matvec + else: + OP = A - sigma * M + OP = _fast_spmatrix_to_csc(OP, hermitian=hermitian) + return SpLuInv(OP).matvec + + +# ARPACK is not threadsafe or reentrant (SAVE variables), so we need a +# lock and a re-entering check. +_ARPACK_LOCK = ReentrancyLock("Nested calls to eigs/eighs not allowed: " + "ARPACK is not re-entrant") + + +def eigs(A, k=6, M=None, sigma=None, which='LM', v0=None, + ncv=None, maxiter=None, tol=0, return_eigenvectors=True, + Minv=None, OPinv=None, OPpart=None): + """ + Find k eigenvalues and eigenvectors of the square matrix A. + + Solves ``A @ x[i] = w[i] * x[i]``, the standard eigenvalue problem + for w[i] eigenvalues with corresponding eigenvectors x[i]. + + If M is specified, solves ``A @ x[i] = w[i] * M @ x[i]``, the + generalized eigenvalue problem for w[i] eigenvalues + with corresponding eigenvectors x[i] + + Parameters + ---------- + A : ndarray, sparse matrix or LinearOperator + An array, sparse matrix, or LinearOperator representing + the operation ``A @ x``, where A is a real or complex square matrix. + k : int, optional + The number of eigenvalues and eigenvectors desired. + `k` must be smaller than N-1. It is not possible to compute all + eigenvectors of a matrix. + M : ndarray, sparse matrix or LinearOperator, optional + An array, sparse matrix, or LinearOperator representing + the operation M@x for the generalized eigenvalue problem + + A @ x = w * M @ x. + + M must represent a real symmetric matrix if A is real, and must + represent a complex Hermitian matrix if A is complex. For best + results, the data type of M should be the same as that of A. + Additionally: + + If `sigma` is None, M is positive definite + + If sigma is specified, M is positive semi-definite + + If sigma is None, eigs requires an operator to compute the solution + of the linear equation ``M @ x = b``. This is done internally via a + (sparse) LU decomposition for an explicit matrix M, or via an + iterative solver for a general linear operator. Alternatively, + the user can supply the matrix or operator Minv, which gives + ``x = Minv @ b = M^-1 @ b``. + sigma : real or complex, optional + Find eigenvalues near sigma using shift-invert mode. This requires + an operator to compute the solution of the linear system + ``[A - sigma * M] @ x = b``, where M is the identity matrix if + unspecified. This is computed internally via a (sparse) LU + decomposition for explicit matrices A & M, or via an iterative + solver if either A or M is a general linear operator. + Alternatively, the user can supply the matrix or operator OPinv, + which gives ``x = OPinv @ b = [A - sigma * M]^-1 @ b``. + For a real matrix A, shift-invert can either be done in imaginary + mode or real mode, specified by the parameter OPpart ('r' or 'i'). + Note that when sigma is specified, the keyword 'which' (below) + refers to the shifted eigenvalues ``w'[i]`` where: + + If A is real and OPpart == 'r' (default), + ``w'[i] = 1/2 * [1/(w[i]-sigma) + 1/(w[i]-conj(sigma))]``. + + If A is real and OPpart == 'i', + ``w'[i] = 1/2i * [1/(w[i]-sigma) - 1/(w[i]-conj(sigma))]``. + + If A is complex, ``w'[i] = 1/(w[i]-sigma)``. + + v0 : ndarray, optional + Starting vector for iteration. + Default: random + ncv : int, optional + The number of Lanczos vectors generated + `ncv` must be greater than `k`; it is recommended that ``ncv > 2*k``. + Default: ``min(n, max(2*k + 1, 20))`` + which : str, ['LM' | 'SM' | 'LR' | 'SR' | 'LI' | 'SI'], optional + Which `k` eigenvectors and eigenvalues to find: + + 'LM' : largest magnitude + + 'SM' : smallest magnitude + + 'LR' : largest real part + + 'SR' : smallest real part + + 'LI' : largest imaginary part + + 'SI' : smallest imaginary part + + When sigma != None, 'which' refers to the shifted eigenvalues w'[i] + (see discussion in 'sigma', above). ARPACK is generally better + at finding large values than small values. If small eigenvalues are + desired, consider using shift-invert mode for better performance. + maxiter : int, optional + Maximum number of Arnoldi update iterations allowed + Default: ``n*10`` + tol : float, optional + Relative accuracy for eigenvalues (stopping criterion) + The default value of 0 implies machine precision. + return_eigenvectors : bool, optional + Return eigenvectors (True) in addition to eigenvalues + Minv : ndarray, sparse matrix or LinearOperator, optional + See notes in M, above. + OPinv : ndarray, sparse matrix or LinearOperator, optional + See notes in sigma, above. + OPpart : {'r' or 'i'}, optional + See notes in sigma, above + + Returns + ------- + w : ndarray + Array of k eigenvalues. + v : ndarray + An array of `k` eigenvectors. + ``v[:, i]`` is the eigenvector corresponding to the eigenvalue w[i]. + + Raises + ------ + ArpackNoConvergence + When the requested convergence is not obtained. + The currently converged eigenvalues and eigenvectors can be found + as ``eigenvalues`` and ``eigenvectors`` attributes of the exception + object. + + See Also + -------- + eigsh : eigenvalues and eigenvectors for symmetric matrix A + svds : singular value decomposition for a matrix A + + Notes + ----- + This function is a wrapper to the ARPACK [1]_ SNEUPD, DNEUPD, CNEUPD, + ZNEUPD, functions which use the Implicitly Restarted Arnoldi Method to + find the eigenvalues and eigenvectors [2]_. + + References + ---------- + .. [1] ARPACK Software, https://github.com/opencollab/arpack-ng + .. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: + Solution of Large Scale Eigenvalue Problems by Implicitly Restarted + Arnoldi Methods. SIAM, Philadelphia, PA, 1998. + + Examples + -------- + Find 6 eigenvectors of the identity matrix: + + >>> import numpy as np + >>> from scipy.sparse.linalg import eigs + >>> id = np.eye(13) + >>> vals, vecs = eigs(id, k=6) + >>> vals + array([ 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]) + >>> vecs.shape + (13, 6) + + """ + if A.shape[0] != A.shape[1]: + raise ValueError(f'expected square matrix (shape={A.shape})') + if M is not None: + if M.shape != A.shape: + raise ValueError(f'wrong M dimensions {M.shape}, should be {A.shape}') + if np.dtype(M.dtype).char.lower() != np.dtype(A.dtype).char.lower(): + warnings.warn('M does not have the same type precision as A. ' + 'This may adversely affect ARPACK convergence', + stacklevel=2) + + n = A.shape[0] + + if k <= 0: + raise ValueError("k=%d must be greater than 0." % k) + + if k >= n - 1: + warnings.warn("k >= N - 1 for N * N square matrix. " + "Attempting to use scipy.linalg.eig instead.", + RuntimeWarning, stacklevel=2) + + if issparse(A): + raise TypeError("Cannot use scipy.linalg.eig for sparse A with " + "k >= N - 1. Use scipy.linalg.eig(A.toarray()) or" + " reduce k.") + if isinstance(A, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eig for LinearOperator " + "A with k >= N - 1.") + if isinstance(M, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eig for LinearOperator " + "M with k >= N - 1.") + + return eig(A, b=M, right=return_eigenvectors) + + if sigma is None: + matvec = _aslinearoperator_with_dtype(A).matvec + + if OPinv is not None: + raise ValueError("OPinv should not be specified " + "with sigma = None.") + if OPpart is not None: + raise ValueError("OPpart should not be specified with " + "sigma = None or complex A") + + if M is None: + #standard eigenvalue problem + mode = 1 + M_matvec = None + Minv_matvec = None + if Minv is not None: + raise ValueError("Minv should not be " + "specified with M = None.") + else: + #general eigenvalue problem + mode = 2 + if Minv is None: + Minv_matvec = get_inv_matvec(M, hermitian=True, tol=tol) + else: + Minv = _aslinearoperator_with_dtype(Minv) + Minv_matvec = Minv.matvec + M_matvec = _aslinearoperator_with_dtype(M).matvec + else: + #sigma is not None: shift-invert mode + if np.issubdtype(A.dtype, np.complexfloating): + if OPpart is not None: + raise ValueError("OPpart should not be specified " + "with sigma=None or complex A") + mode = 3 + elif OPpart is None or OPpart.lower() == 'r': + mode = 3 + elif OPpart.lower() == 'i': + if np.imag(sigma) == 0: + raise ValueError("OPpart cannot be 'i' if sigma is real") + mode = 4 + else: + raise ValueError("OPpart must be one of ('r','i')") + + matvec = _aslinearoperator_with_dtype(A).matvec + if Minv is not None: + raise ValueError("Minv should not be specified when sigma is") + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=False, tol=tol) + else: + OPinv = _aslinearoperator_with_dtype(OPinv) + Minv_matvec = OPinv.matvec + if M is None: + M_matvec = None + else: + M_matvec = _aslinearoperator_with_dtype(M).matvec + + params = _UnsymmetricArpackParams(n, k, A.dtype.char, matvec, mode, + M_matvec, Minv_matvec, sigma, + ncv, v0, maxiter, which, tol) + + with _ARPACK_LOCK: + while not params.converged: + params.iterate() + + return params.extract(return_eigenvectors) + + +def eigsh(A, k=6, M=None, sigma=None, which='LM', v0=None, + ncv=None, maxiter=None, tol=0, return_eigenvectors=True, + Minv=None, OPinv=None, mode='normal'): + """ + Find k eigenvalues and eigenvectors of the real symmetric square matrix + or complex Hermitian matrix A. + + Solves ``A @ x[i] = w[i] * x[i]``, the standard eigenvalue problem for + w[i] eigenvalues with corresponding eigenvectors x[i]. + + If M is specified, solves ``A @ x[i] = w[i] * M @ x[i]``, the + generalized eigenvalue problem for w[i] eigenvalues + with corresponding eigenvectors x[i]. + + Note that there is no specialized routine for the case when A is a complex + Hermitian matrix. In this case, ``eigsh()`` will call ``eigs()`` and return the + real parts of the eigenvalues thus obtained. + + Parameters + ---------- + A : ndarray, sparse matrix or LinearOperator + A square operator representing the operation ``A @ x``, where ``A`` is + real symmetric or complex Hermitian. For buckling mode (see below) + ``A`` must additionally be positive-definite. + k : int, optional + The number of eigenvalues and eigenvectors desired. + `k` must be smaller than N. It is not possible to compute all + eigenvectors of a matrix. + + Returns + ------- + w : array + Array of k eigenvalues. + v : array + An array representing the `k` eigenvectors. The column ``v[:, i]`` is + the eigenvector corresponding to the eigenvalue ``w[i]``. + + Other Parameters + ---------------- + M : An N x N matrix, array, sparse matrix, or linear operator representing + the operation ``M @ x`` for the generalized eigenvalue problem + + A @ x = w * M @ x. + + M must represent a real symmetric matrix if A is real, and must + represent a complex Hermitian matrix if A is complex. For best + results, the data type of M should be the same as that of A. + Additionally: + + If sigma is None, M is symmetric positive definite. + + If sigma is specified, M is symmetric positive semi-definite. + + In buckling mode, M is symmetric indefinite. + + If sigma is None, eigsh requires an operator to compute the solution + of the linear equation ``M @ x = b``. This is done internally via a + (sparse) LU decomposition for an explicit matrix M, or via an + iterative solver for a general linear operator. Alternatively, + the user can supply the matrix or operator Minv, which gives + ``x = Minv @ b = M^-1 @ b``. + sigma : real + Find eigenvalues near sigma using shift-invert mode. This requires + an operator to compute the solution of the linear system + ``[A - sigma * M] x = b``, where M is the identity matrix if + unspecified. This is computed internally via a (sparse) LU + decomposition for explicit matrices A & M, or via an iterative + solver if either A or M is a general linear operator. + Alternatively, the user can supply the matrix or operator OPinv, + which gives ``x = OPinv @ b = [A - sigma * M]^-1 @ b``. + Note that when sigma is specified, the keyword 'which' refers to + the shifted eigenvalues ``w'[i]`` where: + + if mode == 'normal', ``w'[i] = 1 / (w[i] - sigma)``. + + if mode == 'cayley', ``w'[i] = (w[i] + sigma) / (w[i] - sigma)``. + + if mode == 'buckling', ``w'[i] = w[i] / (w[i] - sigma)``. + + (see further discussion in 'mode' below) + v0 : ndarray, optional + Starting vector for iteration. + Default: random + ncv : int, optional + The number of Lanczos vectors generated ncv must be greater than k and + smaller than n; it is recommended that ``ncv > 2*k``. + Default: ``min(n, max(2*k + 1, 20))`` + which : str ['LM' | 'SM' | 'LA' | 'SA' | 'BE'] + If A is a complex Hermitian matrix, 'BE' is invalid. + Which `k` eigenvectors and eigenvalues to find: + + 'LM' : Largest (in magnitude) eigenvalues. + + 'SM' : Smallest (in magnitude) eigenvalues. + + 'LA' : Largest (algebraic) eigenvalues. + + 'SA' : Smallest (algebraic) eigenvalues. + + 'BE' : Half (k/2) from each end of the spectrum. + + When k is odd, return one more (k/2+1) from the high end. + When sigma != None, 'which' refers to the shifted eigenvalues ``w'[i]`` + (see discussion in 'sigma', above). ARPACK is generally better + at finding large values than small values. If small eigenvalues are + desired, consider using shift-invert mode for better performance. + maxiter : int, optional + Maximum number of Arnoldi update iterations allowed. + Default: ``n*10`` + tol : float + Relative accuracy for eigenvalues (stopping criterion). + The default value of 0 implies machine precision. + Minv : N x N matrix, array, sparse matrix, or LinearOperator + See notes in M, above. + OPinv : N x N matrix, array, sparse matrix, or LinearOperator + See notes in sigma, above. + return_eigenvectors : bool + Return eigenvectors (True) in addition to eigenvalues. + This value determines the order in which eigenvalues are sorted. + The sort order is also dependent on the `which` variable. + + For which = 'LM' or 'SA': + If `return_eigenvectors` is True, eigenvalues are sorted by + algebraic value. + + If `return_eigenvectors` is False, eigenvalues are sorted by + absolute value. + + For which = 'BE' or 'LA': + eigenvalues are always sorted by algebraic value. + + For which = 'SM': + If `return_eigenvectors` is True, eigenvalues are sorted by + algebraic value. + + If `return_eigenvectors` is False, eigenvalues are sorted by + decreasing absolute value. + + mode : string ['normal' | 'buckling' | 'cayley'] + Specify strategy to use for shift-invert mode. This argument applies + only for real-valued A and sigma != None. For shift-invert mode, + ARPACK internally solves the eigenvalue problem + ``OP @ x'[i] = w'[i] * B @ x'[i]`` + and transforms the resulting Ritz vectors x'[i] and Ritz values w'[i] + into the desired eigenvectors and eigenvalues of the problem + ``A @ x[i] = w[i] * M @ x[i]``. + The modes are as follows: + + 'normal' : + OP = [A - sigma * M]^-1 @ M, + B = M, + w'[i] = 1 / (w[i] - sigma) + + 'buckling' : + OP = [A - sigma * M]^-1 @ A, + B = A, + w'[i] = w[i] / (w[i] - sigma) + + 'cayley' : + OP = [A - sigma * M]^-1 @ [A + sigma * M], + B = M, + w'[i] = (w[i] + sigma) / (w[i] - sigma) + + The choice of mode will affect which eigenvalues are selected by + the keyword 'which', and can also impact the stability of + convergence (see [2] for a discussion). + + Raises + ------ + ArpackNoConvergence + When the requested convergence is not obtained. + + The currently converged eigenvalues and eigenvectors can be found + as ``eigenvalues`` and ``eigenvectors`` attributes of the exception + object. + + See Also + -------- + eigs : eigenvalues and eigenvectors for a general (nonsymmetric) matrix A + svds : singular value decomposition for a matrix A + + Notes + ----- + This function is a wrapper to the ARPACK [1]_ SSEUPD and DSEUPD + functions which use the Implicitly Restarted Lanczos Method to + find the eigenvalues and eigenvectors [2]_. + + References + ---------- + .. [1] ARPACK Software, https://github.com/opencollab/arpack-ng + .. [2] R. B. Lehoucq, D. C. Sorensen, and C. Yang, ARPACK USERS GUIDE: + Solution of Large Scale Eigenvalue Problems by Implicitly Restarted + Arnoldi Methods. SIAM, Philadelphia, PA, 1998. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import eigsh + >>> identity = np.eye(13) + >>> eigenvalues, eigenvectors = eigsh(identity, k=6) + >>> eigenvalues + array([1., 1., 1., 1., 1., 1.]) + >>> eigenvectors.shape + (13, 6) + + """ + # complex Hermitian matrices should be solved with eigs + if np.issubdtype(A.dtype, np.complexfloating): + if mode != 'normal': + raise ValueError("mode=%s cannot be used with " + "complex matrix A" % mode) + if which == 'BE': + raise ValueError("which='BE' cannot be used with complex matrix A") + elif which == 'LA': + which = 'LR' + elif which == 'SA': + which = 'SR' + ret = eigs(A, k, M=M, sigma=sigma, which=which, v0=v0, + ncv=ncv, maxiter=maxiter, tol=tol, + return_eigenvectors=return_eigenvectors, Minv=Minv, + OPinv=OPinv) + + if return_eigenvectors: + return ret[0].real, ret[1] + else: + return ret.real + + if A.shape[0] != A.shape[1]: + raise ValueError(f'expected square matrix (shape={A.shape})') + if M is not None: + if M.shape != A.shape: + raise ValueError(f'wrong M dimensions {M.shape}, should be {A.shape}') + if np.dtype(M.dtype).char.lower() != np.dtype(A.dtype).char.lower(): + warnings.warn('M does not have the same type precision as A. ' + 'This may adversely affect ARPACK convergence', + stacklevel=2) + + n = A.shape[0] + + if k <= 0: + raise ValueError("k must be greater than 0.") + + if k >= n: + warnings.warn("k >= N for N * N square matrix. " + "Attempting to use scipy.linalg.eigh instead.", + RuntimeWarning, stacklevel=2) + + if issparse(A): + raise TypeError("Cannot use scipy.linalg.eigh for sparse A with " + "k >= N. Use scipy.linalg.eigh(A.toarray()) or" + " reduce k.") + if isinstance(A, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eigh for LinearOperator " + "A with k >= N.") + if isinstance(M, LinearOperator): + raise TypeError("Cannot use scipy.linalg.eigh for LinearOperator " + "M with k >= N.") + + return eigh(A, b=M, eigvals_only=not return_eigenvectors) + + if sigma is None: + A = _aslinearoperator_with_dtype(A) + matvec = A.matvec + + if OPinv is not None: + raise ValueError("OPinv should not be specified " + "with sigma = None.") + if M is None: + #standard eigenvalue problem + mode = 1 + M_matvec = None + Minv_matvec = None + if Minv is not None: + raise ValueError("Minv should not be " + "specified with M = None.") + else: + #general eigenvalue problem + mode = 2 + if Minv is None: + Minv_matvec = get_inv_matvec(M, hermitian=True, tol=tol) + else: + Minv = _aslinearoperator_with_dtype(Minv) + Minv_matvec = Minv.matvec + M_matvec = _aslinearoperator_with_dtype(M).matvec + else: + # sigma is not None: shift-invert mode + if Minv is not None: + raise ValueError("Minv should not be specified when sigma is") + + # normal mode + if mode == 'normal': + mode = 3 + matvec = None + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=True, tol=tol) + else: + OPinv = _aslinearoperator_with_dtype(OPinv) + Minv_matvec = OPinv.matvec + if M is None: + M_matvec = None + else: + M = _aslinearoperator_with_dtype(M) + M_matvec = M.matvec + + # buckling mode + elif mode == 'buckling': + mode = 4 + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=True, tol=tol) + else: + Minv_matvec = _aslinearoperator_with_dtype(OPinv).matvec + matvec = _aslinearoperator_with_dtype(A).matvec + M_matvec = None + + # cayley-transform mode + elif mode == 'cayley': + mode = 5 + matvec = _aslinearoperator_with_dtype(A).matvec + if OPinv is None: + Minv_matvec = get_OPinv_matvec(A, M, sigma, + hermitian=True, tol=tol) + else: + Minv_matvec = _aslinearoperator_with_dtype(OPinv).matvec + if M is None: + M_matvec = None + else: + M_matvec = _aslinearoperator_with_dtype(M).matvec + + # unrecognized mode + else: + raise ValueError("unrecognized mode '%s'" % mode) + + params = _SymmetricArpackParams(n, k, A.dtype.char, matvec, mode, + M_matvec, Minv_matvec, sigma, + ncv, v0, maxiter, which, tol) + + with _ARPACK_LOCK: + while not params.converged: + params.iterate() + + return params.extract(return_eigenvectors) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d534d274f5dbe2001c459b9e11954bb3cbf6edc Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__pycache__/test_arpack.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__pycache__/test_arpack.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2b8f83ad2426e6586070df69790c35b4eab77fb Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/__pycache__/test_arpack.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py new file mode 100644 index 0000000000000000000000000000000000000000..1cf73f28e21d4f29d067ae84bf63e5291acbe810 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py @@ -0,0 +1,718 @@ +__usage__ = """ +To run tests locally: + python tests/test_arpack.py [-l] [-v] + +""" + +import threading +import itertools + +import numpy as np + +from numpy.testing import assert_allclose, assert_equal, suppress_warnings +from pytest import raises as assert_raises +import pytest + +from numpy import dot, conj, random +from scipy.linalg import eig, eigh +from scipy.sparse import csc_matrix, csr_matrix, diags, rand +from scipy.sparse.linalg import LinearOperator, aslinearoperator +from scipy.sparse.linalg._eigen.arpack import (eigs, eigsh, arpack, + ArpackNoConvergence) + + +from scipy._lib._gcutils import assert_deallocated, IS_PYPY + + +# precision for tests +_ndigits = {'f': 3, 'd': 11, 'F': 3, 'D': 11} + + +def _get_test_tolerance(type_char, mattype=None, D_type=None, which=None): + """ + Return tolerance values suitable for a given test: + + Parameters + ---------- + type_char : {'f', 'd', 'F', 'D'} + Data type in ARPACK eigenvalue problem + mattype : {csr_matrix, aslinearoperator, asarray}, optional + Linear operator type + + Returns + ------- + tol + Tolerance to pass to the ARPACK routine + rtol + Relative tolerance for outputs + atol + Absolute tolerance for outputs + + """ + + rtol = {'f': 3000 * np.finfo(np.float32).eps, + 'F': 3000 * np.finfo(np.float32).eps, + 'd': 2000 * np.finfo(np.float64).eps, + 'D': 2000 * np.finfo(np.float64).eps}[type_char] + atol = rtol + tol = 0 + + if mattype is aslinearoperator and type_char in ('f', 'F'): + # iterative methods in single precision: worse errors + # also: bump ARPACK tolerance so that the iterative method converges + tol = 30 * np.finfo(np.float32).eps + rtol *= 5 + + if mattype is csr_matrix and type_char in ('f', 'F'): + # sparse in single precision: worse errors + rtol *= 5 + + if ( + which in ('LM', 'SM', 'LA') + and D_type.name == "gen-hermitian-Mc" + ): + if type_char == 'F': + # missing case 1, 2, and more, from PR 14798 + rtol *= 5 + + if type_char == 'D': + # missing more cases, from PR 14798 + rtol *= 10 + atol *= 10 + + return tol, rtol, atol + + +def generate_matrix(N, complex_=False, hermitian=False, + pos_definite=False, sparse=False): + M = np.random.random((N, N)) + if complex_: + M = M + 1j * np.random.random((N, N)) + + if hermitian: + if pos_definite: + if sparse: + i = np.arange(N) + j = np.random.randint(N, size=N-2) + i, j = np.meshgrid(i, j) + M[i, j] = 0 + M = np.dot(M.conj(), M.T) + else: + M = np.dot(M.conj(), M.T) + if sparse: + i = np.random.randint(N, size=N * N // 4) + j = np.random.randint(N, size=N * N // 4) + ind = np.nonzero(i == j) + j[ind] = (j[ind] + 1) % N + M[i, j] = 0 + M[j, i] = 0 + else: + if sparse: + i = np.random.randint(N, size=N * N // 2) + j = np.random.randint(N, size=N * N // 2) + M[i, j] = 0 + return M + + +def generate_matrix_symmetric(N, pos_definite=False, sparse=False): + M = np.random.random((N, N)) + + M = 0.5 * (M + M.T) # Make M symmetric + + if pos_definite: + Id = N * np.eye(N) + if sparse: + M = csr_matrix(M) + M += Id + else: + if sparse: + M = csr_matrix(M) + + return M + + +def assert_allclose_cc(actual, desired, **kw): + """Almost equal or complex conjugates almost equal""" + try: + assert_allclose(actual, desired, **kw) + except AssertionError: + assert_allclose(actual, conj(desired), **kw) + + +def argsort_which(eigenvalues, typ, k, which, + sigma=None, OPpart=None, mode=None): + """Return sorted indices of eigenvalues using the "which" keyword + from eigs and eigsh""" + if sigma is None: + reval = np.round(eigenvalues, decimals=_ndigits[typ]) + else: + if mode is None or mode == 'normal': + if OPpart is None: + reval = 1. / (eigenvalues - sigma) + elif OPpart == 'r': + reval = 0.5 * (1. / (eigenvalues - sigma) + + 1. / (eigenvalues - np.conj(sigma))) + elif OPpart == 'i': + reval = -0.5j * (1. / (eigenvalues - sigma) + - 1. / (eigenvalues - np.conj(sigma))) + elif mode == 'cayley': + reval = (eigenvalues + sigma) / (eigenvalues - sigma) + elif mode == 'buckling': + reval = eigenvalues / (eigenvalues - sigma) + else: + raise ValueError("mode='%s' not recognized" % mode) + + reval = np.round(reval, decimals=_ndigits[typ]) + + if which in ['LM', 'SM']: + ind = np.argsort(abs(reval)) + elif which in ['LR', 'SR', 'LA', 'SA', 'BE']: + ind = np.argsort(np.real(reval)) + elif which in ['LI', 'SI']: + # for LI,SI ARPACK returns largest,smallest abs(imaginary) why? + if typ.islower(): + ind = np.argsort(abs(np.imag(reval))) + else: + ind = np.argsort(np.imag(reval)) + else: + raise ValueError("which='%s' is unrecognized" % which) + + if which in ['LM', 'LA', 'LR', 'LI']: + return ind[-k:] + elif which in ['SM', 'SA', 'SR', 'SI']: + return ind[:k] + elif which == 'BE': + return np.concatenate((ind[:k//2], ind[k//2-k:])) + + +def eval_evec(symmetric, d, typ, k, which, v0=None, sigma=None, + mattype=np.asarray, OPpart=None, mode='normal'): + general = ('bmat' in d) + + if symmetric: + eigs_func = eigsh + else: + eigs_func = eigs + + if general: + err = ("error for {}:general, typ={}, which={}, sigma={}, " + "mattype={}, OPpart={}, mode={}".format(eigs_func.__name__, + typ, which, sigma, + mattype.__name__, + OPpart, mode)) + else: + err = ("error for {}:standard, typ={}, which={}, sigma={}, " + "mattype={}, OPpart={}, mode={}".format(eigs_func.__name__, + typ, which, sigma, + mattype.__name__, + OPpart, mode)) + + a = d['mat'].astype(typ) + ac = mattype(a) + + if general: + b = d['bmat'].astype(typ) + bc = mattype(b) + + # get exact eigenvalues + exact_eval = d['eval'].astype(typ.upper()) + ind = argsort_which(exact_eval, typ, k, which, + sigma, OPpart, mode) + exact_eval = exact_eval[ind] + + # compute arpack eigenvalues + kwargs = dict(which=which, v0=v0, sigma=sigma) + if eigs_func is eigsh: + kwargs['mode'] = mode + else: + kwargs['OPpart'] = OPpart + + # compute suitable tolerances + kwargs['tol'], rtol, atol = _get_test_tolerance(typ, mattype, d, which) + # on rare occasions, ARPACK routines return results that are proper + # eigenvalues and -vectors, but not necessarily the ones requested in + # the parameter which. This is inherent to the Krylov methods, and + # should not be treated as a failure. If such a rare situation + # occurs, the calculation is tried again (but at most a few times). + ntries = 0 + while ntries < 5: + # solve + if general: + try: + eigenvalues, evec = eigs_func(ac, k, bc, **kwargs) + except ArpackNoConvergence: + kwargs['maxiter'] = 20*a.shape[0] + eigenvalues, evec = eigs_func(ac, k, bc, **kwargs) + else: + try: + eigenvalues, evec = eigs_func(ac, k, **kwargs) + except ArpackNoConvergence: + kwargs['maxiter'] = 20*a.shape[0] + eigenvalues, evec = eigs_func(ac, k, **kwargs) + + ind = argsort_which(eigenvalues, typ, k, which, + sigma, OPpart, mode) + eigenvalues = eigenvalues[ind] + evec = evec[:, ind] + + try: + # check eigenvalues + assert_allclose_cc(eigenvalues, exact_eval, rtol=rtol, atol=atol, + err_msg=err) + check_evecs = True + except AssertionError: + check_evecs = False + ntries += 1 + + if check_evecs: + # check eigenvectors + LHS = np.dot(a, evec) + if general: + RHS = eigenvalues * np.dot(b, evec) + else: + RHS = eigenvalues * evec + + assert_allclose(LHS, RHS, rtol=rtol, atol=atol, err_msg=err) + break + + # check eigenvalues + assert_allclose_cc(eigenvalues, exact_eval, rtol=rtol, atol=atol, err_msg=err) + + +class DictWithRepr(dict): + def __init__(self, name): + self.name = name + + def __repr__(self): + return "<%s>" % self.name + + +class SymmetricParams: + def __init__(self): + self.eigs = eigsh + self.which = ['LM', 'SM', 'LA', 'SA', 'BE'] + self.mattypes = [csr_matrix, aslinearoperator, np.asarray] + self.sigmas_modes = {None: ['normal'], + 0.5: ['normal', 'buckling', 'cayley']} + + # generate matrices + # these should all be float32 so that the eigenvalues + # are the same in float32 and float64 + N = 6 + np.random.seed(2300) + Ar = generate_matrix(N, hermitian=True, + pos_definite=True).astype('f').astype('d') + M = generate_matrix(N, hermitian=True, + pos_definite=True).astype('f').astype('d') + Ac = generate_matrix(N, hermitian=True, pos_definite=True, + complex_=True).astype('F').astype('D') + Mc = generate_matrix(N, hermitian=True, pos_definite=True, + complex_=True).astype('F').astype('D') + v0 = np.random.random(N) + + # standard symmetric problem + SS = DictWithRepr("std-symmetric") + SS['mat'] = Ar + SS['v0'] = v0 + SS['eval'] = eigh(SS['mat'], eigvals_only=True) + + # general symmetric problem + GS = DictWithRepr("gen-symmetric") + GS['mat'] = Ar + GS['bmat'] = M + GS['v0'] = v0 + GS['eval'] = eigh(GS['mat'], GS['bmat'], eigvals_only=True) + + # standard hermitian problem + SH = DictWithRepr("std-hermitian") + SH['mat'] = Ac + SH['v0'] = v0 + SH['eval'] = eigh(SH['mat'], eigvals_only=True) + + # general hermitian problem + GH = DictWithRepr("gen-hermitian") + GH['mat'] = Ac + GH['bmat'] = M + GH['v0'] = v0 + GH['eval'] = eigh(GH['mat'], GH['bmat'], eigvals_only=True) + + # general hermitian problem with hermitian M + GHc = DictWithRepr("gen-hermitian-Mc") + GHc['mat'] = Ac + GHc['bmat'] = Mc + GHc['v0'] = v0 + GHc['eval'] = eigh(GHc['mat'], GHc['bmat'], eigvals_only=True) + + self.real_test_cases = [SS, GS] + self.complex_test_cases = [SH, GH, GHc] + + +class NonSymmetricParams: + def __init__(self): + self.eigs = eigs + self.which = ['LM', 'LR', 'LI'] # , 'SM', 'LR', 'SR', 'LI', 'SI'] + self.mattypes = [csr_matrix, aslinearoperator, np.asarray] + self.sigmas_OPparts = {None: [None], + 0.1: ['r'], + 0.1 + 0.1j: ['r', 'i']} + + # generate matrices + # these should all be float32 so that the eigenvalues + # are the same in float32 and float64 + N = 6 + np.random.seed(2300) + Ar = generate_matrix(N).astype('f').astype('d') + M = generate_matrix(N, hermitian=True, + pos_definite=True).astype('f').astype('d') + Ac = generate_matrix(N, complex_=True).astype('F').astype('D') + v0 = np.random.random(N) + + # standard real nonsymmetric problem + SNR = DictWithRepr("std-real-nonsym") + SNR['mat'] = Ar + SNR['v0'] = v0 + SNR['eval'] = eig(SNR['mat'], left=False, right=False) + + # general real nonsymmetric problem + GNR = DictWithRepr("gen-real-nonsym") + GNR['mat'] = Ar + GNR['bmat'] = M + GNR['v0'] = v0 + GNR['eval'] = eig(GNR['mat'], GNR['bmat'], left=False, right=False) + + # standard complex nonsymmetric problem + SNC = DictWithRepr("std-cmplx-nonsym") + SNC['mat'] = Ac + SNC['v0'] = v0 + SNC['eval'] = eig(SNC['mat'], left=False, right=False) + + # general complex nonsymmetric problem + GNC = DictWithRepr("gen-cmplx-nonsym") + GNC['mat'] = Ac + GNC['bmat'] = M + GNC['v0'] = v0 + GNC['eval'] = eig(GNC['mat'], GNC['bmat'], left=False, right=False) + + self.real_test_cases = [SNR, GNR] + self.complex_test_cases = [SNC, GNC] + + +def test_symmetric_modes(): + params = SymmetricParams() + k = 2 + symmetric = True + for D in params.real_test_cases: + for typ in 'fd': + for which in params.which: + for mattype in params.mattypes: + for (sigma, modes) in params.sigmas_modes.items(): + for mode in modes: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype, None, mode) + + +def test_hermitian_modes(): + params = SymmetricParams() + k = 2 + symmetric = True + for D in params.complex_test_cases: + for typ in 'FD': + for which in params.which: + if which == 'BE': + continue # BE invalid for complex + for mattype in params.mattypes: + for sigma in params.sigmas_modes: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype) + + +def test_symmetric_starting_vector(): + params = SymmetricParams() + symmetric = True + for k in [1, 2, 3, 4, 5]: + for D in params.real_test_cases: + for typ in 'fd': + v0 = random.rand(len(D['v0'])).astype(typ) + eval_evec(symmetric, D, typ, k, 'LM', v0) + + +def test_symmetric_no_convergence(): + np.random.seed(1234) + m = generate_matrix(30, hermitian=True, pos_definite=True) + tol, rtol, atol = _get_test_tolerance('d') + try: + w, v = eigsh(m, 4, which='LM', v0=m[:, 0], maxiter=5, tol=tol, ncv=9) + raise AssertionError("Spurious no-error exit") + except ArpackNoConvergence as err: + k = len(err.eigenvalues) + if k <= 0: + raise AssertionError("Spurious no-eigenvalues-found case") from err + w, v = err.eigenvalues, err.eigenvectors + assert_allclose(dot(m, v), w * v, rtol=rtol, atol=atol) + + +def test_real_nonsymmetric_modes(): + params = NonSymmetricParams() + k = 2 + symmetric = False + for D in params.real_test_cases: + for typ in 'fd': + for which in params.which: + for mattype in params.mattypes: + for sigma, OPparts in params.sigmas_OPparts.items(): + for OPpart in OPparts: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype, OPpart) + + +def test_complex_nonsymmetric_modes(): + params = NonSymmetricParams() + k = 2 + symmetric = False + for D in params.complex_test_cases: + for typ in 'DF': + for which in params.which: + for mattype in params.mattypes: + for sigma in params.sigmas_OPparts: + eval_evec(symmetric, D, typ, k, which, + None, sigma, mattype) + + +def test_standard_nonsymmetric_starting_vector(): + params = NonSymmetricParams() + sigma = None + symmetric = False + for k in [1, 2, 3, 4]: + for d in params.complex_test_cases: + for typ in 'FD': + A = d['mat'] + n = A.shape[0] + v0 = random.rand(n).astype(typ) + eval_evec(symmetric, d, typ, k, "LM", v0, sigma) + + +def test_general_nonsymmetric_starting_vector(): + params = NonSymmetricParams() + sigma = None + symmetric = False + for k in [1, 2, 3, 4]: + for d in params.complex_test_cases: + for typ in 'FD': + A = d['mat'] + n = A.shape[0] + v0 = random.rand(n).astype(typ) + eval_evec(symmetric, d, typ, k, "LM", v0, sigma) + + +def test_standard_nonsymmetric_no_convergence(): + np.random.seed(1234) + m = generate_matrix(30, complex_=True) + tol, rtol, atol = _get_test_tolerance('d') + try: + w, v = eigs(m, 4, which='LM', v0=m[:, 0], maxiter=5, tol=tol) + raise AssertionError("Spurious no-error exit") + except ArpackNoConvergence as err: + k = len(err.eigenvalues) + if k <= 0: + raise AssertionError("Spurious no-eigenvalues-found case") from err + w, v = err.eigenvalues, err.eigenvectors + for ww, vv in zip(w, v.T): + assert_allclose(dot(m, vv), ww * vv, rtol=rtol, atol=atol) + + +def test_eigen_bad_shapes(): + # A is not square. + A = csc_matrix(np.zeros((2, 3))) + assert_raises(ValueError, eigs, A) + + +def test_eigen_bad_kwargs(): + # Test eigen on wrong keyword argument + A = csc_matrix(np.zeros((8, 8))) + assert_raises(ValueError, eigs, A, which='XX') + + +def test_ticket_1459_arpack_crash(): + for dtype in [np.float32, np.float64]: + # This test does not seem to catch the issue for float32, + # but we made the same fix there, just to be sure + + N = 6 + k = 2 + + np.random.seed(2301) + A = np.random.random((N, N)).astype(dtype) + v0 = np.array([-0.71063568258907849895, -0.83185111795729227424, + -0.34365925382227402451, 0.46122533684552280420, + -0.58001341115969040629, -0.78844877570084292984e-01], + dtype=dtype) + + # Should not crash: + evals, evecs = eigs(A, k, v0=v0) + + +@pytest.mark.skipif(IS_PYPY, reason="Test not meaningful on PyPy") +def test_linearoperator_deallocation(): + # Check that the linear operators used by the Arpack wrappers are + # deallocatable by reference counting -- they are big objects, so + # Python's cyclic GC may not collect them fast enough before + # running out of memory if eigs/eigsh are called in a tight loop. + + M_d = np.eye(10) + M_s = csc_matrix(M_d) + M_o = aslinearoperator(M_d) + + with assert_deallocated(lambda: arpack.SpLuInv(M_s)): + pass + with assert_deallocated(lambda: arpack.LuInv(M_d)): + pass + with assert_deallocated(lambda: arpack.IterInv(M_s)): + pass + with assert_deallocated(lambda: arpack.IterOpInv(M_o, None, 0.3)): + pass + with assert_deallocated(lambda: arpack.IterOpInv(M_o, M_o, 0.3)): + pass + +def test_parallel_threads(): + results = [] + v0 = np.random.rand(50) + + def worker(): + x = diags([1, -2, 1], [-1, 0, 1], shape=(50, 50)) + w, v = eigs(x, k=3, v0=v0) + results.append(w) + + w, v = eigsh(x, k=3, v0=v0) + results.append(w) + + threads = [threading.Thread(target=worker) for k in range(10)] + for t in threads: + t.start() + for t in threads: + t.join() + + worker() + + for r in results: + assert_allclose(r, results[-1]) + + +def test_reentering(): + # Just some linear operator that calls eigs recursively + def A_matvec(x): + x = diags([1, -2, 1], [-1, 0, 1], shape=(50, 50)) + w, v = eigs(x, k=1) + return v / w[0] + A = LinearOperator(matvec=A_matvec, dtype=float, shape=(50, 50)) + + # The Fortran code is not reentrant, so this fails (gracefully, not crashing) + assert_raises(RuntimeError, eigs, A, k=1) + assert_raises(RuntimeError, eigsh, A, k=1) + + +def test_regression_arpackng_1315(): + # Check that issue arpack-ng/#1315 is not present. + # Adapted from arpack-ng/TESTS/bug_1315_single.c + # If this fails, then the installed ARPACK library is faulty. + + for dtype in [np.float32, np.float64]: + np.random.seed(1234) + + w0 = np.arange(1, 1000+1).astype(dtype) + A = diags([w0], [0], shape=(1000, 1000)) + + v0 = np.random.rand(1000).astype(dtype) + w, v = eigs(A, k=9, ncv=2*9+1, which="LM", v0=v0) + + assert_allclose(np.sort(w), np.sort(w0[-9:]), + rtol=1e-4) + + +def test_eigs_for_k_greater(): + # Test eigs() for k beyond limits. + A_sparse = diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)) # sparse + A = generate_matrix(4, sparse=False) + M_dense = np.random.random((4, 4)) + M_sparse = generate_matrix(4, sparse=True) + M_linop = aslinearoperator(M_dense) + eig_tuple1 = eig(A, b=M_dense) + eig_tuple2 = eig(A, b=M_sparse) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + + assert_equal(eigs(A, M=M_dense, k=3), eig_tuple1) + assert_equal(eigs(A, M=M_dense, k=4), eig_tuple1) + assert_equal(eigs(A, M=M_dense, k=5), eig_tuple1) + assert_equal(eigs(A, M=M_sparse, k=5), eig_tuple2) + + # M as LinearOperator + assert_raises(TypeError, eigs, A, M=M_linop, k=3) + + # Test 'A' for different types + assert_raises(TypeError, eigs, aslinearoperator(A), k=3) + assert_raises(TypeError, eigs, A_sparse, k=3) + + +def test_eigsh_for_k_greater(): + # Test eigsh() for k beyond limits. + A_sparse = diags([1, -2, 1], [-1, 0, 1], shape=(4, 4)) # sparse + A = generate_matrix(4, sparse=False) + M_dense = generate_matrix_symmetric(4, pos_definite=True) + M_sparse = generate_matrix_symmetric(4, pos_definite=True, sparse=True) + M_linop = aslinearoperator(M_dense) + eig_tuple1 = eigh(A, b=M_dense) + eig_tuple2 = eigh(A, b=M_sparse) + + with suppress_warnings() as sup: + sup.filter(RuntimeWarning) + + assert_equal(eigsh(A, M=M_dense, k=4), eig_tuple1) + assert_equal(eigsh(A, M=M_dense, k=5), eig_tuple1) + assert_equal(eigsh(A, M=M_sparse, k=5), eig_tuple2) + + # M as LinearOperator + assert_raises(TypeError, eigsh, A, M=M_linop, k=4) + + # Test 'A' for different types + assert_raises(TypeError, eigsh, aslinearoperator(A), k=4) + assert_raises(TypeError, eigsh, A_sparse, M=M_dense, k=4) + + +def test_real_eigs_real_k_subset(): + np.random.seed(1) + + n = 10 + A = rand(n, n, density=0.5) + A.data *= 2 + A.data -= 1 + + v0 = np.ones(n) + + whichs = ['LM', 'SM', 'LR', 'SR', 'LI', 'SI'] + dtypes = [np.float32, np.float64] + + for which, sigma, dtype in itertools.product(whichs, [None, 0, 5], dtypes): + prev_w = np.array([], dtype=dtype) + eps = np.finfo(dtype).eps + for k in range(1, 9): + w, z = eigs(A.astype(dtype), k=k, which=which, sigma=sigma, + v0=v0.astype(dtype), tol=0) + assert_allclose(np.linalg.norm(A.dot(z) - z * w), 0, atol=np.sqrt(eps)) + + # Check that the set of eigenvalues for `k` is a subset of that for `k+1` + dist = abs(prev_w[:,None] - w).min(axis=1) + assert_allclose(dist, 0, atol=np.sqrt(eps)) + + prev_w = w + + # Check sort order + if sigma is None: + d = w + else: + d = 1 / (w - sigma) + + if which == 'LM': + # ARPACK is systematic for 'LM', but sort order + # appears not well defined for other modes + assert np.all(np.diff(abs(d)) <= 1e-6) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6ab5330361a6bcc2a8403f9b3788aedae750d57f --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__init__.py @@ -0,0 +1,16 @@ +""" +Locally Optimal Block Preconditioned Conjugate Gradient Method (LOBPCG) + +LOBPCG is a preconditioned eigensolver for large symmetric positive definite +(SPD) generalized eigenproblems. + +Call the function lobpcg - see help for lobpcg.lobpcg. + +""" +from .lobpcg import * + +__all__ = [s for s in dir() if not s.startswith('_')] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__pycache__/lobpcg.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__pycache__/lobpcg.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4d724d4c914900087ba8b5bb7df07fb3e2168f8 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/__pycache__/lobpcg.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py new file mode 100644 index 0000000000000000000000000000000000000000..6bf2a77106abfd8ba83937579fced09243fdcb56 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py @@ -0,0 +1,1112 @@ +""" +Locally Optimal Block Preconditioned Conjugate Gradient Method (LOBPCG). + +References +---------- +.. [1] A. V. Knyazev (2001), + Toward the Optimal Preconditioned Eigensolver: Locally Optimal + Block Preconditioned Conjugate Gradient Method. + SIAM Journal on Scientific Computing 23, no. 2, + pp. 517-541. :doi:`10.1137/S1064827500366124` + +.. [2] A. V. Knyazev, I. Lashuk, M. E. Argentati, and E. Ovchinnikov (2007), + Block Locally Optimal Preconditioned Eigenvalue Xolvers (BLOPEX) + in hypre and PETSc. :arxiv:`0705.2626` + +.. [3] A. V. Knyazev's C and MATLAB implementations: + https://github.com/lobpcg/blopex +""" + +import warnings +import numpy as np +from scipy.linalg import (inv, eigh, cho_factor, cho_solve, + cholesky, LinAlgError) +from scipy.sparse.linalg import LinearOperator +from scipy.sparse import issparse + +__all__ = ["lobpcg"] + + +def _report_nonhermitian(M, name): + """ + Report if `M` is not a Hermitian matrix given its type. + """ + from scipy.linalg import norm + + md = M - M.T.conj() + nmd = norm(md, 1) + tol = 10 * np.finfo(M.dtype).eps + tol = max(tol, tol * norm(M, 1)) + if nmd > tol: + warnings.warn( + f"Matrix {name} of the type {M.dtype} is not Hermitian: " + f"condition: {nmd} < {tol} fails.", + UserWarning, stacklevel=4 + ) + +def _as2d(ar): + """ + If the input array is 2D return it, if it is 1D, append a dimension, + making it a column vector. + """ + if ar.ndim == 2: + return ar + else: # Assume 1! + aux = np.asarray(ar) + aux.shape = (ar.shape[0], 1) + return aux + + +def _makeMatMat(m): + if m is None: + return None + elif callable(m): + return lambda v: m(v) + else: + return lambda v: m @ v + + +def _matmul_inplace(x, y, verbosityLevel=0): + """Perform 'np.matmul' in-place if possible. + + If some sufficient conditions for inplace matmul are met, do so. + Otherwise try inplace update and fall back to overwrite if that fails. + """ + if x.flags["CARRAY"] and x.shape[1] == y.shape[1] and x.dtype == y.dtype: + # conditions where we can guarantee that inplace updates will work; + # i.e. x is not a view/slice, x & y have compatible dtypes, and the + # shape of the result of x @ y matches the shape of x. + np.matmul(x, y, out=x) + else: + # ideally, we'd have an exhaustive list of conditions above when + # inplace updates are possible; since we don't, we opportunistically + # try if it works, and fall back to overwriting if necessary + try: + np.matmul(x, y, out=x) + except Exception: + if verbosityLevel: + warnings.warn( + "Inplace update of x = x @ y failed, " + "x needs to be overwritten.", + UserWarning, stacklevel=3 + ) + x = x @ y + return x + + +def _applyConstraints(blockVectorV, factYBY, blockVectorBY, blockVectorY): + """Changes blockVectorV in-place.""" + YBV = blockVectorBY.T.conj() @ blockVectorV + tmp = cho_solve(factYBY, YBV) + blockVectorV -= blockVectorY @ tmp + + +def _b_orthonormalize(B, blockVectorV, blockVectorBV=None, + verbosityLevel=0): + """in-place B-orthonormalize the given block vector using Cholesky.""" + if blockVectorBV is None: + if B is None: + blockVectorBV = blockVectorV + else: + try: + blockVectorBV = B(blockVectorV) + except Exception as e: + if verbosityLevel: + warnings.warn( + f"Secondary MatMul call failed with error\n" + f"{e}\n", + UserWarning, stacklevel=3 + ) + return None, None, None + if blockVectorBV.shape != blockVectorV.shape: + raise ValueError( + f"The shape {blockVectorV.shape} " + f"of the orthogonalized matrix not preserved\n" + f"and changed to {blockVectorBV.shape} " + f"after multiplying by the secondary matrix.\n" + ) + + VBV = blockVectorV.T.conj() @ blockVectorBV + try: + # VBV is a Cholesky factor from now on... + VBV = cholesky(VBV, overwrite_a=True) + VBV = inv(VBV, overwrite_a=True) + blockVectorV = _matmul_inplace( + blockVectorV, VBV, + verbosityLevel=verbosityLevel + ) + if B is not None: + blockVectorBV = _matmul_inplace( + blockVectorBV, VBV, + verbosityLevel=verbosityLevel + ) + return blockVectorV, blockVectorBV, VBV + except LinAlgError: + if verbosityLevel: + warnings.warn( + "Cholesky has failed.", + UserWarning, stacklevel=3 + ) + return None, None, None + + +def _get_indx(_lambda, num, largest): + """Get `num` indices into `_lambda` depending on `largest` option.""" + ii = np.argsort(_lambda) + if largest: + ii = ii[:-num - 1:-1] + else: + ii = ii[:num] + + return ii + + +def _handle_gramA_gramB_verbosity(gramA, gramB, verbosityLevel): + if verbosityLevel: + _report_nonhermitian(gramA, "gramA") + _report_nonhermitian(gramB, "gramB") + + +def lobpcg( + A, + X, + B=None, + M=None, + Y=None, + tol=None, + maxiter=None, + largest=True, + verbosityLevel=0, + retLambdaHistory=False, + retResidualNormsHistory=False, + restartControl=20, +): + """Locally Optimal Block Preconditioned Conjugate Gradient Method (LOBPCG). + + LOBPCG is a preconditioned eigensolver for large real symmetric and complex + Hermitian definite generalized eigenproblems. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator, callable object} + The Hermitian linear operator of the problem, usually given by a + sparse matrix. Often called the "stiffness matrix". + X : ndarray, float32 or float64 + Initial approximation to the ``k`` eigenvectors (non-sparse). + If `A` has ``shape=(n,n)`` then `X` must have ``shape=(n,k)``. + B : {sparse matrix, ndarray, LinearOperator, callable object} + Optional. By default ``B = None``, which is equivalent to identity. + The right hand side operator in a generalized eigenproblem if present. + Often called the "mass matrix". Must be Hermitian positive definite. + M : {sparse matrix, ndarray, LinearOperator, callable object} + Optional. By default ``M = None``, which is equivalent to identity. + Preconditioner aiming to accelerate convergence. + Y : ndarray, float32 or float64, default: None + An ``n-by-sizeY`` ndarray of constraints with ``sizeY < n``. + The iterations will be performed in the ``B``-orthogonal complement + of the column-space of `Y`. `Y` must be full rank if present. + tol : scalar, optional + The default is ``tol=n*sqrt(eps)``. + Solver tolerance for the stopping criterion. + maxiter : int, default: 20 + Maximum number of iterations. + largest : bool, default: True + When True, solve for the largest eigenvalues, otherwise the smallest. + verbosityLevel : int, optional + By default ``verbosityLevel=0`` no output. + Controls the solver standard/screen output. + retLambdaHistory : bool, default: False + Whether to return iterative eigenvalue history. + retResidualNormsHistory : bool, default: False + Whether to return iterative history of residual norms. + restartControl : int, optional. + Iterations restart if the residuals jump ``2**restartControl`` times + compared to the smallest recorded in ``retResidualNormsHistory``. + The default is ``restartControl=20``, making the restarts rare for + backward compatibility. + + Returns + ------- + lambda : ndarray of the shape ``(k, )``. + Array of ``k`` approximate eigenvalues. + v : ndarray of the same shape as ``X.shape``. + An array of ``k`` approximate eigenvectors. + lambdaHistory : ndarray, optional. + The eigenvalue history, if `retLambdaHistory` is ``True``. + ResidualNormsHistory : ndarray, optional. + The history of residual norms, if `retResidualNormsHistory` + is ``True``. + + Notes + ----- + The iterative loop runs ``maxit=maxiter`` (20 if ``maxit=None``) + iterations at most and finishes earlier if the tolerance is met. + Breaking backward compatibility with the previous version, LOBPCG + now returns the block of iterative vectors with the best accuracy rather + than the last one iterated, as a cure for possible divergence. + + If ``X.dtype == np.float32`` and user-provided operations/multiplications + by `A`, `B`, and `M` all preserve the ``np.float32`` data type, + all the calculations and the output are in ``np.float32``. + + The size of the iteration history output equals to the number of the best + (limited by `maxit`) iterations plus 3: initial, final, and postprocessing. + + If both `retLambdaHistory` and `retResidualNormsHistory` are ``True``, + the return tuple has the following format + ``(lambda, V, lambda history, residual norms history)``. + + In the following ``n`` denotes the matrix size and ``k`` the number + of required eigenvalues (smallest or largest). + + The LOBPCG code internally solves eigenproblems of the size ``3k`` on every + iteration by calling the dense eigensolver `eigh`, so if ``k`` is not + small enough compared to ``n``, it makes no sense to call the LOBPCG code. + Moreover, if one calls the LOBPCG algorithm for ``5k > n``, it would likely + break internally, so the code calls the standard function `eigh` instead. + It is not that ``n`` should be large for the LOBPCG to work, but rather the + ratio ``n / k`` should be large. It you call LOBPCG with ``k=1`` + and ``n=10``, it works though ``n`` is small. The method is intended + for extremely large ``n / k``. + + The convergence speed depends basically on three factors: + + 1. Quality of the initial approximations `X` to the seeking eigenvectors. + Randomly distributed around the origin vectors work well if no better + choice is known. + + 2. Relative separation of the desired eigenvalues from the rest + of the eigenvalues. One can vary ``k`` to improve the separation. + + 3. Proper preconditioning to shrink the spectral spread. + For example, a rod vibration test problem (under tests + directory) is ill-conditioned for large ``n``, so convergence will be + slow, unless efficient preconditioning is used. For this specific + problem, a good simple preconditioner function would be a linear solve + for `A`, which is easy to code since `A` is tridiagonal. + + References + ---------- + .. [1] A. V. Knyazev (2001), + Toward the Optimal Preconditioned Eigensolver: Locally Optimal + Block Preconditioned Conjugate Gradient Method. + SIAM Journal on Scientific Computing 23, no. 2, + pp. 517-541. :doi:`10.1137/S1064827500366124` + + .. [2] A. V. Knyazev, I. Lashuk, M. E. Argentati, and E. Ovchinnikov + (2007), Block Locally Optimal Preconditioned Eigenvalue Xolvers + (BLOPEX) in hypre and PETSc. :arxiv:`0705.2626` + + .. [3] A. V. Knyazev's C and MATLAB implementations: + https://github.com/lobpcg/blopex + + Examples + -------- + Our first example is minimalistic - find the largest eigenvalue of + a diagonal matrix by solving the non-generalized eigenvalue problem + ``A x = lambda x`` without constraints or preconditioning. + + >>> import numpy as np + >>> from scipy.sparse import spdiags + >>> from scipy.sparse.linalg import LinearOperator, aslinearoperator + >>> from scipy.sparse.linalg import lobpcg + + The square matrix size is + + >>> n = 100 + + and its diagonal entries are 1, ..., 100 defined by + + >>> vals = np.arange(1, n + 1).astype(np.int16) + + The first mandatory input parameter in this test is + the sparse diagonal matrix `A` + of the eigenvalue problem ``A x = lambda x`` to solve. + + >>> A = spdiags(vals, 0, n, n) + >>> A = A.astype(np.int16) + >>> A.toarray() + array([[ 1, 0, 0, ..., 0, 0, 0], + [ 0, 2, 0, ..., 0, 0, 0], + [ 0, 0, 3, ..., 0, 0, 0], + ..., + [ 0, 0, 0, ..., 98, 0, 0], + [ 0, 0, 0, ..., 0, 99, 0], + [ 0, 0, 0, ..., 0, 0, 100]], dtype=int16) + + The second mandatory input parameter `X` is a 2D array with the + row dimension determining the number of requested eigenvalues. + `X` is an initial guess for targeted eigenvectors. + `X` must have linearly independent columns. + If no initial approximations available, randomly oriented vectors + commonly work best, e.g., with components normally distributed + around zero or uniformly distributed on the interval [-1 1]. + Setting the initial approximations to dtype ``np.float32`` + forces all iterative values to dtype ``np.float32`` speeding up + the run while still allowing accurate eigenvalue computations. + + >>> k = 1 + >>> rng = np.random.default_rng() + >>> X = rng.normal(size=(n, k)) + >>> X = X.astype(np.float32) + + >>> eigenvalues, _ = lobpcg(A, X, maxiter=60) + >>> eigenvalues + array([100.]) + >>> eigenvalues.dtype + dtype('float32') + + `lobpcg` needs only access the matrix product with `A` rather + then the matrix itself. Since the matrix `A` is diagonal in + this example, one can write a function of the matrix product + ``A @ X`` using the diagonal values ``vals`` only, e.g., by + element-wise multiplication with broadcasting in the lambda-function + + >>> A_lambda = lambda X: vals[:, np.newaxis] * X + + or the regular function + + >>> def A_matmat(X): + ... return vals[:, np.newaxis] * X + + and use the handle to one of these callables as an input + + >>> eigenvalues, _ = lobpcg(A_lambda, X, maxiter=60) + >>> eigenvalues + array([100.]) + >>> eigenvalues, _ = lobpcg(A_matmat, X, maxiter=60) + >>> eigenvalues + array([100.]) + + The traditional callable `LinearOperator` is no longer + necessary but still supported as the input to `lobpcg`. + Specifying ``matmat=A_matmat`` explicitly improves performance. + + >>> A_lo = LinearOperator((n, n), matvec=A_matmat, matmat=A_matmat, dtype=np.int16) + >>> eigenvalues, _ = lobpcg(A_lo, X, maxiter=80) + >>> eigenvalues + array([100.]) + + The least efficient callable option is `aslinearoperator`: + + >>> eigenvalues, _ = lobpcg(aslinearoperator(A), X, maxiter=80) + >>> eigenvalues + array([100.]) + + We now switch to computing the three smallest eigenvalues specifying + + >>> k = 3 + >>> X = np.random.default_rng().normal(size=(n, k)) + + and ``largest=False`` parameter + + >>> eigenvalues, _ = lobpcg(A, X, largest=False, maxiter=90) + >>> print(eigenvalues) + [1. 2. 3.] + + The next example illustrates computing 3 smallest eigenvalues of + the same matrix `A` given by the function handle ``A_matmat`` but + with constraints and preconditioning. + + Constraints - an optional input parameter is a 2D array comprising + of column vectors that the eigenvectors must be orthogonal to + + >>> Y = np.eye(n, 3) + + The preconditioner acts as the inverse of `A` in this example, but + in the reduced precision ``np.float32`` even though the initial `X` + and thus all iterates and the output are in full ``np.float64``. + + >>> inv_vals = 1./vals + >>> inv_vals = inv_vals.astype(np.float32) + >>> M = lambda X: inv_vals[:, np.newaxis] * X + + Let us now solve the eigenvalue problem for the matrix `A` first + without preconditioning requesting 80 iterations + + >>> eigenvalues, _ = lobpcg(A_matmat, X, Y=Y, largest=False, maxiter=80) + >>> eigenvalues + array([4., 5., 6.]) + >>> eigenvalues.dtype + dtype('float64') + + With preconditioning we need only 20 iterations from the same `X` + + >>> eigenvalues, _ = lobpcg(A_matmat, X, Y=Y, M=M, largest=False, maxiter=20) + >>> eigenvalues + array([4., 5., 6.]) + + Note that the vectors passed in `Y` are the eigenvectors of the 3 + smallest eigenvalues. The results returned above are orthogonal to those. + + The primary matrix `A` may be indefinite, e.g., after shifting + ``vals`` by 50 from 1, ..., 100 to -49, ..., 50, we still can compute + the 3 smallest or largest eigenvalues. + + >>> vals = vals - 50 + >>> X = rng.normal(size=(n, k)) + >>> eigenvalues, _ = lobpcg(A_matmat, X, largest=False, maxiter=99) + >>> eigenvalues + array([-49., -48., -47.]) + >>> eigenvalues, _ = lobpcg(A_matmat, X, largest=True, maxiter=99) + >>> eigenvalues + array([50., 49., 48.]) + + """ + blockVectorX = X + bestblockVectorX = blockVectorX + blockVectorY = Y + residualTolerance = tol + if maxiter is None: + maxiter = 20 + + bestIterationNumber = maxiter + + sizeY = 0 + if blockVectorY is not None: + if len(blockVectorY.shape) != 2: + warnings.warn( + f"Expected rank-2 array for argument Y, instead got " + f"{len(blockVectorY.shape)}, " + f"so ignore it and use no constraints.", + UserWarning, stacklevel=2 + ) + blockVectorY = None + else: + sizeY = blockVectorY.shape[1] + + # Block size. + if blockVectorX is None: + raise ValueError("The mandatory initial matrix X cannot be None") + if len(blockVectorX.shape) != 2: + raise ValueError("expected rank-2 array for argument X") + + n, sizeX = blockVectorX.shape + + # Data type of iterates, determined by X, must be inexact + if not np.issubdtype(blockVectorX.dtype, np.inexact): + warnings.warn( + f"Data type for argument X is {blockVectorX.dtype}, " + f"which is not inexact, so casted to np.float32.", + UserWarning, stacklevel=2 + ) + blockVectorX = np.asarray(blockVectorX, dtype=np.float32) + + if retLambdaHistory: + lambdaHistory = np.zeros((maxiter + 3, sizeX), + dtype=blockVectorX.dtype) + if retResidualNormsHistory: + residualNormsHistory = np.zeros((maxiter + 3, sizeX), + dtype=blockVectorX.dtype) + + if verbosityLevel: + aux = "Solving " + if B is None: + aux += "standard" + else: + aux += "generalized" + aux += " eigenvalue problem with" + if M is None: + aux += "out" + aux += " preconditioning\n\n" + aux += "matrix size %d\n" % n + aux += "block size %d\n\n" % sizeX + if blockVectorY is None: + aux += "No constraints\n\n" + else: + if sizeY > 1: + aux += "%d constraints\n\n" % sizeY + else: + aux += "%d constraint\n\n" % sizeY + print(aux) + + if (n - sizeY) < (5 * sizeX): + warnings.warn( + f"The problem size {n} minus the constraints size {sizeY} " + f"is too small relative to the block size {sizeX}. " + f"Using a dense eigensolver instead of LOBPCG iterations." + f"No output of the history of the iterations.", + UserWarning, stacklevel=2 + ) + + sizeX = min(sizeX, n) + + if blockVectorY is not None: + raise NotImplementedError( + "The dense eigensolver does not support constraints." + ) + + # Define the closed range of indices of eigenvalues to return. + if largest: + eigvals = (n - sizeX, n - 1) + else: + eigvals = (0, sizeX - 1) + + try: + if isinstance(A, LinearOperator): + A = A(np.eye(n, dtype=int)) + elif callable(A): + A = A(np.eye(n, dtype=int)) + if A.shape != (n, n): + raise ValueError( + f"The shape {A.shape} of the primary matrix\n" + f"defined by a callable object is wrong.\n" + ) + elif issparse(A): + A = A.toarray() + else: + A = np.asarray(A) + except Exception as e: + raise Exception( + f"Primary MatMul call failed with error\n" + f"{e}\n") + + if B is not None: + try: + if isinstance(B, LinearOperator): + B = B(np.eye(n, dtype=int)) + elif callable(B): + B = B(np.eye(n, dtype=int)) + if B.shape != (n, n): + raise ValueError( + f"The shape {B.shape} of the secondary matrix\n" + f"defined by a callable object is wrong.\n" + ) + elif issparse(B): + B = B.toarray() + else: + B = np.asarray(B) + except Exception as e: + raise Exception( + f"Secondary MatMul call failed with error\n" + f"{e}\n") + + try: + vals, vecs = eigh(A, + B, + subset_by_index=eigvals, + check_finite=False) + if largest: + # Reverse order to be compatible with eigs() in 'LM' mode. + vals = vals[::-1] + vecs = vecs[:, ::-1] + + return vals, vecs + except Exception as e: + raise Exception( + f"Dense eigensolver failed with error\n" + f"{e}\n" + ) + + if (residualTolerance is None) or (residualTolerance <= 0.0): + residualTolerance = np.sqrt(np.finfo(blockVectorX.dtype).eps) * n + + A = _makeMatMat(A) + B = _makeMatMat(B) + M = _makeMatMat(M) + + # Apply constraints to X. + if blockVectorY is not None: + + if B is not None: + blockVectorBY = B(blockVectorY) + if blockVectorBY.shape != blockVectorY.shape: + raise ValueError( + f"The shape {blockVectorY.shape} " + f"of the constraint not preserved\n" + f"and changed to {blockVectorBY.shape} " + f"after multiplying by the secondary matrix.\n" + ) + else: + blockVectorBY = blockVectorY + + # gramYBY is a dense array. + gramYBY = blockVectorY.T.conj() @ blockVectorBY + try: + # gramYBY is a Cholesky factor from now on... + gramYBY = cho_factor(gramYBY, overwrite_a=True) + except LinAlgError as e: + raise ValueError("Linearly dependent constraints") from e + + _applyConstraints(blockVectorX, gramYBY, blockVectorBY, blockVectorY) + + ## + # B-orthonormalize X. + blockVectorX, blockVectorBX, _ = _b_orthonormalize( + B, blockVectorX, verbosityLevel=verbosityLevel) + if blockVectorX is None: + raise ValueError("Linearly dependent initial approximations") + + ## + # Compute the initial Ritz vectors: solve the eigenproblem. + blockVectorAX = A(blockVectorX) + if blockVectorAX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the initial approximations not preserved\n" + f"and changed to {blockVectorAX.shape} " + f"after multiplying by the primary matrix.\n" + ) + + gramXAX = blockVectorX.T.conj() @ blockVectorAX + + _lambda, eigBlockVector = eigh(gramXAX, check_finite=False) + ii = _get_indx(_lambda, sizeX, largest) + _lambda = _lambda[ii] + if retLambdaHistory: + lambdaHistory[0, :] = _lambda + + eigBlockVector = np.asarray(eigBlockVector[:, ii]) + blockVectorX = _matmul_inplace( + blockVectorX, eigBlockVector, + verbosityLevel=verbosityLevel + ) + blockVectorAX = _matmul_inplace( + blockVectorAX, eigBlockVector, + verbosityLevel=verbosityLevel + ) + if B is not None: + blockVectorBX = _matmul_inplace( + blockVectorBX, eigBlockVector, + verbosityLevel=verbosityLevel + ) + + ## + # Active index set. + activeMask = np.ones((sizeX,), dtype=bool) + + ## + # Main iteration loop. + + blockVectorP = None # set during iteration + blockVectorAP = None + blockVectorBP = None + + smallestResidualNorm = np.abs(np.finfo(blockVectorX.dtype).max) + + iterationNumber = -1 + restart = True + forcedRestart = False + explicitGramFlag = False + while iterationNumber < maxiter: + iterationNumber += 1 + + if B is not None: + aux = blockVectorBX * _lambda[np.newaxis, :] + else: + aux = blockVectorX * _lambda[np.newaxis, :] + + blockVectorR = blockVectorAX - aux + + aux = np.sum(blockVectorR.conj() * blockVectorR, 0) + residualNorms = np.sqrt(np.abs(aux)) + if retResidualNormsHistory: + residualNormsHistory[iterationNumber, :] = residualNorms + residualNorm = np.sum(np.abs(residualNorms)) / sizeX + + if residualNorm < smallestResidualNorm: + smallestResidualNorm = residualNorm + bestIterationNumber = iterationNumber + bestblockVectorX = blockVectorX + elif residualNorm > 2**restartControl * smallestResidualNorm: + forcedRestart = True + blockVectorAX = A(blockVectorX) + if blockVectorAX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the restarted iterate not preserved\n" + f"and changed to {blockVectorAX.shape} " + f"after multiplying by the primary matrix.\n" + ) + if B is not None: + blockVectorBX = B(blockVectorX) + if blockVectorBX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the restarted iterate not preserved\n" + f"and changed to {blockVectorBX.shape} " + f"after multiplying by the secondary matrix.\n" + ) + + ii = np.where(residualNorms > residualTolerance, True, False) + activeMask = activeMask & ii + currentBlockSize = activeMask.sum() + + if verbosityLevel: + print(f"iteration {iterationNumber}") + print(f"current block size: {currentBlockSize}") + print(f"eigenvalue(s):\n{_lambda}") + print(f"residual norm(s):\n{residualNorms}") + + if currentBlockSize == 0: + break + + activeBlockVectorR = _as2d(blockVectorR[:, activeMask]) + + if iterationNumber > 0: + activeBlockVectorP = _as2d(blockVectorP[:, activeMask]) + activeBlockVectorAP = _as2d(blockVectorAP[:, activeMask]) + if B is not None: + activeBlockVectorBP = _as2d(blockVectorBP[:, activeMask]) + + if M is not None: + # Apply preconditioner T to the active residuals. + activeBlockVectorR = M(activeBlockVectorR) + + ## + # Apply constraints to the preconditioned residuals. + if blockVectorY is not None: + _applyConstraints(activeBlockVectorR, + gramYBY, + blockVectorBY, + blockVectorY) + + ## + # B-orthogonalize the preconditioned residuals to X. + if B is not None: + activeBlockVectorR = activeBlockVectorR - ( + blockVectorX @ + (blockVectorBX.T.conj() @ activeBlockVectorR) + ) + else: + activeBlockVectorR = activeBlockVectorR - ( + blockVectorX @ + (blockVectorX.T.conj() @ activeBlockVectorR) + ) + + ## + # B-orthonormalize the preconditioned residuals. + aux = _b_orthonormalize( + B, activeBlockVectorR, verbosityLevel=verbosityLevel) + activeBlockVectorR, activeBlockVectorBR, _ = aux + + if activeBlockVectorR is None: + warnings.warn( + f"Failed at iteration {iterationNumber} with accuracies " + f"{residualNorms}\n not reaching the requested " + f"tolerance {residualTolerance}.", + UserWarning, stacklevel=2 + ) + break + activeBlockVectorAR = A(activeBlockVectorR) + + if iterationNumber > 0: + if B is not None: + aux = _b_orthonormalize( + B, activeBlockVectorP, activeBlockVectorBP, + verbosityLevel=verbosityLevel + ) + activeBlockVectorP, activeBlockVectorBP, invR = aux + else: + aux = _b_orthonormalize(B, activeBlockVectorP, + verbosityLevel=verbosityLevel) + activeBlockVectorP, _, invR = aux + # Function _b_orthonormalize returns None if Cholesky fails + if activeBlockVectorP is not None: + activeBlockVectorAP = _matmul_inplace( + activeBlockVectorAP, invR, + verbosityLevel=verbosityLevel + ) + restart = forcedRestart + else: + restart = True + + ## + # Perform the Rayleigh Ritz Procedure: + # Compute symmetric Gram matrices: + + if activeBlockVectorAR.dtype == "float32": + myeps = 1 + else: + myeps = np.sqrt(np.finfo(activeBlockVectorR.dtype).eps) + + if residualNorms.max() > myeps and not explicitGramFlag: + explicitGramFlag = False + else: + # Once explicitGramFlag, forever explicitGramFlag. + explicitGramFlag = True + + # Shared memory assignments to simplify the code + if B is None: + blockVectorBX = blockVectorX + activeBlockVectorBR = activeBlockVectorR + if not restart: + activeBlockVectorBP = activeBlockVectorP + + # Common submatrices: + gramXAR = np.dot(blockVectorX.T.conj(), activeBlockVectorAR) + gramRAR = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorAR) + + gramDtype = activeBlockVectorAR.dtype + if explicitGramFlag: + gramRAR = (gramRAR + gramRAR.T.conj()) / 2 + gramXAX = np.dot(blockVectorX.T.conj(), blockVectorAX) + gramXAX = (gramXAX + gramXAX.T.conj()) / 2 + gramXBX = np.dot(blockVectorX.T.conj(), blockVectorBX) + gramRBR = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorBR) + gramXBR = np.dot(blockVectorX.T.conj(), activeBlockVectorBR) + else: + gramXAX = np.diag(_lambda).astype(gramDtype) + gramXBX = np.eye(sizeX, dtype=gramDtype) + gramRBR = np.eye(currentBlockSize, dtype=gramDtype) + gramXBR = np.zeros((sizeX, currentBlockSize), dtype=gramDtype) + + if not restart: + gramXAP = np.dot(blockVectorX.T.conj(), activeBlockVectorAP) + gramRAP = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorAP) + gramPAP = np.dot(activeBlockVectorP.T.conj(), activeBlockVectorAP) + gramXBP = np.dot(blockVectorX.T.conj(), activeBlockVectorBP) + gramRBP = np.dot(activeBlockVectorR.T.conj(), activeBlockVectorBP) + if explicitGramFlag: + gramPAP = (gramPAP + gramPAP.T.conj()) / 2 + gramPBP = np.dot(activeBlockVectorP.T.conj(), + activeBlockVectorBP) + else: + gramPBP = np.eye(currentBlockSize, dtype=gramDtype) + + gramA = np.block( + [ + [gramXAX, gramXAR, gramXAP], + [gramXAR.T.conj(), gramRAR, gramRAP], + [gramXAP.T.conj(), gramRAP.T.conj(), gramPAP], + ] + ) + gramB = np.block( + [ + [gramXBX, gramXBR, gramXBP], + [gramXBR.T.conj(), gramRBR, gramRBP], + [gramXBP.T.conj(), gramRBP.T.conj(), gramPBP], + ] + ) + + _handle_gramA_gramB_verbosity(gramA, gramB, verbosityLevel) + + try: + _lambda, eigBlockVector = eigh(gramA, + gramB, + check_finite=False) + except LinAlgError as e: + # raise ValueError("eigh failed in lobpcg iterations") from e + if verbosityLevel: + warnings.warn( + f"eigh failed at iteration {iterationNumber} \n" + f"with error {e} causing a restart.\n", + UserWarning, stacklevel=2 + ) + # try again after dropping the direction vectors P from RR + restart = True + + if restart: + gramA = np.block([[gramXAX, gramXAR], [gramXAR.T.conj(), gramRAR]]) + gramB = np.block([[gramXBX, gramXBR], [gramXBR.T.conj(), gramRBR]]) + + _handle_gramA_gramB_verbosity(gramA, gramB, verbosityLevel) + + try: + _lambda, eigBlockVector = eigh(gramA, + gramB, + check_finite=False) + except LinAlgError as e: + # raise ValueError("eigh failed in lobpcg iterations") from e + warnings.warn( + f"eigh failed at iteration {iterationNumber} with error\n" + f"{e}\n", + UserWarning, stacklevel=2 + ) + break + + ii = _get_indx(_lambda, sizeX, largest) + _lambda = _lambda[ii] + eigBlockVector = eigBlockVector[:, ii] + if retLambdaHistory: + lambdaHistory[iterationNumber + 1, :] = _lambda + + # Compute Ritz vectors. + if B is not None: + if not restart: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX: + sizeX + currentBlockSize] + eigBlockVectorP = eigBlockVector[sizeX + currentBlockSize:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + pp += np.dot(activeBlockVectorP, eigBlockVectorP) + + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + app += np.dot(activeBlockVectorAP, eigBlockVectorP) + + bpp = np.dot(activeBlockVectorBR, eigBlockVectorR) + bpp += np.dot(activeBlockVectorBP, eigBlockVectorP) + else: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + bpp = np.dot(activeBlockVectorBR, eigBlockVectorR) + + blockVectorX = np.dot(blockVectorX, eigBlockVectorX) + pp + blockVectorAX = np.dot(blockVectorAX, eigBlockVectorX) + app + blockVectorBX = np.dot(blockVectorBX, eigBlockVectorX) + bpp + + blockVectorP, blockVectorAP, blockVectorBP = pp, app, bpp + + else: + if not restart: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX: + sizeX + currentBlockSize] + eigBlockVectorP = eigBlockVector[sizeX + currentBlockSize:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + pp += np.dot(activeBlockVectorP, eigBlockVectorP) + + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + app += np.dot(activeBlockVectorAP, eigBlockVectorP) + else: + eigBlockVectorX = eigBlockVector[:sizeX] + eigBlockVectorR = eigBlockVector[sizeX:] + + pp = np.dot(activeBlockVectorR, eigBlockVectorR) + app = np.dot(activeBlockVectorAR, eigBlockVectorR) + + blockVectorX = np.dot(blockVectorX, eigBlockVectorX) + pp + blockVectorAX = np.dot(blockVectorAX, eigBlockVectorX) + app + + blockVectorP, blockVectorAP = pp, app + + if B is not None: + aux = blockVectorBX * _lambda[np.newaxis, :] + else: + aux = blockVectorX * _lambda[np.newaxis, :] + + blockVectorR = blockVectorAX - aux + + aux = np.sum(blockVectorR.conj() * blockVectorR, 0) + residualNorms = np.sqrt(np.abs(aux)) + # Use old lambda in case of early loop exit. + if retLambdaHistory: + lambdaHistory[iterationNumber + 1, :] = _lambda + if retResidualNormsHistory: + residualNormsHistory[iterationNumber + 1, :] = residualNorms + residualNorm = np.sum(np.abs(residualNorms)) / sizeX + if residualNorm < smallestResidualNorm: + smallestResidualNorm = residualNorm + bestIterationNumber = iterationNumber + 1 + bestblockVectorX = blockVectorX + + if np.max(np.abs(residualNorms)) > residualTolerance: + warnings.warn( + f"Exited at iteration {iterationNumber} with accuracies \n" + f"{residualNorms}\n" + f"not reaching the requested tolerance {residualTolerance}.\n" + f"Use iteration {bestIterationNumber} instead with accuracy \n" + f"{smallestResidualNorm}.\n", + UserWarning, stacklevel=2 + ) + + if verbosityLevel: + print(f"Final iterative eigenvalue(s):\n{_lambda}") + print(f"Final iterative residual norm(s):\n{residualNorms}") + + blockVectorX = bestblockVectorX + # Making eigenvectors "exactly" satisfy the blockVectorY constrains + if blockVectorY is not None: + _applyConstraints(blockVectorX, + gramYBY, + blockVectorBY, + blockVectorY) + + # Making eigenvectors "exactly" othonormalized by final "exact" RR + blockVectorAX = A(blockVectorX) + if blockVectorAX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the postprocessing iterate not preserved\n" + f"and changed to {blockVectorAX.shape} " + f"after multiplying by the primary matrix.\n" + ) + gramXAX = np.dot(blockVectorX.T.conj(), blockVectorAX) + + blockVectorBX = blockVectorX + if B is not None: + blockVectorBX = B(blockVectorX) + if blockVectorBX.shape != blockVectorX.shape: + raise ValueError( + f"The shape {blockVectorX.shape} " + f"of the postprocessing iterate not preserved\n" + f"and changed to {blockVectorBX.shape} " + f"after multiplying by the secondary matrix.\n" + ) + + gramXBX = np.dot(blockVectorX.T.conj(), blockVectorBX) + _handle_gramA_gramB_verbosity(gramXAX, gramXBX, verbosityLevel) + gramXAX = (gramXAX + gramXAX.T.conj()) / 2 + gramXBX = (gramXBX + gramXBX.T.conj()) / 2 + try: + _lambda, eigBlockVector = eigh(gramXAX, + gramXBX, + check_finite=False) + except LinAlgError as e: + raise ValueError("eigh has failed in lobpcg postprocessing") from e + + ii = _get_indx(_lambda, sizeX, largest) + _lambda = _lambda[ii] + eigBlockVector = np.asarray(eigBlockVector[:, ii]) + + blockVectorX = np.dot(blockVectorX, eigBlockVector) + blockVectorAX = np.dot(blockVectorAX, eigBlockVector) + + if B is not None: + blockVectorBX = np.dot(blockVectorBX, eigBlockVector) + aux = blockVectorBX * _lambda[np.newaxis, :] + else: + aux = blockVectorX * _lambda[np.newaxis, :] + + blockVectorR = blockVectorAX - aux + + aux = np.sum(blockVectorR.conj() * blockVectorR, 0) + residualNorms = np.sqrt(np.abs(aux)) + + if retLambdaHistory: + lambdaHistory[bestIterationNumber + 1, :] = _lambda + if retResidualNormsHistory: + residualNormsHistory[bestIterationNumber + 1, :] = residualNorms + + if retLambdaHistory: + lambdaHistory = lambdaHistory[ + : bestIterationNumber + 2, :] + if retResidualNormsHistory: + residualNormsHistory = residualNormsHistory[ + : bestIterationNumber + 2, :] + + if np.max(np.abs(residualNorms)) > residualTolerance: + warnings.warn( + f"Exited postprocessing with accuracies \n" + f"{residualNorms}\n" + f"not reaching the requested tolerance {residualTolerance}.", + UserWarning, stacklevel=2 + ) + + if verbosityLevel: + print(f"Final postprocessing eigenvalue(s):\n{_lambda}") + print(f"Final residual norm(s):\n{residualNorms}") + + if retLambdaHistory: + lambdaHistory = np.vsplit(lambdaHistory, np.shape(lambdaHistory)[0]) + lambdaHistory = [np.squeeze(i) for i in lambdaHistory] + if retResidualNormsHistory: + residualNormsHistory = np.vsplit(residualNormsHistory, + np.shape(residualNormsHistory)[0]) + residualNormsHistory = [np.squeeze(i) for i in residualNormsHistory] + + if retLambdaHistory: + if retResidualNormsHistory: + return _lambda, blockVectorX, lambdaHistory, residualNormsHistory + else: + return _lambda, blockVectorX, lambdaHistory + else: + if retResidualNormsHistory: + return _lambda, blockVectorX, residualNormsHistory + else: + return _lambda, blockVectorX diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42e31047e65d114322d4c2d82e51f175b7613213 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__pycache__/test_lobpcg.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__pycache__/test_lobpcg.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2be3712c60ca06b4e7a8d8007e2c614b99714b0 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/__pycache__/test_lobpcg.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py new file mode 100644 index 0000000000000000000000000000000000000000..38b5e3833588d4dcf8cfad213a5dda2ea0da06da --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py @@ -0,0 +1,641 @@ +""" Test functions for the sparse.linalg._eigen.lobpcg module +""" + +import itertools +import platform +import sys +import pytest +import numpy as np +from numpy import ones, r_, diag +from numpy.testing import (assert_almost_equal, assert_equal, + assert_allclose, assert_array_less) + +from scipy import sparse +from scipy.linalg import eig, eigh, toeplitz, orth +from scipy.sparse import spdiags, diags, eye, csr_matrix +from scipy.sparse.linalg import eigs, LinearOperator +from scipy.sparse.linalg._eigen.lobpcg import lobpcg +from scipy.sparse.linalg._eigen.lobpcg.lobpcg import _b_orthonormalize +from scipy._lib._util import np_long, np_ulong + +_IS_32BIT = (sys.maxsize < 2**32) + +INT_DTYPES = {np.intc, np_long, np.longlong, np.uintc, np_ulong, np.ulonglong} +# np.half is unsupported on many test systems so excluded +REAL_DTYPES = {np.float32, np.float64, np.longdouble} +COMPLEX_DTYPES = {np.complex64, np.complex128, np.clongdouble} +# use sorted list to ensure fixed order of tests +VDTYPES = sorted(REAL_DTYPES ^ COMPLEX_DTYPES, key=str) +MDTYPES = sorted(INT_DTYPES ^ REAL_DTYPES ^ COMPLEX_DTYPES, key=str) + + +def sign_align(A, B): + """Align signs of columns of A match those of B: column-wise remove + sign of A by multiplying with its sign then multiply in sign of B. + """ + return np.array([col_A * np.sign(col_A[0]) * np.sign(col_B[0]) + for col_A, col_B in zip(A.T, B.T)]).T + +def ElasticRod(n): + """Build the matrices for the generalized eigenvalue problem of the + fixed-free elastic rod vibration model. + """ + L = 1.0 + le = L/n + rho = 7.85e3 + S = 1.e-4 + E = 2.1e11 + mass = rho*S*le/6. + k = E*S/le + A = k*(diag(r_[2.*ones(n-1), 1])-diag(ones(n-1), 1)-diag(ones(n-1), -1)) + B = mass*(diag(r_[4.*ones(n-1), 2])+diag(ones(n-1), 1)+diag(ones(n-1), -1)) + return A, B + + +def MikotaPair(n): + """Build a pair of full diagonal matrices for the generalized eigenvalue + problem. The Mikota pair acts as a nice test since the eigenvalues are the + squares of the integers n, n=1,2,... + """ + x = np.arange(1, n+1) + B = diag(1./x) + y = np.arange(n-1, 0, -1) + z = np.arange(2*n-1, 0, -2) + A = diag(z)-diag(y, -1)-diag(y, 1) + return A, B + + +def compare_solutions(A, B, m): + """Check eig vs. lobpcg consistency. + """ + n = A.shape[0] + rnd = np.random.RandomState(0) + V = rnd.random((n, m)) + X = orth(V) + eigvals, _ = lobpcg(A, X, B=B, tol=1e-2, maxiter=50, largest=False) + eigvals.sort() + w, _ = eig(A, b=B) + w.sort() + assert_almost_equal(w[:int(m/2)], eigvals[:int(m/2)], decimal=2) + + +def test_Small(): + A, B = ElasticRod(10) + with pytest.warns(UserWarning, match="The problem size"): + compare_solutions(A, B, 10) + A, B = MikotaPair(10) + with pytest.warns(UserWarning, match="The problem size"): + compare_solutions(A, B, 10) + + +def test_ElasticRod(): + A, B = ElasticRod(20) + msg = "Exited at iteration.*|Exited postprocessing with accuracies.*" + with pytest.warns(UserWarning, match=msg): + compare_solutions(A, B, 2) + + +def test_MikotaPair(): + A, B = MikotaPair(20) + compare_solutions(A, B, 2) + + +@pytest.mark.parametrize("n", [50]) +@pytest.mark.parametrize("m", [1, 2, 10]) +@pytest.mark.parametrize("Vdtype", sorted(REAL_DTYPES, key=str)) +@pytest.mark.parametrize("Bdtype", sorted(REAL_DTYPES, key=str)) +@pytest.mark.parametrize("BVdtype", sorted(REAL_DTYPES, key=str)) +def test_b_orthonormalize(n, m, Vdtype, Bdtype, BVdtype): + """Test B-orthonormalization by Cholesky with callable 'B'. + The function '_b_orthonormalize' is key in LOBPCG but may + lead to numerical instabilities. The input vectors are often + badly scaled, so the function needs scale-invariant Cholesky; + see https://netlib.org/lapack/lawnspdf/lawn14.pdf. + """ + rnd = np.random.RandomState(0) + X = rnd.standard_normal((n, m)).astype(Vdtype) + Xcopy = np.copy(X) + vals = np.arange(1, n+1, dtype=float) + B = diags([vals], [0], (n, n)).astype(Bdtype) + BX = B @ X + BX = BX.astype(BVdtype) + dtype = min(X.dtype, B.dtype, BX.dtype) + # np.longdouble tol cannot be achieved on most systems + atol = m * n * max(np.finfo(dtype).eps, np.finfo(np.float64).eps) + + Xo, BXo, _ = _b_orthonormalize(lambda v: B @ v, X, BX) + # Check in-place. + assert_equal(X, Xo) + assert_equal(id(X), id(Xo)) + assert_equal(BX, BXo) + assert_equal(id(BX), id(BXo)) + # Check BXo. + assert_allclose(B @ Xo, BXo, atol=atol, rtol=atol) + # Check B-orthonormality + assert_allclose(Xo.T.conj() @ B @ Xo, np.identity(m), + atol=atol, rtol=atol) + # Repeat without BX in outputs + X = np.copy(Xcopy) + Xo1, BXo1, _ = _b_orthonormalize(lambda v: B @ v, X) + assert_allclose(Xo, Xo1, atol=atol, rtol=atol) + assert_allclose(BXo, BXo1, atol=atol, rtol=atol) + # Check in-place. + assert_equal(X, Xo1) + assert_equal(id(X), id(Xo1)) + # Check BXo1. + assert_allclose(B @ Xo1, BXo1, atol=atol, rtol=atol) + + # Introduce column-scaling in X. + scaling = 1.0 / np.geomspace(10, 1e10, num=m) + X = Xcopy * scaling + X = X.astype(Vdtype) + BX = B @ X + BX = BX.astype(BVdtype) + # Check scaling-invariance of Cholesky-based orthonormalization + Xo1, BXo1, _ = _b_orthonormalize(lambda v: B @ v, X, BX) + # The output should be the same, up the signs of the columns. + Xo1 = sign_align(Xo1, Xo) + assert_allclose(Xo, Xo1, atol=atol, rtol=atol) + BXo1 = sign_align(BXo1, BXo) + assert_allclose(BXo, BXo1, atol=atol, rtol=atol) + + +@pytest.mark.filterwarnings("ignore:Exited at iteration 0") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_nonhermitian_warning(capsys): + """Check the warning of a Ritz matrix being not Hermitian + by feeding a non-Hermitian input matrix. + Also check stdout since verbosityLevel=1 and lack of stderr. + """ + n = 10 + X = np.arange(n * 2).reshape(n, 2).astype(np.float32) + A = np.arange(n * n).reshape(n, n).astype(np.float32) + with pytest.warns(UserWarning, match="Matrix gramA"): + _, _ = lobpcg(A, X, verbosityLevel=1, maxiter=0) + out, err = capsys.readouterr() # Capture output + assert out.startswith("Solving standard eigenvalue") # Test stdout + assert err == '' # Test empty stderr + # Make the matrix symmetric and the UserWarning disappears. + A += A.T + _, _ = lobpcg(A, X, verbosityLevel=1, maxiter=0) + out, err = capsys.readouterr() # Capture output + assert out.startswith("Solving standard eigenvalue") # Test stdout + assert err == '' # Test empty stderr + + +def test_regression(): + """Check the eigenvalue of the identity matrix is one. + """ + # https://mail.python.org/pipermail/scipy-user/2010-October/026944.html + n = 10 + X = np.ones((n, 1)) + A = np.identity(n) + w, _ = lobpcg(A, X) + assert_allclose(w, [1]) + + +@pytest.mark.filterwarnings("ignore:The problem size") +@pytest.mark.parametrize('n, m, m_excluded', [(30, 4, 3), (4, 2, 0)]) +def test_diagonal(n, m, m_excluded): + """Test ``m - m_excluded`` eigenvalues and eigenvectors of + diagonal matrices of the size ``n`` varying matrix formats: + dense array, spare matrix, and ``LinearOperator`` for both + matrixes in the generalized eigenvalue problem ``Av = cBv`` + and for the preconditioner. + """ + rnd = np.random.RandomState(0) + + # Define the generalized eigenvalue problem Av = cBv + # where (c, v) is a generalized eigenpair, + # A is the diagonal matrix whose entries are 1,...n, + # B is the identity matrix. + vals = np.arange(1, n+1, dtype=float) + A_s = diags([vals], [0], (n, n)) + A_a = A_s.toarray() + + def A_f(x): + return A_s @ x + + A_lo = LinearOperator(matvec=A_f, + matmat=A_f, + shape=(n, n), dtype=float) + + B_a = eye(n) + B_s = csr_matrix(B_a) + + def B_f(x): + return B_a @ x + + B_lo = LinearOperator(matvec=B_f, + matmat=B_f, + shape=(n, n), dtype=float) + + # Let the preconditioner M be the inverse of A. + M_s = diags([1./vals], [0], (n, n)) + M_a = M_s.toarray() + + def M_f(x): + return M_s @ x + + M_lo = LinearOperator(matvec=M_f, + matmat=M_f, + shape=(n, n), dtype=float) + + # Pick random initial vectors. + X = rnd.normal(size=(n, m)) + + # Require that the returned eigenvectors be in the orthogonal complement + # of the first few standard basis vectors. + if m_excluded > 0: + Y = np.eye(n, m_excluded) + else: + Y = None + + for A in [A_a, A_s, A_lo]: + for B in [B_a, B_s, B_lo]: + for M in [M_a, M_s, M_lo]: + eigvals, vecs = lobpcg(A, X, B, M=M, Y=Y, + maxiter=40, largest=False) + + assert_allclose(eigvals, np.arange(1+m_excluded, + 1+m_excluded+m)) + _check_eigen(A, eigvals, vecs, rtol=1e-3, atol=1e-3) + + +def _check_eigen(M, w, V, rtol=1e-8, atol=1e-14): + """Check if the eigenvalue residual is small. + """ + mult_wV = np.multiply(w, V) + dot_MV = M.dot(V) + assert_allclose(mult_wV, dot_MV, rtol=rtol, atol=atol) + + +def _check_fiedler(n, p): + """Check the Fiedler vector computation. + """ + # This is not necessarily the recommended way to find the Fiedler vector. + col = np.zeros(n) + col[1] = 1 + A = toeplitz(col) + D = np.diag(A.sum(axis=1)) + L = D - A + # Compute the full eigendecomposition using tricks, e.g. + # http://www.cs.yale.edu/homes/spielman/561/2009/lect02-09.pdf + tmp = np.pi * np.arange(n) / n + analytic_w = 2 * (1 - np.cos(tmp)) + analytic_V = np.cos(np.outer(np.arange(n) + 1/2, tmp)) + _check_eigen(L, analytic_w, analytic_V) + # Compute the full eigendecomposition using eigh. + eigh_w, eigh_V = eigh(L) + _check_eigen(L, eigh_w, eigh_V) + # Check that the first eigenvalue is near zero and that the rest agree. + assert_array_less(np.abs([eigh_w[0], analytic_w[0]]), 1e-14) + assert_allclose(eigh_w[1:], analytic_w[1:]) + + # Check small lobpcg eigenvalues. + X = analytic_V[:, :p] + lobpcg_w, lobpcg_V = lobpcg(L, X, largest=False) + assert_equal(lobpcg_w.shape, (p,)) + assert_equal(lobpcg_V.shape, (n, p)) + _check_eigen(L, lobpcg_w, lobpcg_V) + assert_array_less(np.abs(np.min(lobpcg_w)), 1e-14) + assert_allclose(np.sort(lobpcg_w)[1:], analytic_w[1:p]) + + # Check large lobpcg eigenvalues. + X = analytic_V[:, -p:] + lobpcg_w, lobpcg_V = lobpcg(L, X, largest=True) + assert_equal(lobpcg_w.shape, (p,)) + assert_equal(lobpcg_V.shape, (n, p)) + _check_eigen(L, lobpcg_w, lobpcg_V) + assert_allclose(np.sort(lobpcg_w), analytic_w[-p:]) + + # Look for the Fiedler vector using good but not exactly correct guesses. + fiedler_guess = np.concatenate((np.ones(n//2), -np.ones(n-n//2))) + X = np.vstack((np.ones(n), fiedler_guess)).T + lobpcg_w, _ = lobpcg(L, X, largest=False) + # Mathematically, the smaller eigenvalue should be zero + # and the larger should be the algebraic connectivity. + lobpcg_w = np.sort(lobpcg_w) + assert_allclose(lobpcg_w, analytic_w[:2], atol=1e-14) + + +def test_fiedler_small_8(): + """Check the dense workaround path for small matrices. + """ + # This triggers the dense path because 8 < 2*5. + with pytest.warns(UserWarning, match="The problem size"): + _check_fiedler(8, 2) + + +def test_fiedler_large_12(): + """Check the dense workaround path avoided for non-small matrices. + """ + # This does not trigger the dense path, because 2*5 <= 12. + _check_fiedler(12, 2) + + +@pytest.mark.filterwarnings("ignore:Failed at iteration") +@pytest.mark.filterwarnings("ignore:Exited at iteration") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_failure_to_run_iterations(): + """Check that the code exits gracefully without breaking. Issue #10974. + The code may or not issue a warning, filtered out. Issue #15935, #17954. + """ + rnd = np.random.RandomState(0) + X = rnd.standard_normal((100, 10)) + A = X @ X.T + Q = rnd.standard_normal((X.shape[0], 4)) + eigenvalues, _ = lobpcg(A, Q, maxiter=40, tol=1e-12) + assert np.max(eigenvalues) > 0 + + +def test_failure_to_run_iterations_nonsymmetric(): + """Check that the code exists gracefully without breaking + if the matrix in not symmetric. + """ + A = np.zeros((10, 10)) + A[0, 1] = 1 + Q = np.ones((10, 1)) + msg = "Exited at iteration 2|Exited postprocessing with accuracies.*" + with pytest.warns(UserWarning, match=msg): + eigenvalues, _ = lobpcg(A, Q, maxiter=20) + assert np.max(eigenvalues) > 0 + + +@pytest.mark.filterwarnings("ignore:The problem size") +def test_hermitian(): + """Check complex-value Hermitian cases. + """ + rnd = np.random.RandomState(0) + + sizes = [3, 12] + ks = [1, 2] + gens = [True, False] + + for s, k, gen, dh, dx, db in ( + itertools.product(sizes, ks, gens, gens, gens, gens) + ): + H = rnd.random((s, s)) + 1.j * rnd.random((s, s)) + H = 10 * np.eye(s) + H + H.T.conj() + H = H.astype(np.complex128) if dh else H.astype(np.complex64) + + X = rnd.standard_normal((s, k)) + X = X + 1.j * rnd.standard_normal((s, k)) + X = X.astype(np.complex128) if dx else X.astype(np.complex64) + + if not gen: + B = np.eye(s) + w, v = lobpcg(H, X, maxiter=99, verbosityLevel=0) + # Also test mixing complex H with real B. + wb, _ = lobpcg(H, X, B, maxiter=99, verbosityLevel=0) + assert_allclose(w, wb, rtol=1e-6) + w0, _ = eigh(H) + else: + B = rnd.random((s, s)) + 1.j * rnd.random((s, s)) + B = 10 * np.eye(s) + B.dot(B.T.conj()) + B = B.astype(np.complex128) if db else B.astype(np.complex64) + w, v = lobpcg(H, X, B, maxiter=99, verbosityLevel=0) + w0, _ = eigh(H, B) + + for wx, vx in zip(w, v.T): + # Check eigenvector + assert_allclose(np.linalg.norm(H.dot(vx) - B.dot(vx) * wx) + / np.linalg.norm(H.dot(vx)), + 0, atol=5e-2, rtol=0) + + # Compare eigenvalues + j = np.argmin(abs(w0 - wx)) + assert_allclose(wx, w0[j], rtol=1e-4) + + +# The n=5 case tests the alternative small matrix code path that uses eigh(). +@pytest.mark.filterwarnings("ignore:The problem size") +@pytest.mark.parametrize('n, atol', [(20, 1e-3), (5, 1e-8)]) +def test_eigs_consistency(n, atol): + """Check eigs vs. lobpcg consistency. + """ + vals = np.arange(1, n+1, dtype=np.float64) + A = spdiags(vals, 0, n, n) + rnd = np.random.RandomState(0) + X = rnd.standard_normal((n, 2)) + lvals, lvecs = lobpcg(A, X, largest=True, maxiter=100) + vals, _ = eigs(A, k=2) + + _check_eigen(A, lvals, lvecs, atol=atol, rtol=0) + assert_allclose(np.sort(vals), np.sort(lvals), atol=1e-14) + + +def test_verbosity(): + """Check that nonzero verbosity level code runs. + """ + rnd = np.random.RandomState(0) + X = rnd.standard_normal((10, 10)) + A = X @ X.T + Q = rnd.standard_normal((X.shape[0], 1)) + msg = "Exited at iteration.*|Exited postprocessing with accuracies.*" + with pytest.warns(UserWarning, match=msg): + _, _ = lobpcg(A, Q, maxiter=3, verbosityLevel=9) + + +@pytest.mark.xfail(_IS_32BIT and sys.platform == 'win32', + reason="tolerance violation on windows") +@pytest.mark.xfail(platform.machine() == 'ppc64le', + reason="fails on ppc64le") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_tolerance_float32(): + """Check lobpcg for attainable tolerance in float32. + """ + rnd = np.random.RandomState(0) + n = 50 + m = 3 + vals = -np.arange(1, n + 1) + A = diags([vals], [0], (n, n)) + A = A.astype(np.float32) + X = rnd.standard_normal((n, m)) + X = X.astype(np.float32) + eigvals, _ = lobpcg(A, X, tol=1.25e-5, maxiter=50, verbosityLevel=0) + assert_allclose(eigvals, -np.arange(1, 1 + m), atol=2e-5, rtol=1e-5) + + +@pytest.mark.parametrize("vdtype", VDTYPES) +@pytest.mark.parametrize("mdtype", MDTYPES) +@pytest.mark.parametrize("arr_type", [np.array, + sparse.csr_matrix, + sparse.coo_matrix]) +def test_dtypes(vdtype, mdtype, arr_type): + """Test lobpcg in various dtypes. + """ + rnd = np.random.RandomState(0) + n = 12 + m = 2 + A = arr_type(np.diag(np.arange(1, n + 1)).astype(mdtype)) + X = rnd.random((n, m)) + X = X.astype(vdtype) + eigvals, eigvecs = lobpcg(A, X, tol=1e-2, largest=False) + assert_allclose(eigvals, np.arange(1, 1 + m), atol=1e-1) + # eigenvectors must be nearly real in any case + assert_allclose(np.sum(np.abs(eigvecs - eigvecs.conj())), 0, atol=1e-2) + + +@pytest.mark.filterwarnings("ignore:Exited at iteration") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_inplace_warning(): + """Check lobpcg gives a warning in '_b_orthonormalize' + that in-place orthogonalization is impossible due to dtype mismatch. + """ + rnd = np.random.RandomState(0) + n = 6 + m = 1 + vals = -np.arange(1, n + 1) + A = diags([vals], [0], (n, n)) + A = A.astype(np.cdouble) + X = rnd.standard_normal((n, m)) + with pytest.warns(UserWarning, match="Inplace update"): + eigvals, _ = lobpcg(A, X, maxiter=2, verbosityLevel=1) + + +def test_maxit(): + """Check lobpcg if maxit=maxiter runs maxiter iterations and + if maxit=None runs 20 iterations (the default) + by checking the size of the iteration history output, which should + be the number of iterations plus 3 (initial, final, and postprocessing) + typically when maxiter is small and the choice of the best is passive. + """ + rnd = np.random.RandomState(0) + n = 50 + m = 4 + vals = -np.arange(1, n + 1) + A = diags([vals], [0], (n, n)) + A = A.astype(np.float32) + X = rnd.standard_normal((n, m)) + X = X.astype(np.float64) + msg = "Exited at iteration.*|Exited postprocessing with accuracies.*" + for maxiter in range(1, 4): + with pytest.warns(UserWarning, match=msg): + _, _, l_h, r_h = lobpcg(A, X, tol=1e-8, maxiter=maxiter, + retLambdaHistory=True, + retResidualNormsHistory=True) + assert_allclose(np.shape(l_h)[0], maxiter+3) + assert_allclose(np.shape(r_h)[0], maxiter+3) + with pytest.warns(UserWarning, match=msg): + l, _, l_h, r_h = lobpcg(A, X, tol=1e-8, + retLambdaHistory=True, + retResidualNormsHistory=True) + assert_allclose(np.shape(l_h)[0], 20+3) + assert_allclose(np.shape(r_h)[0], 20+3) + # Check that eigenvalue output is the last one in history + assert_allclose(l, l_h[-1]) + # Make sure that both history outputs are lists + assert isinstance(l_h, list) + assert isinstance(r_h, list) + # Make sure that both history lists are arrays-like + assert_allclose(np.shape(l_h), np.shape(np.asarray(l_h))) + assert_allclose(np.shape(r_h), np.shape(np.asarray(r_h))) + + +@pytest.mark.slow +@pytest.mark.parametrize("n", [15]) +@pytest.mark.parametrize("m", [1, 2]) +@pytest.mark.filterwarnings("ignore:Exited at iteration") +@pytest.mark.filterwarnings("ignore:Exited postprocessing") +def test_diagonal_data_types(n, m): + """Check lobpcg for diagonal matrices for all matrix types. + Constraints are imposed, so a dense eigensolver eig cannot run. + """ + rnd = np.random.RandomState(0) + # Define the generalized eigenvalue problem Av = cBv + # where (c, v) is a generalized eigenpair, + # and where we choose A and B to be diagonal. + vals = np.arange(1, n + 1) + + list_sparse_format = ['bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil'] + for s_f_i, s_f in enumerate(list_sparse_format): + + As64 = diags([vals * vals], [0], (n, n), format=s_f) + As32 = As64.astype(np.float32) + Af64 = As64.toarray() + Af32 = Af64.astype(np.float32) + + def As32f(x): + return As32 @ x + As32LO = LinearOperator(matvec=As32f, + matmat=As32f, + shape=(n, n), + dtype=As32.dtype) + + listA = [Af64, As64, Af32, As32, As32f, As32LO, lambda v: As32 @ v] + + Bs64 = diags([vals], [0], (n, n), format=s_f) + Bf64 = Bs64.toarray() + Bs32 = Bs64.astype(np.float32) + + def Bs32f(x): + return Bs32 @ x + Bs32LO = LinearOperator(matvec=Bs32f, + matmat=Bs32f, + shape=(n, n), + dtype=Bs32.dtype) + listB = [Bf64, Bs64, Bs32, Bs32f, Bs32LO, lambda v: Bs32 @ v] + + # Define the preconditioner function as LinearOperator. + Ms64 = diags([1./vals], [0], (n, n), format=s_f) + + def Ms64precond(x): + return Ms64 @ x + Ms64precondLO = LinearOperator(matvec=Ms64precond, + matmat=Ms64precond, + shape=(n, n), + dtype=Ms64.dtype) + Mf64 = Ms64.toarray() + + def Mf64precond(x): + return Mf64 @ x + Mf64precondLO = LinearOperator(matvec=Mf64precond, + matmat=Mf64precond, + shape=(n, n), + dtype=Mf64.dtype) + Ms32 = Ms64.astype(np.float32) + + def Ms32precond(x): + return Ms32 @ x + Ms32precondLO = LinearOperator(matvec=Ms32precond, + matmat=Ms32precond, + shape=(n, n), + dtype=Ms32.dtype) + Mf32 = Ms32.toarray() + + def Mf32precond(x): + return Mf32 @ x + Mf32precondLO = LinearOperator(matvec=Mf32precond, + matmat=Mf32precond, + shape=(n, n), + dtype=Mf32.dtype) + listM = [None, Ms64, Ms64precondLO, Mf64precondLO, Ms64precond, + Ms32, Ms32precondLO, Mf32precondLO, Ms32precond] + + # Setup matrix of the initial approximation to the eigenvectors + # (cannot be sparse array). + Xf64 = rnd.random((n, m)) + Xf32 = Xf64.astype(np.float32) + listX = [Xf64, Xf32] + + # Require that the returned eigenvectors be in the orthogonal complement + # of the first few standard basis vectors (cannot be sparse array). + m_excluded = 3 + Yf64 = np.eye(n, m_excluded, dtype=float) + Yf32 = np.eye(n, m_excluded, dtype=np.float32) + listY = [Yf64, Yf32] + + tests = list(itertools.product(listA, listB, listM, listX, listY)) + + for A, B, M, X, Y in tests: + # This is one of the slower tests because there are >1,000 configs + # to test here. Flip a biased coin to decide whether to run each + # test to get decent coverage in less time. + if rnd.random() < 0.98: + continue # too many tests + eigvals, _ = lobpcg(A, X, B=B, M=M, Y=Y, tol=1e-4, + maxiter=100, largest=False) + assert_allclose(eigvals, + np.arange(1 + m_excluded, 1 + m_excluded + m), + atol=1e-5) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c2d382f40295563e78af4d09dbb07b1da8f5557 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__pycache__/test_svds.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__pycache__/test_svds.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01ac53ba5110829e6207d010403ae5f61a210a34 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/__pycache__/test_svds.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/test_svds.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/test_svds.py new file mode 100644 index 0000000000000000000000000000000000000000..587d0eb6ede989935ab1d210e8b142613565401f --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_eigen/tests/test_svds.py @@ -0,0 +1,862 @@ +import re +import copy +import numpy as np + +from numpy.testing import assert_allclose, assert_equal, assert_array_equal +import pytest + +from scipy.linalg import svd, null_space +from scipy.sparse import csc_matrix, issparse, spdiags, random +from scipy.sparse.linalg import LinearOperator, aslinearoperator +from scipy.sparse.linalg import svds +from scipy.sparse.linalg._eigen.arpack import ArpackNoConvergence + + +# --- Helper Functions / Classes --- + + +def sorted_svd(m, k, which='LM'): + # Compute svd of a dense matrix m, and return singular vectors/values + # sorted. + if issparse(m): + m = m.toarray() + u, s, vh = svd(m) + if which == 'LM': + ii = np.argsort(s)[-k:] + elif which == 'SM': + ii = np.argsort(s)[:k] + else: + raise ValueError(f"unknown which={which!r}") + + return u[:, ii], s[ii], vh[ii] + + +def _check_svds(A, k, u, s, vh, which="LM", check_usvh_A=False, + check_svd=True, atol=1e-10, rtol=1e-7): + n, m = A.shape + + # Check shapes. + assert_equal(u.shape, (n, k)) + assert_equal(s.shape, (k,)) + assert_equal(vh.shape, (k, m)) + + # Check that the original matrix can be reconstituted. + A_rebuilt = (u*s).dot(vh) + assert_equal(A_rebuilt.shape, A.shape) + if check_usvh_A: + assert_allclose(A_rebuilt, A, atol=atol, rtol=rtol) + + # Check that u is a semi-orthogonal matrix. + uh_u = np.dot(u.T.conj(), u) + assert_equal(uh_u.shape, (k, k)) + assert_allclose(uh_u, np.identity(k), atol=atol, rtol=rtol) + + # Check that vh is a semi-orthogonal matrix. + vh_v = np.dot(vh, vh.T.conj()) + assert_equal(vh_v.shape, (k, k)) + assert_allclose(vh_v, np.identity(k), atol=atol, rtol=rtol) + + # Check that scipy.sparse.linalg.svds ~ scipy.linalg.svd + if check_svd: + u2, s2, vh2 = sorted_svd(A, k, which) + assert_allclose(np.abs(u), np.abs(u2), atol=atol, rtol=rtol) + assert_allclose(s, s2, atol=atol, rtol=rtol) + assert_allclose(np.abs(vh), np.abs(vh2), atol=atol, rtol=rtol) + + +def _check_svds_n(A, k, u, s, vh, which="LM", check_res=True, + check_svd=True, atol=1e-10, rtol=1e-7): + n, m = A.shape + + # Check shapes. + assert_equal(u.shape, (n, k)) + assert_equal(s.shape, (k,)) + assert_equal(vh.shape, (k, m)) + + # Check that u is a semi-orthogonal matrix. + uh_u = np.dot(u.T.conj(), u) + assert_equal(uh_u.shape, (k, k)) + error = np.sum(np.abs(uh_u - np.identity(k))) / (k * k) + assert_allclose(error, 0.0, atol=atol, rtol=rtol) + + # Check that vh is a semi-orthogonal matrix. + vh_v = np.dot(vh, vh.T.conj()) + assert_equal(vh_v.shape, (k, k)) + error = np.sum(np.abs(vh_v - np.identity(k))) / (k * k) + assert_allclose(error, 0.0, atol=atol, rtol=rtol) + + # Check residuals + if check_res: + ru = A.T.conj() @ u - vh.T.conj() * s + rus = np.sum(np.abs(ru)) / (n * k) + rvh = A @ vh.T.conj() - u * s + rvhs = np.sum(np.abs(rvh)) / (m * k) + assert_allclose(rus, 0.0, atol=atol, rtol=rtol) + assert_allclose(rvhs, 0.0, atol=atol, rtol=rtol) + + # Check that scipy.sparse.linalg.svds ~ scipy.linalg.svd + if check_svd: + u2, s2, vh2 = sorted_svd(A, k, which) + assert_allclose(s, s2, atol=atol, rtol=rtol) + A_rebuilt_svd = (u2*s2).dot(vh2) + A_rebuilt = (u*s).dot(vh) + assert_equal(A_rebuilt.shape, A.shape) + error = np.sum(np.abs(A_rebuilt_svd - A_rebuilt)) / (k * k) + assert_allclose(error, 0.0, atol=atol, rtol=rtol) + + +class CheckingLinearOperator(LinearOperator): + def __init__(self, A): + self.A = A + self.dtype = A.dtype + self.shape = A.shape + + def _matvec(self, x): + assert_equal(max(x.shape), np.size(x)) + return self.A.dot(x) + + def _rmatvec(self, x): + assert_equal(max(x.shape), np.size(x)) + return self.A.T.conjugate().dot(x) + + +# --- Test Input Validation --- +# Tests input validation on parameters `k` and `which`. +# Needs better input validation checks for all other parameters. + +class SVDSCommonTests: + + solver = None + + # some of these IV tests could run only once, say with solver=None + + _A_empty_msg = "`A` must not be empty." + _A_dtype_msg = "`A` must be of floating or complex floating data type" + _A_type_msg = "type not understood" + _A_ndim_msg = "array must have ndim <= 2" + _A_validation_inputs = [ + (np.asarray([[]]), ValueError, _A_empty_msg), + (np.asarray([[1, 2], [3, 4]]), ValueError, _A_dtype_msg), + ("hi", TypeError, _A_type_msg), + (np.asarray([[[1., 2.], [3., 4.]]]), ValueError, _A_ndim_msg)] + + @pytest.mark.parametrize("args", _A_validation_inputs) + def test_svds_input_validation_A(self, args): + A, error_type, message = args + with pytest.raises(error_type, match=message): + svds(A, k=1, solver=self.solver) + + @pytest.mark.parametrize("k", [-1, 0, 3, 4, 5, 1.5, "1"]) + def test_svds_input_validation_k_1(self, k): + rng = np.random.default_rng(0) + A = rng.random((4, 3)) + + # propack can do complete SVD + if self.solver == 'propack' and k == 3: + res = svds(A, k=k, solver=self.solver, random_state=0) + _check_svds(A, k, *res, check_usvh_A=True, check_svd=True) + return + + message = ("`k` must be an integer satisfying") + with pytest.raises(ValueError, match=message): + svds(A, k=k, solver=self.solver) + + def test_svds_input_validation_k_2(self): + # I think the stack trace is reasonable when `k` can't be converted + # to an int. + message = "int() argument must be a" + with pytest.raises(TypeError, match=re.escape(message)): + svds(np.eye(10), k=[], solver=self.solver) + + message = "invalid literal for int()" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), k="hi", solver=self.solver) + + @pytest.mark.parametrize("tol", (-1, np.inf, np.nan)) + def test_svds_input_validation_tol_1(self, tol): + message = "`tol` must be a non-negative floating point value." + with pytest.raises(ValueError, match=message): + svds(np.eye(10), tol=tol, solver=self.solver) + + @pytest.mark.parametrize("tol", ([], 'hi')) + def test_svds_input_validation_tol_2(self, tol): + # I think the stack trace is reasonable here + message = "'<' not supported between instances" + with pytest.raises(TypeError, match=message): + svds(np.eye(10), tol=tol, solver=self.solver) + + @pytest.mark.parametrize("which", ('LA', 'SA', 'ekki', 0)) + def test_svds_input_validation_which(self, which): + # Regression test for a github issue. + # https://github.com/scipy/scipy/issues/4590 + # Function was not checking for eigenvalue type and unintended + # values could be returned. + with pytest.raises(ValueError, match="`which` must be in"): + svds(np.eye(10), which=which, solver=self.solver) + + @pytest.mark.parametrize("transpose", (True, False)) + @pytest.mark.parametrize("n", range(4, 9)) + def test_svds_input_validation_v0_1(self, transpose, n): + rng = np.random.default_rng(0) + A = rng.random((5, 7)) + v0 = rng.random(n) + if transpose: + A = A.T + k = 2 + message = "`v0` must have shape" + + required_length = (A.shape[0] if self.solver == 'propack' + else min(A.shape)) + if n != required_length: + with pytest.raises(ValueError, match=message): + svds(A, k=k, v0=v0, solver=self.solver) + + def test_svds_input_validation_v0_2(self): + A = np.ones((10, 10)) + v0 = np.ones((1, 10)) + message = "`v0` must have shape" + with pytest.raises(ValueError, match=message): + svds(A, k=1, v0=v0, solver=self.solver) + + @pytest.mark.parametrize("v0", ("hi", 1, np.ones(10, dtype=int))) + def test_svds_input_validation_v0_3(self, v0): + A = np.ones((10, 10)) + message = "`v0` must be of floating or complex floating data type." + with pytest.raises(ValueError, match=message): + svds(A, k=1, v0=v0, solver=self.solver) + + @pytest.mark.parametrize("maxiter", (-1, 0, 5.5)) + def test_svds_input_validation_maxiter_1(self, maxiter): + message = ("`maxiter` must be a positive integer.") + with pytest.raises(ValueError, match=message): + svds(np.eye(10), maxiter=maxiter, solver=self.solver) + + def test_svds_input_validation_maxiter_2(self): + # I think the stack trace is reasonable when `k` can't be converted + # to an int. + message = "int() argument must be a" + with pytest.raises(TypeError, match=re.escape(message)): + svds(np.eye(10), maxiter=[], solver=self.solver) + + message = "invalid literal for int()" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), maxiter="hi", solver=self.solver) + + @pytest.mark.parametrize("rsv", ('ekki', 10)) + def test_svds_input_validation_return_singular_vectors(self, rsv): + message = "`return_singular_vectors` must be in" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), return_singular_vectors=rsv, solver=self.solver) + + # --- Test Parameters --- + + @pytest.mark.parametrize("k", [3, 5]) + @pytest.mark.parametrize("which", ["LM", "SM"]) + def test_svds_parameter_k_which(self, k, which): + # check that the `k` parameter sets the number of eigenvalues/ + # eigenvectors returned. + # Also check that the `which` parameter sets whether the largest or + # smallest eigenvalues are returned + rng = np.random.default_rng(0) + A = rng.random((10, 10)) + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + res = svds(A, k=k, which=which, solver=self.solver, + random_state=0) + else: + res = svds(A, k=k, which=which, solver=self.solver, + random_state=0) + _check_svds(A, k, *res, which=which, atol=1e-9, rtol=2e-13) + + @pytest.mark.filterwarnings("ignore:Exited", + reason="Ignore LOBPCG early exit.") + # loop instead of parametrize for simplicity + def test_svds_parameter_tol(self): + # check the effect of the `tol` parameter on solver accuracy by solving + # the same problem with varying `tol` and comparing the eigenvalues + # against ground truth computed + n = 100 # matrix size + k = 3 # number of eigenvalues to check + + # generate a random, sparse-ish matrix + # effect isn't apparent for matrices that are too small + rng = np.random.default_rng(0) + A = rng.random((n, n)) + A[A > .1] = 0 + A = A @ A.T + + _, s, _ = svd(A) # calculate ground truth + + # calculate the error as a function of `tol` + A = csc_matrix(A) + + def err(tol): + _, s2, _ = svds(A, k=k, v0=np.ones(n), maxiter=1000, + solver=self.solver, tol=tol, random_state=0) + return np.linalg.norm((s2 - s[k-1::-1])/s[k-1::-1]) + + tols = [1e-4, 1e-2, 1e0] # tolerance levels to check + # for 'arpack' and 'propack', accuracies make discrete steps + accuracies = {'propack': [1e-12, 1e-6, 1e-4], + 'arpack': [2.5e-15, 1e-10, 1e-10], + 'lobpcg': [2e-12, 4e-2, 2]} + + for tol, accuracy in zip(tols, accuracies[self.solver]): + error = err(tol) + assert error < accuracy + + def test_svd_v0(self): + # check that the `v0` parameter affects the solution + n = 100 + k = 1 + # If k != 1, LOBPCG needs more initial vectors, which are generated + # with random_state, so it does not pass w/ k >= 2. + # For some other values of `n`, the AssertionErrors are not raised + # with different v0s, which is reasonable. + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + # with the same v0, solutions are the same, and they are accurate + # v0 takes precedence over random_state + v0a = rng.random(n) + res1a = svds(A, k, v0=v0a, solver=self.solver, random_state=0) + res2a = svds(A, k, v0=v0a, solver=self.solver, random_state=1) + for idx in range(3): + assert_allclose(res1a[idx], res2a[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1a) + + # with the same v0, solutions are the same, and they are accurate + v0b = rng.random(n) + res1b = svds(A, k, v0=v0b, solver=self.solver, random_state=2) + res2b = svds(A, k, v0=v0b, solver=self.solver, random_state=3) + for idx in range(3): + assert_allclose(res1b[idx], res2b[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1b) + + # with different v0, solutions can be numerically different + message = "Arrays are not equal" + with pytest.raises(AssertionError, match=message): + assert_equal(res1a, res1b) + + def test_svd_random_state(self): + # check that the `random_state` parameter affects the solution + # Admittedly, `n` and `k` are chosen so that all solver pass all + # these checks. That's a tall order, since LOBPCG doesn't want to + # achieve the desired accuracy and ARPACK often returns the same + # singular values/vectors for different v0. + n = 100 + k = 1 + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + # with the same random_state, solutions are the same and accurate + res1a = svds(A, k, solver=self.solver, random_state=0) + res2a = svds(A, k, solver=self.solver, random_state=0) + for idx in range(3): + assert_allclose(res1a[idx], res2a[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1a) + + # with the same random_state, solutions are the same and accurate + res1b = svds(A, k, solver=self.solver, random_state=1) + res2b = svds(A, k, solver=self.solver, random_state=1) + for idx in range(3): + assert_allclose(res1b[idx], res2b[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1b) + + # with different random_state, solutions can be numerically different + message = "Arrays are not equal" + with pytest.raises(AssertionError, match=message): + assert_equal(res1a, res1b) + + @pytest.mark.parametrize("random_state", (0, 1, + np.random.RandomState(0), + np.random.default_rng(0))) + def test_svd_random_state_2(self, random_state): + n = 100 + k = 1 + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + random_state_2 = copy.deepcopy(random_state) + + # with the same random_state, solutions are the same and accurate + res1a = svds(A, k, solver=self.solver, random_state=random_state) + res2a = svds(A, k, solver=self.solver, random_state=random_state_2) + for idx in range(3): + assert_allclose(res1a[idx], res2a[idx], rtol=1e-15, atol=2e-16) + _check_svds(A, k, *res1a) + + @pytest.mark.parametrize("random_state", (None, + np.random.RandomState(0), + np.random.default_rng(0))) + @pytest.mark.filterwarnings("ignore:Exited", + reason="Ignore LOBPCG early exit.") + def test_svd_random_state_3(self, random_state): + n = 100 + k = 5 + + rng = np.random.default_rng(0) + A = rng.random((n, n)) + + random_state = copy.deepcopy(random_state) + + # random_state in different state produces accurate - but not + # not necessarily identical - results + res1a = svds(A, k, solver=self.solver, random_state=random_state, maxiter=1000) + res2a = svds(A, k, solver=self.solver, random_state=random_state, maxiter=1000) + _check_svds(A, k, *res1a, atol=2e-7) + _check_svds(A, k, *res2a, atol=2e-7) + + message = "Arrays are not equal" + with pytest.raises(AssertionError, match=message): + assert_equal(res1a, res2a) + + @pytest.mark.filterwarnings("ignore:Exited postprocessing") + def test_svd_maxiter(self): + # check that maxiter works as expected: should not return accurate + # solution after 1 iteration, but should with default `maxiter` + A = np.diag(np.arange(9)).astype(np.float64) + k = 1 + u, s, vh = sorted_svd(A, k) + # Use default maxiter by default + maxiter = None + + if self.solver == 'arpack': + message = "ARPACK error -1: No convergence" + with pytest.raises(ArpackNoConvergence, match=message): + svds(A, k, ncv=3, maxiter=1, solver=self.solver) + elif self.solver == 'lobpcg': + # Set maxiter higher so test passes without changing + # default and breaking backward compatibility (gh-20221) + maxiter = 30 + with pytest.warns(UserWarning, match="Exited at iteration"): + svds(A, k, maxiter=1, solver=self.solver) + elif self.solver == 'propack': + message = "k=1 singular triplets did not converge within" + with pytest.raises(np.linalg.LinAlgError, match=message): + svds(A, k, maxiter=1, solver=self.solver) + + ud, sd, vhd = svds(A, k, solver=self.solver, maxiter=maxiter, + random_state=0) + _check_svds(A, k, ud, sd, vhd, atol=1e-8) + assert_allclose(np.abs(ud), np.abs(u), atol=1e-8) + assert_allclose(np.abs(vhd), np.abs(vh), atol=1e-8) + assert_allclose(np.abs(sd), np.abs(s), atol=1e-9) + + @pytest.mark.parametrize("rsv", (True, False, 'u', 'vh')) + @pytest.mark.parametrize("shape", ((5, 7), (6, 6), (7, 5))) + def test_svd_return_singular_vectors(self, rsv, shape): + # check that the return_singular_vectors parameter works as expected + rng = np.random.default_rng(0) + A = rng.random(shape) + k = 2 + M, N = shape + u, s, vh = sorted_svd(A, k) + + respect_u = True if self.solver == 'propack' else M <= N + respect_vh = True if self.solver == 'propack' else M > N + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + if rsv is False: + s2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(s2, s) + elif rsv == 'u' and respect_u: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + assert vh2 is None + elif rsv == 'vh' and respect_vh: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert u2 is None + assert_allclose(s2, s) + assert_allclose(np.abs(vh2), np.abs(vh)) + else: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + if u2 is not None: + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + if vh2 is not None: + assert_allclose(np.abs(vh2), np.abs(vh)) + else: + if rsv is False: + s2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(s2, s) + elif rsv == 'u' and respect_u: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + assert vh2 is None + elif rsv == 'vh' and respect_vh: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + assert u2 is None + assert_allclose(s2, s) + assert_allclose(np.abs(vh2), np.abs(vh)) + else: + u2, s2, vh2 = svds(A, k, return_singular_vectors=rsv, + solver=self.solver, random_state=rng) + if u2 is not None: + assert_allclose(np.abs(u2), np.abs(u)) + assert_allclose(s2, s) + if vh2 is not None: + assert_allclose(np.abs(vh2), np.abs(vh)) + + # --- Test Basic Functionality --- + # Tests the accuracy of each solver for real and complex matrices provided + # as list, dense array, sparse matrix, and LinearOperator. + + A1 = [[1, 2, 3], [3, 4, 3], [1 + 1j, 0, 2], [0, 0, 1]] + A2 = [[1, 2, 3, 8 + 5j], [3 - 2j, 4, 3, 5], [1, 0, 2, 3], [0, 0, 1, 0]] + + @pytest.mark.filterwarnings("ignore:k >= N - 1", + reason="needed to demonstrate #16725") + @pytest.mark.parametrize('A', (A1, A2)) + @pytest.mark.parametrize('k', range(1, 5)) + # PROPACK fails a lot if @pytest.mark.parametrize('which', ("SM", "LM")) + @pytest.mark.parametrize('real', (True, False)) + @pytest.mark.parametrize('transpose', (False, True)) + # In gh-14299, it was suggested the `svds` should _not_ work with lists + @pytest.mark.parametrize('lo_type', (np.asarray, csc_matrix, + aslinearoperator)) + def test_svd_simple(self, A, k, real, transpose, lo_type): + + A = np.asarray(A) + A = np.real(A) if real else A + A = A.T if transpose else A + A2 = lo_type(A) + + # could check for the appropriate errors, but that is tested above + if k > min(A.shape): + pytest.skip("`k` cannot be greater than `min(A.shape)`") + if self.solver != 'propack' and k >= min(A.shape): + pytest.skip("Only PROPACK supports complete SVD") + if self.solver == 'arpack' and not real and k == min(A.shape) - 1: + pytest.skip("#16725") + + atol = 3e-10 + if self.solver == 'propack': + atol = 3e-9 # otherwise test fails on Linux aarch64 (see gh-19855) + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + u, s, vh = svds(A2, k, solver=self.solver, random_state=0) + else: + u, s, vh = svds(A2, k, solver=self.solver, random_state=0) + _check_svds(A, k, u, s, vh, atol=atol) + + def test_svd_linop(self): + solver = self.solver + + nmks = [(6, 7, 3), + (9, 5, 4), + (10, 8, 5)] + + def reorder(args): + U, s, VH = args + j = np.argsort(s) + return U[:, j], s[j], VH[j, :] + + for n, m, k in nmks: + # Test svds on a LinearOperator. + A = np.random.RandomState(52).randn(n, m) + L = CheckingLinearOperator(A) + + if solver == 'propack': + v0 = np.ones(n) + else: + v0 = np.ones(min(A.shape)) + if solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U1, s1, VH1 = reorder(svds(A, k, v0=v0, solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, v0=v0, solver=solver, + random_state=0)) + else: + U1, s1, VH1 = reorder(svds(A, k, v0=v0, solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, v0=v0, solver=solver, + random_state=0)) + + assert_allclose(np.abs(U1), np.abs(U2)) + assert_allclose(s1, s2) + assert_allclose(np.abs(VH1), np.abs(VH2)) + assert_allclose(np.dot(U1, np.dot(np.diag(s1), VH1)), + np.dot(U2, np.dot(np.diag(s2), VH2))) + + # Try again with which="SM". + A = np.random.RandomState(1909).randn(n, m) + L = CheckingLinearOperator(A) + + # TODO: arpack crashes when v0=v0, which="SM" + kwargs = {'v0': v0} if solver not in {None, 'arpack'} else {} + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U1, s1, VH1 = reorder(svds(A, k, which="SM", solver=solver, + random_state=0, **kwargs)) + U2, s2, VH2 = reorder(svds(L, k, which="SM", solver=solver, + random_state=0, **kwargs)) + else: + U1, s1, VH1 = reorder(svds(A, k, which="SM", solver=solver, + random_state=0, **kwargs)) + U2, s2, VH2 = reorder(svds(L, k, which="SM", solver=solver, + random_state=0, **kwargs)) + + assert_allclose(np.abs(U1), np.abs(U2)) + assert_allclose(s1 + 1, s2 + 1) + assert_allclose(np.abs(VH1), np.abs(VH2)) + assert_allclose(np.dot(U1, np.dot(np.diag(s1), VH1)), + np.dot(U2, np.dot(np.diag(s2), VH2))) + + if k < min(n, m) - 1: + # Complex input and explicit which="LM". + for (dt, eps) in [(complex, 1e-7), (np.complex64, 3e-3)]: + rng = np.random.RandomState(1648) + A = (rng.randn(n, m) + 1j * rng.randn(n, m)).astype(dt) + L = CheckingLinearOperator(A) + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, + match="The problem size"): + U1, s1, VH1 = reorder(svds(A, k, which="LM", + solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, which="LM", + solver=solver, + random_state=0)) + else: + U1, s1, VH1 = reorder(svds(A, k, which="LM", + solver=solver, + random_state=0)) + U2, s2, VH2 = reorder(svds(L, k, which="LM", + solver=solver, + random_state=0)) + + assert_allclose(np.abs(U1), np.abs(U2), rtol=eps) + assert_allclose(s1, s2, rtol=eps) + assert_allclose(np.abs(VH1), np.abs(VH2), rtol=eps) + assert_allclose(np.dot(U1, np.dot(np.diag(s1), VH1)), + np.dot(U2, np.dot(np.diag(s2), VH2)), + rtol=eps) + + SHAPES = ((100, 100), (100, 101), (101, 100)) + + @pytest.mark.filterwarnings("ignore:Exited at iteration") + @pytest.mark.filterwarnings("ignore:Exited postprocessing") + @pytest.mark.parametrize("shape", SHAPES) + # ARPACK supports only dtype float, complex, or np.float32 + @pytest.mark.parametrize("dtype", (float, complex, np.float32)) + def test_small_sigma_sparse(self, shape, dtype): + # https://github.com/scipy/scipy/pull/11829 + solver = self.solver + # 2do: PROPACK fails orthogonality of singular vectors + # if dtype == complex and self.solver == 'propack': + # pytest.skip("PROPACK unsupported for complex dtype") + rng = np.random.default_rng(0) + k = 5 + (m, n) = shape + S = random(m, n, density=0.1, random_state=rng) + if dtype == complex: + S = + 1j * random(m, n, density=0.1, random_state=rng) + e = np.ones(m) + e[0:5] *= 1e1 ** np.arange(-5, 0, 1) + S = spdiags(e, 0, m, m) @ S + S = S.astype(dtype) + u, s, vh = svds(S, k, which='SM', solver=solver, maxiter=1000, + random_state=0) + c_svd = False # partial SVD can be different from full SVD + _check_svds_n(S, k, u, s, vh, which="SM", check_svd=c_svd, atol=2e-1) + + # --- Test Edge Cases --- + # Checks a few edge cases. + + @pytest.mark.parametrize("shape", ((6, 5), (5, 5), (5, 6))) + @pytest.mark.parametrize("dtype", (float, complex)) + def test_svd_LM_ones_matrix(self, shape, dtype): + # Check that svds can deal with matrix_rank less than k in LM mode. + k = 3 + n, m = shape + A = np.ones((n, m), dtype=dtype) + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + else: + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + + _check_svds(A, k, U, s, VH, check_usvh_A=True, check_svd=False) + + # Check that the largest singular value is near sqrt(n*m) + # and the other singular values have been forced to zero. + assert_allclose(np.max(s), np.sqrt(n*m)) + s = np.array(sorted(s)[:-1]) + 1 + z = np.ones_like(s) + assert_allclose(s, z) + + @pytest.mark.filterwarnings("ignore:k >= N - 1", + reason="needed to demonstrate #16725") + @pytest.mark.parametrize("shape", ((3, 4), (4, 4), (4, 3), (4, 2))) + @pytest.mark.parametrize("dtype", (float, complex)) + def test_zero_matrix(self, shape, dtype): + # Check that svds can deal with matrices containing only zeros; + # see https://github.com/scipy/scipy/issues/3452/ + # shape = (4, 2) is included because it is the particular case + # reported in the issue + k = 1 + n, m = shape + A = np.zeros((n, m), dtype=dtype) + + if (self.solver == 'arpack' and dtype is complex + and k == min(A.shape) - 1): + pytest.skip("#16725") + + if self.solver == 'propack': + pytest.skip("PROPACK failures unrelated to PR #16712") + + if self.solver == 'lobpcg': + with pytest.warns(UserWarning, match="The problem size"): + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + else: + U, s, VH = svds(A, k, solver=self.solver, random_state=0) + + # Check some generic properties of svd. + _check_svds(A, k, U, s, VH, check_usvh_A=True, check_svd=False) + + # Check that the singular values are zero. + assert_array_equal(s, 0) + + @pytest.mark.parametrize("shape", ((20, 20), (20, 21), (21, 20))) + # ARPACK supports only dtype float, complex, or np.float32 + @pytest.mark.parametrize("dtype", (float, complex, np.float32)) + @pytest.mark.filterwarnings("ignore:Exited", + reason="Ignore LOBPCG early exit.") + def test_small_sigma(self, shape, dtype): + rng = np.random.default_rng(179847540) + A = rng.random(shape).astype(dtype) + u, _, vh = svd(A, full_matrices=False) + if dtype == np.float32: + e = 10.0 + else: + e = 100.0 + t = e**(-np.arange(len(vh))).astype(dtype) + A = (u*t).dot(vh) + k = 4 + u, s, vh = svds(A, k, solver=self.solver, maxiter=100, random_state=0) + t = np.sum(s > 0) + assert_equal(t, k) + # LOBPCG needs larger atol and rtol to pass + _check_svds_n(A, k, u, s, vh, atol=1e-3, rtol=1e0, check_svd=False) + + # ARPACK supports only dtype float, complex, or np.float32 + @pytest.mark.filterwarnings("ignore:The problem size") + @pytest.mark.parametrize("dtype", (float, complex, np.float32)) + def test_small_sigma2(self, dtype): + rng = np.random.default_rng(179847540) + # create a 10x10 singular matrix with a 4-dim null space + dim = 4 + size = 10 + x = rng.random((size, size-dim)) + y = x[:, :dim] * rng.random(dim) + mat = np.hstack((x, y)) + mat = mat.astype(dtype) + + nz = null_space(mat) + assert_equal(nz.shape[1], dim) + + # Tolerances atol and rtol adjusted to pass np.float32 + # Use non-sparse svd + u, s, vh = svd(mat) + # Singular values are 0: + assert_allclose(s[-dim:], 0, atol=1e-6, rtol=1e0) + # Smallest right singular vectors in null space: + assert_allclose(mat @ vh[-dim:, :].T, 0, atol=1e-6, rtol=1e0) + + # Smallest singular values should be 0 + sp_mat = csc_matrix(mat) + su, ss, svh = svds(sp_mat, k=dim, which='SM', solver=self.solver, + random_state=0) + # Smallest dim singular values are 0: + assert_allclose(ss, 0, atol=1e-5, rtol=1e0) + # Smallest singular vectors via svds in null space: + n, m = mat.shape + if n < m: # else the assert fails with some libraries unclear why + assert_allclose(sp_mat.transpose() @ su, 0, atol=1e-5, rtol=1e0) + assert_allclose(sp_mat @ svh.T, 0, atol=1e-5, rtol=1e0) + +# --- Perform tests with each solver --- + + +class Test_SVDS_once: + @pytest.mark.parametrize("solver", ['ekki', object]) + def test_svds_input_validation_solver(self, solver): + message = "solver must be one of" + with pytest.raises(ValueError, match=message): + svds(np.ones((3, 4)), k=2, solver=solver) + + +class Test_SVDS_ARPACK(SVDSCommonTests): + + def setup_method(self): + self.solver = 'arpack' + + @pytest.mark.parametrize("ncv", list(range(-1, 8)) + [4.5, "5"]) + def test_svds_input_validation_ncv_1(self, ncv): + rng = np.random.default_rng(0) + A = rng.random((6, 7)) + k = 3 + if ncv in {4, 5}: + u, s, vh = svds(A, k=k, ncv=ncv, solver=self.solver, random_state=0) + # partial decomposition, so don't check that u@diag(s)@vh=A; + # do check that scipy.sparse.linalg.svds ~ scipy.linalg.svd + _check_svds(A, k, u, s, vh) + else: + message = ("`ncv` must be an integer satisfying") + with pytest.raises(ValueError, match=message): + svds(A, k=k, ncv=ncv, solver=self.solver) + + def test_svds_input_validation_ncv_2(self): + # I think the stack trace is reasonable when `ncv` can't be converted + # to an int. + message = "int() argument must be a" + with pytest.raises(TypeError, match=re.escape(message)): + svds(np.eye(10), ncv=[], solver=self.solver) + + message = "invalid literal for int()" + with pytest.raises(ValueError, match=message): + svds(np.eye(10), ncv="hi", solver=self.solver) + + # I can't see a robust relationship between `ncv` and relevant outputs + # (e.g. accuracy, time), so no test of the parameter. + + +class Test_SVDS_LOBPCG(SVDSCommonTests): + + def setup_method(self): + self.solver = 'lobpcg' + + +class Test_SVDS_PROPACK(SVDSCommonTests): + + def setup_method(self): + self.solver = 'propack' + + def test_svd_LM_ones_matrix(self): + message = ("PROPACK does not return orthonormal singular vectors " + "associated with zero singular values.") + # There are some other issues with this matrix of all ones, e.g. + # `which='sm'` and `k=1` returns the largest singular value + pytest.xfail(message) + + def test_svd_LM_zeros_matrix(self): + message = ("PROPACK does not return orthonormal singular vectors " + "associated with zero singular values.") + pytest.xfail(message) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_expm_multiply.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_expm_multiply.py new file mode 100644 index 0000000000000000000000000000000000000000..6bc8d83b75a7944193e8060aeb0f841f3a402b16 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_expm_multiply.py @@ -0,0 +1,810 @@ +"""Compute the action of the matrix exponential.""" +from warnings import warn + +import numpy as np + +import scipy.linalg +import scipy.sparse.linalg +from scipy.linalg._decomp_qr import qr +from scipy.sparse._sputils import is_pydata_spmatrix +from scipy.sparse.linalg import aslinearoperator +from scipy.sparse.linalg._interface import IdentityOperator +from scipy.sparse.linalg._onenormest import onenormest + +__all__ = ['expm_multiply'] + + +def _exact_inf_norm(A): + # A compatibility function which should eventually disappear. + if scipy.sparse.issparse(A): + return max(abs(A).sum(axis=1).flat) + elif is_pydata_spmatrix(A): + return max(abs(A).sum(axis=1)) + else: + return np.linalg.norm(A, np.inf) + + +def _exact_1_norm(A): + # A compatibility function which should eventually disappear. + if scipy.sparse.issparse(A): + return max(abs(A).sum(axis=0).flat) + elif is_pydata_spmatrix(A): + return max(abs(A).sum(axis=0)) + else: + return np.linalg.norm(A, 1) + + +def _trace(A): + # A compatibility function which should eventually disappear. + if is_pydata_spmatrix(A): + return A.to_scipy_sparse().trace() + else: + return A.trace() + + +def traceest(A, m3, seed=None): + """Estimate `np.trace(A)` using `3*m3` matrix-vector products. + + The result is not deterministic. + + Parameters + ---------- + A : LinearOperator + Linear operator whose trace will be estimated. Has to be square. + m3 : int + Number of matrix-vector products divided by 3 used to estimate the + trace. + seed : optional + Seed for `numpy.random.default_rng`. + Can be provided to obtain deterministic results. + + Returns + ------- + trace : LinearOperator.dtype + Estimate of the trace + + Notes + ----- + This is the Hutch++ algorithm given in [1]_. + + References + ---------- + .. [1] Meyer, Raphael A., Cameron Musco, Christopher Musco, and David P. + Woodruff. "Hutch++: Optimal Stochastic Trace Estimation." In Symposium + on Simplicity in Algorithms (SOSA), pp. 142-155. Society for Industrial + and Applied Mathematics, 2021 + https://doi.org/10.1137/1.9781611976496.16 + + """ + rng = np.random.default_rng(seed) + if len(A.shape) != 2 or A.shape[-1] != A.shape[-2]: + raise ValueError("Expected A to be like a square matrix.") + n = A.shape[-1] + S = rng.choice([-1.0, +1.0], [n, m3]) + Q, _ = qr(A.matmat(S), overwrite_a=True, mode='economic') + trQAQ = np.trace(Q.conj().T @ A.matmat(Q)) + G = rng.choice([-1, +1], [n, m3]) + right = G - Q@(Q.conj().T @ G) + trGAG = np.trace(right.conj().T @ A.matmat(right)) + return trQAQ + trGAG/m3 + + +def _ident_like(A): + # A compatibility function which should eventually disappear. + if scipy.sparse.issparse(A): + # Creates a sparse matrix in dia format + out = scipy.sparse.eye(A.shape[0], A.shape[1], dtype=A.dtype) + if isinstance(A, scipy.sparse.spmatrix): + return out.asformat(A.format) + return scipy.sparse.dia_array(out).asformat(A.format) + elif is_pydata_spmatrix(A): + import sparse + return sparse.eye(A.shape[0], A.shape[1], dtype=A.dtype) + elif isinstance(A, scipy.sparse.linalg.LinearOperator): + return IdentityOperator(A.shape, dtype=A.dtype) + else: + return np.eye(A.shape[0], A.shape[1], dtype=A.dtype) + + +def expm_multiply(A, B, start=None, stop=None, num=None, + endpoint=None, traceA=None): + """ + Compute the action of the matrix exponential of A on B. + + Parameters + ---------- + A : transposable linear operator + The operator whose exponential is of interest. + B : ndarray + The matrix or vector to be multiplied by the matrix exponential of A. + start : scalar, optional + The starting time point of the sequence. + stop : scalar, optional + The end time point of the sequence, unless `endpoint` is set to False. + In that case, the sequence consists of all but the last of ``num + 1`` + evenly spaced time points, so that `stop` is excluded. + Note that the step size changes when `endpoint` is False. + num : int, optional + Number of time points to use. + endpoint : bool, optional + If True, `stop` is the last time point. Otherwise, it is not included. + traceA : scalar, optional + Trace of `A`. If not given the trace is estimated for linear operators, + or calculated exactly for sparse matrices. It is used to precondition + `A`, thus an approximate trace is acceptable. + For linear operators, `traceA` should be provided to ensure performance + as the estimation is not guaranteed to be reliable for all cases. + + .. versionadded:: 1.9.0 + + Returns + ------- + expm_A_B : ndarray + The result of the action :math:`e^{t_k A} B`. + + Warns + ----- + UserWarning + If `A` is a linear operator and ``traceA=None`` (default). + + Notes + ----- + The optional arguments defining the sequence of evenly spaced time points + are compatible with the arguments of `numpy.linspace`. + + The output ndarray shape is somewhat complicated so I explain it here. + The ndim of the output could be either 1, 2, or 3. + It would be 1 if you are computing the expm action on a single vector + at a single time point. + It would be 2 if you are computing the expm action on a vector + at multiple time points, or if you are computing the expm action + on a matrix at a single time point. + It would be 3 if you want the action on a matrix with multiple + columns at multiple time points. + If multiple time points are requested, expm_A_B[0] will always + be the action of the expm at the first time point, + regardless of whether the action is on a vector or a matrix. + + References + ---------- + .. [1] Awad H. Al-Mohy and Nicholas J. Higham (2011) + "Computing the Action of the Matrix Exponential, + with an Application to Exponential Integrators." + SIAM Journal on Scientific Computing, + 33 (2). pp. 488-511. ISSN 1064-8275 + http://eprints.ma.man.ac.uk/1591/ + + .. [2] Nicholas J. Higham and Awad H. Al-Mohy (2010) + "Computing Matrix Functions." + Acta Numerica, + 19. 159-208. ISSN 0962-4929 + http://eprints.ma.man.ac.uk/1451/ + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import expm, expm_multiply + >>> A = csc_matrix([[1, 0], [0, 1]]) + >>> A.toarray() + array([[1, 0], + [0, 1]], dtype=int64) + >>> B = np.array([np.exp(-1.), np.exp(-2.)]) + >>> B + array([ 0.36787944, 0.13533528]) + >>> expm_multiply(A, B, start=1, stop=2, num=3, endpoint=True) + array([[ 1. , 0.36787944], + [ 1.64872127, 0.60653066], + [ 2.71828183, 1. ]]) + >>> expm(A).dot(B) # Verify 1st timestep + array([ 1. , 0.36787944]) + >>> expm(1.5*A).dot(B) # Verify 2nd timestep + array([ 1.64872127, 0.60653066]) + >>> expm(2*A).dot(B) # Verify 3rd timestep + array([ 2.71828183, 1. ]) + """ + if all(arg is None for arg in (start, stop, num, endpoint)): + X = _expm_multiply_simple(A, B, traceA=traceA) + else: + X, status = _expm_multiply_interval(A, B, start, stop, num, + endpoint, traceA=traceA) + return X + + +def _expm_multiply_simple(A, B, t=1.0, traceA=None, balance=False): + """ + Compute the action of the matrix exponential at a single time point. + + Parameters + ---------- + A : transposable linear operator + The operator whose exponential is of interest. + B : ndarray + The matrix to be multiplied by the matrix exponential of A. + t : float + A time point. + traceA : scalar, optional + Trace of `A`. If not given the trace is estimated for linear operators, + or calculated exactly for sparse matrices. It is used to precondition + `A`, thus an approximate trace is acceptable + balance : bool + Indicates whether or not to apply balancing. + + Returns + ------- + F : ndarray + :math:`e^{t A} B` + + Notes + ----- + This is algorithm (3.2) in Al-Mohy and Higham (2011). + + """ + if balance: + raise NotImplementedError + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + if A.shape[1] != B.shape[0]: + raise ValueError('shapes of matrices A {} and B {} are incompatible' + .format(A.shape, B.shape)) + ident = _ident_like(A) + is_linear_operator = isinstance(A, scipy.sparse.linalg.LinearOperator) + n = A.shape[0] + if len(B.shape) == 1: + n0 = 1 + elif len(B.shape) == 2: + n0 = B.shape[1] + else: + raise ValueError('expected B to be like a matrix or a vector') + u_d = 2**-53 + tol = u_d + if traceA is None: + if is_linear_operator: + warn("Trace of LinearOperator not available, it will be estimated." + " Provide `traceA` to ensure performance.", stacklevel=3) + # m3=1 is bit arbitrary choice, a more accurate trace (larger m3) might + # speed up exponential calculation, but trace estimation is more costly + traceA = traceest(A, m3=1) if is_linear_operator else _trace(A) + mu = traceA / float(n) + A = A - mu * ident + A_1_norm = onenormest(A) if is_linear_operator else _exact_1_norm(A) + if t*A_1_norm == 0: + m_star, s = 0, 1 + else: + ell = 2 + norm_info = LazyOperatorNormInfo(t*A, A_1_norm=t*A_1_norm, ell=ell) + m_star, s = _fragment_3_1(norm_info, n0, tol, ell=ell) + return _expm_multiply_simple_core(A, B, t, mu, m_star, s, tol, balance) + + +def _expm_multiply_simple_core(A, B, t, mu, m_star, s, tol=None, balance=False): + """ + A helper function. + """ + if balance: + raise NotImplementedError + if tol is None: + u_d = 2 ** -53 + tol = u_d + F = B + eta = np.exp(t*mu / float(s)) + for i in range(s): + c1 = _exact_inf_norm(B) + for j in range(m_star): + coeff = t / float(s*(j+1)) + B = coeff * A.dot(B) + c2 = _exact_inf_norm(B) + F = F + B + if c1 + c2 <= tol * _exact_inf_norm(F): + break + c1 = c2 + F = eta * F + B = F + return F + + +# This table helps to compute bounds. +# They seem to have been difficult to calculate, involving symbolic +# manipulation of equations, followed by numerical root finding. +_theta = { + # The first 30 values are from table A.3 of Computing Matrix Functions. + 1: 2.29e-16, + 2: 2.58e-8, + 3: 1.39e-5, + 4: 3.40e-4, + 5: 2.40e-3, + 6: 9.07e-3, + 7: 2.38e-2, + 8: 5.00e-2, + 9: 8.96e-2, + 10: 1.44e-1, + # 11 + 11: 2.14e-1, + 12: 3.00e-1, + 13: 4.00e-1, + 14: 5.14e-1, + 15: 6.41e-1, + 16: 7.81e-1, + 17: 9.31e-1, + 18: 1.09, + 19: 1.26, + 20: 1.44, + # 21 + 21: 1.62, + 22: 1.82, + 23: 2.01, + 24: 2.22, + 25: 2.43, + 26: 2.64, + 27: 2.86, + 28: 3.08, + 29: 3.31, + 30: 3.54, + # The rest are from table 3.1 of + # Computing the Action of the Matrix Exponential. + 35: 4.7, + 40: 6.0, + 45: 7.2, + 50: 8.5, + 55: 9.9, + } + + +def _onenormest_matrix_power(A, p, + t=2, itmax=5, compute_v=False, compute_w=False): + """ + Efficiently estimate the 1-norm of A^p. + + Parameters + ---------- + A : ndarray + Matrix whose 1-norm of a power is to be computed. + p : int + Non-negative integer power. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + """ + #XXX Eventually turn this into an API function in the _onenormest module, + #XXX and remove its underscore, + #XXX but wait until expm_multiply goes into scipy. + from scipy.sparse.linalg._onenormest import onenormest + return onenormest(aslinearoperator(A) ** p) + +class LazyOperatorNormInfo: + """ + Information about an operator is lazily computed. + + The information includes the exact 1-norm of the operator, + in addition to estimates of 1-norms of powers of the operator. + This uses the notation of Computing the Action (2011). + This class is specialized enough to probably not be of general interest + outside of this module. + + """ + + def __init__(self, A, A_1_norm=None, ell=2, scale=1): + """ + Provide the operator and some norm-related information. + + Parameters + ---------- + A : linear operator + The operator of interest. + A_1_norm : float, optional + The exact 1-norm of A. + ell : int, optional + A technical parameter controlling norm estimation quality. + scale : int, optional + If specified, return the norms of scale*A instead of A. + + """ + self._A = A + self._A_1_norm = A_1_norm + self._ell = ell + self._d = {} + self._scale = scale + + def set_scale(self,scale): + """ + Set the scale parameter. + """ + self._scale = scale + + def onenorm(self): + """ + Compute the exact 1-norm. + """ + if self._A_1_norm is None: + self._A_1_norm = _exact_1_norm(self._A) + return self._scale*self._A_1_norm + + def d(self, p): + """ + Lazily estimate :math:`d_p(A) ~= || A^p ||^(1/p)` where :math:`||.||` is the 1-norm. + """ + if p not in self._d: + est = _onenormest_matrix_power(self._A, p, self._ell) + self._d[p] = est ** (1.0 / p) + return self._scale*self._d[p] + + def alpha(self, p): + """ + Lazily compute max(d(p), d(p+1)). + """ + return max(self.d(p), self.d(p+1)) + +def _compute_cost_div_m(m, p, norm_info): + """ + A helper function for computing bounds. + + This is equation (3.10). + It measures cost in terms of the number of required matrix products. + + Parameters + ---------- + m : int + A valid key of _theta. + p : int + A matrix power. + norm_info : LazyOperatorNormInfo + Information about 1-norms of related operators. + + Returns + ------- + cost_div_m : int + Required number of matrix products divided by m. + + """ + return int(np.ceil(norm_info.alpha(p) / _theta[m])) + + +def _compute_p_max(m_max): + """ + Compute the largest positive integer p such that p*(p-1) <= m_max + 1. + + Do this in a slightly dumb way, but safe and not too slow. + + Parameters + ---------- + m_max : int + A count related to bounds. + + """ + sqrt_m_max = np.sqrt(m_max) + p_low = int(np.floor(sqrt_m_max)) + p_high = int(np.ceil(sqrt_m_max + 1)) + return max(p for p in range(p_low, p_high+1) if p*(p-1) <= m_max + 1) + + +def _fragment_3_1(norm_info, n0, tol, m_max=55, ell=2): + """ + A helper function for the _expm_multiply_* functions. + + Parameters + ---------- + norm_info : LazyOperatorNormInfo + Information about norms of certain linear operators of interest. + n0 : int + Number of columns in the _expm_multiply_* B matrix. + tol : float + Expected to be + :math:`2^{-24}` for single precision or + :math:`2^{-53}` for double precision. + m_max : int + A value related to a bound. + ell : int + The number of columns used in the 1-norm approximation. + This is usually taken to be small, maybe between 1 and 5. + + Returns + ------- + best_m : int + Related to bounds for error control. + best_s : int + Amount of scaling. + + Notes + ----- + This is code fragment (3.1) in Al-Mohy and Higham (2011). + The discussion of default values for m_max and ell + is given between the definitions of equation (3.11) + and the definition of equation (3.12). + + """ + if ell < 1: + raise ValueError('expected ell to be a positive integer') + best_m = None + best_s = None + if _condition_3_13(norm_info.onenorm(), n0, m_max, ell): + for m, theta in _theta.items(): + s = int(np.ceil(norm_info.onenorm() / theta)) + if best_m is None or m * s < best_m * best_s: + best_m = m + best_s = s + else: + # Equation (3.11). + for p in range(2, _compute_p_max(m_max) + 1): + for m in range(p*(p-1)-1, m_max+1): + if m in _theta: + s = _compute_cost_div_m(m, p, norm_info) + if best_m is None or m * s < best_m * best_s: + best_m = m + best_s = s + best_s = max(best_s, 1) + return best_m, best_s + + +def _condition_3_13(A_1_norm, n0, m_max, ell): + """ + A helper function for the _expm_multiply_* functions. + + Parameters + ---------- + A_1_norm : float + The precomputed 1-norm of A. + n0 : int + Number of columns in the _expm_multiply_* B matrix. + m_max : int + A value related to a bound. + ell : int + The number of columns used in the 1-norm approximation. + This is usually taken to be small, maybe between 1 and 5. + + Returns + ------- + value : bool + Indicates whether or not the condition has been met. + + Notes + ----- + This is condition (3.13) in Al-Mohy and Higham (2011). + + """ + + # This is the rhs of equation (3.12). + p_max = _compute_p_max(m_max) + a = 2 * ell * p_max * (p_max + 3) + + # Evaluate the condition (3.13). + b = _theta[m_max] / float(n0 * m_max) + return A_1_norm <= a * b + + +def _expm_multiply_interval(A, B, start=None, stop=None, num=None, + endpoint=None, traceA=None, balance=False, + status_only=False): + """ + Compute the action of the matrix exponential at multiple time points. + + Parameters + ---------- + A : transposable linear operator + The operator whose exponential is of interest. + B : ndarray + The matrix to be multiplied by the matrix exponential of A. + start : scalar, optional + The starting time point of the sequence. + stop : scalar, optional + The end time point of the sequence, unless `endpoint` is set to False. + In that case, the sequence consists of all but the last of ``num + 1`` + evenly spaced time points, so that `stop` is excluded. + Note that the step size changes when `endpoint` is False. + num : int, optional + Number of time points to use. + traceA : scalar, optional + Trace of `A`. If not given the trace is estimated for linear operators, + or calculated exactly for sparse matrices. It is used to precondition + `A`, thus an approximate trace is acceptable + endpoint : bool, optional + If True, `stop` is the last time point. Otherwise, it is not included. + balance : bool + Indicates whether or not to apply balancing. + status_only : bool + A flag that is set to True for some debugging and testing operations. + + Returns + ------- + F : ndarray + :math:`e^{t_k A} B` + status : int + An integer status for testing and debugging. + + Notes + ----- + This is algorithm (5.2) in Al-Mohy and Higham (2011). + + There seems to be a typo, where line 15 of the algorithm should be + moved to line 6.5 (between lines 6 and 7). + + """ + if balance: + raise NotImplementedError + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + if A.shape[1] != B.shape[0]: + raise ValueError('shapes of matrices A {} and B {} are incompatible' + .format(A.shape, B.shape)) + ident = _ident_like(A) + is_linear_operator = isinstance(A, scipy.sparse.linalg.LinearOperator) + n = A.shape[0] + if len(B.shape) == 1: + n0 = 1 + elif len(B.shape) == 2: + n0 = B.shape[1] + else: + raise ValueError('expected B to be like a matrix or a vector') + u_d = 2**-53 + tol = u_d + if traceA is None: + if is_linear_operator: + warn("Trace of LinearOperator not available, it will be estimated." + " Provide `traceA` to ensure performance.", stacklevel=3) + # m3=5 is bit arbitrary choice, a more accurate trace (larger m3) might + # speed up exponential calculation, but trace estimation is also costly + # an educated guess would need to consider the number of time points + traceA = traceest(A, m3=5) if is_linear_operator else _trace(A) + mu = traceA / float(n) + + # Get the linspace samples, attempting to preserve the linspace defaults. + linspace_kwargs = {'retstep': True} + if num is not None: + linspace_kwargs['num'] = num + if endpoint is not None: + linspace_kwargs['endpoint'] = endpoint + samples, step = np.linspace(start, stop, **linspace_kwargs) + + # Convert the linspace output to the notation used by the publication. + nsamples = len(samples) + if nsamples < 2: + raise ValueError('at least two time points are required') + q = nsamples - 1 + h = step + t_0 = samples[0] + t_q = samples[q] + + # Define the output ndarray. + # Use an ndim=3 shape, such that the last two indices + # are the ones that may be involved in level 3 BLAS operations. + X_shape = (nsamples,) + B.shape + X = np.empty(X_shape, dtype=np.result_type(A.dtype, B.dtype, float)) + t = t_q - t_0 + A = A - mu * ident + A_1_norm = onenormest(A) if is_linear_operator else _exact_1_norm(A) + ell = 2 + norm_info = LazyOperatorNormInfo(t*A, A_1_norm=t*A_1_norm, ell=ell) + if t*A_1_norm == 0: + m_star, s = 0, 1 + else: + m_star, s = _fragment_3_1(norm_info, n0, tol, ell=ell) + + # Compute the expm action up to the initial time point. + X[0] = _expm_multiply_simple_core(A, B, t_0, mu, m_star, s) + + # Compute the expm action at the rest of the time points. + if q <= s: + if status_only: + return 0 + else: + return _expm_multiply_interval_core_0(A, X, + h, mu, q, norm_info, tol, ell,n0) + elif not (q % s): + if status_only: + return 1 + else: + return _expm_multiply_interval_core_1(A, X, + h, mu, m_star, s, q, tol) + elif (q % s): + if status_only: + return 2 + else: + return _expm_multiply_interval_core_2(A, X, + h, mu, m_star, s, q, tol) + else: + raise Exception('internal error') + + +def _expm_multiply_interval_core_0(A, X, h, mu, q, norm_info, tol, ell, n0): + """ + A helper function, for the case q <= s. + """ + + # Compute the new values of m_star and s which should be applied + # over intervals of size t/q + if norm_info.onenorm() == 0: + m_star, s = 0, 1 + else: + norm_info.set_scale(1./q) + m_star, s = _fragment_3_1(norm_info, n0, tol, ell=ell) + norm_info.set_scale(1) + + for k in range(q): + X[k+1] = _expm_multiply_simple_core(A, X[k], h, mu, m_star, s) + return X, 0 + + +def _expm_multiply_interval_core_1(A, X, h, mu, m_star, s, q, tol): + """ + A helper function, for the case q > s and q % s == 0. + """ + d = q // s + input_shape = X.shape[1:] + K_shape = (m_star + 1, ) + input_shape + K = np.empty(K_shape, dtype=X.dtype) + for i in range(s): + Z = X[i*d] + K[0] = Z + high_p = 0 + for k in range(1, d+1): + F = K[0] + c1 = _exact_inf_norm(F) + for p in range(1, m_star+1): + if p > high_p: + K[p] = h * A.dot(K[p-1]) / float(p) + coeff = float(pow(k, p)) + F = F + coeff * K[p] + inf_norm_K_p_1 = _exact_inf_norm(K[p]) + c2 = coeff * inf_norm_K_p_1 + if c1 + c2 <= tol * _exact_inf_norm(F): + break + c1 = c2 + X[k + i*d] = np.exp(k*h*mu) * F + return X, 1 + + +def _expm_multiply_interval_core_2(A, X, h, mu, m_star, s, q, tol): + """ + A helper function, for the case q > s and q % s > 0. + """ + d = q // s + j = q // d + r = q - d * j + input_shape = X.shape[1:] + K_shape = (m_star + 1, ) + input_shape + K = np.empty(K_shape, dtype=X.dtype) + for i in range(j + 1): + Z = X[i*d] + K[0] = Z + high_p = 0 + if i < j: + effective_d = d + else: + effective_d = r + for k in range(1, effective_d+1): + F = K[0] + c1 = _exact_inf_norm(F) + for p in range(1, m_star+1): + if p == high_p + 1: + K[p] = h * A.dot(K[p-1]) / float(p) + high_p = p + coeff = float(pow(k, p)) + F = F + coeff * K[p] + inf_norm_K_p_1 = _exact_inf_norm(K[p]) + c2 = coeff * inf_norm_K_p_1 + if c1 + c2 <= tol * _exact_inf_norm(F): + break + c1 = c2 + X[k + i*d] = np.exp(k*h*mu) * F + return X, 2 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_interface.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..7c515167c326e27f4dce21cbfa5c052995afc7da --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_interface.py @@ -0,0 +1,896 @@ +"""Abstract linear algebra library. + +This module defines a class hierarchy that implements a kind of "lazy" +matrix representation, called the ``LinearOperator``. It can be used to do +linear algebra with extremely large sparse or structured matrices, without +representing those explicitly in memory. Such matrices can be added, +multiplied, transposed, etc. + +As a motivating example, suppose you want have a matrix where almost all of +the elements have the value one. The standard sparse matrix representation +skips the storage of zeros, but not ones. By contrast, a LinearOperator is +able to represent such matrices efficiently. First, we need a compact way to +represent an all-ones matrix:: + + >>> import numpy as np + >>> from scipy.sparse.linalg._interface import LinearOperator + >>> class Ones(LinearOperator): + ... def __init__(self, shape): + ... super().__init__(dtype=None, shape=shape) + ... def _matvec(self, x): + ... return np.repeat(x.sum(), self.shape[0]) + +Instances of this class emulate ``np.ones(shape)``, but using a constant +amount of storage, independent of ``shape``. The ``_matvec`` method specifies +how this linear operator multiplies with (operates on) a vector. We can now +add this operator to a sparse matrix that stores only offsets from one:: + + >>> from scipy.sparse.linalg._interface import aslinearoperator + >>> from scipy.sparse import csr_matrix + >>> offsets = csr_matrix([[1, 0, 2], [0, -1, 0], [0, 0, 3]]) + >>> A = aslinearoperator(offsets) + Ones(offsets.shape) + >>> A.dot([1, 2, 3]) + array([13, 4, 15]) + +The result is the same as that given by its dense, explicitly-stored +counterpart:: + + >>> (np.ones(A.shape, A.dtype) + offsets.toarray()).dot([1, 2, 3]) + array([13, 4, 15]) + +Several algorithms in the ``scipy.sparse`` library are able to operate on +``LinearOperator`` instances. +""" + +import warnings + +import numpy as np + +from scipy.sparse import issparse +from scipy.sparse._sputils import isshape, isintlike, asmatrix, is_pydata_spmatrix + +__all__ = ['LinearOperator', 'aslinearoperator'] + + +class LinearOperator: + """Common interface for performing matrix vector products + + Many iterative methods (e.g. cg, gmres) do not need to know the + individual entries of a matrix to solve a linear system A*x=b. + Such solvers only require the computation of matrix vector + products, A*v where v is a dense vector. This class serves as + an abstract interface between iterative solvers and matrix-like + objects. + + To construct a concrete LinearOperator, either pass appropriate + callables to the constructor of this class, or subclass it. + + A subclass must implement either one of the methods ``_matvec`` + and ``_matmat``, and the attributes/properties ``shape`` (pair of + integers) and ``dtype`` (may be None). It may call the ``__init__`` + on this class to have these attributes validated. Implementing + ``_matvec`` automatically implements ``_matmat`` (using a naive + algorithm) and vice-versa. + + Optionally, a subclass may implement ``_rmatvec`` or ``_adjoint`` + to implement the Hermitian adjoint (conjugate transpose). As with + ``_matvec`` and ``_matmat``, implementing either ``_rmatvec`` or + ``_adjoint`` implements the other automatically. Implementing + ``_adjoint`` is preferable; ``_rmatvec`` is mostly there for + backwards compatibility. + + Parameters + ---------- + shape : tuple + Matrix dimensions (M, N). + matvec : callable f(v) + Returns returns A * v. + rmatvec : callable f(v) + Returns A^H * v, where A^H is the conjugate transpose of A. + matmat : callable f(V) + Returns A * V, where V is a dense matrix with dimensions (N, K). + dtype : dtype + Data type of the matrix. + rmatmat : callable f(V) + Returns A^H * V, where V is a dense matrix with dimensions (M, K). + + Attributes + ---------- + args : tuple + For linear operators describing products etc. of other linear + operators, the operands of the binary operation. + ndim : int + Number of dimensions (this is always 2) + + See Also + -------- + aslinearoperator : Construct LinearOperators + + Notes + ----- + The user-defined matvec() function must properly handle the case + where v has shape (N,) as well as the (N,1) case. The shape of + the return type is handled internally by LinearOperator. + + LinearOperator instances can also be multiplied, added with each + other and exponentiated, all lazily: the result of these operations + is always a new, composite LinearOperator, that defers linear + operations to the original operators and combines the results. + + More details regarding how to subclass a LinearOperator and several + examples of concrete LinearOperator instances can be found in the + external project `PyLops `_. + + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import LinearOperator + >>> def mv(v): + ... return np.array([2*v[0], 3*v[1]]) + ... + >>> A = LinearOperator((2,2), matvec=mv) + >>> A + <2x2 _CustomLinearOperator with dtype=float64> + >>> A.matvec(np.ones(2)) + array([ 2., 3.]) + >>> A * np.ones(2) + array([ 2., 3.]) + + """ + + ndim = 2 + # Necessary for right matmul with numpy arrays. + __array_ufunc__ = None + + def __new__(cls, *args, **kwargs): + if cls is LinearOperator: + # Operate as _CustomLinearOperator factory. + return super().__new__(_CustomLinearOperator) + else: + obj = super().__new__(cls) + + if (type(obj)._matvec == LinearOperator._matvec + and type(obj)._matmat == LinearOperator._matmat): + warnings.warn("LinearOperator subclass should implement" + " at least one of _matvec and _matmat.", + category=RuntimeWarning, stacklevel=2) + + return obj + + def __init__(self, dtype, shape): + """Initialize this LinearOperator. + + To be called by subclasses. ``dtype`` may be None; ``shape`` should + be convertible to a length-2 tuple. + """ + if dtype is not None: + dtype = np.dtype(dtype) + + shape = tuple(shape) + if not isshape(shape): + raise ValueError(f"invalid shape {shape!r} (must be 2-d)") + + self.dtype = dtype + self.shape = shape + + def _init_dtype(self): + """Called from subclasses at the end of the __init__ routine. + """ + if self.dtype is None: + v = np.zeros(self.shape[-1]) + self.dtype = np.asarray(self.matvec(v)).dtype + + def _matmat(self, X): + """Default matrix-matrix multiplication handler. + + Falls back on the user-defined _matvec method, so defining that will + define matrix multiplication (though in a very suboptimal way). + """ + + return np.hstack([self.matvec(col.reshape(-1,1)) for col in X.T]) + + def _matvec(self, x): + """Default matrix-vector multiplication handler. + + If self is a linear operator of shape (M, N), then this method will + be called on a shape (N,) or (N, 1) ndarray, and should return a + shape (M,) or (M, 1) ndarray. + + This default implementation falls back on _matmat, so defining that + will define matrix-vector multiplication as well. + """ + return self.matmat(x.reshape(-1, 1)) + + def matvec(self, x): + """Matrix-vector multiplication. + + Performs the operation y=A*x where A is an MxN linear + operator and x is a column vector or 1-d array. + + Parameters + ---------- + x : {matrix, ndarray} + An array with shape (N,) or (N,1). + + Returns + ------- + y : {matrix, ndarray} + A matrix or ndarray with shape (M,) or (M,1) depending + on the type and shape of the x argument. + + Notes + ----- + This matvec wraps the user-specified matvec routine or overridden + _matvec method to ensure that y has the correct shape and type. + + """ + + x = np.asanyarray(x) + + M,N = self.shape + + if x.shape != (N,) and x.shape != (N,1): + raise ValueError('dimension mismatch') + + y = self._matvec(x) + + if isinstance(x, np.matrix): + y = asmatrix(y) + else: + y = np.asarray(y) + + if x.ndim == 1: + y = y.reshape(M) + elif x.ndim == 2: + y = y.reshape(M,1) + else: + raise ValueError('invalid shape returned by user-defined matvec()') + + return y + + def rmatvec(self, x): + """Adjoint matrix-vector multiplication. + + Performs the operation y = A^H * x where A is an MxN linear + operator and x is a column vector or 1-d array. + + Parameters + ---------- + x : {matrix, ndarray} + An array with shape (M,) or (M,1). + + Returns + ------- + y : {matrix, ndarray} + A matrix or ndarray with shape (N,) or (N,1) depending + on the type and shape of the x argument. + + Notes + ----- + This rmatvec wraps the user-specified rmatvec routine or overridden + _rmatvec method to ensure that y has the correct shape and type. + + """ + + x = np.asanyarray(x) + + M,N = self.shape + + if x.shape != (M,) and x.shape != (M,1): + raise ValueError('dimension mismatch') + + y = self._rmatvec(x) + + if isinstance(x, np.matrix): + y = asmatrix(y) + else: + y = np.asarray(y) + + if x.ndim == 1: + y = y.reshape(N) + elif x.ndim == 2: + y = y.reshape(N,1) + else: + raise ValueError('invalid shape returned by user-defined rmatvec()') + + return y + + def _rmatvec(self, x): + """Default implementation of _rmatvec; defers to adjoint.""" + if type(self)._adjoint == LinearOperator._adjoint: + # _adjoint not overridden, prevent infinite recursion + raise NotImplementedError + else: + return self.H.matvec(x) + + def matmat(self, X): + """Matrix-matrix multiplication. + + Performs the operation y=A*X where A is an MxN linear + operator and X dense N*K matrix or ndarray. + + Parameters + ---------- + X : {matrix, ndarray} + An array with shape (N,K). + + Returns + ------- + Y : {matrix, ndarray} + A matrix or ndarray with shape (M,K) depending on + the type of the X argument. + + Notes + ----- + This matmat wraps any user-specified matmat routine or overridden + _matmat method to ensure that y has the correct type. + + """ + if not (issparse(X) or is_pydata_spmatrix(X)): + X = np.asanyarray(X) + + if X.ndim != 2: + raise ValueError(f'expected 2-d ndarray or matrix, not {X.ndim}-d') + + if X.shape[0] != self.shape[1]: + raise ValueError(f'dimension mismatch: {self.shape}, {X.shape}') + + try: + Y = self._matmat(X) + except Exception as e: + if issparse(X) or is_pydata_spmatrix(X): + raise TypeError( + "Unable to multiply a LinearOperator with a sparse matrix." + " Wrap the matrix in aslinearoperator first." + ) from e + raise + + if isinstance(Y, np.matrix): + Y = asmatrix(Y) + + return Y + + def rmatmat(self, X): + """Adjoint matrix-matrix multiplication. + + Performs the operation y = A^H * x where A is an MxN linear + operator and x is a column vector or 1-d array, or 2-d array. + The default implementation defers to the adjoint. + + Parameters + ---------- + X : {matrix, ndarray} + A matrix or 2D array. + + Returns + ------- + Y : {matrix, ndarray} + A matrix or 2D array depending on the type of the input. + + Notes + ----- + This rmatmat wraps the user-specified rmatmat routine. + + """ + if not (issparse(X) or is_pydata_spmatrix(X)): + X = np.asanyarray(X) + + if X.ndim != 2: + raise ValueError('expected 2-d ndarray or matrix, not %d-d' + % X.ndim) + + if X.shape[0] != self.shape[0]: + raise ValueError(f'dimension mismatch: {self.shape}, {X.shape}') + + try: + Y = self._rmatmat(X) + except Exception as e: + if issparse(X) or is_pydata_spmatrix(X): + raise TypeError( + "Unable to multiply a LinearOperator with a sparse matrix." + " Wrap the matrix in aslinearoperator() first." + ) from e + raise + + if isinstance(Y, np.matrix): + Y = asmatrix(Y) + return Y + + def _rmatmat(self, X): + """Default implementation of _rmatmat defers to rmatvec or adjoint.""" + if type(self)._adjoint == LinearOperator._adjoint: + return np.hstack([self.rmatvec(col.reshape(-1, 1)) for col in X.T]) + else: + return self.H.matmat(X) + + def __call__(self, x): + return self*x + + def __mul__(self, x): + return self.dot(x) + + def __truediv__(self, other): + if not np.isscalar(other): + raise ValueError("Can only divide a linear operator by a scalar.") + + return _ScaledLinearOperator(self, 1.0/other) + + def dot(self, x): + """Matrix-matrix or matrix-vector multiplication. + + Parameters + ---------- + x : array_like + 1-d or 2-d array, representing a vector or matrix. + + Returns + ------- + Ax : array + 1-d or 2-d array (depending on the shape of x) that represents + the result of applying this linear operator on x. + + """ + if isinstance(x, LinearOperator): + return _ProductLinearOperator(self, x) + elif np.isscalar(x): + return _ScaledLinearOperator(self, x) + else: + if not issparse(x) and not is_pydata_spmatrix(x): + # Sparse matrices shouldn't be converted to numpy arrays. + x = np.asarray(x) + + if x.ndim == 1 or x.ndim == 2 and x.shape[1] == 1: + return self.matvec(x) + elif x.ndim == 2: + return self.matmat(x) + else: + raise ValueError('expected 1-d or 2-d array or matrix, got %r' + % x) + + def __matmul__(self, other): + if np.isscalar(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self.__mul__(other) + + def __rmatmul__(self, other): + if np.isscalar(other): + raise ValueError("Scalar operands are not allowed, " + "use '*' instead") + return self.__rmul__(other) + + def __rmul__(self, x): + if np.isscalar(x): + return _ScaledLinearOperator(self, x) + else: + return self._rdot(x) + + def _rdot(self, x): + """Matrix-matrix or matrix-vector multiplication from the right. + + Parameters + ---------- + x : array_like + 1-d or 2-d array, representing a vector or matrix. + + Returns + ------- + xA : array + 1-d or 2-d array (depending on the shape of x) that represents + the result of applying this linear operator on x from the right. + + Notes + ----- + This is copied from dot to implement right multiplication. + """ + if isinstance(x, LinearOperator): + return _ProductLinearOperator(x, self) + elif np.isscalar(x): + return _ScaledLinearOperator(self, x) + else: + if not issparse(x) and not is_pydata_spmatrix(x): + # Sparse matrices shouldn't be converted to numpy arrays. + x = np.asarray(x) + + # We use transpose instead of rmatvec/rmatmat to avoid + # unnecessary complex conjugation if possible. + if x.ndim == 1 or x.ndim == 2 and x.shape[0] == 1: + return self.T.matvec(x.T).T + elif x.ndim == 2: + return self.T.matmat(x.T).T + else: + raise ValueError('expected 1-d or 2-d array or matrix, got %r' + % x) + + def __pow__(self, p): + if np.isscalar(p): + return _PowerLinearOperator(self, p) + else: + return NotImplemented + + def __add__(self, x): + if isinstance(x, LinearOperator): + return _SumLinearOperator(self, x) + else: + return NotImplemented + + def __neg__(self): + return _ScaledLinearOperator(self, -1) + + def __sub__(self, x): + return self.__add__(-x) + + def __repr__(self): + M,N = self.shape + if self.dtype is None: + dt = 'unspecified dtype' + else: + dt = 'dtype=' + str(self.dtype) + + return '<%dx%d %s with %s>' % (M, N, self.__class__.__name__, dt) + + def adjoint(self): + """Hermitian adjoint. + + Returns the Hermitian adjoint of self, aka the Hermitian + conjugate or Hermitian transpose. For a complex matrix, the + Hermitian adjoint is equal to the conjugate transpose. + + Can be abbreviated self.H instead of self.adjoint(). + + Returns + ------- + A_H : LinearOperator + Hermitian adjoint of self. + """ + return self._adjoint() + + H = property(adjoint) + + def transpose(self): + """Transpose this linear operator. + + Returns a LinearOperator that represents the transpose of this one. + Can be abbreviated self.T instead of self.transpose(). + """ + return self._transpose() + + T = property(transpose) + + def _adjoint(self): + """Default implementation of _adjoint; defers to rmatvec.""" + return _AdjointLinearOperator(self) + + def _transpose(self): + """ Default implementation of _transpose; defers to rmatvec + conj""" + return _TransposedLinearOperator(self) + + +class _CustomLinearOperator(LinearOperator): + """Linear operator defined in terms of user-specified operations.""" + + def __init__(self, shape, matvec, rmatvec=None, matmat=None, + dtype=None, rmatmat=None): + super().__init__(dtype, shape) + + self.args = () + + self.__matvec_impl = matvec + self.__rmatvec_impl = rmatvec + self.__rmatmat_impl = rmatmat + self.__matmat_impl = matmat + + self._init_dtype() + + def _matmat(self, X): + if self.__matmat_impl is not None: + return self.__matmat_impl(X) + else: + return super()._matmat(X) + + def _matvec(self, x): + return self.__matvec_impl(x) + + def _rmatvec(self, x): + func = self.__rmatvec_impl + if func is None: + raise NotImplementedError("rmatvec is not defined") + return self.__rmatvec_impl(x) + + def _rmatmat(self, X): + if self.__rmatmat_impl is not None: + return self.__rmatmat_impl(X) + else: + return super()._rmatmat(X) + + def _adjoint(self): + return _CustomLinearOperator(shape=(self.shape[1], self.shape[0]), + matvec=self.__rmatvec_impl, + rmatvec=self.__matvec_impl, + matmat=self.__rmatmat_impl, + rmatmat=self.__matmat_impl, + dtype=self.dtype) + + +class _AdjointLinearOperator(LinearOperator): + """Adjoint of arbitrary Linear Operator""" + + def __init__(self, A): + shape = (A.shape[1], A.shape[0]) + super().__init__(dtype=A.dtype, shape=shape) + self.A = A + self.args = (A,) + + def _matvec(self, x): + return self.A._rmatvec(x) + + def _rmatvec(self, x): + return self.A._matvec(x) + + def _matmat(self, x): + return self.A._rmatmat(x) + + def _rmatmat(self, x): + return self.A._matmat(x) + +class _TransposedLinearOperator(LinearOperator): + """Transposition of arbitrary Linear Operator""" + + def __init__(self, A): + shape = (A.shape[1], A.shape[0]) + super().__init__(dtype=A.dtype, shape=shape) + self.A = A + self.args = (A,) + + def _matvec(self, x): + # NB. np.conj works also on sparse matrices + return np.conj(self.A._rmatvec(np.conj(x))) + + def _rmatvec(self, x): + return np.conj(self.A._matvec(np.conj(x))) + + def _matmat(self, x): + # NB. np.conj works also on sparse matrices + return np.conj(self.A._rmatmat(np.conj(x))) + + def _rmatmat(self, x): + return np.conj(self.A._matmat(np.conj(x))) + +def _get_dtype(operators, dtypes=None): + if dtypes is None: + dtypes = [] + for obj in operators: + if obj is not None and hasattr(obj, 'dtype'): + dtypes.append(obj.dtype) + return np.result_type(*dtypes) + + +class _SumLinearOperator(LinearOperator): + def __init__(self, A, B): + if not isinstance(A, LinearOperator) or \ + not isinstance(B, LinearOperator): + raise ValueError('both operands have to be a LinearOperator') + if A.shape != B.shape: + raise ValueError(f'cannot add {A} and {B}: shape mismatch') + self.args = (A, B) + super().__init__(_get_dtype([A, B]), A.shape) + + def _matvec(self, x): + return self.args[0].matvec(x) + self.args[1].matvec(x) + + def _rmatvec(self, x): + return self.args[0].rmatvec(x) + self.args[1].rmatvec(x) + + def _rmatmat(self, x): + return self.args[0].rmatmat(x) + self.args[1].rmatmat(x) + + def _matmat(self, x): + return self.args[0].matmat(x) + self.args[1].matmat(x) + + def _adjoint(self): + A, B = self.args + return A.H + B.H + + +class _ProductLinearOperator(LinearOperator): + def __init__(self, A, B): + if not isinstance(A, LinearOperator) or \ + not isinstance(B, LinearOperator): + raise ValueError('both operands have to be a LinearOperator') + if A.shape[1] != B.shape[0]: + raise ValueError(f'cannot multiply {A} and {B}: shape mismatch') + super().__init__(_get_dtype([A, B]), + (A.shape[0], B.shape[1])) + self.args = (A, B) + + def _matvec(self, x): + return self.args[0].matvec(self.args[1].matvec(x)) + + def _rmatvec(self, x): + return self.args[1].rmatvec(self.args[0].rmatvec(x)) + + def _rmatmat(self, x): + return self.args[1].rmatmat(self.args[0].rmatmat(x)) + + def _matmat(self, x): + return self.args[0].matmat(self.args[1].matmat(x)) + + def _adjoint(self): + A, B = self.args + return B.H * A.H + + +class _ScaledLinearOperator(LinearOperator): + def __init__(self, A, alpha): + if not isinstance(A, LinearOperator): + raise ValueError('LinearOperator expected as A') + if not np.isscalar(alpha): + raise ValueError('scalar expected as alpha') + if isinstance(A, _ScaledLinearOperator): + A, alpha_original = A.args + # Avoid in-place multiplication so that we don't accidentally mutate + # the original prefactor. + alpha = alpha * alpha_original + + dtype = _get_dtype([A], [type(alpha)]) + super().__init__(dtype, A.shape) + self.args = (A, alpha) + + def _matvec(self, x): + return self.args[1] * self.args[0].matvec(x) + + def _rmatvec(self, x): + return np.conj(self.args[1]) * self.args[0].rmatvec(x) + + def _rmatmat(self, x): + return np.conj(self.args[1]) * self.args[0].rmatmat(x) + + def _matmat(self, x): + return self.args[1] * self.args[0].matmat(x) + + def _adjoint(self): + A, alpha = self.args + return A.H * np.conj(alpha) + + +class _PowerLinearOperator(LinearOperator): + def __init__(self, A, p): + if not isinstance(A, LinearOperator): + raise ValueError('LinearOperator expected as A') + if A.shape[0] != A.shape[1]: + raise ValueError('square LinearOperator expected, got %r' % A) + if not isintlike(p) or p < 0: + raise ValueError('non-negative integer expected as p') + + super().__init__(_get_dtype([A]), A.shape) + self.args = (A, p) + + def _power(self, fun, x): + res = np.array(x, copy=True) + for i in range(self.args[1]): + res = fun(res) + return res + + def _matvec(self, x): + return self._power(self.args[0].matvec, x) + + def _rmatvec(self, x): + return self._power(self.args[0].rmatvec, x) + + def _rmatmat(self, x): + return self._power(self.args[0].rmatmat, x) + + def _matmat(self, x): + return self._power(self.args[0].matmat, x) + + def _adjoint(self): + A, p = self.args + return A.H ** p + + +class MatrixLinearOperator(LinearOperator): + def __init__(self, A): + super().__init__(A.dtype, A.shape) + self.A = A + self.__adj = None + self.args = (A,) + + def _matmat(self, X): + return self.A.dot(X) + + def _adjoint(self): + if self.__adj is None: + self.__adj = _AdjointMatrixOperator(self) + return self.__adj + +class _AdjointMatrixOperator(MatrixLinearOperator): + def __init__(self, adjoint): + self.A = adjoint.A.T.conj() + self.__adjoint = adjoint + self.args = (adjoint,) + self.shape = adjoint.shape[1], adjoint.shape[0] + + @property + def dtype(self): + return self.__adjoint.dtype + + def _adjoint(self): + return self.__adjoint + + +class IdentityOperator(LinearOperator): + def __init__(self, shape, dtype=None): + super().__init__(dtype, shape) + + def _matvec(self, x): + return x + + def _rmatvec(self, x): + return x + + def _rmatmat(self, x): + return x + + def _matmat(self, x): + return x + + def _adjoint(self): + return self + + +def aslinearoperator(A): + """Return A as a LinearOperator. + + 'A' may be any of the following types: + - ndarray + - matrix + - sparse matrix (e.g. csr_matrix, lil_matrix, etc.) + - LinearOperator + - An object with .shape and .matvec attributes + + See the LinearOperator documentation for additional information. + + Notes + ----- + If 'A' has no .dtype attribute, the data type is determined by calling + :func:`LinearOperator.matvec()` - set the .dtype attribute to prevent this + call upon the linear operator creation. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import aslinearoperator + >>> M = np.array([[1,2,3],[4,5,6]], dtype=np.int32) + >>> aslinearoperator(M) + <2x3 MatrixLinearOperator with dtype=int32> + """ + if isinstance(A, LinearOperator): + return A + + elif isinstance(A, np.ndarray) or isinstance(A, np.matrix): + if A.ndim > 2: + raise ValueError('array must have ndim <= 2') + A = np.atleast_2d(np.asarray(A)) + return MatrixLinearOperator(A) + + elif issparse(A) or is_pydata_spmatrix(A): + return MatrixLinearOperator(A) + + else: + if hasattr(A, 'shape') and hasattr(A, 'matvec'): + rmatvec = None + rmatmat = None + dtype = None + + if hasattr(A, 'rmatvec'): + rmatvec = A.rmatvec + if hasattr(A, 'rmatmat'): + rmatmat = A.rmatmat + if hasattr(A, 'dtype'): + dtype = A.dtype + return LinearOperator(A.shape, A.matvec, rmatvec=rmatvec, + rmatmat=rmatmat, dtype=dtype) + + else: + raise TypeError('type not understood') diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3b57274542928e79c234bb6955849a90be21990e --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/__init__.py @@ -0,0 +1,20 @@ +"Iterative Solvers for Sparse Linear Systems" + +#from info import __doc__ +from .iterative import * +from .minres import minres +from .lgmres import lgmres +from .lsqr import lsqr +from .lsmr import lsmr +from ._gcrotmk import gcrotmk +from .tfqmr import tfqmr + +__all__ = [ + 'bicg', 'bicgstab', 'cg', 'cgs', 'gcrotmk', 'gmres', + 'lgmres', 'lsmr', 'lsqr', + 'minres', 'qmr', 'tfqmr' +] + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/iterative.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/iterative.py new file mode 100644 index 0000000000000000000000000000000000000000..0176654cfc80cb35a4f17c5871537cfde2054e49 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/iterative.py @@ -0,0 +1,1000 @@ +import warnings +import numpy as np +from scipy.sparse.linalg._interface import LinearOperator +from .utils import make_system +from scipy.linalg import get_lapack_funcs + +__all__ = ['bicg', 'bicgstab', 'cg', 'cgs', 'gmres', 'qmr'] + + +def _get_atol_rtol(name, b_norm, atol=0., rtol=1e-5): + """ + A helper function to handle tolerance normalization + """ + if atol == 'legacy' or atol is None or atol < 0: + msg = (f"'scipy.sparse.linalg.{name}' called with invalid `atol`={atol}; " + "if set, `atol` must be a real, non-negative number.") + raise ValueError(msg) + + atol = max(float(atol), float(rtol) * float(b_norm)) + + return atol, rtol + + +def bicg(A, b, x0=None, *, rtol=1e-5, atol=0., maxiter=None, M=None, callback=None): + """Use BIConjugate Gradient iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import bicg + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1.]]) + >>> b = np.array([2., 4., -1.]) + >>> x, exitCode = bicg(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('bicg', bnrm2, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + matvec, rmatvec = A.matvec, A.rmatvec + psolve, rpsolve = M.matvec, M.rmatvec + + rhotol = np.finfo(x.dtype.char).eps**2 + + # Dummy values to initialize vars, silence linter warnings + rho_prev, p, ptilde = None, None, None + + r = b - matvec(x) if x.any() else b.copy() + rtilde = r.copy() + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + z = psolve(r) + ztilde = rpsolve(rtilde) + # order matters in this dot product + rho_cur = dotprod(rtilde, z) + + if np.abs(rho_cur) < rhotol: # Breakdown case + return postprocess, -10 + + if iteration > 0: + beta = rho_cur / rho_prev + p *= beta + p += z + ptilde *= beta.conj() + ptilde += ztilde + else: # First spin + p = z.copy() + ptilde = ztilde.copy() + + q = matvec(p) + qtilde = rmatvec(ptilde) + rv = dotprod(ptilde, q) + + if rv == 0: + return postprocess(x), -11 + + alpha = rho_cur / rv + x += alpha*p + r -= alpha*q + rtilde -= alpha.conj()*qtilde + rho_prev = rho_cur + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +def bicgstab(A, b, x0=None, *, rtol=1e-5, atol=0., maxiter=None, M=None, + callback=None): + """Use BIConjugate Gradient STABilized iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import bicgstab + >>> R = np.array([[4, 2, 0, 1], + ... [3, 0, 0, 2], + ... [0, 1, 1, 1], + ... [0, 2, 1, 0]]) + >>> A = csc_matrix(R) + >>> b = np.array([-1, -0.5, -1, 2]) + >>> x, exit_code = bicgstab(A, b, atol=1e-5) + >>> print(exit_code) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('bicgstab', bnrm2, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + matvec = A.matvec + psolve = M.matvec + + # These values make no sense but coming from original Fortran code + # sqrt might have been meant instead. + rhotol = np.finfo(x.dtype.char).eps**2 + omegatol = rhotol + + # Dummy values to initialize vars, silence linter warnings + rho_prev, omega, alpha, p, v = None, None, None, None, None + + r = b - matvec(x) if x.any() else b.copy() + rtilde = r.copy() + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + rho = dotprod(rtilde, r) + if np.abs(rho) < rhotol: # rho breakdown + return postprocess(x), -10 + + if iteration > 0: + if np.abs(omega) < omegatol: # omega breakdown + return postprocess(x), -11 + + beta = (rho / rho_prev) * (alpha / omega) + p -= omega*v + p *= beta + p += r + else: # First spin + s = np.empty_like(r) + p = r.copy() + + phat = psolve(p) + v = matvec(phat) + rv = dotprod(rtilde, v) + if rv == 0: + return postprocess(x), -11 + alpha = rho / rv + r -= alpha*v + s[:] = r[:] + + if np.linalg.norm(s) < atol: + x += alpha*phat + return postprocess(x), 0 + + shat = psolve(s) + t = matvec(shat) + omega = dotprod(t, s) / dotprod(t, t) + x += alpha*phat + x += omega*shat + r -= omega*t + rho_prev = rho + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +def cg(A, b, x0=None, *, rtol=1e-5, atol=0., maxiter=None, M=None, callback=None): + """Use Conjugate Gradient iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + ``A`` must represent a hermitian, positive definite matrix. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import cg + >>> P = np.array([[4, 0, 1, 0], + ... [0, 5, 0, 0], + ... [1, 0, 3, 2], + ... [0, 0, 2, 4]]) + >>> A = csc_matrix(P) + >>> b = np.array([-1, -0.5, -1, 2]) + >>> x, exit_code = cg(A, b, atol=1e-5) + >>> print(exit_code) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('cg', bnrm2, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + + if maxiter is None: + maxiter = n*10 + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + matvec = A.matvec + psolve = M.matvec + r = b - matvec(x) if x.any() else b.copy() + + # Dummy value to initialize var, silences warnings + rho_prev, p = None, None + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + z = psolve(r) + rho_cur = dotprod(r, z) + if iteration > 0: + beta = rho_cur / rho_prev + p *= beta + p += z + else: # First spin + p = np.empty_like(r) + p[:] = z[:] + + q = matvec(p) + alpha = rho_cur / dotprod(p, q) + x += alpha*p + r -= alpha*q + rho_prev = rho_cur + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +def cgs(A, b, x0=None, *, rtol=1e-5, atol=0., maxiter=None, M=None, callback=None): + """Use Conjugate Gradient Squared iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real-valued N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import cgs + >>> R = np.array([[4, 2, 0, 1], + ... [3, 0, 0, 2], + ... [0, 1, 1, 1], + ... [0, 2, 1, 0]]) + >>> A = csc_matrix(R) + >>> b = np.array([-1, -0.5, -1, 2]) + >>> x, exit_code = cgs(A, b) + >>> print(exit_code) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('cgs', bnrm2, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + n = len(b) + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + matvec = A.matvec + psolve = M.matvec + + rhotol = np.finfo(x.dtype.char).eps**2 + + r = b - matvec(x) if x.any() else b.copy() + + rtilde = r.copy() + bnorm = np.linalg.norm(b) + if bnorm == 0: + bnorm = 1 + + # Dummy values to initialize vars, silence linter warnings + rho_prev, p, u, q = None, None, None, None + + for iteration in range(maxiter): + rnorm = np.linalg.norm(r) + if rnorm < atol: # Are we done? + return postprocess(x), 0 + + rho_cur = dotprod(rtilde, r) + if np.abs(rho_cur) < rhotol: # Breakdown case + return postprocess, -10 + + if iteration > 0: + beta = rho_cur / rho_prev + + # u = r + beta * q + # p = u + beta * (q + beta * p); + u[:] = r[:] + u += beta*q + + p *= beta + p += q + p *= beta + p += u + + else: # First spin + p = r.copy() + u = r.copy() + q = np.empty_like(r) + + phat = psolve(p) + vhat = matvec(phat) + rv = dotprod(rtilde, vhat) + + if rv == 0: # Dot product breakdown + return postprocess(x), -11 + + alpha = rho_cur / rv + q[:] = u[:] + q -= alpha*vhat + uhat = psolve(u + q) + x += alpha*uhat + + # Due to numerical error build-up the actual residual is computed + # instead of the following two lines that were in the original + # FORTRAN templates, still using a single matvec. + + # qhat = matvec(uhat) + # r -= alpha*qhat + r = b - matvec(x) + + rho_prev = rho_cur + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter + + +def gmres(A, b, x0=None, *, rtol=1e-5, atol=0., restart=None, maxiter=None, M=None, + callback=None, callback_type=None): + """ + Use Generalized Minimal RESidual iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution (a vector of zeros by default). + atol, rtol : float + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + restart : int, optional + Number of iterations between restarts. Larger values increase + iteration cost, but may be necessary for convergence. + If omitted, ``min(20, n)`` is used. + maxiter : int, optional + Maximum number of iterations (restart cycles). Iteration will stop + after maxiter steps even if the specified tolerance has not been + achieved. See `callback_type`. + M : {sparse matrix, ndarray, LinearOperator} + Inverse of the preconditioner of A. M should approximate the + inverse of A and be easy to solve for (see Notes). Effective + preconditioning dramatically improves the rate of convergence, + which implies that fewer iterations are needed to reach a given + error tolerance. By default, no preconditioner is used. + In this implementation, left preconditioning is used, + and the preconditioned residual is minimized. However, the final + convergence is tested with respect to the ``b - A @ x`` residual. + callback : function + User-supplied function to call after each iteration. It is called + as `callback(args)`, where `args` are selected by `callback_type`. + callback_type : {'x', 'pr_norm', 'legacy'}, optional + Callback function argument requested: + - ``x``: current iterate (ndarray), called on every restart + - ``pr_norm``: relative (preconditioned) residual norm (float), + called on every inner iteration + - ``legacy`` (default): same as ``pr_norm``, but also changes the + meaning of `maxiter` to count inner iterations instead of restart + cycles. + + This keyword has no effect if `callback` is not set. + + Returns + ------- + x : ndarray + The converged solution. + info : int + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + + See Also + -------- + LinearOperator + + Notes + ----- + A preconditioner, P, is chosen such that P is close to A but easy to solve + for. The preconditioner parameter required by this routine is + ``M = P^-1``. The inverse should preferably not be calculated + explicitly. Rather, use the following template to produce M:: + + # Construct a linear operator that computes P^-1 @ x. + import scipy.sparse.linalg as spla + M_x = lambda x: spla.spsolve(P, x) + M = spla.LinearOperator((n, n), M_x) + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import gmres + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = gmres(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + if callback is not None and callback_type is None: + # Warn about 'callback_type' semantic changes. + # Probably should be removed only in far future, Scipy 2.0 or so. + msg = ("scipy.sparse.linalg.gmres called without specifying " + "`callback_type`. The default value will be changed in" + " a future release. For compatibility, specify a value " + "for `callback_type` explicitly, e.g., " + "``gmres(..., callback_type='pr_norm')``, or to retain the " + "old behavior ``gmres(..., callback_type='legacy')``" + ) + warnings.warn(msg, category=DeprecationWarning, stacklevel=3) + + if callback_type is None: + callback_type = 'legacy' + + if callback_type not in ('x', 'pr_norm', 'legacy'): + raise ValueError(f"Unknown callback_type: {callback_type!r}") + + if callback is None: + callback_type = None + + A, M, x, b, postprocess = make_system(A, M, x0, b) + matvec = A.matvec + psolve = M.matvec + n = len(b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('gmres', bnrm2, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + eps = np.finfo(x.dtype.char).eps + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + if maxiter is None: + maxiter = n*10 + + if restart is None: + restart = 20 + restart = min(restart, n) + + Mb_nrm2 = np.linalg.norm(psolve(b)) + + # ==================================================== + # =========== Tolerance control from gh-8400 ========= + # ==================================================== + # Tolerance passed to GMRESREVCOM applies to the inner + # iteration and deals with the left-preconditioned + # residual. + ptol_max_factor = 1. + ptol = Mb_nrm2 * min(ptol_max_factor, atol / bnrm2) + presid = 0. + # ==================================================== + lartg = get_lapack_funcs('lartg', dtype=x.dtype) + + # allocate internal variables + v = np.empty([restart+1, n], dtype=x.dtype) + h = np.zeros([restart, restart+1], dtype=x.dtype) + givens = np.zeros([restart, 2], dtype=x.dtype) + + # legacy iteration count + inner_iter = 0 + + for iteration in range(maxiter): + if iteration == 0: + r = b - matvec(x) if x.any() else b.copy() + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + + v[0, :] = psolve(r) + tmp = np.linalg.norm(v[0, :]) + v[0, :] *= (1 / tmp) + # RHS of the Hessenberg problem + S = np.zeros(restart+1, dtype=x.dtype) + S[0] = tmp + + breakdown = False + for col in range(restart): + av = matvec(v[col, :]) + w = psolve(av) + + # Modified Gram-Schmidt + h0 = np.linalg.norm(w) + for k in range(col+1): + tmp = dotprod(v[k, :], w) + h[col, k] = tmp + w -= tmp*v[k, :] + + h1 = np.linalg.norm(w) + h[col, col + 1] = h1 + v[col + 1, :] = w[:] + + # Exact solution indicator + if h1 <= eps*h0: + h[col, col + 1] = 0 + breakdown = True + else: + v[col + 1, :] *= (1 / h1) + + # apply past Givens rotations to current h column + for k in range(col): + c, s = givens[k, 0], givens[k, 1] + n0, n1 = h[col, [k, k+1]] + h[col, [k, k + 1]] = [c*n0 + s*n1, -s.conj()*n0 + c*n1] + + # get and apply current rotation to h and S + c, s, mag = lartg(h[col, col], h[col, col+1]) + givens[col, :] = [c, s] + h[col, [col, col+1]] = mag, 0 + + # S[col+1] component is always 0 + tmp = -np.conjugate(s)*S[col] + S[[col, col + 1]] = [c*S[col], tmp] + presid = np.abs(tmp) + inner_iter += 1 + + if callback_type in ('legacy', 'pr_norm'): + callback(presid / bnrm2) + # Legacy behavior + if callback_type == 'legacy' and inner_iter == maxiter: + break + if presid <= ptol or breakdown: + break + + # Solve h(col, col) upper triangular system and allow pseudo-solve + # singular cases as in (but without the f2py copies): + # y = trsv(h[:col+1, :col+1].T, S[:col+1]) + + if h[col, col] == 0: + S[col] = 0 + + y = np.zeros([col+1], dtype=x.dtype) + y[:] = S[:col+1] + for k in range(col, 0, -1): + if y[k] != 0: + y[k] /= h[k, k] + tmp = y[k] + y[:k] -= tmp*h[k, :k] + if y[0] != 0: + y[0] /= h[0, 0] + + x += y @ v[:col+1, :] + + r = b - matvec(x) + rnorm = np.linalg.norm(r) + + # Legacy exit + if callback_type == 'legacy' and inner_iter == maxiter: + return postprocess(x), 0 if rnorm <= atol else maxiter + + if callback_type == 'x': + callback(x) + + if rnorm <= atol: + break + elif breakdown: + # Reached breakdown (= exact solution), but the external + # tolerance check failed. Bail out with failure. + break + elif presid <= ptol: + # Inner loop passed but outer didn't + ptol_max_factor = max(eps, 0.25 * ptol_max_factor) + else: + ptol_max_factor = min(1.0, 1.5 * ptol_max_factor) + + ptol = presid * min(ptol_max_factor, atol / rnorm) + + info = 0 if (rnorm <= atol) else maxiter + return postprocess(x), info + + +def qmr(A, b, x0=None, *, rtol=1e-5, atol=0., maxiter=None, M1=None, M2=None, + callback=None): + """Use Quasi-Minimal Residual iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real-valued N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + atol, rtol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``atol=0.`` and ``rtol=1e-5``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M1 : {sparse matrix, ndarray, LinearOperator} + Left preconditioner for A. + M2 : {sparse matrix, ndarray, LinearOperator} + Right preconditioner for A. Used together with the left + preconditioner M1. The matrix M1@A@M2 should have better + conditioned than A alone. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : parameter breakdown + + See Also + -------- + LinearOperator + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import qmr + >>> A = csc_matrix([[3., 2., 0.], [1., -1., 0.], [0., 5., 1.]]) + >>> b = np.array([2., 4., -1.]) + >>> x, exitCode = qmr(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + A_ = A + A, M, x, b, postprocess = make_system(A, None, x0, b) + bnrm2 = np.linalg.norm(b) + + atol, _ = _get_atol_rtol('qmr', bnrm2, atol, rtol) + + if bnrm2 == 0: + return postprocess(b), 0 + + if M1 is None and M2 is None: + if hasattr(A_, 'psolve'): + def left_psolve(b): + return A_.psolve(b, 'left') + + def right_psolve(b): + return A_.psolve(b, 'right') + + def left_rpsolve(b): + return A_.rpsolve(b, 'left') + + def right_rpsolve(b): + return A_.rpsolve(b, 'right') + M1 = LinearOperator(A.shape, + matvec=left_psolve, + rmatvec=left_rpsolve) + M2 = LinearOperator(A.shape, + matvec=right_psolve, + rmatvec=right_rpsolve) + else: + def id(b): + return b + M1 = LinearOperator(A.shape, matvec=id, rmatvec=id) + M2 = LinearOperator(A.shape, matvec=id, rmatvec=id) + + n = len(b) + if maxiter is None: + maxiter = n*10 + + dotprod = np.vdot if np.iscomplexobj(x) else np.dot + + rhotol = np.finfo(x.dtype.char).eps + betatol = rhotol + gammatol = rhotol + deltatol = rhotol + epsilontol = rhotol + xitol = rhotol + + r = b - A.matvec(x) if x.any() else b.copy() + + vtilde = r.copy() + y = M1.matvec(vtilde) + rho = np.linalg.norm(y) + wtilde = r.copy() + z = M2.rmatvec(wtilde) + xi = np.linalg.norm(z) + gamma, eta, theta = 1, -1, 0 + v = np.empty_like(vtilde) + w = np.empty_like(wtilde) + + # Dummy values to initialize vars, silence linter warnings + epsilon, q, d, p, s = None, None, None, None, None + + for iteration in range(maxiter): + if np.linalg.norm(r) < atol: # Are we done? + return postprocess(x), 0 + if np.abs(rho) < rhotol: # rho breakdown + return postprocess(x), -10 + if np.abs(xi) < xitol: # xi breakdown + return postprocess(x), -15 + + v[:] = vtilde[:] + v *= (1 / rho) + y *= (1 / rho) + w[:] = wtilde[:] + w *= (1 / xi) + z *= (1 / xi) + delta = dotprod(z, y) + + if np.abs(delta) < deltatol: # delta breakdown + return postprocess(x), -13 + + ytilde = M2.matvec(y) + ztilde = M1.rmatvec(z) + + if iteration > 0: + ytilde -= (xi * delta / epsilon) * p + p[:] = ytilde[:] + ztilde -= (rho * (delta / epsilon).conj()) * q + q[:] = ztilde[:] + else: # First spin + p = ytilde.copy() + q = ztilde.copy() + + ptilde = A.matvec(p) + epsilon = dotprod(q, ptilde) + if np.abs(epsilon) < epsilontol: # epsilon breakdown + return postprocess(x), -14 + + beta = epsilon / delta + if np.abs(beta) < betatol: # beta breakdown + return postprocess(x), -11 + + vtilde[:] = ptilde[:] + vtilde -= beta*v + y = M1.matvec(vtilde) + + rho_prev = rho + rho = np.linalg.norm(y) + wtilde[:] = w[:] + wtilde *= - beta.conj() + wtilde += A.rmatvec(q) + z = M2.rmatvec(wtilde) + xi = np.linalg.norm(z) + gamma_prev = gamma + theta_prev = theta + theta = rho / (gamma_prev * np.abs(beta)) + gamma = 1 / np.sqrt(1 + theta**2) + + if np.abs(gamma) < gammatol: # gamma breakdown + return postprocess(x), -12 + + eta *= -(rho_prev / beta) * (gamma / gamma_prev)**2 + + if iteration > 0: + d *= (theta_prev * gamma) ** 2 + d += eta*p + s *= (theta_prev * gamma) ** 2 + s += eta*ptilde + else: + d = p.copy() + d *= eta + s = ptilde.copy() + s *= eta + + x += d + r -= s + + if callback: + callback(x) + + else: # for loop exhausted + # Return incomplete progress + return postprocess(x), maxiter diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lgmres.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lgmres.py new file mode 100644 index 0000000000000000000000000000000000000000..3e105f5283a60ffe657c930b5b71b8783f311f94 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lgmres.py @@ -0,0 +1,230 @@ +# Copyright (C) 2009, Pauli Virtanen +# Distributed under the same license as SciPy. + +import numpy as np +from numpy.linalg import LinAlgError +from scipy.linalg import get_blas_funcs +from .iterative import _get_atol_rtol +from .utils import make_system + +from ._gcrotmk import _fgmres + +__all__ = ['lgmres'] + + +def lgmres(A, b, x0=None, *, rtol=1e-5, atol=0., maxiter=1000, M=None, callback=None, + inner_m=30, outer_k=3, outer_v=None, store_outer_Av=True, + prepend_outer_v=False): + """ + Solve a matrix equation using the LGMRES algorithm. + + The LGMRES algorithm [1]_ [2]_ is designed to avoid some problems + in the convergence in restarted GMRES, and often converges in fewer + iterations. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : ndarray + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``rtol=1e-5``, the default for ``atol`` is ``0.0``. + maxiter : int, optional + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator}, optional + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function, optional + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + inner_m : int, optional + Number of inner GMRES iterations per each outer iteration. + outer_k : int, optional + Number of vectors to carry between inner GMRES iterations. + According to [1]_, good values are in the range of 1...3. + However, note that if you want to use the additional vectors to + accelerate solving multiple similar problems, larger values may + be beneficial. + outer_v : list of tuples, optional + List containing tuples ``(v, Av)`` of vectors and corresponding + matrix-vector products, used to augment the Krylov subspace, and + carried between inner GMRES iterations. The element ``Av`` can + be `None` if the matrix-vector product should be re-evaluated. + This parameter is modified in-place by `lgmres`, and can be used + to pass "guess" vectors in and out of the algorithm when solving + similar problems. + store_outer_Av : bool, optional + Whether LGMRES should store also A@v in addition to vectors `v` + in the `outer_v` list. Default is True. + prepend_outer_v : bool, optional + Whether to put outer_v augmentation vectors before Krylov iterates. + In standard LGMRES, prepend_outer_v=False. + + Returns + ------- + x : ndarray + The converged solution. + info : int + Provides convergence information: + + - 0 : successful exit + - >0 : convergence to tolerance not achieved, number of iterations + - <0 : illegal input or breakdown + + Notes + ----- + The LGMRES algorithm [1]_ [2]_ is designed to avoid the + slowing of convergence in restarted GMRES, due to alternating + residual vectors. Typically, it often outperforms GMRES(m) of + comparable memory requirements by some measure, or at least is not + much worse. + + Another advantage in this algorithm is that you can supply it with + 'guess' vectors in the `outer_v` argument that augment the Krylov + subspace. If the solution lies close to the span of these vectors, + the algorithm converges faster. This can be useful if several very + similar matrices need to be inverted one after another, such as in + Newton-Krylov iteration where the Jacobian matrix often changes + little in the nonlinear steps. + + References + ---------- + .. [1] A.H. Baker and E.R. Jessup and T. Manteuffel, "A Technique for + Accelerating the Convergence of Restarted GMRES", SIAM J. Matrix + Anal. Appl. 26, 962 (2005). + .. [2] A.H. Baker, "On Improving the Performance of the Linear Solver + restarted GMRES", PhD thesis, University of Colorado (2003). + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import lgmres + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = lgmres(A, b, atol=1e-5) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + A,M,x,b,postprocess = make_system(A,M,x0,b) + + if not np.isfinite(b).all(): + raise ValueError("RHS must contain only finite numbers") + + matvec = A.matvec + psolve = M.matvec + + if outer_v is None: + outer_v = [] + + axpy, dot, scal = None, None, None + nrm2 = get_blas_funcs('nrm2', [b]) + + b_norm = nrm2(b) + + # we call this to get the right atol/rtol and raise errors as necessary + atol, rtol = _get_atol_rtol('lgmres', b_norm, atol, rtol) + + if b_norm == 0: + x = b + return (postprocess(x), 0) + + ptol_max_factor = 1.0 + + for k_outer in range(maxiter): + r_outer = matvec(x) - b + + # -- callback + if callback is not None: + callback(x) + + # -- determine input type routines + if axpy is None: + if np.iscomplexobj(r_outer) and not np.iscomplexobj(x): + x = x.astype(r_outer.dtype) + axpy, dot, scal, nrm2 = get_blas_funcs(['axpy', 'dot', 'scal', 'nrm2'], + (x, r_outer)) + + # -- check stopping condition + r_norm = nrm2(r_outer) + if r_norm <= max(atol, rtol * b_norm): + break + + # -- inner LGMRES iteration + v0 = -psolve(r_outer) + inner_res_0 = nrm2(v0) + + if inner_res_0 == 0: + rnorm = nrm2(r_outer) + raise RuntimeError("Preconditioner returned a zero vector; " + "|v| ~ %.1g, |M v| = 0" % rnorm) + + v0 = scal(1.0/inner_res_0, v0) + + ptol = min(ptol_max_factor, max(atol, rtol*b_norm)/r_norm) + + try: + Q, R, B, vs, zs, y, pres = _fgmres(matvec, + v0, + inner_m, + lpsolve=psolve, + atol=ptol, + outer_v=outer_v, + prepend_outer_v=prepend_outer_v) + y *= inner_res_0 + if not np.isfinite(y).all(): + # Overflow etc. in computation. There's no way to + # recover from this, so we have to bail out. + raise LinAlgError() + except LinAlgError: + # Floating point over/underflow, non-finite result from + # matmul etc. -- report failure. + return postprocess(x), k_outer + 1 + + # Inner loop tolerance control + if pres > ptol: + ptol_max_factor = min(1.0, 1.5 * ptol_max_factor) + else: + ptol_max_factor = max(1e-16, 0.25 * ptol_max_factor) + + # -- GMRES terminated: eval solution + dx = zs[0]*y[0] + for w, yc in zip(zs[1:], y[1:]): + dx = axpy(w, dx, dx.shape[0], yc) # dx += w*yc + + # -- Store LGMRES augmentation vectors + nx = nrm2(dx) + if nx > 0: + if store_outer_Av: + q = Q.dot(R.dot(y)) + ax = vs[0]*q[0] + for v, qc in zip(vs[1:], q[1:]): + ax = axpy(v, ax, ax.shape[0], qc) + outer_v.append((dx/nx, ax/nx)) + else: + outer_v.append((dx/nx, None)) + + # -- Retain only a finite number of augmentation vectors + while len(outer_v) > outer_k: + del outer_v[0] + + # -- Apply step + x += dx + else: + # didn't converge ... + return postprocess(x), maxiter + + return postprocess(x), 0 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lsmr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lsmr.py new file mode 100644 index 0000000000000000000000000000000000000000..e9dd114a78b558742a4cea0ec8847378607f940d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lsmr.py @@ -0,0 +1,486 @@ +""" +Copyright (C) 2010 David Fong and Michael Saunders + +LSMR uses an iterative method. + +07 Jun 2010: Documentation updated +03 Jun 2010: First release version in Python + +David Chin-lung Fong clfong@stanford.edu +Institute for Computational and Mathematical Engineering +Stanford University + +Michael Saunders saunders@stanford.edu +Systems Optimization Laboratory +Dept of MS&E, Stanford University. + +""" + +__all__ = ['lsmr'] + +from numpy import zeros, inf, atleast_1d, result_type +from numpy.linalg import norm +from math import sqrt +from scipy.sparse.linalg._interface import aslinearoperator + +from scipy.sparse.linalg._isolve.lsqr import _sym_ortho + + +def lsmr(A, b, damp=0.0, atol=1e-6, btol=1e-6, conlim=1e8, + maxiter=None, show=False, x0=None): + """Iterative solver for least-squares problems. + + lsmr solves the system of linear equations ``Ax = b``. If the system + is inconsistent, it solves the least-squares problem ``min ||b - Ax||_2``. + ``A`` is a rectangular matrix of dimension m-by-n, where all cases are + allowed: m = n, m > n, or m < n. ``b`` is a vector of length m. + The matrix A may be dense or sparse (usually sparse). + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + Matrix A in the linear system. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^H x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : array_like, shape (m,) + Vector ``b`` in the linear system. + damp : float + Damping factor for regularized least-squares. `lsmr` solves + the regularized least-squares problem:: + + min ||(b) - ( A )x|| + ||(0) (damp*I) ||_2 + + where damp is a scalar. If damp is None or 0, the system + is solved without regularization. Default is 0. + atol, btol : float, optional + Stopping tolerances. `lsmr` continues iterations until a + certain backward error estimate is smaller than some quantity + depending on atol and btol. Let ``r = b - Ax`` be the + residual vector for the current approximate solution ``x``. + If ``Ax = b`` seems to be consistent, `lsmr` terminates + when ``norm(r) <= atol * norm(A) * norm(x) + btol * norm(b)``. + Otherwise, `lsmr` terminates when ``norm(A^H r) <= + atol * norm(A) * norm(r)``. If both tolerances are 1.0e-6 (default), + the final ``norm(r)`` should be accurate to about 6 + digits. (The final ``x`` will usually have fewer correct digits, + depending on ``cond(A)`` and the size of LAMBDA.) If `atol` + or `btol` is None, a default value of 1.0e-6 will be used. + Ideally, they should be estimates of the relative error in the + entries of ``A`` and ``b`` respectively. For example, if the entries + of ``A`` have 7 correct digits, set ``atol = 1e-7``. This prevents + the algorithm from doing unnecessary work beyond the + uncertainty of the input data. + conlim : float, optional + `lsmr` terminates if an estimate of ``cond(A)`` exceeds + `conlim`. For compatible systems ``Ax = b``, conlim could be + as large as 1.0e+12 (say). For least-squares problems, + `conlim` should be less than 1.0e+8. If `conlim` is None, the + default value is 1e+8. Maximum precision can be obtained by + setting ``atol = btol = conlim = 0``, but the number of + iterations may then be excessive. Default is 1e8. + maxiter : int, optional + `lsmr` terminates if the number of iterations reaches + `maxiter`. The default is ``maxiter = min(m, n)``. For + ill-conditioned systems, a larger value of `maxiter` may be + needed. Default is False. + show : bool, optional + Print iterations logs if ``show=True``. Default is False. + x0 : array_like, shape (n,), optional + Initial guess of ``x``, if None zeros are used. Default is None. + + .. versionadded:: 1.0.0 + + Returns + ------- + x : ndarray of float + Least-square solution returned. + istop : int + istop gives the reason for stopping:: + + istop = 0 means x=0 is a solution. If x0 was given, then x=x0 is a + solution. + = 1 means x is an approximate solution to A@x = B, + according to atol and btol. + = 2 means x approximately solves the least-squares problem + according to atol. + = 3 means COND(A) seems to be greater than CONLIM. + = 4 is the same as 1 with atol = btol = eps (machine + precision) + = 5 is the same as 2 with atol = eps. + = 6 is the same as 3 with CONLIM = 1/eps. + = 7 means ITN reached maxiter before the other stopping + conditions were satisfied. + + itn : int + Number of iterations used. + normr : float + ``norm(b-Ax)`` + normar : float + ``norm(A^H (b - Ax))`` + norma : float + ``norm(A)`` + conda : float + Condition number of A. + normx : float + ``norm(x)`` + + Notes + ----- + + .. versionadded:: 0.11.0 + + References + ---------- + .. [1] D. C.-L. Fong and M. A. Saunders, + "LSMR: An iterative algorithm for sparse least-squares problems", + SIAM J. Sci. Comput., vol. 33, pp. 2950-2971, 2011. + :arxiv:`1006.0758` + .. [2] LSMR Software, https://web.stanford.edu/group/SOL/software/lsmr/ + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import lsmr + >>> A = csc_matrix([[1., 0.], [1., 1.], [0., 1.]], dtype=float) + + The first example has the trivial solution ``[0, 0]`` + + >>> b = np.array([0., 0., 0.], dtype=float) + >>> x, istop, itn, normr = lsmr(A, b)[:4] + >>> istop + 0 + >>> x + array([0., 0.]) + + The stopping code `istop=0` returned indicates that a vector of zeros was + found as a solution. The returned solution `x` indeed contains + ``[0., 0.]``. The next example has a non-trivial solution: + + >>> b = np.array([1., 0., -1.], dtype=float) + >>> x, istop, itn, normr = lsmr(A, b)[:4] + >>> istop + 1 + >>> x + array([ 1., -1.]) + >>> itn + 1 + >>> normr + 4.440892098500627e-16 + + As indicated by `istop=1`, `lsmr` found a solution obeying the tolerance + limits. The given solution ``[1., -1.]`` obviously solves the equation. The + remaining return values include information about the number of iterations + (`itn=1`) and the remaining difference of left and right side of the solved + equation. + The final example demonstrates the behavior in the case where there is no + solution for the equation: + + >>> b = np.array([1., 0.01, -1.], dtype=float) + >>> x, istop, itn, normr = lsmr(A, b)[:4] + >>> istop + 2 + >>> x + array([ 1.00333333, -0.99666667]) + >>> A.dot(x)-b + array([ 0.00333333, -0.00333333, 0.00333333]) + >>> normr + 0.005773502691896255 + + `istop` indicates that the system is inconsistent and thus `x` is rather an + approximate solution to the corresponding least-squares problem. `normr` + contains the minimal distance that was found. + """ + + A = aslinearoperator(A) + b = atleast_1d(b) + if b.ndim > 1: + b = b.squeeze() + + msg = ('The exact solution is x = 0, or x = x0, if x0 was given ', + 'Ax - b is small enough, given atol, btol ', + 'The least-squares solution is good enough, given atol ', + 'The estimate of cond(Abar) has exceeded conlim ', + 'Ax - b is small enough for this machine ', + 'The least-squares solution is good enough for this machine', + 'Cond(Abar) seems to be too large for this machine ', + 'The iteration limit has been reached ') + + hdg1 = ' itn x(1) norm r norm Ar' + hdg2 = ' compatible LS norm A cond A' + pfreq = 20 # print frequency (for repeating the heading) + pcount = 0 # print counter + + m, n = A.shape + + # stores the num of singular values + minDim = min([m, n]) + + if maxiter is None: + maxiter = minDim + + if x0 is None: + dtype = result_type(A, b, float) + else: + dtype = result_type(A, b, x0, float) + + if show: + print(' ') + print('LSMR Least-squares solution of Ax = b\n') + print(f'The matrix A has {m} rows and {n} columns') + print('damp = %20.14e\n' % (damp)) + print(f'atol = {atol:8.2e} conlim = {conlim:8.2e}\n') + print(f'btol = {btol:8.2e} maxiter = {maxiter:8g}\n') + + u = b + normb = norm(b) + if x0 is None: + x = zeros(n, dtype) + beta = normb.copy() + else: + x = atleast_1d(x0.copy()) + u = u - A.matvec(x) + beta = norm(u) + + if beta > 0: + u = (1 / beta) * u + v = A.rmatvec(u) + alpha = norm(v) + else: + v = zeros(n, dtype) + alpha = 0 + + if alpha > 0: + v = (1 / alpha) * v + + # Initialize variables for 1st iteration. + + itn = 0 + zetabar = alpha * beta + alphabar = alpha + rho = 1 + rhobar = 1 + cbar = 1 + sbar = 0 + + h = v.copy() + hbar = zeros(n, dtype) + + # Initialize variables for estimation of ||r||. + + betadd = beta + betad = 0 + rhodold = 1 + tautildeold = 0 + thetatilde = 0 + zeta = 0 + d = 0 + + # Initialize variables for estimation of ||A|| and cond(A) + + normA2 = alpha * alpha + maxrbar = 0 + minrbar = 1e+100 + normA = sqrt(normA2) + condA = 1 + normx = 0 + + # Items for use in stopping rules, normb set earlier + istop = 0 + ctol = 0 + if conlim > 0: + ctol = 1 / conlim + normr = beta + + # Reverse the order here from the original matlab code because + # there was an error on return when arnorm==0 + normar = alpha * beta + if normar == 0: + if show: + print(msg[0]) + return x, istop, itn, normr, normar, normA, condA, normx + + if normb == 0: + x[()] = 0 + return x, istop, itn, normr, normar, normA, condA, normx + + if show: + print(' ') + print(hdg1, hdg2) + test1 = 1 + test2 = alpha / beta + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {normr:10.3e} {normar:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + print(''.join([str1, str2, str3])) + + # Main iteration loop. + while itn < maxiter: + itn = itn + 1 + + # Perform the next step of the bidiagonalization to obtain the + # next beta, u, alpha, v. These satisfy the relations + # beta*u = A@v - alpha*u, + # alpha*v = A'@u - beta*v. + + u *= -alpha + u += A.matvec(v) + beta = norm(u) + + if beta > 0: + u *= (1 / beta) + v *= -beta + v += A.rmatvec(u) + alpha = norm(v) + if alpha > 0: + v *= (1 / alpha) + + # At this point, beta = beta_{k+1}, alpha = alpha_{k+1}. + + # Construct rotation Qhat_{k,2k+1}. + + chat, shat, alphahat = _sym_ortho(alphabar, damp) + + # Use a plane rotation (Q_i) to turn B_i to R_i + + rhoold = rho + c, s, rho = _sym_ortho(alphahat, beta) + thetanew = s*alpha + alphabar = c*alpha + + # Use a plane rotation (Qbar_i) to turn R_i^T to R_i^bar + + rhobarold = rhobar + zetaold = zeta + thetabar = sbar * rho + rhotemp = cbar * rho + cbar, sbar, rhobar = _sym_ortho(cbar * rho, thetanew) + zeta = cbar * zetabar + zetabar = - sbar * zetabar + + # Update h, h_hat, x. + + hbar *= - (thetabar * rho / (rhoold * rhobarold)) + hbar += h + x += (zeta / (rho * rhobar)) * hbar + h *= - (thetanew / rho) + h += v + + # Estimate of ||r||. + + # Apply rotation Qhat_{k,2k+1}. + betaacute = chat * betadd + betacheck = -shat * betadd + + # Apply rotation Q_{k,k+1}. + betahat = c * betaacute + betadd = -s * betaacute + + # Apply rotation Qtilde_{k-1}. + # betad = betad_{k-1} here. + + thetatildeold = thetatilde + ctildeold, stildeold, rhotildeold = _sym_ortho(rhodold, thetabar) + thetatilde = stildeold * rhobar + rhodold = ctildeold * rhobar + betad = - stildeold * betad + ctildeold * betahat + + # betad = betad_k here. + # rhodold = rhod_k here. + + tautildeold = (zetaold - thetatildeold * tautildeold) / rhotildeold + taud = (zeta - thetatilde * tautildeold) / rhodold + d = d + betacheck * betacheck + normr = sqrt(d + (betad - taud)**2 + betadd * betadd) + + # Estimate ||A||. + normA2 = normA2 + beta * beta + normA = sqrt(normA2) + normA2 = normA2 + alpha * alpha + + # Estimate cond(A). + maxrbar = max(maxrbar, rhobarold) + if itn > 1: + minrbar = min(minrbar, rhobarold) + condA = max(maxrbar, rhotemp) / min(minrbar, rhotemp) + + # Test for convergence. + + # Compute norms for convergence testing. + normar = abs(zetabar) + normx = norm(x) + + # Now use these norms to estimate certain other quantities, + # some of which will be small near a solution. + + test1 = normr / normb + if (normA * normr) != 0: + test2 = normar / (normA * normr) + else: + test2 = inf + test3 = 1 / condA + t1 = test1 / (1 + normA * normx / normb) + rtol = btol + atol * normA * normx / normb + + # The following tests guard against extremely small values of + # atol, btol or ctol. (The user may have set any or all of + # the parameters atol, btol, conlim to 0.) + # The effect is equivalent to the normAl tests using + # atol = eps, btol = eps, conlim = 1/eps. + + if itn >= maxiter: + istop = 7 + if 1 + test3 <= 1: + istop = 6 + if 1 + test2 <= 1: + istop = 5 + if 1 + t1 <= 1: + istop = 4 + + # Allow for tolerances set by the user. + + if test3 <= ctol: + istop = 3 + if test2 <= atol: + istop = 2 + if test1 <= rtol: + istop = 1 + + # See if it is time to print something. + + if show: + if (n <= 40) or (itn <= 10) or (itn >= maxiter - 10) or \ + (itn % 10 == 0) or (test3 <= 1.1 * ctol) or \ + (test2 <= 1.1 * atol) or (test1 <= 1.1 * rtol) or \ + (istop != 0): + + if pcount >= pfreq: + pcount = 0 + print(' ') + print(hdg1, hdg2) + pcount = pcount + 1 + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {normr:10.3e} {normar:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + str4 = f' {normA:8.1e} {condA:8.1e}' + print(''.join([str1, str2, str3, str4])) + + if istop > 0: + break + + # Print the stopping condition. + + if show: + print(' ') + print('LSMR finished') + print(msg[istop]) + print(f'istop ={istop:8g} normr ={normr:8.1e}') + print(f' normA ={normA:8.1e} normAr ={normar:8.1e}') + print(f'itn ={itn:8g} condA ={condA:8.1e}') + print(' normx =%8.1e' % (normx)) + print(str1, str2) + print(str3, str4) + + return x, istop, itn, normr, normar, normA, condA, normx diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lsqr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lsqr.py new file mode 100644 index 0000000000000000000000000000000000000000..010f61bc5412f96a31ef5303f09b5167556e80ce --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/lsqr.py @@ -0,0 +1,587 @@ +"""Sparse Equations and Least Squares. + +The original Fortran code was written by C. C. Paige and M. A. Saunders as +described in + +C. C. Paige and M. A. Saunders, LSQR: An algorithm for sparse linear +equations and sparse least squares, TOMS 8(1), 43--71 (1982). + +C. C. Paige and M. A. Saunders, Algorithm 583; LSQR: Sparse linear +equations and least-squares problems, TOMS 8(2), 195--209 (1982). + +It is licensed under the following BSD license: + +Copyright (c) 2006, Systems Optimization Laboratory +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Stanford University nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The Fortran code was translated to Python for use in CVXOPT by Jeffery +Kline with contributions by Mridul Aanjaneya and Bob Myhill. + +Adapted for SciPy by Stefan van der Walt. + +""" + +__all__ = ['lsqr'] + +import numpy as np +from math import sqrt +from scipy.sparse.linalg._interface import aslinearoperator + +eps = np.finfo(np.float64).eps + + +def _sym_ortho(a, b): + """ + Stable implementation of Givens rotation. + + Notes + ----- + The routine 'SymOrtho' was added for numerical stability. This is + recommended by S.-C. Choi in [1]_. It removes the unpleasant potential of + ``1/eps`` in some important places (see, for example text following + "Compute the next plane rotation Qk" in minres.py). + + References + ---------- + .. [1] S.-C. Choi, "Iterative Methods for Singular Linear Equations + and Least-Squares Problems", Dissertation, + http://www.stanford.edu/group/SOL/dissertations/sou-cheng-choi-thesis.pdf + + """ + if b == 0: + return np.sign(a), 0, abs(a) + elif a == 0: + return 0, np.sign(b), abs(b) + elif abs(b) > abs(a): + tau = a / b + s = np.sign(b) / sqrt(1 + tau * tau) + c = s * tau + r = b / s + else: + tau = b / a + c = np.sign(a) / sqrt(1+tau*tau) + s = c * tau + r = a / c + return c, s, r + + +def lsqr(A, b, damp=0.0, atol=1e-6, btol=1e-6, conlim=1e8, + iter_lim=None, show=False, calc_var=False, x0=None): + """Find the least-squares solution to a large, sparse, linear system + of equations. + + The function solves ``Ax = b`` or ``min ||Ax - b||^2`` or + ``min ||Ax - b||^2 + d^2 ||x - x0||^2``. + + The matrix A may be square or rectangular (over-determined or + under-determined), and may have any rank. + + :: + + 1. Unsymmetric equations -- solve Ax = b + + 2. Linear least squares -- solve Ax = b + in the least-squares sense + + 3. Damped least squares -- solve ( A )*x = ( b ) + ( damp*I ) ( damp*x0 ) + in the least-squares sense + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + Representation of an m-by-n matrix. + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` and ``A^T x`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : array_like, shape (m,) + Right-hand side vector ``b``. + damp : float + Damping coefficient. Default is 0. + atol, btol : float, optional + Stopping tolerances. `lsqr` continues iterations until a + certain backward error estimate is smaller than some quantity + depending on atol and btol. Let ``r = b - Ax`` be the + residual vector for the current approximate solution ``x``. + If ``Ax = b`` seems to be consistent, `lsqr` terminates + when ``norm(r) <= atol * norm(A) * norm(x) + btol * norm(b)``. + Otherwise, `lsqr` terminates when ``norm(A^H r) <= + atol * norm(A) * norm(r)``. If both tolerances are 1.0e-6 (default), + the final ``norm(r)`` should be accurate to about 6 + digits. (The final ``x`` will usually have fewer correct digits, + depending on ``cond(A)`` and the size of LAMBDA.) If `atol` + or `btol` is None, a default value of 1.0e-6 will be used. + Ideally, they should be estimates of the relative error in the + entries of ``A`` and ``b`` respectively. For example, if the entries + of ``A`` have 7 correct digits, set ``atol = 1e-7``. This prevents + the algorithm from doing unnecessary work beyond the + uncertainty of the input data. + conlim : float, optional + Another stopping tolerance. lsqr terminates if an estimate of + ``cond(A)`` exceeds `conlim`. For compatible systems ``Ax = + b``, `conlim` could be as large as 1.0e+12 (say). For + least-squares problems, conlim should be less than 1.0e+8. + Maximum precision can be obtained by setting ``atol = btol = + conlim = zero``, but the number of iterations may then be + excessive. Default is 1e8. + iter_lim : int, optional + Explicit limitation on number of iterations (for safety). + show : bool, optional + Display an iteration log. Default is False. + calc_var : bool, optional + Whether to estimate diagonals of ``(A'A + damp^2*I)^{-1}``. + x0 : array_like, shape (n,), optional + Initial guess of x, if None zeros are used. Default is None. + + .. versionadded:: 1.0.0 + + Returns + ------- + x : ndarray of float + The final solution. + istop : int + Gives the reason for termination. + 1 means x is an approximate solution to Ax = b. + 2 means x approximately solves the least-squares problem. + itn : int + Iteration number upon termination. + r1norm : float + ``norm(r)``, where ``r = b - Ax``. + r2norm : float + ``sqrt( norm(r)^2 + damp^2 * norm(x - x0)^2 )``. Equal to `r1norm` + if ``damp == 0``. + anorm : float + Estimate of Frobenius norm of ``Abar = [[A]; [damp*I]]``. + acond : float + Estimate of ``cond(Abar)``. + arnorm : float + Estimate of ``norm(A'@r - damp^2*(x - x0))``. + xnorm : float + ``norm(x)`` + var : ndarray of float + If ``calc_var`` is True, estimates all diagonals of + ``(A'A)^{-1}`` (if ``damp == 0``) or more generally ``(A'A + + damp^2*I)^{-1}``. This is well defined if A has full column + rank or ``damp > 0``. (Not sure what var means if ``rank(A) + < n`` and ``damp = 0.``) + + Notes + ----- + LSQR uses an iterative method to approximate the solution. The + number of iterations required to reach a certain accuracy depends + strongly on the scaling of the problem. Poor scaling of the rows + or columns of A should therefore be avoided where possible. + + For example, in problem 1 the solution is unaltered by + row-scaling. If a row of A is very small or large compared to + the other rows of A, the corresponding row of ( A b ) should be + scaled up or down. + + In problems 1 and 2, the solution x is easily recovered + following column-scaling. Unless better information is known, + the nonzero columns of A should be scaled so that they all have + the same Euclidean norm (e.g., 1.0). + + In problem 3, there is no freedom to re-scale if damp is + nonzero. However, the value of damp should be assigned only + after attention has been paid to the scaling of A. + + The parameter damp is intended to help regularize + ill-conditioned systems, by preventing the true solution from + being very large. Another aid to regularization is provided by + the parameter acond, which may be used to terminate iterations + before the computed solution becomes very large. + + If some initial estimate ``x0`` is known and if ``damp == 0``, + one could proceed as follows: + + 1. Compute a residual vector ``r0 = b - A@x0``. + 2. Use LSQR to solve the system ``A@dx = r0``. + 3. Add the correction dx to obtain a final solution ``x = x0 + dx``. + + This requires that ``x0`` be available before and after the call + to LSQR. To judge the benefits, suppose LSQR takes k1 iterations + to solve A@x = b and k2 iterations to solve A@dx = r0. + If x0 is "good", norm(r0) will be smaller than norm(b). + If the same stopping tolerances atol and btol are used for each + system, k1 and k2 will be similar, but the final solution x0 + dx + should be more accurate. The only way to reduce the total work + is to use a larger stopping tolerance for the second system. + If some value btol is suitable for A@x = b, the larger value + btol*norm(b)/norm(r0) should be suitable for A@dx = r0. + + Preconditioning is another way to reduce the number of iterations. + If it is possible to solve a related system ``M@x = b`` + efficiently, where M approximates A in some helpful way (e.g. M - + A has low rank or its elements are small relative to those of A), + LSQR may converge more rapidly on the system ``A@M(inverse)@z = + b``, after which x can be recovered by solving M@x = z. + + If A is symmetric, LSQR should not be used! + + Alternatives are the symmetric conjugate-gradient method (cg) + and/or SYMMLQ. SYMMLQ is an implementation of symmetric cg that + applies to any symmetric A and will converge more rapidly than + LSQR. If A is positive definite, there are other implementations + of symmetric cg that require slightly less work per iteration than + SYMMLQ (but will take the same number of iterations). + + References + ---------- + .. [1] C. C. Paige and M. A. Saunders (1982a). + "LSQR: An algorithm for sparse linear equations and + sparse least squares", ACM TOMS 8(1), 43-71. + .. [2] C. C. Paige and M. A. Saunders (1982b). + "Algorithm 583. LSQR: Sparse linear equations and least + squares problems", ACM TOMS 8(2), 195-209. + .. [3] M. A. Saunders (1995). "Solution of sparse rectangular + systems using LSQR and CRAIG", BIT 35, 588-604. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import lsqr + >>> A = csc_matrix([[1., 0.], [1., 1.], [0., 1.]], dtype=float) + + The first example has the trivial solution ``[0, 0]`` + + >>> b = np.array([0., 0., 0.], dtype=float) + >>> x, istop, itn, normr = lsqr(A, b)[:4] + >>> istop + 0 + >>> x + array([ 0., 0.]) + + The stopping code `istop=0` returned indicates that a vector of zeros was + found as a solution. The returned solution `x` indeed contains + ``[0., 0.]``. The next example has a non-trivial solution: + + >>> b = np.array([1., 0., -1.], dtype=float) + >>> x, istop, itn, r1norm = lsqr(A, b)[:4] + >>> istop + 1 + >>> x + array([ 1., -1.]) + >>> itn + 1 + >>> r1norm + 4.440892098500627e-16 + + As indicated by `istop=1`, `lsqr` found a solution obeying the tolerance + limits. The given solution ``[1., -1.]`` obviously solves the equation. The + remaining return values include information about the number of iterations + (`itn=1`) and the remaining difference of left and right side of the solved + equation. + The final example demonstrates the behavior in the case where there is no + solution for the equation: + + >>> b = np.array([1., 0.01, -1.], dtype=float) + >>> x, istop, itn, r1norm = lsqr(A, b)[:4] + >>> istop + 2 + >>> x + array([ 1.00333333, -0.99666667]) + >>> A.dot(x)-b + array([ 0.00333333, -0.00333333, 0.00333333]) + >>> r1norm + 0.005773502691896255 + + `istop` indicates that the system is inconsistent and thus `x` is rather an + approximate solution to the corresponding least-squares problem. `r1norm` + contains the norm of the minimal residual that was found. + """ + A = aslinearoperator(A) + b = np.atleast_1d(b) + if b.ndim > 1: + b = b.squeeze() + + m, n = A.shape + if iter_lim is None: + iter_lim = 2 * n + var = np.zeros(n) + + msg = ('The exact solution is x = 0 ', + 'Ax - b is small enough, given atol, btol ', + 'The least-squares solution is good enough, given atol ', + 'The estimate of cond(Abar) has exceeded conlim ', + 'Ax - b is small enough for this machine ', + 'The least-squares solution is good enough for this machine', + 'Cond(Abar) seems to be too large for this machine ', + 'The iteration limit has been reached ') + + if show: + print(' ') + print('LSQR Least-squares solution of Ax = b') + str1 = f'The matrix A has {m} rows and {n} columns' + str2 = f'damp = {damp:20.14e} calc_var = {calc_var:8g}' + str3 = f'atol = {atol:8.2e} conlim = {conlim:8.2e}' + str4 = f'btol = {btol:8.2e} iter_lim = {iter_lim:8g}' + print(str1) + print(str2) + print(str3) + print(str4) + + itn = 0 + istop = 0 + ctol = 0 + if conlim > 0: + ctol = 1/conlim + anorm = 0 + acond = 0 + dampsq = damp**2 + ddnorm = 0 + res2 = 0 + xnorm = 0 + xxnorm = 0 + z = 0 + cs2 = -1 + sn2 = 0 + + # Set up the first vectors u and v for the bidiagonalization. + # These satisfy beta*u = b - A@x, alfa*v = A'@u. + u = b + bnorm = np.linalg.norm(b) + + if x0 is None: + x = np.zeros(n) + beta = bnorm.copy() + else: + x = np.asarray(x0) + u = u - A.matvec(x) + beta = np.linalg.norm(u) + + if beta > 0: + u = (1/beta) * u + v = A.rmatvec(u) + alfa = np.linalg.norm(v) + else: + v = x.copy() + alfa = 0 + + if alfa > 0: + v = (1/alfa) * v + w = v.copy() + + rhobar = alfa + phibar = beta + rnorm = beta + r1norm = rnorm + r2norm = rnorm + + # Reverse the order here from the original matlab code because + # there was an error on return when arnorm==0 + arnorm = alfa * beta + if arnorm == 0: + if show: + print(msg[0]) + return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var + + head1 = ' Itn x[0] r1norm r2norm ' + head2 = ' Compatible LS Norm A Cond A' + + if show: + print(' ') + print(head1, head2) + test1 = 1 + test2 = alfa / beta + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {r1norm:10.3e} {r2norm:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + print(str1, str2, str3) + + # Main iteration loop. + while itn < iter_lim: + itn = itn + 1 + # Perform the next step of the bidiagonalization to obtain the + # next beta, u, alfa, v. These satisfy the relations + # beta*u = a@v - alfa*u, + # alfa*v = A'@u - beta*v. + u = A.matvec(v) - alfa * u + beta = np.linalg.norm(u) + + if beta > 0: + u = (1/beta) * u + anorm = sqrt(anorm**2 + alfa**2 + beta**2 + dampsq) + v = A.rmatvec(u) - beta * v + alfa = np.linalg.norm(v) + if alfa > 0: + v = (1 / alfa) * v + + # Use a plane rotation to eliminate the damping parameter. + # This alters the diagonal (rhobar) of the lower-bidiagonal matrix. + if damp > 0: + rhobar1 = sqrt(rhobar**2 + dampsq) + cs1 = rhobar / rhobar1 + sn1 = damp / rhobar1 + psi = sn1 * phibar + phibar = cs1 * phibar + else: + # cs1 = 1 and sn1 = 0 + rhobar1 = rhobar + psi = 0. + + # Use a plane rotation to eliminate the subdiagonal element (beta) + # of the lower-bidiagonal matrix, giving an upper-bidiagonal matrix. + cs, sn, rho = _sym_ortho(rhobar1, beta) + + theta = sn * alfa + rhobar = -cs * alfa + phi = cs * phibar + phibar = sn * phibar + tau = sn * phi + + # Update x and w. + t1 = phi / rho + t2 = -theta / rho + dk = (1 / rho) * w + + x = x + t1 * w + w = v + t2 * w + ddnorm = ddnorm + np.linalg.norm(dk)**2 + + if calc_var: + var = var + dk**2 + + # Use a plane rotation on the right to eliminate the + # super-diagonal element (theta) of the upper-bidiagonal matrix. + # Then use the result to estimate norm(x). + delta = sn2 * rho + gambar = -cs2 * rho + rhs = phi - delta * z + zbar = rhs / gambar + xnorm = sqrt(xxnorm + zbar**2) + gamma = sqrt(gambar**2 + theta**2) + cs2 = gambar / gamma + sn2 = theta / gamma + z = rhs / gamma + xxnorm = xxnorm + z**2 + + # Test for convergence. + # First, estimate the condition of the matrix Abar, + # and the norms of rbar and Abar'rbar. + acond = anorm * sqrt(ddnorm) + res1 = phibar**2 + res2 = res2 + psi**2 + rnorm = sqrt(res1 + res2) + arnorm = alfa * abs(tau) + + # Distinguish between + # r1norm = ||b - Ax|| and + # r2norm = rnorm in current code + # = sqrt(r1norm^2 + damp^2*||x - x0||^2). + # Estimate r1norm from + # r1norm = sqrt(r2norm^2 - damp^2*||x - x0||^2). + # Although there is cancellation, it might be accurate enough. + if damp > 0: + r1sq = rnorm**2 - dampsq * xxnorm + r1norm = sqrt(abs(r1sq)) + if r1sq < 0: + r1norm = -r1norm + else: + r1norm = rnorm + r2norm = rnorm + + # Now use these norms to estimate certain other quantities, + # some of which will be small near a solution. + test1 = rnorm / bnorm + test2 = arnorm / (anorm * rnorm + eps) + test3 = 1 / (acond + eps) + t1 = test1 / (1 + anorm * xnorm / bnorm) + rtol = btol + atol * anorm * xnorm / bnorm + + # The following tests guard against extremely small values of + # atol, btol or ctol. (The user may have set any or all of + # the parameters atol, btol, conlim to 0.) + # The effect is equivalent to the normal tests using + # atol = eps, btol = eps, conlim = 1/eps. + if itn >= iter_lim: + istop = 7 + if 1 + test3 <= 1: + istop = 6 + if 1 + test2 <= 1: + istop = 5 + if 1 + t1 <= 1: + istop = 4 + + # Allow for tolerances set by the user. + if test3 <= ctol: + istop = 3 + if test2 <= atol: + istop = 2 + if test1 <= rtol: + istop = 1 + + if show: + # See if it is time to print something. + prnt = False + if n <= 40: + prnt = True + if itn <= 10: + prnt = True + if itn >= iter_lim-10: + prnt = True + # if itn%10 == 0: prnt = True + if test3 <= 2*ctol: + prnt = True + if test2 <= 10*atol: + prnt = True + if test1 <= 10*rtol: + prnt = True + if istop != 0: + prnt = True + + if prnt: + str1 = f'{itn:6g} {x[0]:12.5e}' + str2 = f' {r1norm:10.3e} {r2norm:10.3e}' + str3 = f' {test1:8.1e} {test2:8.1e}' + str4 = f' {anorm:8.1e} {acond:8.1e}' + print(str1, str2, str3, str4) + + if istop != 0: + break + + # End of iteration loop. + # Print the stopping condition. + if show: + print(' ') + print('LSQR finished') + print(msg[istop]) + print(' ') + str1 = f'istop ={istop:8g} r1norm ={r1norm:8.1e}' + str2 = f'anorm ={anorm:8.1e} arnorm ={arnorm:8.1e}' + str3 = f'itn ={itn:8g} r2norm ={r2norm:8.1e}' + str4 = f'acond ={acond:8.1e} xnorm ={xnorm:8.1e}' + print(str1 + ' ' + str2) + print(str3 + ' ' + str4) + print(' ') + + return x, istop, itn, r1norm, r2norm, anorm, acond, arnorm, xnorm, var diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/minres.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/minres.py new file mode 100644 index 0000000000000000000000000000000000000000..4efb992ba921d79b4e3753d92f2ed82d149bc220 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/minres.py @@ -0,0 +1,372 @@ +from numpy import inner, zeros, inf, finfo +from numpy.linalg import norm +from math import sqrt + +from .utils import make_system + +__all__ = ['minres'] + + +def minres(A, b, x0=None, *, rtol=1e-5, shift=0.0, maxiter=None, + M=None, callback=None, show=False, check=False): + """ + Use MINimum RESidual iteration to solve Ax=b + + MINRES minimizes norm(Ax - b) for a real symmetric matrix A. Unlike + the Conjugate Gradient method, A can be indefinite or singular. + + If shift != 0 then the method solves (A - shift*I)x = b + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real symmetric N-by-N matrix of the linear system + Alternatively, ``A`` can be a linear operator which can + produce ``Ax`` using, e.g., + ``scipy.sparse.linalg.LinearOperator``. + b : ndarray + Right hand side of the linear system. Has shape (N,) or (N,1). + + Returns + ------- + x : ndarray + The converged solution. + info : integer + Provides convergence information: + 0 : successful exit + >0 : convergence to tolerance not achieved, number of iterations + <0 : illegal input or breakdown + + Other Parameters + ---------------- + x0 : ndarray + Starting guess for the solution. + shift : float + Value to apply to the system ``(A - shift * I)x = b``. Default is 0. + rtol : float + Tolerance to achieve. The algorithm terminates when the relative + residual is below ``rtol``. + maxiter : integer + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + M : {sparse matrix, ndarray, LinearOperator} + Preconditioner for A. The preconditioner should approximate the + inverse of A. Effective preconditioning dramatically improves the + rate of convergence, which implies that fewer iterations are needed + to reach a given error tolerance. + callback : function + User-supplied function to call after each iteration. It is called + as callback(xk), where xk is the current solution vector. + show : bool + If ``True``, print out a summary and metrics related to the solution + during iterations. Default is ``False``. + check : bool + If ``True``, run additional input validation to check that `A` and + `M` (if specified) are symmetric. Default is ``False``. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import minres + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> A = A + A.T + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = minres(A, b) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + + References + ---------- + Solution of sparse indefinite systems of linear equations, + C. C. Paige and M. A. Saunders (1975), + SIAM J. Numer. Anal. 12(4), pp. 617-629. + https://web.stanford.edu/group/SOL/software/minres/ + + This file is a translation of the following MATLAB implementation: + https://web.stanford.edu/group/SOL/software/minres/minres-matlab.zip + + """ + A, M, x, b, postprocess = make_system(A, M, x0, b) + + matvec = A.matvec + psolve = M.matvec + + first = 'Enter minres. ' + last = 'Exit minres. ' + + n = A.shape[0] + + if maxiter is None: + maxiter = 5 * n + + msg = [' beta2 = 0. If M = I, b and x are eigenvectors ', # -1 + ' beta1 = 0. The exact solution is x0 ', # 0 + ' A solution to Ax = b was found, given rtol ', # 1 + ' A least-squares solution was found, given rtol ', # 2 + ' Reasonable accuracy achieved, given eps ', # 3 + ' x has converged to an eigenvector ', # 4 + ' acond has exceeded 0.1/eps ', # 5 + ' The iteration limit was reached ', # 6 + ' A does not define a symmetric matrix ', # 7 + ' M does not define a symmetric matrix ', # 8 + ' M does not define a pos-def preconditioner '] # 9 + + if show: + print(first + 'Solution of symmetric Ax = b') + print(first + f'n = {n:3g} shift = {shift:23.14e}') + print(first + f'itnlim = {maxiter:3g} rtol = {rtol:11.2e}') + print() + + istop = 0 + itn = 0 + Anorm = 0 + Acond = 0 + rnorm = 0 + ynorm = 0 + + xtype = x.dtype + + eps = finfo(xtype).eps + + # Set up y and v for the first Lanczos vector v1. + # y = beta1 P' v1, where P = C**(-1). + # v is really P' v1. + + if x0 is None: + r1 = b.copy() + else: + r1 = b - A@x + y = psolve(r1) + + beta1 = inner(r1, y) + + if beta1 < 0: + raise ValueError('indefinite preconditioner') + elif beta1 == 0: + return (postprocess(x), 0) + + bnorm = norm(b) + if bnorm == 0: + x = b + return (postprocess(x), 0) + + beta1 = sqrt(beta1) + + if check: + # are these too strict? + + # see if A is symmetric + w = matvec(y) + r2 = matvec(w) + s = inner(w,w) + t = inner(y,r2) + z = abs(s - t) + epsa = (s + eps) * eps**(1.0/3.0) + if z > epsa: + raise ValueError('non-symmetric matrix') + + # see if M is symmetric + r2 = psolve(y) + s = inner(y,y) + t = inner(r1,r2) + z = abs(s - t) + epsa = (s + eps) * eps**(1.0/3.0) + if z > epsa: + raise ValueError('non-symmetric preconditioner') + + # Initialize other quantities + oldb = 0 + beta = beta1 + dbar = 0 + epsln = 0 + qrnorm = beta1 + phibar = beta1 + rhs1 = beta1 + rhs2 = 0 + tnorm2 = 0 + gmax = 0 + gmin = finfo(xtype).max + cs = -1 + sn = 0 + w = zeros(n, dtype=xtype) + w2 = zeros(n, dtype=xtype) + r2 = r1 + + if show: + print() + print() + print(' Itn x(1) Compatible LS norm(A) cond(A) gbar/|A|') + + while itn < maxiter: + itn += 1 + + s = 1.0/beta + v = s*y + + y = matvec(v) + y = y - shift * v + + if itn >= 2: + y = y - (beta/oldb)*r1 + + alfa = inner(v,y) + y = y - (alfa/beta)*r2 + r1 = r2 + r2 = y + y = psolve(r2) + oldb = beta + beta = inner(r2,y) + if beta < 0: + raise ValueError('non-symmetric matrix') + beta = sqrt(beta) + tnorm2 += alfa**2 + oldb**2 + beta**2 + + if itn == 1: + if beta/beta1 <= 10*eps: + istop = -1 # Terminate later + + # Apply previous rotation Qk-1 to get + # [deltak epslnk+1] = [cs sn][dbark 0 ] + # [gbar k dbar k+1] [sn -cs][alfak betak+1]. + + oldeps = epsln + delta = cs * dbar + sn * alfa # delta1 = 0 deltak + gbar = sn * dbar - cs * alfa # gbar 1 = alfa1 gbar k + epsln = sn * beta # epsln2 = 0 epslnk+1 + dbar = - cs * beta # dbar 2 = beta2 dbar k+1 + root = norm([gbar, dbar]) + Arnorm = phibar * root + + # Compute the next plane rotation Qk + + gamma = norm([gbar, beta]) # gammak + gamma = max(gamma, eps) + cs = gbar / gamma # ck + sn = beta / gamma # sk + phi = cs * phibar # phik + phibar = sn * phibar # phibark+1 + + # Update x. + + denom = 1.0/gamma + w1 = w2 + w2 = w + w = (v - oldeps*w1 - delta*w2) * denom + x = x + phi*w + + # Go round again. + + gmax = max(gmax, gamma) + gmin = min(gmin, gamma) + z = rhs1 / gamma + rhs1 = rhs2 - delta*z + rhs2 = - epsln*z + + # Estimate various norms and test for convergence. + + Anorm = sqrt(tnorm2) + ynorm = norm(x) + epsa = Anorm * eps + epsx = Anorm * ynorm * eps + epsr = Anorm * ynorm * rtol + diag = gbar + + if diag == 0: + diag = epsa + + qrnorm = phibar + rnorm = qrnorm + if ynorm == 0 or Anorm == 0: + test1 = inf + else: + test1 = rnorm / (Anorm*ynorm) # ||r|| / (||A|| ||x||) + if Anorm == 0: + test2 = inf + else: + test2 = root / Anorm # ||Ar|| / (||A|| ||r||) + + # Estimate cond(A). + # In this version we look at the diagonals of R in the + # factorization of the lower Hessenberg matrix, Q @ H = R, + # where H is the tridiagonal matrix from Lanczos with one + # extra row, beta(k+1) e_k^T. + + Acond = gmax/gmin + + # See if any of the stopping criteria are satisfied. + # In rare cases, istop is already -1 from above (Abar = const*I). + + if istop == 0: + t1 = 1 + test1 # These tests work if rtol < eps + t2 = 1 + test2 + if t2 <= 1: + istop = 2 + if t1 <= 1: + istop = 1 + + if itn >= maxiter: + istop = 6 + if Acond >= 0.1/eps: + istop = 4 + if epsx >= beta1: + istop = 3 + # if rnorm <= epsx : istop = 2 + # if rnorm <= epsr : istop = 1 + if test2 <= rtol: + istop = 2 + if test1 <= rtol: + istop = 1 + + # See if it is time to print something. + + prnt = False + if n <= 40: + prnt = True + if itn <= 10: + prnt = True + if itn >= maxiter-10: + prnt = True + if itn % 10 == 0: + prnt = True + if qrnorm <= 10*epsx: + prnt = True + if qrnorm <= 10*epsr: + prnt = True + if Acond <= 1e-2/eps: + prnt = True + if istop != 0: + prnt = True + + if show and prnt: + str1 = f'{itn:6g} {x[0]:12.5e} {test1:10.3e}' + str2 = f' {test2:10.3e}' + str3 = f' {Anorm:8.1e} {Acond:8.1e} {gbar/Anorm:8.1e}' + + print(str1 + str2 + str3) + + if itn % 10 == 0: + print() + + if callback is not None: + callback(x) + + if istop != 0: + break # TODO check this + + if show: + print() + print(last + f' istop = {istop:3g} itn ={itn:5g}') + print(last + f' Anorm = {Anorm:12.4e} Acond = {Acond:12.4e}') + print(last + f' rnorm = {rnorm:12.4e} ynorm = {ynorm:12.4e}') + print(last + f' Arnorm = {Arnorm:12.4e}') + print(last + msg[istop+1]) + + if istop == 6: + info = maxiter + else: + info = 0 + + return (postprocess(x),info) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsqr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsqr.py new file mode 100644 index 0000000000000000000000000000000000000000..c46290ac3d7af3b7571659d6219157dcd25e29c3 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/tests/test_lsqr.py @@ -0,0 +1,120 @@ +import numpy as np +from numpy.testing import assert_allclose, assert_array_equal, assert_equal +import pytest +import scipy.sparse +import scipy.sparse.linalg +from scipy.sparse.linalg import lsqr + +# Set up a test problem +n = 35 +G = np.eye(n) +normal = np.random.normal +norm = np.linalg.norm + +for jj in range(5): + gg = normal(size=n) + hh = gg * gg.T + G += (hh + hh.T) * 0.5 + G += normal(size=n) * normal(size=n) + +b = normal(size=n) + +# tolerance for atol/btol keywords of lsqr() +tol = 2e-10 +# tolerances for testing the results of the lsqr() call with assert_allclose +# These tolerances are a bit fragile - see discussion in gh-15301. +atol_test = 4e-10 +rtol_test = 2e-8 +show = False +maxit = None + + +def test_lsqr_basic(): + b_copy = b.copy() + xo, *_ = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit) + assert_array_equal(b_copy, b) + + svx = np.linalg.solve(G, b) + assert_allclose(xo, svx, atol=atol_test, rtol=rtol_test) + + # Now the same but with damp > 0. + # This is equivalent to solving the extended system: + # ( G ) @ x = ( b ) + # ( damp*I ) ( 0 ) + damp = 1.5 + xo, *_ = lsqr( + G, b, damp=damp, show=show, atol=tol, btol=tol, iter_lim=maxit) + + Gext = np.r_[G, damp * np.eye(G.shape[1])] + bext = np.r_[b, np.zeros(G.shape[1])] + svx, *_ = np.linalg.lstsq(Gext, bext, rcond=None) + assert_allclose(xo, svx, atol=atol_test, rtol=rtol_test) + + +def test_gh_2466(): + row = np.array([0, 0]) + col = np.array([0, 1]) + val = np.array([1, -1]) + A = scipy.sparse.coo_matrix((val, (row, col)), shape=(1, 2)) + b = np.asarray([4]) + lsqr(A, b) + + +def test_well_conditioned_problems(): + # Test that sparse the lsqr solver returns the right solution + # on various problems with different random seeds. + # This is a non-regression test for a potential ZeroDivisionError + # raised when computing the `test2` & `test3` convergence conditions. + n = 10 + A_sparse = scipy.sparse.eye(n, n) + A_dense = A_sparse.toarray() + + with np.errstate(invalid='raise'): + for seed in range(30): + rng = np.random.RandomState(seed + 10) + beta = rng.rand(n) + beta[beta == 0] = 0.00001 # ensure that all the betas are not null + b = A_sparse @ beta[:, np.newaxis] + output = lsqr(A_sparse, b, show=show) + + # Check that the termination condition corresponds to an approximate + # solution to Ax = b + assert_equal(output[1], 1) + solution = output[0] + + # Check that we recover the ground truth solution + assert_allclose(solution, beta) + + # Sanity check: compare to the dense array solver + reference_solution = np.linalg.solve(A_dense, b).ravel() + assert_allclose(solution, reference_solution) + + +def test_b_shapes(): + # Test b being a scalar. + A = np.array([[1.0, 2.0]]) + b = 3.0 + x = lsqr(A, b)[0] + assert norm(A.dot(x) - b) == pytest.approx(0) + + # Test b being a column vector. + A = np.eye(10) + b = np.ones((10, 1)) + x = lsqr(A, b)[0] + assert norm(A.dot(x) - b.ravel()) == pytest.approx(0) + + +def test_initialization(): + # Test the default setting is the same as zeros + b_copy = b.copy() + x_ref = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit) + x0 = np.zeros(x_ref[0].shape) + x = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit, x0=x0) + assert_array_equal(b_copy, b) + assert_allclose(x_ref[0], x[0]) + + # Test warm-start with single iteration + x0 = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=1)[0] + x = lsqr(G, b, show=show, atol=tol, btol=tol, iter_lim=maxit, x0=x0) + assert_allclose(x_ref[0], x[0]) + assert_array_equal(b_copy, b) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/tfqmr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/tfqmr.py new file mode 100644 index 0000000000000000000000000000000000000000..2966dc7bcc8196c07c1d6363e2eabfebcce06856 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/tfqmr.py @@ -0,0 +1,179 @@ +import numpy as np +from .iterative import _get_atol_rtol +from .utils import make_system + + +__all__ = ['tfqmr'] + + +def tfqmr(A, b, x0=None, *, rtol=1e-5, atol=0., maxiter=None, M=None, + callback=None, show=False): + """ + Use Transpose-Free Quasi-Minimal Residual iteration to solve ``Ax = b``. + + Parameters + ---------- + A : {sparse matrix, ndarray, LinearOperator} + The real or complex N-by-N matrix of the linear system. + Alternatively, `A` can be a linear operator which can + produce ``Ax`` using, e.g., + `scipy.sparse.linalg.LinearOperator`. + b : {ndarray} + Right hand side of the linear system. Has shape (N,) or (N,1). + x0 : {ndarray} + Starting guess for the solution. + rtol, atol : float, optional + Parameters for the convergence test. For convergence, + ``norm(b - A @ x) <= max(rtol*norm(b), atol)`` should be satisfied. + The default is ``rtol=1e-5``, the default for ``atol`` is ``0.0``. + maxiter : int, optional + Maximum number of iterations. Iteration will stop after maxiter + steps even if the specified tolerance has not been achieved. + Default is ``min(10000, ndofs * 10)``, where ``ndofs = A.shape[0]``. + M : {sparse matrix, ndarray, LinearOperator} + Inverse of the preconditioner of A. M should approximate the + inverse of A and be easy to solve for (see Notes). Effective + preconditioning dramatically improves the rate of convergence, + which implies that fewer iterations are needed to reach a given + error tolerance. By default, no preconditioner is used. + callback : function, optional + User-supplied function to call after each iteration. It is called + as `callback(xk)`, where `xk` is the current solution vector. + show : bool, optional + Specify ``show = True`` to show the convergence, ``show = False`` is + to close the output of the convergence. + Default is `False`. + + Returns + ------- + x : ndarray + The converged solution. + info : int + Provides convergence information: + + - 0 : successful exit + - >0 : convergence to tolerance not achieved, number of iterations + - <0 : illegal input or breakdown + + Notes + ----- + The Transpose-Free QMR algorithm is derived from the CGS algorithm. + However, unlike CGS, the convergence curves for the TFQMR method is + smoothed by computing a quasi minimization of the residual norm. The + implementation supports left preconditioner, and the "residual norm" + to compute in convergence criterion is actually an upper bound on the + actual residual norm ``||b - Axk||``. + + References + ---------- + .. [1] R. W. Freund, A Transpose-Free Quasi-Minimal Residual Algorithm for + Non-Hermitian Linear Systems, SIAM J. Sci. Comput., 14(2), 470-482, + 1993. + .. [2] Y. Saad, Iterative Methods for Sparse Linear Systems, 2nd edition, + SIAM, Philadelphia, 2003. + .. [3] C. T. Kelley, Iterative Methods for Linear and Nonlinear Equations, + number 16 in Frontiers in Applied Mathematics, SIAM, Philadelphia, + 1995. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import tfqmr + >>> A = csc_matrix([[3, 2, 0], [1, -1, 0], [0, 5, 1]], dtype=float) + >>> b = np.array([2, 4, -1], dtype=float) + >>> x, exitCode = tfqmr(A, b, atol=0.0) + >>> print(exitCode) # 0 indicates successful convergence + 0 + >>> np.allclose(A.dot(x), b) + True + """ + + # Check data type + dtype = A.dtype + if np.issubdtype(dtype, np.int64): + dtype = float + A = A.astype(dtype) + if np.issubdtype(b.dtype, np.int64): + b = b.astype(dtype) + + A, M, x, b, postprocess = make_system(A, M, x0, b) + + # Check if the R.H.S is a zero vector + if np.linalg.norm(b) == 0.: + x = b.copy() + return (postprocess(x), 0) + + ndofs = A.shape[0] + if maxiter is None: + maxiter = min(10000, ndofs * 10) + + if x0 is None: + r = b.copy() + else: + r = b - A.matvec(x) + u = r + w = r.copy() + # Take rstar as b - Ax0, that is rstar := r = b - Ax0 mathematically + rstar = r + v = M.matvec(A.matvec(r)) + uhat = v + d = theta = eta = 0. + # at this point we know rstar == r, so rho is always real + rho = np.inner(rstar.conjugate(), r).real + rhoLast = rho + r0norm = np.sqrt(rho) + tau = r0norm + if r0norm == 0: + return (postprocess(x), 0) + + # we call this to get the right atol and raise errors as necessary + atol, _ = _get_atol_rtol('tfqmr', r0norm, atol, rtol) + + for iter in range(maxiter): + even = iter % 2 == 0 + if (even): + vtrstar = np.inner(rstar.conjugate(), v) + # Check breakdown + if vtrstar == 0.: + return (postprocess(x), -1) + alpha = rho / vtrstar + uNext = u - alpha * v # [1]-(5.6) + w -= alpha * uhat # [1]-(5.8) + d = u + (theta**2 / alpha) * eta * d # [1]-(5.5) + # [1]-(5.2) + theta = np.linalg.norm(w) / tau + c = np.sqrt(1. / (1 + theta**2)) + tau *= theta * c + # Calculate step and direction [1]-(5.4) + eta = (c**2) * alpha + z = M.matvec(d) + x += eta * z + + if callback is not None: + callback(x) + + # Convergence criterion + if tau * np.sqrt(iter+1) < atol: + if (show): + print("TFQMR: Linear solve converged due to reach TOL " + f"iterations {iter+1}") + return (postprocess(x), 0) + + if (not even): + # [1]-(5.7) + rho = np.inner(rstar.conjugate(), w) + beta = rho / rhoLast + u = w + beta * u + v = beta * uhat + (beta**2) * v + uhat = M.matvec(A.matvec(u)) + v += uhat + else: + uhat = M.matvec(A.matvec(uNext)) + u = uNext + rhoLast = rho + + if (show): + print("TFQMR: Linear solve not converged due to reach MAXIT " + f"iterations {iter+1}") + return (postprocess(x), maxiter) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/utils.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..80f37fc1cf63fa0352fd93d62be758f87c065db5 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_isolve/utils.py @@ -0,0 +1,127 @@ +__docformat__ = "restructuredtext en" + +__all__ = [] + + +from numpy import asanyarray, asarray, array, zeros + +from scipy.sparse.linalg._interface import aslinearoperator, LinearOperator, \ + IdentityOperator + +_coerce_rules = {('f','f'):'f', ('f','d'):'d', ('f','F'):'F', + ('f','D'):'D', ('d','f'):'d', ('d','d'):'d', + ('d','F'):'D', ('d','D'):'D', ('F','f'):'F', + ('F','d'):'D', ('F','F'):'F', ('F','D'):'D', + ('D','f'):'D', ('D','d'):'D', ('D','F'):'D', + ('D','D'):'D'} + + +def coerce(x,y): + if x not in 'fdFD': + x = 'd' + if y not in 'fdFD': + y = 'd' + return _coerce_rules[x,y] + + +def id(x): + return x + + +def make_system(A, M, x0, b): + """Make a linear system Ax=b + + Parameters + ---------- + A : LinearOperator + sparse or dense matrix (or any valid input to aslinearoperator) + M : {LinearOperator, Nones} + preconditioner + sparse or dense matrix (or any valid input to aslinearoperator) + x0 : {array_like, str, None} + initial guess to iterative method. + ``x0 = 'Mb'`` means using the nonzero initial guess ``M @ b``. + Default is `None`, which means using the zero initial guess. + b : array_like + right hand side + + Returns + ------- + (A, M, x, b, postprocess) + A : LinearOperator + matrix of the linear system + M : LinearOperator + preconditioner + x : rank 1 ndarray + initial guess + b : rank 1 ndarray + right hand side + postprocess : function + converts the solution vector to the appropriate + type and dimensions (e.g. (N,1) matrix) + + """ + A_ = A + A = aslinearoperator(A) + + if A.shape[0] != A.shape[1]: + raise ValueError(f'expected square matrix, but got shape={(A.shape,)}') + + N = A.shape[0] + + b = asanyarray(b) + + if not (b.shape == (N,1) or b.shape == (N,)): + raise ValueError(f'shapes of A {A.shape} and b {b.shape} are ' + 'incompatible') + + if b.dtype.char not in 'fdFD': + b = b.astype('d') # upcast non-FP types to double + + def postprocess(x): + return x + + if hasattr(A,'dtype'): + xtype = A.dtype.char + else: + xtype = A.matvec(b).dtype.char + xtype = coerce(xtype, b.dtype.char) + + b = asarray(b,dtype=xtype) # make b the same type as x + b = b.ravel() + + # process preconditioner + if M is None: + if hasattr(A_,'psolve'): + psolve = A_.psolve + else: + psolve = id + if hasattr(A_,'rpsolve'): + rpsolve = A_.rpsolve + else: + rpsolve = id + if psolve is id and rpsolve is id: + M = IdentityOperator(shape=A.shape, dtype=A.dtype) + else: + M = LinearOperator(A.shape, matvec=psolve, rmatvec=rpsolve, + dtype=A.dtype) + else: + M = aslinearoperator(M) + if A.shape != M.shape: + raise ValueError('matrix and preconditioner have different shapes') + + # set initial guess + if x0 is None: + x = zeros(N, dtype=xtype) + elif isinstance(x0, str): + if x0 == 'Mb': # use nonzero initial guess ``M @ b`` + bCopy = b.copy() + x = M.matvec(bCopy) + else: + x = array(x0, dtype=xtype) + if not (x.shape == (N, 1) or x.shape == (N,)): + raise ValueError(f'shapes of A {A.shape} and ' + f'x0 {x.shape} are incompatible') + x = x.ravel() + + return A, M, x, b, postprocess diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_matfuncs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_matfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..1c531e25b4069a7d87c68bf2e73a15fcad45b7e6 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_matfuncs.py @@ -0,0 +1,940 @@ +""" +Sparse matrix functions +""" + +# +# Authors: Travis Oliphant, March 2002 +# Anthony Scopatz, August 2012 (Sparse Updates) +# Jake Vanderplas, August 2012 (Sparse Updates) +# + +__all__ = ['expm', 'inv', 'matrix_power'] + +import numpy as np +from scipy.linalg._basic import solve, solve_triangular + +from scipy.sparse._base import issparse +from scipy.sparse.linalg import spsolve +from scipy.sparse._sputils import is_pydata_spmatrix, isintlike + +import scipy.sparse +import scipy.sparse.linalg +from scipy.sparse.linalg._interface import LinearOperator +from scipy.sparse._construct import eye + +from ._expm_multiply import _ident_like, _exact_1_norm as _onenorm + + +UPPER_TRIANGULAR = 'upper_triangular' + + +def inv(A): + """ + Compute the inverse of a sparse matrix + + Parameters + ---------- + A : (M, M) sparse matrix + square matrix to be inverted + + Returns + ------- + Ainv : (M, M) sparse matrix + inverse of `A` + + Notes + ----- + This computes the sparse inverse of `A`. If the inverse of `A` is expected + to be non-sparse, it will likely be faster to convert `A` to dense and use + `scipy.linalg.inv`. + + Examples + -------- + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import inv + >>> A = csc_matrix([[1., 0.], [1., 2.]]) + >>> Ainv = inv(A) + >>> Ainv + + >>> A.dot(Ainv) + + >>> A.dot(Ainv).toarray() + array([[ 1., 0.], + [ 0., 1.]]) + + .. versionadded:: 0.12.0 + + """ + # Check input + if not (scipy.sparse.issparse(A) or is_pydata_spmatrix(A)): + raise TypeError('Input must be a sparse matrix') + + # Use sparse direct solver to solve "AX = I" accurately + I = _ident_like(A) + Ainv = spsolve(A, I) + return Ainv + + +def _onenorm_matrix_power_nnm(A, p): + """ + Compute the 1-norm of a non-negative integer power of a non-negative matrix. + + Parameters + ---------- + A : a square ndarray or matrix or sparse matrix + Input matrix with non-negative entries. + p : non-negative integer + The power to which the matrix is to be raised. + + Returns + ------- + out : float + The 1-norm of the matrix power p of A. + + """ + # Check input + if int(p) != p or p < 0: + raise ValueError('expected non-negative integer p') + p = int(p) + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + + # Explicitly make a column vector so that this works when A is a + # numpy matrix (in addition to ndarray and sparse matrix). + v = np.ones((A.shape[0], 1), dtype=float) + M = A.T + for i in range(p): + v = M.dot(v) + return np.max(v) + + +def _is_upper_triangular(A): + # This function could possibly be of wider interest. + if issparse(A): + lower_part = scipy.sparse.tril(A, -1) + # Check structural upper triangularity, + # then coincidental upper triangularity if needed. + return lower_part.nnz == 0 or lower_part.count_nonzero() == 0 + elif is_pydata_spmatrix(A): + import sparse + lower_part = sparse.tril(A, -1) + return lower_part.nnz == 0 + else: + return not np.tril(A, -1).any() + + +def _smart_matrix_product(A, B, alpha=None, structure=None): + """ + A matrix product that knows about sparse and structured matrices. + + Parameters + ---------- + A : 2d ndarray + First matrix. + B : 2d ndarray + Second matrix. + alpha : float + The matrix product will be scaled by this constant. + structure : str, optional + A string describing the structure of both matrices `A` and `B`. + Only `upper_triangular` is currently supported. + + Returns + ------- + M : 2d ndarray + Matrix product of A and B. + + """ + if len(A.shape) != 2: + raise ValueError('expected A to be a rectangular matrix') + if len(B.shape) != 2: + raise ValueError('expected B to be a rectangular matrix') + f = None + if structure == UPPER_TRIANGULAR: + if (not issparse(A) and not issparse(B) + and not is_pydata_spmatrix(A) and not is_pydata_spmatrix(B)): + f, = scipy.linalg.get_blas_funcs(('trmm',), (A, B)) + if f is not None: + if alpha is None: + alpha = 1. + out = f(alpha, A, B) + else: + if alpha is None: + out = A.dot(B) + else: + out = alpha * A.dot(B) + return out + + +class MatrixPowerOperator(LinearOperator): + + def __init__(self, A, p, structure=None): + if A.ndim != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + if p < 0: + raise ValueError('expected p to be a non-negative integer') + self._A = A + self._p = p + self._structure = structure + self.dtype = A.dtype + self.ndim = A.ndim + self.shape = A.shape + + def _matvec(self, x): + for i in range(self._p): + x = self._A.dot(x) + return x + + def _rmatvec(self, x): + A_T = self._A.T + x = x.ravel() + for i in range(self._p): + x = A_T.dot(x) + return x + + def _matmat(self, X): + for i in range(self._p): + X = _smart_matrix_product(self._A, X, structure=self._structure) + return X + + @property + def T(self): + return MatrixPowerOperator(self._A.T, self._p) + + +class ProductOperator(LinearOperator): + """ + For now, this is limited to products of multiple square matrices. + """ + + def __init__(self, *args, **kwargs): + self._structure = kwargs.get('structure', None) + for A in args: + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError( + 'For now, the ProductOperator implementation is ' + 'limited to the product of multiple square matrices.') + if args: + n = args[0].shape[0] + for A in args: + for d in A.shape: + if d != n: + raise ValueError( + 'The square matrices of the ProductOperator ' + 'must all have the same shape.') + self.shape = (n, n) + self.ndim = len(self.shape) + self.dtype = np.result_type(*[x.dtype for x in args]) + self._operator_sequence = args + + def _matvec(self, x): + for A in reversed(self._operator_sequence): + x = A.dot(x) + return x + + def _rmatvec(self, x): + x = x.ravel() + for A in self._operator_sequence: + x = A.T.dot(x) + return x + + def _matmat(self, X): + for A in reversed(self._operator_sequence): + X = _smart_matrix_product(A, X, structure=self._structure) + return X + + @property + def T(self): + T_args = [A.T for A in reversed(self._operator_sequence)] + return ProductOperator(*T_args) + + +def _onenormest_matrix_power(A, p, + t=2, itmax=5, compute_v=False, compute_w=False, structure=None): + """ + Efficiently estimate the 1-norm of A^p. + + Parameters + ---------- + A : ndarray + Matrix whose 1-norm of a power is to be computed. + p : int + Non-negative integer power. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + """ + return scipy.sparse.linalg.onenormest( + MatrixPowerOperator(A, p, structure=structure)) + + +def _onenormest_product(operator_seq, + t=2, itmax=5, compute_v=False, compute_w=False, structure=None): + """ + Efficiently estimate the 1-norm of the matrix product of the args. + + Parameters + ---------- + operator_seq : linear operator sequence + Matrices whose 1-norm of product is to be computed. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + structure : str, optional + A string describing the structure of all operators. + Only `upper_triangular` is currently supported. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + """ + return scipy.sparse.linalg.onenormest( + ProductOperator(*operator_seq, structure=structure)) + + +class _ExpmPadeHelper: + """ + Help lazily evaluate a matrix exponential. + + The idea is to not do more work than we need for high expm precision, + so we lazily compute matrix powers and store or precompute + other properties of the matrix. + + """ + + def __init__(self, A, structure=None, use_exact_onenorm=False): + """ + Initialize the object. + + Parameters + ---------- + A : a dense or sparse square numpy matrix or ndarray + The matrix to be exponentiated. + structure : str, optional + A string describing the structure of matrix `A`. + Only `upper_triangular` is currently supported. + use_exact_onenorm : bool, optional + If True then only the exact one-norm of matrix powers and products + will be used. Otherwise, the one-norm of powers and products + may initially be estimated. + """ + self.A = A + self._A2 = None + self._A4 = None + self._A6 = None + self._A8 = None + self._A10 = None + self._d4_exact = None + self._d6_exact = None + self._d8_exact = None + self._d10_exact = None + self._d4_approx = None + self._d6_approx = None + self._d8_approx = None + self._d10_approx = None + self.ident = _ident_like(A) + self.structure = structure + self.use_exact_onenorm = use_exact_onenorm + + @property + def A2(self): + if self._A2 is None: + self._A2 = _smart_matrix_product( + self.A, self.A, structure=self.structure) + return self._A2 + + @property + def A4(self): + if self._A4 is None: + self._A4 = _smart_matrix_product( + self.A2, self.A2, structure=self.structure) + return self._A4 + + @property + def A6(self): + if self._A6 is None: + self._A6 = _smart_matrix_product( + self.A4, self.A2, structure=self.structure) + return self._A6 + + @property + def A8(self): + if self._A8 is None: + self._A8 = _smart_matrix_product( + self.A6, self.A2, structure=self.structure) + return self._A8 + + @property + def A10(self): + if self._A10 is None: + self._A10 = _smart_matrix_product( + self.A4, self.A6, structure=self.structure) + return self._A10 + + @property + def d4_tight(self): + if self._d4_exact is None: + self._d4_exact = _onenorm(self.A4)**(1/4.) + return self._d4_exact + + @property + def d6_tight(self): + if self._d6_exact is None: + self._d6_exact = _onenorm(self.A6)**(1/6.) + return self._d6_exact + + @property + def d8_tight(self): + if self._d8_exact is None: + self._d8_exact = _onenorm(self.A8)**(1/8.) + return self._d8_exact + + @property + def d10_tight(self): + if self._d10_exact is None: + self._d10_exact = _onenorm(self.A10)**(1/10.) + return self._d10_exact + + @property + def d4_loose(self): + if self.use_exact_onenorm: + return self.d4_tight + if self._d4_exact is not None: + return self._d4_exact + else: + if self._d4_approx is None: + self._d4_approx = _onenormest_matrix_power(self.A2, 2, + structure=self.structure)**(1/4.) + return self._d4_approx + + @property + def d6_loose(self): + if self.use_exact_onenorm: + return self.d6_tight + if self._d6_exact is not None: + return self._d6_exact + else: + if self._d6_approx is None: + self._d6_approx = _onenormest_matrix_power(self.A2, 3, + structure=self.structure)**(1/6.) + return self._d6_approx + + @property + def d8_loose(self): + if self.use_exact_onenorm: + return self.d8_tight + if self._d8_exact is not None: + return self._d8_exact + else: + if self._d8_approx is None: + self._d8_approx = _onenormest_matrix_power(self.A4, 2, + structure=self.structure)**(1/8.) + return self._d8_approx + + @property + def d10_loose(self): + if self.use_exact_onenorm: + return self.d10_tight + if self._d10_exact is not None: + return self._d10_exact + else: + if self._d10_approx is None: + self._d10_approx = _onenormest_product((self.A4, self.A6), + structure=self.structure)**(1/10.) + return self._d10_approx + + def pade3(self): + b = (120., 60., 12., 1.) + U = _smart_matrix_product(self.A, + b[3]*self.A2 + b[1]*self.ident, + structure=self.structure) + V = b[2]*self.A2 + b[0]*self.ident + return U, V + + def pade5(self): + b = (30240., 15120., 3360., 420., 30., 1.) + U = _smart_matrix_product(self.A, + b[5]*self.A4 + b[3]*self.A2 + b[1]*self.ident, + structure=self.structure) + V = b[4]*self.A4 + b[2]*self.A2 + b[0]*self.ident + return U, V + + def pade7(self): + b = (17297280., 8648640., 1995840., 277200., 25200., 1512., 56., 1.) + U = _smart_matrix_product(self.A, + b[7]*self.A6 + b[5]*self.A4 + b[3]*self.A2 + b[1]*self.ident, + structure=self.structure) + V = b[6]*self.A6 + b[4]*self.A4 + b[2]*self.A2 + b[0]*self.ident + return U, V + + def pade9(self): + b = (17643225600., 8821612800., 2075673600., 302702400., 30270240., + 2162160., 110880., 3960., 90., 1.) + U = _smart_matrix_product(self.A, + (b[9]*self.A8 + b[7]*self.A6 + b[5]*self.A4 + + b[3]*self.A2 + b[1]*self.ident), + structure=self.structure) + V = (b[8]*self.A8 + b[6]*self.A6 + b[4]*self.A4 + + b[2]*self.A2 + b[0]*self.ident) + return U, V + + def pade13_scaled(self, s): + b = (64764752532480000., 32382376266240000., 7771770303897600., + 1187353796428800., 129060195264000., 10559470521600., + 670442572800., 33522128640., 1323241920., 40840800., 960960., + 16380., 182., 1.) + B = self.A * 2**-s + B2 = self.A2 * 2**(-2*s) + B4 = self.A4 * 2**(-4*s) + B6 = self.A6 * 2**(-6*s) + U2 = _smart_matrix_product(B6, + b[13]*B6 + b[11]*B4 + b[9]*B2, + structure=self.structure) + U = _smart_matrix_product(B, + (U2 + b[7]*B6 + b[5]*B4 + + b[3]*B2 + b[1]*self.ident), + structure=self.structure) + V2 = _smart_matrix_product(B6, + b[12]*B6 + b[10]*B4 + b[8]*B2, + structure=self.structure) + V = V2 + b[6]*B6 + b[4]*B4 + b[2]*B2 + b[0]*self.ident + return U, V + + +def expm(A): + """ + Compute the matrix exponential using Pade approximation. + + Parameters + ---------- + A : (M,M) array_like or sparse matrix + 2D Array or Matrix (sparse or dense) to be exponentiated + + Returns + ------- + expA : (M,M) ndarray + Matrix exponential of `A` + + Notes + ----- + This is algorithm (6.1) which is a simplification of algorithm (5.1). + + .. versionadded:: 0.12.0 + + References + ---------- + .. [1] Awad H. Al-Mohy and Nicholas J. Higham (2009) + "A New Scaling and Squaring Algorithm for the Matrix Exponential." + SIAM Journal on Matrix Analysis and Applications. + 31 (3). pp. 970-989. ISSN 1095-7162 + + Examples + -------- + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import expm + >>> A = csc_matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) + >>> A.toarray() + array([[1, 0, 0], + [0, 2, 0], + [0, 0, 3]], dtype=int64) + >>> Aexp = expm(A) + >>> Aexp + + >>> Aexp.toarray() + array([[ 2.71828183, 0. , 0. ], + [ 0. , 7.3890561 , 0. ], + [ 0. , 0. , 20.08553692]]) + """ + return _expm(A, use_exact_onenorm='auto') + + +def _expm(A, use_exact_onenorm): + # Core of expm, separated to allow testing exact and approximate + # algorithms. + + # Avoid indiscriminate asarray() to allow sparse or other strange arrays. + if isinstance(A, (list, tuple, np.matrix)): + A = np.asarray(A) + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected a square matrix') + + # gracefully handle size-0 input, + # carefully handling sparse scenario + if A.shape == (0, 0): + out = np.zeros([0, 0], dtype=A.dtype) + if issparse(A) or is_pydata_spmatrix(A): + return A.__class__(out) + return out + + # Trivial case + if A.shape == (1, 1): + out = [[np.exp(A[0, 0])]] + + # Avoid indiscriminate casting to ndarray to + # allow for sparse or other strange arrays + if issparse(A) or is_pydata_spmatrix(A): + return A.__class__(out) + + return np.array(out) + + # Ensure input is of float type, to avoid integer overflows etc. + if ((isinstance(A, np.ndarray) or issparse(A) or is_pydata_spmatrix(A)) + and not np.issubdtype(A.dtype, np.inexact)): + A = A.astype(float) + + # Detect upper triangularity. + structure = UPPER_TRIANGULAR if _is_upper_triangular(A) else None + + if use_exact_onenorm == "auto": + # Hardcode a matrix order threshold for exact vs. estimated one-norms. + use_exact_onenorm = A.shape[0] < 200 + + # Track functions of A to help compute the matrix exponential. + h = _ExpmPadeHelper( + A, structure=structure, use_exact_onenorm=use_exact_onenorm) + + # Try Pade order 3. + eta_1 = max(h.d4_loose, h.d6_loose) + if eta_1 < 1.495585217958292e-002 and _ell(h.A, 3) == 0: + U, V = h.pade3() + return _solve_P_Q(U, V, structure=structure) + + # Try Pade order 5. + eta_2 = max(h.d4_tight, h.d6_loose) + if eta_2 < 2.539398330063230e-001 and _ell(h.A, 5) == 0: + U, V = h.pade5() + return _solve_P_Q(U, V, structure=structure) + + # Try Pade orders 7 and 9. + eta_3 = max(h.d6_tight, h.d8_loose) + if eta_3 < 9.504178996162932e-001 and _ell(h.A, 7) == 0: + U, V = h.pade7() + return _solve_P_Q(U, V, structure=structure) + if eta_3 < 2.097847961257068e+000 and _ell(h.A, 9) == 0: + U, V = h.pade9() + return _solve_P_Q(U, V, structure=structure) + + # Use Pade order 13. + eta_4 = max(h.d8_loose, h.d10_loose) + eta_5 = min(eta_3, eta_4) + theta_13 = 4.25 + + # Choose smallest s>=0 such that 2**(-s) eta_5 <= theta_13 + if eta_5 == 0: + # Nilpotent special case + s = 0 + else: + s = max(int(np.ceil(np.log2(eta_5 / theta_13))), 0) + s = s + _ell(2**-s * h.A, 13) + U, V = h.pade13_scaled(s) + X = _solve_P_Q(U, V, structure=structure) + if structure == UPPER_TRIANGULAR: + # Invoke Code Fragment 2.1. + X = _fragment_2_1(X, h.A, s) + else: + # X = r_13(A)^(2^s) by repeated squaring. + for i in range(s): + X = X.dot(X) + return X + + +def _solve_P_Q(U, V, structure=None): + """ + A helper function for expm_2009. + + Parameters + ---------- + U : ndarray + Pade numerator. + V : ndarray + Pade denominator. + structure : str, optional + A string describing the structure of both matrices `U` and `V`. + Only `upper_triangular` is currently supported. + + Notes + ----- + The `structure` argument is inspired by similar args + for theano and cvxopt functions. + + """ + P = U + V + Q = -U + V + if issparse(U) or is_pydata_spmatrix(U): + return spsolve(Q, P) + elif structure is None: + return solve(Q, P) + elif structure == UPPER_TRIANGULAR: + return solve_triangular(Q, P) + else: + raise ValueError('unsupported matrix structure: ' + str(structure)) + + +def _exp_sinch(a, x): + """ + Stably evaluate exp(a)*sinh(x)/x + + Notes + ----- + The strategy of falling back to a sixth order Taylor expansion + was suggested by the Spallation Neutron Source docs + which was found on the internet by google search. + http://www.ornl.gov/~t6p/resources/xal/javadoc/gov/sns/tools/math/ElementaryFunction.html + The details of the cutoff point and the Horner-like evaluation + was picked without reference to anything in particular. + + Note that sinch is not currently implemented in scipy.special, + whereas the "engineer's" definition of sinc is implemented. + The implementation of sinc involves a scaling factor of pi + that distinguishes it from the "mathematician's" version of sinc. + + """ + + # If x is small then use sixth order Taylor expansion. + # How small is small? I am using the point where the relative error + # of the approximation is less than 1e-14. + # If x is large then directly evaluate sinh(x) / x. + if abs(x) < 0.0135: + x2 = x*x + return np.exp(a) * (1 + (x2/6.)*(1 + (x2/20.)*(1 + (x2/42.)))) + else: + return (np.exp(a + x) - np.exp(a - x)) / (2*x) + + +def _eq_10_42(lam_1, lam_2, t_12): + """ + Equation (10.42) of Functions of Matrices: Theory and Computation. + + Notes + ----- + This is a helper function for _fragment_2_1 of expm_2009. + Equation (10.42) is on page 251 in the section on Schur algorithms. + In particular, section 10.4.3 explains the Schur-Parlett algorithm. + expm([[lam_1, t_12], [0, lam_1]) + = + [[exp(lam_1), t_12*exp((lam_1 + lam_2)/2)*sinch((lam_1 - lam_2)/2)], + [0, exp(lam_2)] + """ + + # The plain formula t_12 * (exp(lam_2) - exp(lam_2)) / (lam_2 - lam_1) + # apparently suffers from cancellation, according to Higham's textbook. + # A nice implementation of sinch, defined as sinh(x)/x, + # will apparently work around the cancellation. + a = 0.5 * (lam_1 + lam_2) + b = 0.5 * (lam_1 - lam_2) + return t_12 * _exp_sinch(a, b) + + +def _fragment_2_1(X, T, s): + """ + A helper function for expm_2009. + + Notes + ----- + The argument X is modified in-place, but this modification is not the same + as the returned value of the function. + This function also takes pains to do things in ways that are compatible + with sparse matrices, for example by avoiding fancy indexing + and by using methods of the matrices whenever possible instead of + using functions of the numpy or scipy libraries themselves. + + """ + # Form X = r_m(2^-s T) + # Replace diag(X) by exp(2^-s diag(T)). + n = X.shape[0] + diag_T = np.ravel(T.diagonal().copy()) + + # Replace diag(X) by exp(2^-s diag(T)). + scale = 2 ** -s + exp_diag = np.exp(scale * diag_T) + for k in range(n): + X[k, k] = exp_diag[k] + + for i in range(s-1, -1, -1): + X = X.dot(X) + + # Replace diag(X) by exp(2^-i diag(T)). + scale = 2 ** -i + exp_diag = np.exp(scale * diag_T) + for k in range(n): + X[k, k] = exp_diag[k] + + # Replace (first) superdiagonal of X by explicit formula + # for superdiagonal of exp(2^-i T) from Eq (10.42) of + # the author's 2008 textbook + # Functions of Matrices: Theory and Computation. + for k in range(n-1): + lam_1 = scale * diag_T[k] + lam_2 = scale * diag_T[k+1] + t_12 = scale * T[k, k+1] + value = _eq_10_42(lam_1, lam_2, t_12) + X[k, k+1] = value + + # Return the updated X matrix. + return X + + +def _ell(A, m): + """ + A helper function for expm_2009. + + Parameters + ---------- + A : linear operator + A linear operator whose norm of power we care about. + m : int + The power of the linear operator + + Returns + ------- + value : int + A value related to a bound. + + """ + if len(A.shape) != 2 or A.shape[0] != A.shape[1]: + raise ValueError('expected A to be like a square matrix') + + # The c_i are explained in (2.2) and (2.6) of the 2005 expm paper. + # They are coefficients of terms of a generating function series expansion. + c_i = {3: 100800., + 5: 10059033600., + 7: 4487938430976000., + 9: 5914384781877411840000., + 13: 113250775606021113483283660800000000. + } + abs_c_recip = c_i[m] + + # This is explained after Eq. (1.2) of the 2009 expm paper. + # It is the "unit roundoff" of IEEE double precision arithmetic. + u = 2**-53 + + # Compute the one-norm of matrix power p of abs(A). + A_abs_onenorm = _onenorm_matrix_power_nnm(abs(A), 2*m + 1) + + # Treat zero norm as a special case. + if not A_abs_onenorm: + return 0 + + alpha = A_abs_onenorm / (_onenorm(A) * abs_c_recip) + log2_alpha_div_u = np.log2(alpha/u) + value = int(np.ceil(log2_alpha_div_u / (2 * m))) + return max(value, 0) + +def matrix_power(A, power): + """ + Raise a square matrix to the integer power, `power`. + + For non-negative integers, ``A**power`` is computed using repeated + matrix multiplications. Negative integers are not supported. + + Parameters + ---------- + A : (M, M) square sparse array or matrix + sparse array that will be raised to power `power` + power : int + Exponent used to raise sparse array `A` + + Returns + ------- + A**power : (M, M) sparse array or matrix + The output matrix will be the same shape as A, and will preserve + the class of A, but the format of the output may be changed. + + Notes + ----- + This uses a recursive implementation of the matrix power. For computing + the matrix power using a reasonably large `power`, this may be less efficient + than computing the product directly, using A @ A @ ... @ A. + This is contingent upon the number of nonzero entries in the matrix. + + .. versionadded:: 1.12.0 + + Examples + -------- + >>> from scipy import sparse + >>> A = sparse.csc_array([[0,1,0],[1,0,1],[0,1,0]]) + >>> A.todense() + array([[0, 1, 0], + [1, 0, 1], + [0, 1, 0]]) + >>> (A @ A).todense() + array([[1, 0, 1], + [0, 2, 0], + [1, 0, 1]]) + >>> A2 = sparse.linalg.matrix_power(A, 2) + >>> A2.todense() + array([[1, 0, 1], + [0, 2, 0], + [1, 0, 1]]) + >>> A4 = sparse.linalg.matrix_power(A, 4) + >>> A4.todense() + array([[2, 0, 2], + [0, 4, 0], + [2, 0, 2]]) + + """ + M, N = A.shape + if M != N: + raise TypeError('sparse matrix is not square') + + if isintlike(power): + power = int(power) + if power < 0: + raise ValueError('exponent must be >= 0') + + if power == 0: + return eye(M, dtype=A.dtype) + + if power == 1: + return A.copy() + + tmp = matrix_power(A, power // 2) + if power % 2: + return A @ tmp @ tmp + else: + return tmp @ tmp + else: + raise ValueError("exponent must be an integer") diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_norm.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_norm.py new file mode 100644 index 0000000000000000000000000000000000000000..38f3a6d7a6f84ec315b3177b384eef5c5d93311a --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_norm.py @@ -0,0 +1,193 @@ +"""Sparse matrix norms. + +""" +import numpy as np +from scipy.sparse import issparse +from scipy.sparse.linalg import svds +import scipy.sparse as sp + +from numpy import sqrt, abs + +__all__ = ['norm'] + + +def _sparse_frobenius_norm(x): + data = sp._sputils._todata(x) + return np.linalg.norm(data) + + +def norm(x, ord=None, axis=None): + """ + Norm of a sparse matrix + + This function is able to return one of seven different matrix norms, + depending on the value of the ``ord`` parameter. + + Parameters + ---------- + x : a sparse matrix + Input sparse matrix. + ord : {non-zero int, inf, -inf, 'fro'}, optional + Order of the norm (see table under ``Notes``). inf means numpy's + `inf` object. + axis : {int, 2-tuple of ints, None}, optional + If `axis` is an integer, it specifies the axis of `x` along which to + compute the vector norms. If `axis` is a 2-tuple, it specifies the + axes that hold 2-D matrices, and the matrix norms of these matrices + are computed. If `axis` is None then either a vector norm (when `x` + is 1-D) or a matrix norm (when `x` is 2-D) is returned. + + Returns + ------- + n : float or ndarray + + Notes + ----- + Some of the ord are not implemented because some associated functions like, + _multi_svd_norm, are not yet available for sparse matrix. + + This docstring is modified based on numpy.linalg.norm. + https://github.com/numpy/numpy/blob/main/numpy/linalg/linalg.py + + The following norms can be calculated: + + ===== ============================ + ord norm for sparse matrices + ===== ============================ + None Frobenius norm + 'fro' Frobenius norm + inf max(sum(abs(x), axis=1)) + -inf min(sum(abs(x), axis=1)) + 0 abs(x).sum(axis=axis) + 1 max(sum(abs(x), axis=0)) + -1 min(sum(abs(x), axis=0)) + 2 Spectral norm (the largest singular value) + -2 Not implemented + other Not implemented + ===== ============================ + + The Frobenius norm is given by [1]_: + + :math:`||A||_F = [\\sum_{i,j} abs(a_{i,j})^2]^{1/2}` + + References + ---------- + .. [1] G. H. Golub and C. F. Van Loan, *Matrix Computations*, + Baltimore, MD, Johns Hopkins University Press, 1985, pg. 15 + + Examples + -------- + >>> from scipy.sparse import * + >>> import numpy as np + >>> from scipy.sparse.linalg import norm + >>> a = np.arange(9) - 4 + >>> a + array([-4, -3, -2, -1, 0, 1, 2, 3, 4]) + >>> b = a.reshape((3, 3)) + >>> b + array([[-4, -3, -2], + [-1, 0, 1], + [ 2, 3, 4]]) + + >>> b = csr_matrix(b) + >>> norm(b) + 7.745966692414834 + >>> norm(b, 'fro') + 7.745966692414834 + >>> norm(b, np.inf) + 9 + >>> norm(b, -np.inf) + 2 + >>> norm(b, 1) + 7 + >>> norm(b, -1) + 6 + + The matrix 2-norm or the spectral norm is the largest singular + value, computed approximately and with limitations. + + >>> b = diags([-1, 1], [0, 1], shape=(9, 10)) + >>> norm(b, 2) + 1.9753... + """ + if not issparse(x): + raise TypeError("input is not sparse. use numpy.linalg.norm") + + # Check the default case first and handle it immediately. + if axis is None and ord in (None, 'fro', 'f'): + return _sparse_frobenius_norm(x) + + # Some norms require functions that are not implemented for all types. + x = x.tocsr() + + if axis is None: + axis = (0, 1) + elif not isinstance(axis, tuple): + msg = "'axis' must be None, an integer or a tuple of integers" + try: + int_axis = int(axis) + except TypeError as e: + raise TypeError(msg) from e + if axis != int_axis: + raise TypeError(msg) + axis = (int_axis,) + + nd = 2 + if len(axis) == 2: + row_axis, col_axis = axis + if not (-nd <= row_axis < nd and -nd <= col_axis < nd): + message = f'Invalid axis {axis!r} for an array with shape {x.shape!r}' + raise ValueError(message) + if row_axis % nd == col_axis % nd: + raise ValueError('Duplicate axes given.') + if ord == 2: + # Only solver="lobpcg" supports all numpy dtypes + _, s, _ = svds(x, k=1, solver="lobpcg") + return s[0] + elif ord == -2: + raise NotImplementedError + #return _multi_svd_norm(x, row_axis, col_axis, amin) + elif ord == 1: + return abs(x).sum(axis=row_axis).max(axis=col_axis)[0,0] + elif ord == np.inf: + return abs(x).sum(axis=col_axis).max(axis=row_axis)[0,0] + elif ord == -1: + return abs(x).sum(axis=row_axis).min(axis=col_axis)[0,0] + elif ord == -np.inf: + return abs(x).sum(axis=col_axis).min(axis=row_axis)[0,0] + elif ord in (None, 'f', 'fro'): + # The axis order does not matter for this norm. + return _sparse_frobenius_norm(x) + else: + raise ValueError("Invalid norm order for matrices.") + elif len(axis) == 1: + a, = axis + if not (-nd <= a < nd): + message = f'Invalid axis {axis!r} for an array with shape {x.shape!r}' + raise ValueError(message) + if ord == np.inf: + M = abs(x).max(axis=a) + elif ord == -np.inf: + M = abs(x).min(axis=a) + elif ord == 0: + # Zero norm + M = (x != 0).sum(axis=a) + elif ord == 1: + # special case for speedup + M = abs(x).sum(axis=a) + elif ord in (2, None): + M = sqrt(abs(x).power(2).sum(axis=a)) + else: + try: + ord + 1 + except TypeError as e: + raise ValueError('Invalid norm order for vectors.') from e + M = np.power(abs(x).power(ord).sum(axis=a), 1 / ord) + if hasattr(M, 'toarray'): + return M.toarray().ravel() + elif hasattr(M, 'A'): + return M.A.ravel() + else: + return M.ravel() + else: + raise ValueError("Improper number of dimensions to norm.") diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_onenormest.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_onenormest.py new file mode 100644 index 0000000000000000000000000000000000000000..c3e383aa6370b3ef11b8c2cf57d2cf85da66d02d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_onenormest.py @@ -0,0 +1,467 @@ +"""Sparse block 1-norm estimator. +""" + +import numpy as np +from scipy.sparse.linalg import aslinearoperator + + +__all__ = ['onenormest'] + + +def onenormest(A, t=2, itmax=5, compute_v=False, compute_w=False): + """ + Compute a lower bound of the 1-norm of a sparse matrix. + + Parameters + ---------- + A : ndarray or other linear operator + A linear operator that can be transposed and that can + produce matrix products. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + Larger values take longer and use more memory + but give more accurate output. + itmax : int, optional + Use at most this many iterations. + compute_v : bool, optional + Request a norm-maximizing linear operator input vector if True. + compute_w : bool, optional + Request a norm-maximizing linear operator output vector if True. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + + Notes + ----- + This is algorithm 2.4 of [1]. + + In [2] it is described as follows. + "This algorithm typically requires the evaluation of + about 4t matrix-vector products and almost invariably + produces a norm estimate (which is, in fact, a lower + bound on the norm) correct to within a factor 3." + + .. versionadded:: 0.13.0 + + References + ---------- + .. [1] Nicholas J. Higham and Francoise Tisseur (2000), + "A Block Algorithm for Matrix 1-Norm Estimation, + with an Application to 1-Norm Pseudospectra." + SIAM J. Matrix Anal. Appl. Vol. 21, No. 4, pp. 1185-1201. + + .. [2] Awad H. Al-Mohy and Nicholas J. Higham (2009), + "A new scaling and squaring algorithm for the matrix exponential." + SIAM J. Matrix Anal. Appl. Vol. 31, No. 3, pp. 970-989. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse import csc_matrix + >>> from scipy.sparse.linalg import onenormest + >>> A = csc_matrix([[1., 0., 0.], [5., 8., 2.], [0., -1., 0.]], dtype=float) + >>> A.toarray() + array([[ 1., 0., 0.], + [ 5., 8., 2.], + [ 0., -1., 0.]]) + >>> onenormest(A) + 9.0 + >>> np.linalg.norm(A.toarray(), ord=1) + 9.0 + """ + + # Check the input. + A = aslinearoperator(A) + if A.shape[0] != A.shape[1]: + raise ValueError('expected the operator to act like a square matrix') + + # If the operator size is small compared to t, + # then it is easier to compute the exact norm. + # Otherwise estimate the norm. + n = A.shape[1] + if t >= n: + A_explicit = np.asarray(aslinearoperator(A).matmat(np.identity(n))) + if A_explicit.shape != (n, n): + raise Exception('internal error: ', + 'unexpected shape ' + str(A_explicit.shape)) + col_abs_sums = abs(A_explicit).sum(axis=0) + if col_abs_sums.shape != (n, ): + raise Exception('internal error: ', + 'unexpected shape ' + str(col_abs_sums.shape)) + argmax_j = np.argmax(col_abs_sums) + v = elementary_vector(n, argmax_j) + w = A_explicit[:, argmax_j] + est = col_abs_sums[argmax_j] + else: + est, v, w, nmults, nresamples = _onenormest_core(A, A.H, t, itmax) + + # Report the norm estimate along with some certificates of the estimate. + if compute_v or compute_w: + result = (est,) + if compute_v: + result += (v,) + if compute_w: + result += (w,) + return result + else: + return est + + +def _blocked_elementwise(func): + """ + Decorator for an elementwise function, to apply it blockwise along + first dimension, to avoid excessive memory usage in temporaries. + """ + block_size = 2**20 + + def wrapper(x): + if x.shape[0] < block_size: + return func(x) + else: + y0 = func(x[:block_size]) + y = np.zeros((x.shape[0],) + y0.shape[1:], dtype=y0.dtype) + y[:block_size] = y0 + del y0 + for j in range(block_size, x.shape[0], block_size): + y[j:j+block_size] = func(x[j:j+block_size]) + return y + return wrapper + + +@_blocked_elementwise +def sign_round_up(X): + """ + This should do the right thing for both real and complex matrices. + + From Higham and Tisseur: + "Everything in this section remains valid for complex matrices + provided that sign(A) is redefined as the matrix (aij / |aij|) + (and sign(0) = 1) transposes are replaced by conjugate transposes." + + """ + Y = X.copy() + Y[Y == 0] = 1 + Y /= np.abs(Y) + return Y + + +@_blocked_elementwise +def _max_abs_axis1(X): + return np.max(np.abs(X), axis=1) + + +def _sum_abs_axis0(X): + block_size = 2**20 + r = None + for j in range(0, X.shape[0], block_size): + y = np.sum(np.abs(X[j:j+block_size]), axis=0) + if r is None: + r = y + else: + r += y + return r + + +def elementary_vector(n, i): + v = np.zeros(n, dtype=float) + v[i] = 1 + return v + + +def vectors_are_parallel(v, w): + # Columns are considered parallel when they are equal or negative. + # Entries are required to be in {-1, 1}, + # which guarantees that the magnitudes of the vectors are identical. + if v.ndim != 1 or v.shape != w.shape: + raise ValueError('expected conformant vectors with entries in {-1,1}') + n = v.shape[0] + return np.dot(v, w) == n + + +def every_col_of_X_is_parallel_to_a_col_of_Y(X, Y): + for v in X.T: + if not any(vectors_are_parallel(v, w) for w in Y.T): + return False + return True + + +def column_needs_resampling(i, X, Y=None): + # column i of X needs resampling if either + # it is parallel to a previous column of X or + # it is parallel to a column of Y + n, t = X.shape + v = X[:, i] + if any(vectors_are_parallel(v, X[:, j]) for j in range(i)): + return True + if Y is not None: + if any(vectors_are_parallel(v, w) for w in Y.T): + return True + return False + + +def resample_column(i, X): + X[:, i] = np.random.randint(0, 2, size=X.shape[0])*2 - 1 + + +def less_than_or_close(a, b): + return np.allclose(a, b) or (a < b) + + +def _algorithm_2_2(A, AT, t): + """ + This is Algorithm 2.2. + + Parameters + ---------- + A : ndarray or other linear operator + A linear operator that can produce matrix products. + AT : ndarray or other linear operator + The transpose of A. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + + Returns + ------- + g : sequence + A non-negative decreasing vector + such that g[j] is a lower bound for the 1-norm + of the column of A of jth largest 1-norm. + The first entry of this vector is therefore a lower bound + on the 1-norm of the linear operator A. + This sequence has length t. + ind : sequence + The ith entry of ind is the index of the column A whose 1-norm + is given by g[i]. + This sequence of indices has length t, and its entries are + chosen from range(n), possibly with repetition, + where n is the order of the operator A. + + Notes + ----- + This algorithm is mainly for testing. + It uses the 'ind' array in a way that is similar to + its usage in algorithm 2.4. This algorithm 2.2 may be easier to test, + so it gives a chance of uncovering bugs related to indexing + which could have propagated less noticeably to algorithm 2.4. + + """ + A_linear_operator = aslinearoperator(A) + AT_linear_operator = aslinearoperator(AT) + n = A_linear_operator.shape[0] + + # Initialize the X block with columns of unit 1-norm. + X = np.ones((n, t)) + if t > 1: + X[:, 1:] = np.random.randint(0, 2, size=(n, t-1))*2 - 1 + X /= float(n) + + # Iteratively improve the lower bounds. + # Track extra things, to assert invariants for debugging. + g_prev = None + h_prev = None + k = 1 + ind = range(t) + while True: + Y = np.asarray(A_linear_operator.matmat(X)) + g = _sum_abs_axis0(Y) + best_j = np.argmax(g) + g.sort() + g = g[::-1] + S = sign_round_up(Y) + Z = np.asarray(AT_linear_operator.matmat(S)) + h = _max_abs_axis1(Z) + + # If this algorithm runs for fewer than two iterations, + # then its return values do not have the properties indicated + # in the description of the algorithm. + # In particular, the entries of g are not 1-norms of any + # column of A until the second iteration. + # Therefore we will require the algorithm to run for at least + # two iterations, even though this requirement is not stated + # in the description of the algorithm. + if k >= 2: + if less_than_or_close(max(h), np.dot(Z[:, best_j], X[:, best_j])): + break + ind = np.argsort(h)[::-1][:t] + h = h[ind] + for j in range(t): + X[:, j] = elementary_vector(n, ind[j]) + + # Check invariant (2.2). + if k >= 2: + if not less_than_or_close(g_prev[0], h_prev[0]): + raise Exception('invariant (2.2) is violated') + if not less_than_or_close(h_prev[0], g[0]): + raise Exception('invariant (2.2) is violated') + + # Check invariant (2.3). + if k >= 3: + for j in range(t): + if not less_than_or_close(g[j], g_prev[j]): + raise Exception('invariant (2.3) is violated') + + # Update for the next iteration. + g_prev = g + h_prev = h + k += 1 + + # Return the lower bounds and the corresponding column indices. + return g, ind + + +def _onenormest_core(A, AT, t, itmax): + """ + Compute a lower bound of the 1-norm of a sparse matrix. + + Parameters + ---------- + A : ndarray or other linear operator + A linear operator that can produce matrix products. + AT : ndarray or other linear operator + The transpose of A. + t : int, optional + A positive parameter controlling the tradeoff between + accuracy versus time and memory usage. + itmax : int, optional + Use at most this many iterations. + + Returns + ------- + est : float + An underestimate of the 1-norm of the sparse matrix. + v : ndarray, optional + The vector such that ||Av||_1 == est*||v||_1. + It can be thought of as an input to the linear operator + that gives an output with particularly large norm. + w : ndarray, optional + The vector Av which has relatively large 1-norm. + It can be thought of as an output of the linear operator + that is relatively large in norm compared to the input. + nmults : int, optional + The number of matrix products that were computed. + nresamples : int, optional + The number of times a parallel column was observed, + necessitating a re-randomization of the column. + + Notes + ----- + This is algorithm 2.4. + + """ + # This function is a more or less direct translation + # of Algorithm 2.4 from the Higham and Tisseur (2000) paper. + A_linear_operator = aslinearoperator(A) + AT_linear_operator = aslinearoperator(AT) + if itmax < 2: + raise ValueError('at least two iterations are required') + if t < 1: + raise ValueError('at least one column is required') + n = A.shape[0] + if t >= n: + raise ValueError('t should be smaller than the order of A') + # Track the number of big*small matrix multiplications + # and the number of resamplings. + nmults = 0 + nresamples = 0 + # "We now explain our choice of starting matrix. We take the first + # column of X to be the vector of 1s [...] This has the advantage that + # for a matrix with nonnegative elements the algorithm converges + # with an exact estimate on the second iteration, and such matrices + # arise in applications [...]" + X = np.ones((n, t), dtype=float) + # "The remaining columns are chosen as rand{-1,1}, + # with a check for and correction of parallel columns, + # exactly as for S in the body of the algorithm." + if t > 1: + for i in range(1, t): + # These are technically initial samples, not resamples, + # so the resampling count is not incremented. + resample_column(i, X) + for i in range(t): + while column_needs_resampling(i, X): + resample_column(i, X) + nresamples += 1 + # "Choose starting matrix X with columns of unit 1-norm." + X /= float(n) + # "indices of used unit vectors e_j" + ind_hist = np.zeros(0, dtype=np.intp) + est_old = 0 + S = np.zeros((n, t), dtype=float) + k = 1 + ind = None + while True: + Y = np.asarray(A_linear_operator.matmat(X)) + nmults += 1 + mags = _sum_abs_axis0(Y) + est = np.max(mags) + best_j = np.argmax(mags) + if est > est_old or k == 2: + if k >= 2: + ind_best = ind[best_j] + w = Y[:, best_j] + # (1) + if k >= 2 and est <= est_old: + est = est_old + break + est_old = est + S_old = S + if k > itmax: + break + S = sign_round_up(Y) + del Y + # (2) + if every_col_of_X_is_parallel_to_a_col_of_Y(S, S_old): + break + if t > 1: + # "Ensure that no column of S is parallel to another column of S + # or to a column of S_old by replacing columns of S by rand{-1,1}." + for i in range(t): + while column_needs_resampling(i, S, S_old): + resample_column(i, S) + nresamples += 1 + del S_old + # (3) + Z = np.asarray(AT_linear_operator.matmat(S)) + nmults += 1 + h = _max_abs_axis1(Z) + del Z + # (4) + if k >= 2 and max(h) == h[ind_best]: + break + # "Sort h so that h_first >= ... >= h_last + # and re-order ind correspondingly." + # + # Later on, we will need at most t+len(ind_hist) largest + # entries, so drop the rest + ind = np.argsort(h)[::-1][:t+len(ind_hist)].copy() + del h + if t > 1: + # (5) + # Break if the most promising t vectors have been visited already. + if np.isin(ind[:t], ind_hist).all(): + break + # Put the most promising unvisited vectors at the front of the list + # and put the visited vectors at the end of the list. + # Preserve the order of the indices induced by the ordering of h. + seen = np.isin(ind, ind_hist) + ind = np.concatenate((ind[~seen], ind[seen])) + for j in range(t): + X[:, j] = elementary_vector(n, ind[j]) + + new_ind = ind[:t][~np.isin(ind[:t], ind_hist)] + ind_hist = np.concatenate((ind_hist, new_ind)) + k += 1 + v = elementary_vector(n, ind_best) + return est, v, w, nmults, nresamples diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_special_sparse_arrays.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_special_sparse_arrays.py new file mode 100644 index 0000000000000000000000000000000000000000..ee68fce865c5d4cf7ed8d7e2fa23e1bd14bbefe8 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_special_sparse_arrays.py @@ -0,0 +1,948 @@ +import numpy as np +from scipy.sparse.linalg import LinearOperator +from scipy.sparse import kron, eye, dia_array + +__all__ = ['LaplacianNd'] +# Sakurai and Mikota classes are intended for tests and benchmarks +# and explicitly not included in the public API of this module. + + +class LaplacianNd(LinearOperator): + """ + The grid Laplacian in ``N`` dimensions and its eigenvalues/eigenvectors. + + Construct Laplacian on a uniform rectangular grid in `N` dimensions + and output its eigenvalues and eigenvectors. + The Laplacian ``L`` is square, negative definite, real symmetric array + with signed integer entries and zeros otherwise. + + Parameters + ---------- + grid_shape : tuple + A tuple of integers of length ``N`` (corresponding to the dimension of + the Lapacian), where each entry gives the size of that dimension. The + Laplacian matrix is square of the size ``np.prod(grid_shape)``. + boundary_conditions : {'neumann', 'dirichlet', 'periodic'}, optional + The type of the boundary conditions on the boundaries of the grid. + Valid values are ``'dirichlet'`` or ``'neumann'``(default) or + ``'periodic'``. + dtype : dtype + Numerical type of the array. Default is ``np.int8``. + + Methods + ------- + toarray() + Construct a dense array from Laplacian data + tosparse() + Construct a sparse array from Laplacian data + eigenvalues(m=None) + Construct a 1D array of `m` largest (smallest in absolute value) + eigenvalues of the Laplacian matrix in ascending order. + eigenvectors(m=None): + Construct the array with columns made of `m` eigenvectors (``float``) + of the ``Nd`` Laplacian corresponding to the `m` ordered eigenvalues. + + .. versionadded:: 1.12.0 + + Notes + ----- + Compared to the MATLAB/Octave implementation [1] of 1-, 2-, and 3-D + Laplacian, this code allows the arbitrary N-D case and the matrix-free + callable option, but is currently limited to pure Dirichlet, Neumann or + Periodic boundary conditions only. + + The Laplacian matrix of a graph (`scipy.sparse.csgraph.laplacian`) of a + rectangular grid corresponds to the negative Laplacian with the Neumann + conditions, i.e., ``boundary_conditions = 'neumann'``. + + All eigenvalues and eigenvectors of the discrete Laplacian operator for + an ``N``-dimensional regular grid of shape `grid_shape` with the grid + step size ``h=1`` are analytically known [2]. + + References + ---------- + .. [1] https://github.com/lobpcg/blopex/blob/master/blopex_\ +tools/matlab/laplacian/laplacian.m + .. [2] "Eigenvalues and eigenvectors of the second derivative", Wikipedia + https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors_\ +of_the_second_derivative + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg import LaplacianNd + >>> from scipy.sparse import diags, csgraph + >>> from scipy.linalg import eigvalsh + + The one-dimensional Laplacian demonstrated below for pure Neumann boundary + conditions on a regular grid with ``n=6`` grid points is exactly the + negative graph Laplacian for the undirected linear graph with ``n`` + vertices using the sparse adjacency matrix ``G`` represented by the + famous tri-diagonal matrix: + + >>> n = 6 + >>> G = diags(np.ones(n - 1), 1, format='csr') + >>> Lf = csgraph.laplacian(G, symmetrized=True, form='function') + >>> grid_shape = (n, ) + >>> lap = LaplacianNd(grid_shape, boundary_conditions='neumann') + >>> np.array_equal(lap.matmat(np.eye(n)), -Lf(np.eye(n))) + True + + Since all matrix entries of the Laplacian are integers, ``'int8'`` is + the default dtype for storing matrix representations. + + >>> lap.tosparse() + + >>> lap.toarray() + array([[-1, 1, 0, 0, 0, 0], + [ 1, -2, 1, 0, 0, 0], + [ 0, 1, -2, 1, 0, 0], + [ 0, 0, 1, -2, 1, 0], + [ 0, 0, 0, 1, -2, 1], + [ 0, 0, 0, 0, 1, -1]], dtype=int8) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + + Any number of extreme eigenvalues and/or eigenvectors can be computed. + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='periodic') + >>> lap.eigenvalues() + array([-4., -3., -3., -1., -1., 0.]) + >>> lap.eigenvalues()[-2:] + array([-1., 0.]) + >>> lap.eigenvalues(2) + array([-1., 0.]) + >>> lap.eigenvectors(1) + array([[0.40824829], + [0.40824829], + [0.40824829], + [0.40824829], + [0.40824829], + [0.40824829]]) + >>> lap.eigenvectors(2) + array([[ 0.5 , 0.40824829], + [ 0. , 0.40824829], + [-0.5 , 0.40824829], + [-0.5 , 0.40824829], + [ 0. , 0.40824829], + [ 0.5 , 0.40824829]]) + >>> lap.eigenvectors() + array([[ 0.40824829, 0.28867513, 0.28867513, 0.5 , 0.5 , + 0.40824829], + [-0.40824829, -0.57735027, -0.57735027, 0. , 0. , + 0.40824829], + [ 0.40824829, 0.28867513, 0.28867513, -0.5 , -0.5 , + 0.40824829], + [-0.40824829, 0.28867513, 0.28867513, -0.5 , -0.5 , + 0.40824829], + [ 0.40824829, -0.57735027, -0.57735027, 0. , 0. , + 0.40824829], + [-0.40824829, 0.28867513, 0.28867513, 0.5 , 0.5 , + 0.40824829]]) + + The two-dimensional Laplacian is illustrated on a regular grid with + ``grid_shape = (2, 3)`` points in each dimension. + + >>> grid_shape = (2, 3) + >>> n = np.prod(grid_shape) + + Numeration of grid points is as follows: + + >>> np.arange(n).reshape(grid_shape + (-1,)) + array([[[0], + [1], + [2]], + + [[3], + [4], + [5]]]) + + Each of the boundary conditions ``'dirichlet'``, ``'periodic'``, and + ``'neumann'`` is illustrated separately; with ``'dirichlet'`` + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='dirichlet') + >>> lap.tosparse() + + >>> lap.toarray() + array([[-4, 1, 0, 1, 0, 0], + [ 1, -4, 1, 0, 1, 0], + [ 0, 1, -4, 0, 0, 1], + [ 1, 0, 0, -4, 1, 0], + [ 0, 1, 0, 1, -4, 1], + [ 0, 0, 1, 0, 1, -4]], dtype=int8) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + >>> lap.eigenvalues() + array([-6.41421356, -5. , -4.41421356, -3.58578644, -3. , + -1.58578644]) + >>> eigvals = eigvalsh(lap.toarray().astype(np.float64)) + >>> np.allclose(lap.eigenvalues(), eigvals) + True + >>> np.allclose(lap.toarray() @ lap.eigenvectors(), + ... lap.eigenvectors() @ np.diag(lap.eigenvalues())) + True + + with ``'periodic'`` + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='periodic') + >>> lap.tosparse() + + >>> lap.toarray() + array([[-4, 1, 1, 2, 0, 0], + [ 1, -4, 1, 0, 2, 0], + [ 1, 1, -4, 0, 0, 2], + [ 2, 0, 0, -4, 1, 1], + [ 0, 2, 0, 1, -4, 1], + [ 0, 0, 2, 1, 1, -4]], dtype=int8) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + >>> lap.eigenvalues() + array([-7., -7., -4., -3., -3., 0.]) + >>> eigvals = eigvalsh(lap.toarray().astype(np.float64)) + >>> np.allclose(lap.eigenvalues(), eigvals) + True + >>> np.allclose(lap.toarray() @ lap.eigenvectors(), + ... lap.eigenvectors() @ np.diag(lap.eigenvalues())) + True + + and with ``'neumann'`` + + >>> lap = LaplacianNd(grid_shape, boundary_conditions='neumann') + >>> lap.tosparse() + + >>> lap.toarray() + array([[-2, 1, 0, 1, 0, 0], + [ 1, -3, 1, 0, 1, 0], + [ 0, 1, -2, 0, 0, 1], + [ 1, 0, 0, -2, 1, 0], + [ 0, 1, 0, 1, -3, 1], + [ 0, 0, 1, 0, 1, -2]]) + >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray()) + True + >>> np.array_equal(lap.tosparse().toarray(), lap.toarray()) + True + >>> lap.eigenvalues() + array([-5., -3., -3., -2., -1., 0.]) + >>> eigvals = eigvalsh(lap.toarray().astype(np.float64)) + >>> np.allclose(lap.eigenvalues(), eigvals) + True + >>> np.allclose(lap.toarray() @ lap.eigenvectors(), + ... lap.eigenvectors() @ np.diag(lap.eigenvalues())) + True + + """ + + def __init__(self, grid_shape, *, + boundary_conditions='neumann', + dtype=np.int8): + + if boundary_conditions not in ('dirichlet', 'neumann', 'periodic'): + raise ValueError( + f"Unknown value {boundary_conditions!r} is given for " + "'boundary_conditions' parameter. The valid options are " + "'dirichlet', 'periodic', and 'neumann' (default)." + ) + + self.grid_shape = grid_shape + self.boundary_conditions = boundary_conditions + # LaplacianNd folds all dimensions in `grid_shape` into a single one + N = np.prod(grid_shape) + super().__init__(dtype=dtype, shape=(N, N)) + + def _eigenvalue_ordering(self, m): + """Compute `m` largest eigenvalues in each of the ``N`` directions, + i.e., up to ``m * N`` total, order them and return `m` largest. + """ + grid_shape = self.grid_shape + if m is None: + indices = np.indices(grid_shape) + Leig = np.zeros(grid_shape) + else: + grid_shape_min = min(grid_shape, + tuple(np.ones_like(grid_shape) * m)) + indices = np.indices(grid_shape_min) + Leig = np.zeros(grid_shape_min) + + for j, n in zip(indices, grid_shape): + if self.boundary_conditions == 'dirichlet': + Leig += -4 * np.sin(np.pi * (j + 1) / (2 * (n + 1))) ** 2 + elif self.boundary_conditions == 'neumann': + Leig += -4 * np.sin(np.pi * j / (2 * n)) ** 2 + else: # boundary_conditions == 'periodic' + Leig += -4 * np.sin(np.pi * np.floor((j + 1) / 2) / n) ** 2 + + Leig_ravel = Leig.ravel() + ind = np.argsort(Leig_ravel) + eigenvalues = Leig_ravel[ind] + if m is not None: + eigenvalues = eigenvalues[-m:] + ind = ind[-m:] + + return eigenvalues, ind + + def eigenvalues(self, m=None): + """Return the requested number of eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of smallest eigenvalues to return. + If not provided, then all eigenvalues will be returned. + + Returns + ------- + eigenvalues : float array + The requested `m` smallest or all eigenvalues, in ascending order. + """ + eigenvalues, _ = self._eigenvalue_ordering(m) + return eigenvalues + + def _ev1d(self, j, n): + """Return 1 eigenvector in 1d with index `j` + and number of grid points `n` where ``j < n``. + """ + if self.boundary_conditions == 'dirichlet': + i = np.pi * (np.arange(n) + 1) / (n + 1) + ev = np.sqrt(2. / (n + 1.)) * np.sin(i * (j + 1)) + elif self.boundary_conditions == 'neumann': + i = np.pi * (np.arange(n) + 0.5) / n + ev = np.sqrt((1. if j == 0 else 2.) / n) * np.cos(i * j) + else: # boundary_conditions == 'periodic' + if j == 0: + ev = np.sqrt(1. / n) * np.ones(n) + elif j + 1 == n and n % 2 == 0: + ev = np.sqrt(1. / n) * np.tile([1, -1], n//2) + else: + i = 2. * np.pi * (np.arange(n) + 0.5) / n + ev = np.sqrt(2. / n) * np.cos(i * np.floor((j + 1) / 2)) + # make small values exact zeros correcting round-off errors + # due to symmetry of eigenvectors the exact 0. is correct + ev[np.abs(ev) < np.finfo(np.float64).eps] = 0. + return ev + + def _one_eve(self, k): + """Return 1 eigenvector in Nd with multi-index `j` + as a tensor product of the corresponding 1d eigenvectors. + """ + phi = [self._ev1d(j, n) for j, n in zip(k, self.grid_shape)] + result = phi[0] + for phi in phi[1:]: + result = np.tensordot(result, phi, axes=0) + return np.asarray(result).ravel() + + def eigenvectors(self, m=None): + """Return the requested number of eigenvectors for ordered eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of eigenvectors to return. If not provided, + then all eigenvectors will be returned. + + Returns + ------- + eigenvectors : float array + An array with columns made of the requested `m` or all eigenvectors. + The columns are ordered according to the `m` ordered eigenvalues. + """ + _, ind = self._eigenvalue_ordering(m) + if m is None: + grid_shape_min = self.grid_shape + else: + grid_shape_min = min(self.grid_shape, + tuple(np.ones_like(self.grid_shape) * m)) + + N_indices = np.unravel_index(ind, grid_shape_min) + N_indices = [tuple(x) for x in zip(*N_indices)] + eigenvectors_list = [self._one_eve(k) for k in N_indices] + return np.column_stack(eigenvectors_list) + + def toarray(self): + """ + Converts the Laplacian data to a dense array. + + Returns + ------- + L : ndarray + The shape is ``(N, N)`` where ``N = np.prod(grid_shape)``. + + """ + grid_shape = self.grid_shape + n = np.prod(grid_shape) + L = np.zeros([n, n], dtype=np.int8) + # Scratch arrays + L_i = np.empty_like(L) + Ltemp = np.empty_like(L) + + for ind, dim in enumerate(grid_shape): + # Start zeroing out L_i + L_i[:] = 0 + # Allocate the top left corner with the kernel of L_i + # Einsum returns writable view of arrays + np.einsum("ii->i", L_i[:dim, :dim])[:] = -2 + np.einsum("ii->i", L_i[: dim - 1, 1:dim])[:] = 1 + np.einsum("ii->i", L_i[1:dim, : dim - 1])[:] = 1 + + if self.boundary_conditions == 'neumann': + L_i[0, 0] = -1 + L_i[dim - 1, dim - 1] = -1 + elif self.boundary_conditions == 'periodic': + if dim > 1: + L_i[0, dim - 1] += 1 + L_i[dim - 1, 0] += 1 + else: + L_i[0, 0] += 1 + + # kron is too slow for large matrices hence the next two tricks + # 1- kron(eye, mat) is block_diag(mat, mat, ...) + # 2- kron(mat, eye) can be performed by 4d stride trick + + # 1- + new_dim = dim + # for block_diag we tile the top left portion on the diagonal + if ind > 0: + tiles = np.prod(grid_shape[:ind]) + for j in range(1, tiles): + L_i[j*dim:(j+1)*dim, j*dim:(j+1)*dim] = L_i[:dim, :dim] + new_dim += dim + # 2- + # we need the keep L_i, but reset the array + Ltemp[:new_dim, :new_dim] = L_i[:new_dim, :new_dim] + tiles = int(np.prod(grid_shape[ind+1:])) + # Zero out the top left, the rest is already 0 + L_i[:new_dim, :new_dim] = 0 + idx = [x for x in range(tiles)] + L_i.reshape( + (new_dim, tiles, + new_dim, tiles) + )[:, idx, :, idx] = Ltemp[:new_dim, :new_dim] + + L += L_i + + return L.astype(self.dtype) + + def tosparse(self): + """ + Constructs a sparse array from the Laplacian data. The returned sparse + array format is dependent on the selected boundary conditions. + + Returns + ------- + L : scipy.sparse.sparray + The shape is ``(N, N)`` where ``N = np.prod(grid_shape)``. + + """ + N = len(self.grid_shape) + p = np.prod(self.grid_shape) + L = dia_array((p, p), dtype=np.int8) + + for i in range(N): + dim = self.grid_shape[i] + data = np.ones([3, dim], dtype=np.int8) + data[1, :] *= -2 + + if self.boundary_conditions == 'neumann': + data[1, 0] = -1 + data[1, -1] = -1 + + L_i = dia_array((data, [-1, 0, 1]), shape=(dim, dim), + dtype=np.int8 + ) + + if self.boundary_conditions == 'periodic': + t = dia_array((dim, dim), dtype=np.int8) + t.setdiag([1], k=-dim+1) + t.setdiag([1], k=dim-1) + L_i += t + + for j in range(i): + L_i = kron(eye(self.grid_shape[j], dtype=np.int8), L_i) + for j in range(i + 1, N): + L_i = kron(L_i, eye(self.grid_shape[j], dtype=np.int8)) + L += L_i + return L.astype(self.dtype) + + def _matvec(self, x): + grid_shape = self.grid_shape + N = len(grid_shape) + X = x.reshape(grid_shape + (-1,)) + Y = -2 * N * X + for i in range(N): + Y += np.roll(X, 1, axis=i) + Y += np.roll(X, -1, axis=i) + if self.boundary_conditions in ('neumann', 'dirichlet'): + Y[(slice(None),)*i + (0,) + (slice(None),)*(N-i-1) + ] -= np.roll(X, 1, axis=i)[ + (slice(None),) * i + (0,) + (slice(None),) * (N-i-1) + ] + Y[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] -= np.roll(X, -1, axis=i)[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] + + if self.boundary_conditions == 'neumann': + Y[ + (slice(None),) * i + (0,) + (slice(None),) * (N-i-1) + ] += np.roll(X, 0, axis=i)[ + (slice(None),) * i + (0,) + (slice(None),) * (N-i-1) + ] + Y[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] += np.roll(X, 0, axis=i)[ + (slice(None),) * i + (-1,) + (slice(None),) * (N-i-1) + ] + + return Y.reshape(-1, X.shape[-1]) + + def _matmat(self, x): + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class Sakurai(LinearOperator): + """ + Construct a Sakurai matrix in various formats and its eigenvalues. + + Constructs the "Sakurai" matrix motivated by reference [1]_: + square real symmetric positive definite and 5-diagonal + with the main digonal ``[5, 6, 6, ..., 6, 6, 5], the ``+1`` and ``-1`` + diagonals filled with ``-4``, and the ``+2`` and ``-2`` diagonals + made of ``1``. Its eigenvalues are analytically known to be + ``16. * np.power(np.cos(0.5 * k * np.pi / (n + 1)), 4)``. + The matrix gets ill-conditioned with its size growing. + It is useful for testing and benchmarking sparse eigenvalue solvers + especially those taking advantage of its banded 5-diagonal structure. + See the notes below for details. + + Parameters + ---------- + n : int + The size of the matrix. + dtype : dtype + Numerical type of the array. Default is ``np.int8``. + + Methods + ------- + toarray() + Construct a dense array from Laplacian data + tosparse() + Construct a sparse array from Laplacian data + tobanded() + The Sakurai matrix in the format for banded symmetric matrices, + i.e., (3, n) ndarray with 3 upper diagonals + placing the main diagonal at the bottom. + eigenvalues + All eigenvalues of the Sakurai matrix ordered ascending. + + Notes + ----- + Reference [1]_ introduces a generalized eigenproblem for the matrix pair + `A` and `B` where `A` is the identity so we turn it into an eigenproblem + just for the matrix `B` that this function outputs in various formats + together with its eigenvalues. + + .. versionadded:: 1.12.0 + + References + ---------- + .. [1] T. Sakurai, H. Tadano, Y. Inadomi, and U. Nagashima, + "A moment-based method for large-scale generalized + eigenvalue problems", + Appl. Num. Anal. Comp. Math. Vol. 1 No. 2 (2004). + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg._special_sparse_arrays import Sakurai + >>> from scipy.linalg import eig_banded + >>> n = 6 + >>> sak = Sakurai(n) + + Since all matrix entries are small integers, ``'int8'`` is + the default dtype for storing matrix representations. + + >>> sak.toarray() + array([[ 5, -4, 1, 0, 0, 0], + [-4, 6, -4, 1, 0, 0], + [ 1, -4, 6, -4, 1, 0], + [ 0, 1, -4, 6, -4, 1], + [ 0, 0, 1, -4, 6, -4], + [ 0, 0, 0, 1, -4, 5]], dtype=int8) + >>> sak.tobanded() + array([[ 1, 1, 1, 1, 1, 1], + [-4, -4, -4, -4, -4, -4], + [ 5, 6, 6, 6, 6, 5]], dtype=int8) + >>> sak.tosparse() + + >>> np.array_equal(sak.dot(np.eye(n)), sak.tosparse().toarray()) + True + >>> sak.eigenvalues() + array([0.03922866, 0.56703972, 2.41789479, 5.97822974, + 10.54287655, 14.45473055]) + >>> sak.eigenvalues(2) + array([0.03922866, 0.56703972]) + + The banded form can be used in scipy functions for banded matrices, e.g., + + >>> e = eig_banded(sak.tobanded(), eigvals_only=True) + >>> np.allclose(sak.eigenvalues, e, atol= n * n * n * np.finfo(float).eps) + True + + """ + def __init__(self, n, dtype=np.int8): + self.n = n + self.dtype = dtype + shape = (n, n) + super().__init__(dtype, shape) + + def eigenvalues(self, m=None): + """Return the requested number of eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of smallest eigenvalues to return. + If not provided, then all eigenvalues will be returned. + + Returns + ------- + eigenvalues : `np.float64` array + The requested `m` smallest or all eigenvalues, in ascending order. + """ + if m is None: + m = self.n + k = np.arange(self.n + 1 -m, self.n + 1) + return np.flip(16. * np.power(np.cos(0.5 * k * np.pi / (self.n + 1)), 4)) + + def tobanded(self): + """ + Construct the Sakurai matrix as a banded array. + """ + d0 = np.r_[5, 6 * np.ones(self.n - 2, dtype=self.dtype), 5] + d1 = -4 * np.ones(self.n, dtype=self.dtype) + d2 = np.ones(self.n, dtype=self.dtype) + return np.array([d2, d1, d0]).astype(self.dtype) + + def tosparse(self): + """ + Construct the Sakurai matrix is a sparse format. + """ + from scipy.sparse import spdiags + d = self.tobanded() + # the banded format has the main diagonal at the bottom + # `spdiags` has no `dtype` parameter so inherits dtype from banded + return spdiags([d[0], d[1], d[2], d[1], d[0]], [-2, -1, 0, 1, 2], + self.n, self.n) + + def toarray(self): + return self.tosparse().toarray() + + def _matvec(self, x): + """ + Construct matrix-free callable banded-matrix-vector multiplication by + the Sakurai matrix without constructing or storing the matrix itself + using the knowledge of its entries and the 5-diagonal format. + """ + x = x.reshape(self.n, -1) + result_dtype = np.promote_types(x.dtype, self.dtype) + sx = np.zeros_like(x, dtype=result_dtype) + sx[0, :] = 5 * x[0, :] - 4 * x[1, :] + x[2, :] + sx[-1, :] = 5 * x[-1, :] - 4 * x[-2, :] + x[-3, :] + sx[1: -1, :] = (6 * x[1: -1, :] - 4 * (x[:-2, :] + x[2:, :]) + + np.pad(x[:-3, :], ((1, 0), (0, 0))) + + np.pad(x[3:, :], ((0, 1), (0, 0)))) + return sx + + def _matmat(self, x): + """ + Construct matrix-free callable matrix-matrix multiplication by + the Sakurai matrix without constructing or storing the matrix itself + by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``. + """ + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class MikotaM(LinearOperator): + """ + Construct a mass matrix in various formats of Mikota pair. + + The mass matrix `M` is square real diagonal + positive definite with entries that are reciprocal to integers. + + Parameters + ---------- + shape : tuple of int + The shape of the matrix. + dtype : dtype + Numerical type of the array. Default is ``np.float64``. + + Methods + ------- + toarray() + Construct a dense array from Mikota data + tosparse() + Construct a sparse array from Mikota data + tobanded() + The format for banded symmetric matrices, + i.e., (1, n) ndarray with the main diagonal. + """ + def __init__(self, shape, dtype=np.float64): + self.shape = shape + self.dtype = dtype + super().__init__(dtype, shape) + + def _diag(self): + # The matrix is constructed from its diagonal 1 / [1, ..., N+1]; + # compute in a function to avoid duplicated code & storage footprint + return (1. / np.arange(1, self.shape[0] + 1)).astype(self.dtype) + + def tobanded(self): + return self._diag() + + def tosparse(self): + from scipy.sparse import diags + return diags([self._diag()], [0], shape=self.shape, dtype=self.dtype) + + def toarray(self): + return np.diag(self._diag()).astype(self.dtype) + + def _matvec(self, x): + """ + Construct matrix-free callable banded-matrix-vector multiplication by + the Mikota mass matrix without constructing or storing the matrix itself + using the knowledge of its entries and the diagonal format. + """ + x = x.reshape(self.shape[0], -1) + return self._diag()[:, np.newaxis] * x + + def _matmat(self, x): + """ + Construct matrix-free callable matrix-matrix multiplication by + the Mikota mass matrix without constructing or storing the matrix itself + by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``. + """ + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class MikotaK(LinearOperator): + """ + Construct a stiffness matrix in various formats of Mikota pair. + + The stiffness matrix `K` is square real tri-diagonal symmetric + positive definite with integer entries. + + Parameters + ---------- + shape : tuple of int + The shape of the matrix. + dtype : dtype + Numerical type of the array. Default is ``np.int32``. + + Methods + ------- + toarray() + Construct a dense array from Mikota data + tosparse() + Construct a sparse array from Mikota data + tobanded() + The format for banded symmetric matrices, + i.e., (2, n) ndarray with 2 upper diagonals + placing the main diagonal at the bottom. + """ + def __init__(self, shape, dtype=np.int32): + self.shape = shape + self.dtype = dtype + super().__init__(dtype, shape) + # The matrix is constructed from its diagonals; + # we precompute these to avoid duplicating the computation + n = shape[0] + self._diag0 = np.arange(2 * n - 1, 0, -2, dtype=self.dtype) + self._diag1 = - np.arange(n - 1, 0, -1, dtype=self.dtype) + + def tobanded(self): + return np.array([np.pad(self._diag1, (1, 0), 'constant'), self._diag0]) + + def tosparse(self): + from scipy.sparse import diags + return diags([self._diag1, self._diag0, self._diag1], [-1, 0, 1], + shape=self.shape, dtype=self.dtype) + + def toarray(self): + return self.tosparse().toarray() + + def _matvec(self, x): + """ + Construct matrix-free callable banded-matrix-vector multiplication by + the Mikota stiffness matrix without constructing or storing the matrix + itself using the knowledge of its entries and the 3-diagonal format. + """ + x = x.reshape(self.shape[0], -1) + result_dtype = np.promote_types(x.dtype, self.dtype) + kx = np.zeros_like(x, dtype=result_dtype) + d1 = self._diag1 + d0 = self._diag0 + kx[0, :] = d0[0] * x[0, :] + d1[0] * x[1, :] + kx[-1, :] = d1[-1] * x[-2, :] + d0[-1] * x[-1, :] + kx[1: -1, :] = (d1[:-1, None] * x[: -2, :] + + d0[1: -1, None] * x[1: -1, :] + + d1[1:, None] * x[2:, :]) + return kx + + def _matmat(self, x): + """ + Construct matrix-free callable matrix-matrix multiplication by + the Stiffness mass matrix without constructing or storing the matrix itself + by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``. + """ + return self._matvec(x) + + def _adjoint(self): + return self + + def _transpose(self): + return self + + +class MikotaPair: + """ + Construct the Mikota pair of matrices in various formats and + eigenvalues of the generalized eigenproblem with them. + + The Mikota pair of matrices [1, 2]_ models a vibration problem + of a linear mass-spring system with the ends attached where + the stiffness of the springs and the masses increase along + the system length such that vibration frequencies are subsequent + integers 1, 2, ..., `n` where `n` is the number of the masses. Thus, + eigenvalues of the generalized eigenvalue problem for + the matrix pair `K` and `M` where `K` is the system stiffness matrix + and `M` is the system mass matrix are the squares of the integers, + i.e., 1, 4, 9, ..., ``n * n``. + + The stiffness matrix `K` is square real tri-diagonal symmetric + positive definite. The mass matrix `M` is diagonal with diagonal + entries 1, 1/2, 1/3, ...., ``1/n``. Both matrices get + ill-conditioned with `n` growing. + + Parameters + ---------- + n : int + The size of the matrices of the Mikota pair. + dtype : dtype + Numerical type of the array. Default is ``np.float64``. + + Attributes + ---------- + eigenvalues : 1D ndarray, ``np.uint64`` + All eigenvalues of the Mikota pair ordered ascending. + + Methods + ------- + MikotaK() + A `LinearOperator` custom object for the stiffness matrix. + MikotaM() + A `LinearOperator` custom object for the mass matrix. + + .. versionadded:: 1.12.0 + + References + ---------- + .. [1] J. Mikota, "Frequency tuning of chain structure multibody oscillators + to place the natural frequencies at omega1 and N-1 integer multiples + omega2,..., omegaN", Z. Angew. Math. Mech. 81 (2001), S2, S201-S202. + Appl. Num. Anal. Comp. Math. Vol. 1 No. 2 (2004). + .. [2] Peter C. Muller and Metin Gurgoze, + "Natural frequencies of a multi-degree-of-freedom vibration system", + Proc. Appl. Math. Mech. 6, 319-320 (2006). + http://dx.doi.org/10.1002/pamm.200610141. + + Examples + -------- + >>> import numpy as np + >>> from scipy.sparse.linalg._special_sparse_arrays import MikotaPair + >>> n = 6 + >>> mik = MikotaPair(n) + >>> mik_k = mik.k + >>> mik_m = mik.m + >>> mik_k.toarray() + array([[11., -5., 0., 0., 0., 0.], + [-5., 9., -4., 0., 0., 0.], + [ 0., -4., 7., -3., 0., 0.], + [ 0., 0., -3., 5., -2., 0.], + [ 0., 0., 0., -2., 3., -1.], + [ 0., 0., 0., 0., -1., 1.]]) + >>> mik_k.tobanded() + array([[ 0., -5., -4., -3., -2., -1.], + [11., 9., 7., 5., 3., 1.]]) + >>> mik_m.tobanded() + array([1. , 0.5 , 0.33333333, 0.25 , 0.2 , + 0.16666667]) + >>> mik_k.tosparse() + + >>> mik_m.tosparse() + + >>> np.array_equal(mik_k(np.eye(n)), mik_k.toarray()) + True + >>> np.array_equal(mik_m(np.eye(n)), mik_m.toarray()) + True + >>> mik.eigenvalues() + array([ 1, 4, 9, 16, 25, 36]) + >>> mik.eigenvalues(2) + array([ 1, 4]) + + """ + def __init__(self, n, dtype=np.float64): + self.n = n + self.dtype = dtype + self.shape = (n, n) + self.m = MikotaM(self.shape, self.dtype) + self.k = MikotaK(self.shape, self.dtype) + + def eigenvalues(self, m=None): + """Return the requested number of eigenvalues. + + Parameters + ---------- + m : int, optional + The positive number of smallest eigenvalues to return. + If not provided, then all eigenvalues will be returned. + + Returns + ------- + eigenvalues : `np.uint64` array + The requested `m` smallest or all eigenvalues, in ascending order. + """ + if m is None: + m = self.n + arange_plus1 = np.arange(1, m + 1, dtype=np.uint64) + return arange_plus1 * arange_plus1 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_svdp.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_svdp.py new file mode 100644 index 0000000000000000000000000000000000000000..9b85d6c7eefe59c7049f42e0c6ff00331085afa0 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/_svdp.py @@ -0,0 +1,315 @@ +""" +Python wrapper for PROPACK +-------------------------- + +PROPACK is a collection of Fortran routines for iterative computation +of partial SVDs of large matrices or linear operators. + +Based on BSD licensed pypropack project: + http://github.com/jakevdp/pypropack + Author: Jake Vanderplas + +PROPACK source is BSD licensed, and available at + http://soi.stanford.edu/~rmunk/PROPACK/ +""" + +__all__ = ['_svdp'] + +import numpy as np + +from scipy._lib._util import check_random_state +from scipy.sparse.linalg import aslinearoperator +from scipy.linalg import LinAlgError + +from ._propack import _spropack # type: ignore[attr-defined] +from ._propack import _dpropack # type: ignore[attr-defined] +from ._propack import _cpropack # type: ignore[attr-defined] +from ._propack import _zpropack # type: ignore[attr-defined] + + +_lansvd_dict = { + 'f': _spropack.slansvd, + 'd': _dpropack.dlansvd, + 'F': _cpropack.clansvd, + 'D': _zpropack.zlansvd, +} + + +_lansvd_irl_dict = { + 'f': _spropack.slansvd_irl, + 'd': _dpropack.dlansvd_irl, + 'F': _cpropack.clansvd_irl, + 'D': _zpropack.zlansvd_irl, +} + +_which_converter = { + 'LM': 'L', + 'SM': 'S', +} + + +class _AProd: + """ + Wrapper class for linear operator + + The call signature of the __call__ method matches the callback of + the PROPACK routines. + """ + def __init__(self, A): + try: + self.A = aslinearoperator(A) + except TypeError: + self.A = aslinearoperator(np.asarray(A)) + + def __call__(self, transa, m, n, x, y, sparm, iparm): + if transa == 'n': + y[:] = self.A.matvec(x) + else: + y[:] = self.A.rmatvec(x) + + @property + def shape(self): + return self.A.shape + + @property + def dtype(self): + try: + return self.A.dtype + except AttributeError: + return self.A.matvec(np.zeros(self.A.shape[1])).dtype + + +def _svdp(A, k, which='LM', irl_mode=True, kmax=None, + compute_u=True, compute_v=True, v0=None, full_output=False, tol=0, + delta=None, eta=None, anorm=0, cgs=False, elr=True, + min_relgap=0.002, shifts=None, maxiter=None, random_state=None): + """ + Compute the singular value decomposition of a linear operator using PROPACK + + Parameters + ---------- + A : array_like, sparse matrix, or LinearOperator + Operator for which SVD will be computed. If `A` is a LinearOperator + object, it must define both ``matvec`` and ``rmatvec`` methods. + k : int + Number of singular values/vectors to compute + which : {"LM", "SM"} + Which singular triplets to compute: + - 'LM': compute triplets corresponding to the `k` largest singular + values + - 'SM': compute triplets corresponding to the `k` smallest singular + values + `which='SM'` requires `irl_mode=True`. Computes largest singular + values by default. + irl_mode : bool, optional + If `True`, then compute SVD using IRL (implicitly restarted Lanczos) + mode. Default is `True`. + kmax : int, optional + Maximal number of iterations / maximal dimension of the Krylov + subspace. Default is ``10 * k``. + compute_u : bool, optional + If `True` (default) then compute left singular vectors, `u`. + compute_v : bool, optional + If `True` (default) then compute right singular vectors, `v`. + tol : float, optional + The desired relative accuracy for computed singular values. + If not specified, it will be set based on machine precision. + v0 : array_like, optional + Starting vector for iterations: must be of length ``A.shape[0]``. + If not specified, PROPACK will generate a starting vector. + full_output : bool, optional + If `True`, then return sigma_bound. Default is `False`. + delta : float, optional + Level of orthogonality to maintain between Lanczos vectors. + Default is set based on machine precision. + eta : float, optional + Orthogonality cutoff. During reorthogonalization, vectors with + component larger than `eta` along the Lanczos vector will be purged. + Default is set based on machine precision. + anorm : float, optional + Estimate of ``||A||``. Default is `0`. + cgs : bool, optional + If `True`, reorthogonalization is done using classical Gram-Schmidt. + If `False` (default), it is done using modified Gram-Schmidt. + elr : bool, optional + If `True` (default), then extended local orthogonality is enforced + when obtaining singular vectors. + min_relgap : float, optional + The smallest relative gap allowed between any shift in IRL mode. + Default is `0.001`. Accessed only if ``irl_mode=True``. + shifts : int, optional + Number of shifts per restart in IRL mode. Default is determined + to satisfy ``k <= min(kmax-shifts, m, n)``. Must be + >= 0, but choosing 0 might lead to performance degradation. + Accessed only if ``irl_mode=True``. + maxiter : int, optional + Maximum number of restarts in IRL mode. Default is `1000`. + Accessed only if ``irl_mode=True``. + random_state : {None, int, `numpy.random.Generator`, + `numpy.random.RandomState`}, optional + + Pseudorandom number generator state used to generate resamples. + + If `random_state` is ``None`` (or `np.random`), the + `numpy.random.RandomState` singleton is used. + If `random_state` is an int, a new ``RandomState`` instance is used, + seeded with `random_state`. + If `random_state` is already a ``Generator`` or ``RandomState`` + instance then that instance is used. + + Returns + ------- + u : ndarray + The `k` largest (``which="LM"``) or smallest (``which="SM"``) left + singular vectors, ``shape == (A.shape[0], 3)``, returned only if + ``compute_u=True``. + sigma : ndarray + The top `k` singular values, ``shape == (k,)`` + vt : ndarray + The `k` largest (``which="LM"``) or smallest (``which="SM"``) right + singular vectors, ``shape == (3, A.shape[1])``, returned only if + ``compute_v=True``. + sigma_bound : ndarray + the error bounds on the singular values sigma, returned only if + ``full_output=True``. + + """ + random_state = check_random_state(random_state) + + which = which.upper() + if which not in {'LM', 'SM'}: + raise ValueError("`which` must be either 'LM' or 'SM'") + if not irl_mode and which == 'SM': + raise ValueError("`which`='SM' requires irl_mode=True") + + aprod = _AProd(A) + typ = aprod.dtype.char + + try: + lansvd_irl = _lansvd_irl_dict[typ] + lansvd = _lansvd_dict[typ] + except KeyError: + # work with non-supported types using native system precision + if np.iscomplexobj(np.empty(0, dtype=typ)): + typ = np.dtype(complex).char + else: + typ = np.dtype(float).char + lansvd_irl = _lansvd_irl_dict[typ] + lansvd = _lansvd_dict[typ] + + m, n = aprod.shape + if (k < 1) or (k > min(m, n)): + raise ValueError("k must be positive and not greater than m or n") + + if kmax is None: + kmax = 10*k + if maxiter is None: + maxiter = 1000 + + # guard against unnecessarily large kmax + kmax = min(m + 1, n + 1, kmax) + if kmax < k: + raise ValueError( + "kmax must be greater than or equal to k, " + f"but kmax ({kmax}) < k ({k})") + + # convert python args to fortran args + jobu = 'y' if compute_u else 'n' + jobv = 'y' if compute_v else 'n' + + # these will be the output arrays + u = np.zeros((m, kmax + 1), order='F', dtype=typ) + v = np.zeros((n, kmax), order='F', dtype=typ) + + # Specify the starting vector. if v0 is all zero, PROPACK will generate + # a random starting vector: the random seed cannot be controlled in that + # case, so we'll instead use numpy to generate a random vector + if v0 is None: + u[:, 0] = random_state.uniform(size=m) + if np.iscomplexobj(np.empty(0, dtype=typ)): # complex type + u[:, 0] += 1j * random_state.uniform(size=m) + else: + try: + u[:, 0] = v0 + except ValueError: + raise ValueError(f"v0 must be of length {m}") + + # process options for the fit + if delta is None: + delta = np.sqrt(np.finfo(typ).eps) + if eta is None: + eta = np.finfo(typ).eps ** 0.75 + + if irl_mode: + doption = np.array((delta, eta, anorm, min_relgap), dtype=typ.lower()) + + # validate or find default shifts + if shifts is None: + shifts = kmax - k + if k > min(kmax - shifts, m, n): + raise ValueError('shifts must satisfy ' + 'k <= min(kmax-shifts, m, n)!') + elif shifts < 0: + raise ValueError('shifts must be >= 0!') + + else: + doption = np.array((delta, eta, anorm), dtype=typ.lower()) + + ioption = np.array((int(bool(cgs)), int(bool(elr))), dtype='i') + + # If computing `u` or `v` (left and right singular vectors, + # respectively), `blocksize` controls how large a fraction of the + # work is done via fast BLAS level 3 operations. A larger blocksize + # may lead to faster computation at the expense of greater memory + # consumption. `blocksize` must be ``>= 1``. Choosing blocksize + # of 16, but docs don't specify; it's almost surely a + # power of 2. + blocksize = 16 + + # Determine lwork & liwork: + # the required lengths are specified in the PROPACK documentation + if compute_u or compute_v: + lwork = m + n + 9*kmax + 5*kmax*kmax + 4 + max( + 3*kmax*kmax + 4*kmax + 4, + blocksize*max(m, n)) + liwork = 8*kmax + else: + lwork = m + n + 9*kmax + 2*kmax*kmax + 4 + max(m + n, 4*kmax + 4) + liwork = 2*kmax + 1 + work = np.empty(lwork, dtype=typ.lower()) + iwork = np.empty(liwork, dtype=np.int32) + + # dummy arguments: these are passed to aprod, and not used in this wrapper + dparm = np.empty(1, dtype=typ.lower()) + iparm = np.empty(1, dtype=np.int32) + + if typ.isupper(): + # PROPACK documentation is unclear on the required length of zwork. + # Use the same length Julia's wrapper uses + # see https://github.com/JuliaSmoothOptimizers/PROPACK.jl/ + zwork = np.empty(m + n + 32*m, dtype=typ) + works = work, zwork, iwork + else: + works = work, iwork + + if irl_mode: + u, sigma, bnd, v, info = lansvd_irl(_which_converter[which], jobu, + jobv, m, n, shifts, k, maxiter, + aprod, u, v, tol, *works, doption, + ioption, dparm, iparm) + else: + u, sigma, bnd, v, info = lansvd(jobu, jobv, m, n, k, aprod, u, v, tol, + *works, doption, ioption, dparm, iparm) + + if info > 0: + raise LinAlgError( + f"An invariant subspace of dimension {info} was found.") + elif info < 0: + raise LinAlgError( + f"k={k} singular triplets did not converge within " + f"kmax={kmax} iterations") + + # info == 0: The K largest (or smallest) singular triplets were computed + # successfully! + + return u[:, :k], sigma, v[:, :k].conj().T, bnd diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/dsolve.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/dsolve.py new file mode 100644 index 0000000000000000000000000000000000000000..45139f6b280d047386652577d9e1c8d2aaeb7033 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/dsolve.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'MatrixRankWarning', 'SuperLU', 'factorized', + 'spilu', 'splu', 'spsolve', + 'spsolve_triangular', 'use_solver', 'test' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="dsolve", + private_modules=["_dsolve"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/eigen.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/eigen.py new file mode 100644 index 0000000000000000000000000000000000000000..588986d6650aad334e6a9a682ed76cef94295298 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/eigen.py @@ -0,0 +1,21 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'ArpackError', 'ArpackNoConvergence', 'ArpackError', + 'eigs', 'eigsh', 'lobpcg', 'svds', 'test' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="eigen", + private_modules=["_eigen"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/interface.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/interface.py new file mode 100644 index 0000000000000000000000000000000000000000..24f40f185b1328b16e7e239e5a165cc6b1ed4317 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/interface.py @@ -0,0 +1,20 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'LinearOperator', 'aslinearoperator', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="interface", + private_modules=["_interface"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/isolve.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/isolve.py new file mode 100644 index 0000000000000000000000000000000000000000..e032ddd9c673be3bc8790adad3bdae1839127050 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/isolve.py @@ -0,0 +1,22 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = [ # noqa: F822 + 'bicg', 'bicgstab', 'cg', 'cgs', 'gcrotmk', 'gmres', + 'lgmres', 'lsmr', 'lsqr', + 'minres', 'qmr', 'tfqmr', 'test' +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="isolve", + private_modules=["_isolve"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/matfuncs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/matfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..8ed877ff1aa6f5a5466ce94729b9225dcce37b36 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/matfuncs.py @@ -0,0 +1,18 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse.linalg` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + + +__all__ = ["expm", "inv", "spsolve", "LinearOperator"] # noqa: F822 + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse.linalg", module="matfuncs", + private_modules=["_matfuncs"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c218d46ba281d48572015dabc9c68e0ad5f37306 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_expm_multiply.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_expm_multiply.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1d7d2871c2a1b6838f6e1393d68b4bb9328f9cc Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_expm_multiply.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_interface.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_interface.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef607729c920b9e454ba3c4d862382590482d848 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_interface.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_matfuncs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_matfuncs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e9985a7bfe48fc93ff0231a6ae781e42aafbb4f Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_matfuncs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_norm.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_norm.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7af66930a21c31122675fbe97465d446520d80e Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_norm.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_onenormest.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_onenormest.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ede1dbdebc8c1c9543b4e87997016f379ecde302 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_onenormest.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_propack.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_propack.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0cd82dcece996600bf221768588581751131172 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_propack.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_pydata_sparse.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_pydata_sparse.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ba82c0f01827c4c2a51cc1f87ad574af75b420d Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_pydata_sparse.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_special_sparse_arrays.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_special_sparse_arrays.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93a8d97ea642f1f09e8d0e8630ec51010704e0e3 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/__pycache__/test_special_sparse_arrays.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_expm_multiply.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_expm_multiply.py new file mode 100644 index 0000000000000000000000000000000000000000..858ce11b9d4b3370534dd6c8887676ff00501e17 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_expm_multiply.py @@ -0,0 +1,349 @@ +"""Test functions for the sparse.linalg._expm_multiply module.""" +from functools import partial +from itertools import product + +import numpy as np +import pytest +from numpy.testing import (assert_allclose, assert_, assert_equal, + suppress_warnings) +from scipy.sparse import SparseEfficiencyWarning +from scipy.sparse.linalg import aslinearoperator +import scipy.linalg +from scipy.sparse.linalg import expm as sp_expm +from scipy.sparse.linalg._expm_multiply import (_theta, _compute_p_max, + _onenormest_matrix_power, expm_multiply, _expm_multiply_simple, + _expm_multiply_interval) +from scipy._lib._util import np_long + + +IMPRECISE = {np.single, np.csingle} +REAL_DTYPES = {np.intc, np_long, np.longlong, + np.float32, np.float64, np.longdouble} +COMPLEX_DTYPES = {np.complex64, np.complex128, np.clongdouble} +# use sorted list to ensure fixed order of tests +DTYPES = sorted(REAL_DTYPES ^ COMPLEX_DTYPES, key=str) + + +def estimated(func): + """If trace is estimated, it should warn. + + We warn that estimation of trace might impact performance. + All result have to be correct nevertheless! + + """ + def wrapped(*args, **kwds): + with pytest.warns(UserWarning, + match="Trace of LinearOperator not available"): + return func(*args, **kwds) + return wrapped + + +def less_than_or_close(a, b): + return np.allclose(a, b) or (a < b) + + +class TestExpmActionSimple: + """ + These tests do not consider the case of multiple time steps in one call. + """ + + def test_theta_monotonicity(self): + pairs = sorted(_theta.items()) + for (m_a, theta_a), (m_b, theta_b) in zip(pairs[:-1], pairs[1:]): + assert_(theta_a < theta_b) + + def test_p_max_default(self): + m_max = 55 + expected_p_max = 8 + observed_p_max = _compute_p_max(m_max) + assert_equal(observed_p_max, expected_p_max) + + def test_p_max_range(self): + for m_max in range(1, 55+1): + p_max = _compute_p_max(m_max) + assert_(p_max*(p_max - 1) <= m_max + 1) + p_too_big = p_max + 1 + assert_(p_too_big*(p_too_big - 1) > m_max + 1) + + def test_onenormest_matrix_power(self): + np.random.seed(1234) + n = 40 + nsamples = 10 + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + for p in range(4): + if not p: + M = np.identity(n) + else: + M = np.dot(M, A) + estimated = _onenormest_matrix_power(A, p) + exact = np.linalg.norm(M, 1) + assert_(less_than_or_close(estimated, exact)) + assert_(less_than_or_close(exact, 3*estimated)) + + def test_expm_multiply(self): + np.random.seed(1234) + n = 40 + k = 3 + nsamples = 10 + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + B = np.random.randn(n, k) + observed = expm_multiply(A, B) + expected = np.dot(sp_expm(A), B) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), B) + assert_allclose(observed, expected) + traceA = np.trace(A) + observed = expm_multiply(aslinearoperator(A), B, traceA=traceA) + assert_allclose(observed, expected) + + def test_matrix_vector_multiply(self): + np.random.seed(1234) + n = 40 + nsamples = 10 + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + v = np.random.randn(n) + observed = expm_multiply(A, v) + expected = np.dot(sp_expm(A), v) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), v) + assert_allclose(observed, expected) + + def test_scaled_expm_multiply(self): + np.random.seed(1234) + n = 40 + k = 3 + nsamples = 10 + for i, t in product(range(nsamples), [0.2, 1.0, 1.5]): + with np.errstate(invalid='ignore'): + A = scipy.linalg.inv(np.random.randn(n, n)) + B = np.random.randn(n, k) + observed = _expm_multiply_simple(A, B, t=t) + expected = np.dot(sp_expm(t*A), B) + assert_allclose(observed, expected) + observed = estimated(_expm_multiply_simple)( + aslinearoperator(A), B, t=t + ) + assert_allclose(observed, expected) + + def test_scaled_expm_multiply_single_timepoint(self): + np.random.seed(1234) + t = 0.1 + n = 5 + k = 2 + A = np.random.randn(n, n) + B = np.random.randn(n, k) + observed = _expm_multiply_simple(A, B, t=t) + expected = sp_expm(t*A).dot(B) + assert_allclose(observed, expected) + observed = estimated(_expm_multiply_simple)( + aslinearoperator(A), B, t=t + ) + assert_allclose(observed, expected) + + def test_sparse_expm_multiply(self): + np.random.seed(1234) + n = 40 + k = 3 + nsamples = 10 + for i in range(nsamples): + A = scipy.sparse.rand(n, n, density=0.05) + B = np.random.randn(n, k) + observed = expm_multiply(A, B) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format") + sup.filter(SparseEfficiencyWarning, + "spsolve is more efficient when sparse b is in the" + " CSC matrix format") + expected = sp_expm(A).dot(B) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), B) + assert_allclose(observed, expected) + + def test_complex(self): + A = np.array([ + [1j, 1j], + [0, 1j]], dtype=complex) + B = np.array([1j, 1j]) + observed = expm_multiply(A, B) + expected = np.array([ + 1j * np.exp(1j) + 1j * (1j*np.cos(1) - np.sin(1)), + 1j * np.exp(1j)], dtype=complex) + assert_allclose(observed, expected) + observed = estimated(expm_multiply)(aslinearoperator(A), B) + assert_allclose(observed, expected) + + +class TestExpmActionInterval: + + @pytest.mark.fail_slow(5) + def test_sparse_expm_multiply_interval(self): + np.random.seed(1234) + start = 0.1 + stop = 3.2 + n = 40 + k = 3 + endpoint = True + for num in (14, 13, 2): + A = scipy.sparse.rand(n, n, density=0.05) + B = np.random.randn(n, k) + v = np.random.randn(n) + for target in (B, v): + X = expm_multiply(A, target, start=start, stop=stop, + num=num, endpoint=endpoint) + samples = np.linspace(start=start, stop=stop, + num=num, endpoint=endpoint) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format") + sup.filter(SparseEfficiencyWarning, + "spsolve is more efficient when sparse b is in" + " the CSC matrix format") + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(target)) + + @pytest.mark.fail_slow(5) + def test_expm_multiply_interval_vector(self): + np.random.seed(1234) + interval = {'start': 0.1, 'stop': 3.2, 'endpoint': True} + for num, n in product([14, 13, 2], [1, 2, 5, 20, 40]): + A = scipy.linalg.inv(np.random.randn(n, n)) + v = np.random.randn(n) + samples = np.linspace(num=num, **interval) + X = expm_multiply(A, v, num=num, **interval) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(v)) + # test for linear operator with unknown trace -> estimate trace + Xguess = estimated(expm_multiply)(aslinearoperator(A), v, + num=num, **interval) + # test for linear operator with given trace + Xgiven = expm_multiply(aslinearoperator(A), v, num=num, **interval, + traceA=np.trace(A)) + # test robustness for linear operator with wrong trace + Xwrong = expm_multiply(aslinearoperator(A), v, num=num, **interval, + traceA=np.trace(A)*5) + for sol_guess, sol_given, sol_wrong, t in zip(Xguess, Xgiven, + Xwrong, samples): + correct = sp_expm(t*A).dot(v) + assert_allclose(sol_guess, correct) + assert_allclose(sol_given, correct) + assert_allclose(sol_wrong, correct) + + @pytest.mark.fail_slow(5) + def test_expm_multiply_interval_matrix(self): + np.random.seed(1234) + interval = {'start': 0.1, 'stop': 3.2, 'endpoint': True} + for num, n, k in product([14, 13, 2], [1, 2, 5, 20, 40], [1, 2]): + A = scipy.linalg.inv(np.random.randn(n, n)) + B = np.random.randn(n, k) + samples = np.linspace(num=num, **interval) + X = expm_multiply(A, B, num=num, **interval) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(B)) + X = estimated(expm_multiply)(aslinearoperator(A), B, num=num, + **interval) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(B)) + + def test_sparse_expm_multiply_interval_dtypes(self): + # Test A & B int + A = scipy.sparse.diags(np.arange(5),format='csr', dtype=int) + B = np.ones(5, dtype=int) + Aexpm = scipy.sparse.diags(np.exp(np.arange(5)),format='csr') + assert_allclose(expm_multiply(A,B,0,1)[-1], Aexpm.dot(B)) + + # Test A complex, B int + A = scipy.sparse.diags(-1j*np.arange(5),format='csr', dtype=complex) + B = np.ones(5, dtype=int) + Aexpm = scipy.sparse.diags(np.exp(-1j*np.arange(5)),format='csr') + assert_allclose(expm_multiply(A,B,0,1)[-1], Aexpm.dot(B)) + + # Test A int, B complex + A = scipy.sparse.diags(np.arange(5),format='csr', dtype=int) + B = np.full(5, 1j, dtype=complex) + Aexpm = scipy.sparse.diags(np.exp(np.arange(5)),format='csr') + assert_allclose(expm_multiply(A,B,0,1)[-1], Aexpm.dot(B)) + + def test_expm_multiply_interval_status_0(self): + self._help_test_specific_expm_interval_status(0) + + def test_expm_multiply_interval_status_1(self): + self._help_test_specific_expm_interval_status(1) + + def test_expm_multiply_interval_status_2(self): + self._help_test_specific_expm_interval_status(2) + + def _help_test_specific_expm_interval_status(self, target_status): + np.random.seed(1234) + start = 0.1 + stop = 3.2 + num = 13 + endpoint = True + n = 5 + k = 2 + nrepeats = 10 + nsuccesses = 0 + for num in [14, 13, 2] * nrepeats: + A = np.random.randn(n, n) + B = np.random.randn(n, k) + status = _expm_multiply_interval(A, B, + start=start, stop=stop, num=num, endpoint=endpoint, + status_only=True) + if status == target_status: + X, status = _expm_multiply_interval(A, B, + start=start, stop=stop, num=num, endpoint=endpoint, + status_only=False) + assert_equal(X.shape, (num, n, k)) + samples = np.linspace(start=start, stop=stop, + num=num, endpoint=endpoint) + for solution, t in zip(X, samples): + assert_allclose(solution, sp_expm(t*A).dot(B)) + nsuccesses += 1 + if not nsuccesses: + msg = 'failed to find a status-' + str(target_status) + ' interval' + raise Exception(msg) + + +@pytest.mark.parametrize("dtype_a", DTYPES) +@pytest.mark.parametrize("dtype_b", DTYPES) +@pytest.mark.parametrize("b_is_matrix", [False, True]) +def test_expm_multiply_dtype(dtype_a, dtype_b, b_is_matrix): + """Make sure `expm_multiply` handles all numerical dtypes correctly.""" + assert_allclose_ = (partial(assert_allclose, rtol=1.2e-3, atol=1e-5) + if {dtype_a, dtype_b} & IMPRECISE else assert_allclose) + rng = np.random.default_rng(1234) + # test data + n = 7 + b_shape = (n, 3) if b_is_matrix else (n, ) + if dtype_a in REAL_DTYPES: + A = scipy.linalg.inv(rng.random([n, n])).astype(dtype_a) + else: + A = scipy.linalg.inv( + rng.random([n, n]) + 1j*rng.random([n, n]) + ).astype(dtype_a) + if dtype_b in REAL_DTYPES: + B = (2*rng.random(b_shape)).astype(dtype_b) + else: + B = (rng.random(b_shape) + 1j*rng.random(b_shape)).astype(dtype_b) + + # single application + sol_mat = expm_multiply(A, B) + sol_op = estimated(expm_multiply)(aslinearoperator(A), B) + direct_sol = np.dot(sp_expm(A), B) + assert_allclose_(sol_mat, direct_sol) + assert_allclose_(sol_op, direct_sol) + sol_op = expm_multiply(aslinearoperator(A), B, traceA=np.trace(A)) + assert_allclose_(sol_op, direct_sol) + + # for time points + interval = {'start': 0.1, 'stop': 3.2, 'num': 13, 'endpoint': True} + samples = np.linspace(**interval) + X_mat = expm_multiply(A, B, **interval) + X_op = estimated(expm_multiply)(aslinearoperator(A), B, **interval) + for sol_mat, sol_op, t in zip(X_mat, X_op, samples): + direct_sol = sp_expm(t*A).dot(B) + assert_allclose_(sol_mat, direct_sol) + assert_allclose_(sol_op, direct_sol) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_interface.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..217946e23358ec6250a03c6bb10e39615638b493 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_interface.py @@ -0,0 +1,481 @@ +"""Test functions for the sparse.linalg._interface module +""" + +from functools import partial +from itertools import product +import operator +from pytest import raises as assert_raises, warns +from numpy.testing import assert_, assert_equal + +import numpy as np +import scipy.sparse as sparse + +import scipy.sparse.linalg._interface as interface +from scipy.sparse._sputils import matrix + + +class TestLinearOperator: + def setup_method(self): + self.A = np.array([[1,2,3], + [4,5,6]]) + self.B = np.array([[1,2], + [3,4], + [5,6]]) + self.C = np.array([[1,2], + [3,4]]) + + def test_matvec(self): + def get_matvecs(A): + return [{ + 'shape': A.shape, + 'matvec': lambda x: np.dot(A, x).reshape(A.shape[0]), + 'rmatvec': lambda x: np.dot(A.T.conj(), + x).reshape(A.shape[1]) + }, + { + 'shape': A.shape, + 'matvec': lambda x: np.dot(A, x), + 'rmatvec': lambda x: np.dot(A.T.conj(), x), + 'rmatmat': lambda x: np.dot(A.T.conj(), x), + 'matmat': lambda x: np.dot(A, x) + }] + + for matvecs in get_matvecs(self.A): + A = interface.LinearOperator(**matvecs) + + assert_(A.args == ()) + + assert_equal(A.matvec(np.array([1,2,3])), [14,32]) + assert_equal(A.matvec(np.array([[1],[2],[3]])), [[14],[32]]) + assert_equal(A * np.array([1,2,3]), [14,32]) + assert_equal(A * np.array([[1],[2],[3]]), [[14],[32]]) + assert_equal(A.dot(np.array([1,2,3])), [14,32]) + assert_equal(A.dot(np.array([[1],[2],[3]])), [[14],[32]]) + + assert_equal(A.matvec(matrix([[1],[2],[3]])), [[14],[32]]) + assert_equal(A * matrix([[1],[2],[3]]), [[14],[32]]) + assert_equal(A.dot(matrix([[1],[2],[3]])), [[14],[32]]) + + assert_equal((2*A)*[1,1,1], [12,30]) + assert_equal((2 * A).rmatvec([1, 1]), [10, 14, 18]) + assert_equal((2*A).H.matvec([1,1]), [10, 14, 18]) + assert_equal((2*A)*[[1],[1],[1]], [[12],[30]]) + assert_equal((2 * A).matmat([[1], [1], [1]]), [[12], [30]]) + assert_equal((A*2)*[1,1,1], [12,30]) + assert_equal((A*2)*[[1],[1],[1]], [[12],[30]]) + assert_equal((2j*A)*[1,1,1], [12j,30j]) + assert_equal((A+A)*[1,1,1], [12, 30]) + assert_equal((A + A).rmatvec([1, 1]), [10, 14, 18]) + assert_equal((A+A).H.matvec([1,1]), [10, 14, 18]) + assert_equal((A+A)*[[1],[1],[1]], [[12], [30]]) + assert_equal((A+A).matmat([[1],[1],[1]]), [[12], [30]]) + assert_equal((-A)*[1,1,1], [-6,-15]) + assert_equal((-A)*[[1],[1],[1]], [[-6],[-15]]) + assert_equal((A-A)*[1,1,1], [0,0]) + assert_equal((A - A) * [[1], [1], [1]], [[0], [0]]) + + X = np.array([[1, 2], [3, 4]]) + # A_asarray = np.array([[1, 2, 3], [4, 5, 6]]) + assert_equal((2 * A).rmatmat(X), np.dot((2 * self.A).T, X)) + assert_equal((A * 2).rmatmat(X), np.dot((self.A * 2).T, X)) + assert_equal((2j * A).rmatmat(X), + np.dot((2j * self.A).T.conj(), X)) + assert_equal((A * 2j).rmatmat(X), + np.dot((self.A * 2j).T.conj(), X)) + assert_equal((A + A).rmatmat(X), + np.dot((self.A + self.A).T, X)) + assert_equal((A + 2j * A).rmatmat(X), + np.dot((self.A + 2j * self.A).T.conj(), X)) + assert_equal((-A).rmatmat(X), np.dot((-self.A).T, X)) + assert_equal((A - A).rmatmat(X), + np.dot((self.A - self.A).T, X)) + assert_equal((2j * A).rmatmat(2j * X), + np.dot((2j * self.A).T.conj(), 2j * X)) + + z = A+A + assert_(len(z.args) == 2 and z.args[0] is A and z.args[1] is A) + z = 2*A + assert_(len(z.args) == 2 and z.args[0] is A and z.args[1] == 2) + + assert_(isinstance(A.matvec([1, 2, 3]), np.ndarray)) + assert_(isinstance(A.matvec(np.array([[1],[2],[3]])), np.ndarray)) + assert_(isinstance(A * np.array([1,2,3]), np.ndarray)) + assert_(isinstance(A * np.array([[1],[2],[3]]), np.ndarray)) + assert_(isinstance(A.dot(np.array([1,2,3])), np.ndarray)) + assert_(isinstance(A.dot(np.array([[1],[2],[3]])), np.ndarray)) + + assert_(isinstance(A.matvec(matrix([[1],[2],[3]])), np.ndarray)) + assert_(isinstance(A * matrix([[1],[2],[3]]), np.ndarray)) + assert_(isinstance(A.dot(matrix([[1],[2],[3]])), np.ndarray)) + + assert_(isinstance(2*A, interface._ScaledLinearOperator)) + assert_(isinstance(2j*A, interface._ScaledLinearOperator)) + assert_(isinstance(A+A, interface._SumLinearOperator)) + assert_(isinstance(-A, interface._ScaledLinearOperator)) + assert_(isinstance(A-A, interface._SumLinearOperator)) + assert_(isinstance(A/2, interface._ScaledLinearOperator)) + assert_(isinstance(A/2j, interface._ScaledLinearOperator)) + assert_(((A * 3) / 3).args[0] is A) # check for simplification + + # Test that prefactor is of _ScaledLinearOperator is not mutated + # when the operator is multiplied by a number + result = A @ np.array([1, 2, 3]) + B = A * 3 + C = A / 5 + assert_equal(A @ np.array([1, 2, 3]), result) + + assert_((2j*A).dtype == np.complex128) + + # Test division by non-scalar + msg = "Can only divide a linear operator by a scalar." + with assert_raises(ValueError, match=msg): + A / np.array([1, 2]) + + assert_raises(ValueError, A.matvec, np.array([1,2])) + assert_raises(ValueError, A.matvec, np.array([1,2,3,4])) + assert_raises(ValueError, A.matvec, np.array([[1],[2]])) + assert_raises(ValueError, A.matvec, np.array([[1],[2],[3],[4]])) + + assert_raises(ValueError, lambda: A*A) + assert_raises(ValueError, lambda: A**2) + + for matvecsA, matvecsB in product(get_matvecs(self.A), + get_matvecs(self.B)): + A = interface.LinearOperator(**matvecsA) + B = interface.LinearOperator(**matvecsB) + # AtimesB = np.array([[22, 28], [49, 64]]) + AtimesB = self.A.dot(self.B) + X = np.array([[1, 2], [3, 4]]) + + assert_equal((A * B).rmatmat(X), np.dot((AtimesB).T, X)) + assert_equal((2j * A * B).rmatmat(X), + np.dot((2j * AtimesB).T.conj(), X)) + + assert_equal((A*B)*[1,1], [50,113]) + assert_equal((A*B)*[[1],[1]], [[50],[113]]) + assert_equal((A*B).matmat([[1],[1]]), [[50],[113]]) + + assert_equal((A * B).rmatvec([1, 1]), [71, 92]) + assert_equal((A * B).H.matvec([1, 1]), [71, 92]) + + assert_(isinstance(A*B, interface._ProductLinearOperator)) + + assert_raises(ValueError, lambda: A+B) + assert_raises(ValueError, lambda: A**2) + + z = A*B + assert_(len(z.args) == 2 and z.args[0] is A and z.args[1] is B) + + for matvecsC in get_matvecs(self.C): + C = interface.LinearOperator(**matvecsC) + X = np.array([[1, 2], [3, 4]]) + + assert_equal(C.rmatmat(X), np.dot((self.C).T, X)) + assert_equal((C**2).rmatmat(X), + np.dot((np.dot(self.C, self.C)).T, X)) + + assert_equal((C**2)*[1,1], [17,37]) + assert_equal((C**2).rmatvec([1, 1]), [22, 32]) + assert_equal((C**2).H.matvec([1, 1]), [22, 32]) + assert_equal((C**2).matmat([[1],[1]]), [[17],[37]]) + + assert_(isinstance(C**2, interface._PowerLinearOperator)) + + def test_matmul(self): + D = {'shape': self.A.shape, + 'matvec': lambda x: np.dot(self.A, x).reshape(self.A.shape[0]), + 'rmatvec': lambda x: np.dot(self.A.T.conj(), + x).reshape(self.A.shape[1]), + 'rmatmat': lambda x: np.dot(self.A.T.conj(), x), + 'matmat': lambda x: np.dot(self.A, x)} + A = interface.LinearOperator(**D) + B = np.array([[1 + 1j, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + b = B[0] + + assert_equal(operator.matmul(A, b), A * b) + assert_equal(operator.matmul(A, b.reshape(-1, 1)), A * b.reshape(-1, 1)) + assert_equal(operator.matmul(A, B), A * B) + assert_equal(operator.matmul(b, A.H), b * A.H) + assert_equal(operator.matmul(b.reshape(1, -1), A.H), b.reshape(1, -1) * A.H) + assert_equal(operator.matmul(B, A.H), B * A.H) + assert_raises(ValueError, operator.matmul, A, 2) + assert_raises(ValueError, operator.matmul, 2, A) + + +class TestAsLinearOperator: + def setup_method(self): + self.cases = [] + + def make_cases(original, dtype): + cases = [] + + cases.append((matrix(original, dtype=dtype), original)) + cases.append((np.array(original, dtype=dtype), original)) + cases.append((sparse.csr_matrix(original, dtype=dtype), original)) + + # Test default implementations of _adjoint and _rmatvec, which + # refer to each other. + def mv(x, dtype): + y = original.dot(x) + if len(x.shape) == 2: + y = y.reshape(-1, 1) + return y + + def rmv(x, dtype): + return original.T.conj().dot(x) + + class BaseMatlike(interface.LinearOperator): + args = () + + def __init__(self, dtype): + self.dtype = np.dtype(dtype) + self.shape = original.shape + + def _matvec(self, x): + return mv(x, self.dtype) + + class HasRmatvec(BaseMatlike): + args = () + + def _rmatvec(self,x): + return rmv(x, self.dtype) + + class HasAdjoint(BaseMatlike): + args = () + + def _adjoint(self): + shape = self.shape[1], self.shape[0] + matvec = partial(rmv, dtype=self.dtype) + rmatvec = partial(mv, dtype=self.dtype) + return interface.LinearOperator(matvec=matvec, + rmatvec=rmatvec, + dtype=self.dtype, + shape=shape) + + class HasRmatmat(HasRmatvec): + def _matmat(self, x): + return original.dot(x) + + def _rmatmat(self, x): + return original.T.conj().dot(x) + + cases.append((HasRmatvec(dtype), original)) + cases.append((HasAdjoint(dtype), original)) + cases.append((HasRmatmat(dtype), original)) + return cases + + original = np.array([[1,2,3], [4,5,6]]) + self.cases += make_cases(original, np.int32) + self.cases += make_cases(original, np.float32) + self.cases += make_cases(original, np.float64) + self.cases += [(interface.aslinearoperator(M).T, A.T) + for M, A in make_cases(original.T, np.float64)] + self.cases += [(interface.aslinearoperator(M).H, A.T.conj()) + for M, A in make_cases(original.T, np.float64)] + + original = np.array([[1, 2j, 3j], [4j, 5j, 6]]) + self.cases += make_cases(original, np.complex128) + self.cases += [(interface.aslinearoperator(M).T, A.T) + for M, A in make_cases(original.T, np.complex128)] + self.cases += [(interface.aslinearoperator(M).H, A.T.conj()) + for M, A in make_cases(original.T, np.complex128)] + + def test_basic(self): + + for M, A_array in self.cases: + A = interface.aslinearoperator(M) + M,N = A.shape + + xs = [np.array([1, 2, 3]), + np.array([[1], [2], [3]])] + ys = [np.array([1, 2]), np.array([[1], [2]])] + + if A.dtype == np.complex128: + xs += [np.array([1, 2j, 3j]), + np.array([[1], [2j], [3j]])] + ys += [np.array([1, 2j]), np.array([[1], [2j]])] + + x2 = np.array([[1, 4], [2, 5], [3, 6]]) + + for x in xs: + assert_equal(A.matvec(x), A_array.dot(x)) + assert_equal(A * x, A_array.dot(x)) + + assert_equal(A.matmat(x2), A_array.dot(x2)) + assert_equal(A * x2, A_array.dot(x2)) + + for y in ys: + assert_equal(A.rmatvec(y), A_array.T.conj().dot(y)) + assert_equal(A.T.matvec(y), A_array.T.dot(y)) + assert_equal(A.H.matvec(y), A_array.T.conj().dot(y)) + + for y in ys: + if y.ndim < 2: + continue + assert_equal(A.rmatmat(y), A_array.T.conj().dot(y)) + assert_equal(A.T.matmat(y), A_array.T.dot(y)) + assert_equal(A.H.matmat(y), A_array.T.conj().dot(y)) + + if hasattr(M,'dtype'): + assert_equal(A.dtype, M.dtype) + + assert_(hasattr(A, 'args')) + + def test_dot(self): + + for M, A_array in self.cases: + A = interface.aslinearoperator(M) + M,N = A.shape + + x0 = np.array([1, 2, 3]) + x1 = np.array([[1], [2], [3]]) + x2 = np.array([[1, 4], [2, 5], [3, 6]]) + + assert_equal(A.dot(x0), A_array.dot(x0)) + assert_equal(A.dot(x1), A_array.dot(x1)) + assert_equal(A.dot(x2), A_array.dot(x2)) + + +def test_repr(): + A = interface.LinearOperator(shape=(1, 1), matvec=lambda x: 1) + repr_A = repr(A) + assert_('unspecified dtype' not in repr_A, repr_A) + + +def test_identity(): + ident = interface.IdentityOperator((3, 3)) + assert_equal(ident * [1, 2, 3], [1, 2, 3]) + assert_equal(ident.dot(np.arange(9).reshape(3, 3)).ravel(), np.arange(9)) + + assert_raises(ValueError, ident.matvec, [1, 2, 3, 4]) + + +def test_attributes(): + A = interface.aslinearoperator(np.arange(16).reshape(4, 4)) + + def always_four_ones(x): + x = np.asarray(x) + assert_(x.shape == (3,) or x.shape == (3, 1)) + return np.ones(4) + + B = interface.LinearOperator(shape=(4, 3), matvec=always_four_ones) + + for op in [A, B, A * B, A.H, A + A, B + B, A**4]: + assert_(hasattr(op, "dtype")) + assert_(hasattr(op, "shape")) + assert_(hasattr(op, "_matvec")) + +def matvec(x): + """ Needed for test_pickle as local functions are not pickleable """ + return np.zeros(3) + +def test_pickle(): + import pickle + + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + A = interface.LinearOperator((3, 3), matvec) + s = pickle.dumps(A, protocol=protocol) + B = pickle.loads(s) + + for k in A.__dict__: + assert_equal(getattr(A, k), getattr(B, k)) + +def test_inheritance(): + class Empty(interface.LinearOperator): + pass + + with warns(RuntimeWarning, match="should implement at least"): + assert_raises(TypeError, Empty) + + class Identity(interface.LinearOperator): + def __init__(self, n): + super().__init__(dtype=None, shape=(n, n)) + + def _matvec(self, x): + return x + + id3 = Identity(3) + assert_equal(id3.matvec([1, 2, 3]), [1, 2, 3]) + assert_raises(NotImplementedError, id3.rmatvec, [4, 5, 6]) + + class MatmatOnly(interface.LinearOperator): + def __init__(self, A): + super().__init__(A.dtype, A.shape) + self.A = A + + def _matmat(self, x): + return self.A.dot(x) + + mm = MatmatOnly(np.random.randn(5, 3)) + assert_equal(mm.matvec(np.random.randn(3)).shape, (5,)) + +def test_dtypes_of_operator_sum(): + # gh-6078 + + mat_complex = np.random.rand(2,2) + 1j * np.random.rand(2,2) + mat_real = np.random.rand(2,2) + + complex_operator = interface.aslinearoperator(mat_complex) + real_operator = interface.aslinearoperator(mat_real) + + sum_complex = complex_operator + complex_operator + sum_real = real_operator + real_operator + + assert_equal(sum_real.dtype, np.float64) + assert_equal(sum_complex.dtype, np.complex128) + +def test_no_double_init(): + call_count = [0] + + def matvec(v): + call_count[0] += 1 + return v + + # It should call matvec exactly once (in order to determine the + # operator dtype) + interface.LinearOperator((2, 2), matvec=matvec) + assert_equal(call_count[0], 1) + +def test_adjoint_conjugate(): + X = np.array([[1j]]) + A = interface.aslinearoperator(X) + + B = 1j * A + Y = 1j * X + + v = np.array([1]) + + assert_equal(B.dot(v), Y.dot(v)) + assert_equal(B.H.dot(v), Y.T.conj().dot(v)) + +def test_ndim(): + X = np.array([[1]]) + A = interface.aslinearoperator(X) + assert_equal(A.ndim, 2) + +def test_transpose_noconjugate(): + X = np.array([[1j]]) + A = interface.aslinearoperator(X) + + B = 1j * A + Y = 1j * X + + v = np.array([1]) + + assert_equal(B.dot(v), Y.dot(v)) + assert_equal(B.T.dot(v), Y.T.dot(v)) + +def test_sparse_matmat_exception(): + A = interface.LinearOperator((2, 2), matvec=lambda x: x) + B = sparse.identity(2) + msg = "Unable to multiply a LinearOperator with a sparse matrix." + with assert_raises(TypeError, match=msg): + A @ B + with assert_raises(TypeError, match=msg): + B @ A + with assert_raises(ValueError): + A @ np.identity(4) + with assert_raises(ValueError): + np.identity(4) @ A diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_matfuncs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_matfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..5a5e8444db27b796fbc67cbf5939813ce51f8dca --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_matfuncs.py @@ -0,0 +1,592 @@ +# +# Created by: Pearu Peterson, March 2002 +# +""" Test functions for scipy.linalg._matfuncs module + +""" +import math + +import numpy as np +from numpy import array, eye, exp, random +from numpy.testing import ( + assert_allclose, assert_, assert_array_almost_equal, assert_equal, + assert_array_almost_equal_nulp, suppress_warnings) + +from scipy.sparse import csc_matrix, csc_array, SparseEfficiencyWarning +from scipy.sparse._construct import eye as speye +from scipy.sparse.linalg._matfuncs import (expm, _expm, + ProductOperator, MatrixPowerOperator, + _onenorm_matrix_power_nnm, matrix_power) +from scipy.sparse._sputils import matrix +from scipy.linalg import logm +from scipy.special import factorial, binom +import scipy.sparse +import scipy.sparse.linalg + + +def _burkardt_13_power(n, p): + """ + A helper function for testing matrix functions. + + Parameters + ---------- + n : integer greater than 1 + Order of the square matrix to be returned. + p : non-negative integer + Power of the matrix. + + Returns + ------- + out : ndarray representing a square matrix + A Forsythe matrix of order n, raised to the power p. + + """ + # Input validation. + if n != int(n) or n < 2: + raise ValueError('n must be an integer greater than 1') + n = int(n) + if p != int(p) or p < 0: + raise ValueError('p must be a non-negative integer') + p = int(p) + + # Construct the matrix explicitly. + a, b = divmod(p, n) + large = np.power(10.0, -n*a) + small = large * np.power(10.0, -n) + return np.diag([large]*(n-b), b) + np.diag([small]*b, b-n) + + +def test_onenorm_matrix_power_nnm(): + np.random.seed(1234) + for n in range(1, 5): + for p in range(5): + M = np.random.random((n, n)) + Mp = np.linalg.matrix_power(M, p) + observed = _onenorm_matrix_power_nnm(M, p) + expected = np.linalg.norm(Mp, 1) + assert_allclose(observed, expected) + +def test_matrix_power(): + np.random.seed(1234) + row, col = np.random.randint(0, 4, size=(2, 6)) + data = np.random.random(size=(6,)) + Amat = csc_matrix((data, (row, col)), shape=(4, 4)) + A = csc_array((data, (row, col)), shape=(4, 4)) + Adense = A.toarray() + for power in (2, 5, 6): + Apow = matrix_power(A, power).toarray() + Amat_pow = (Amat**power).toarray() + Adense_pow = np.linalg.matrix_power(Adense, power) + assert_allclose(Apow, Adense_pow) + assert_allclose(Apow, Amat_pow) + + +class TestExpM: + def test_zero_ndarray(self): + a = array([[0.,0],[0,0]]) + assert_array_almost_equal(expm(a),[[1,0],[0,1]]) + + def test_zero_sparse(self): + a = csc_matrix([[0.,0],[0,0]]) + assert_array_almost_equal(expm(a).toarray(),[[1,0],[0,1]]) + + def test_zero_matrix(self): + a = matrix([[0.,0],[0,0]]) + assert_array_almost_equal(expm(a),[[1,0],[0,1]]) + + def test_misc_types(self): + A = expm(np.array([[1]])) + assert_allclose(expm(((1,),)), A) + assert_allclose(expm([[1]]), A) + assert_allclose(expm(matrix([[1]])), A) + assert_allclose(expm(np.array([[1]])), A) + assert_allclose(expm(csc_matrix([[1]])).toarray(), A) + B = expm(np.array([[1j]])) + assert_allclose(expm(((1j,),)), B) + assert_allclose(expm([[1j]]), B) + assert_allclose(expm(matrix([[1j]])), B) + assert_allclose(expm(csc_matrix([[1j]])).toarray(), B) + + def test_bidiagonal_sparse(self): + A = csc_matrix([ + [1, 3, 0], + [0, 1, 5], + [0, 0, 2]], dtype=float) + e1 = math.exp(1) + e2 = math.exp(2) + expected = np.array([ + [e1, 3*e1, 15*(e2 - 2*e1)], + [0, e1, 5*(e2 - e1)], + [0, 0, e2]], dtype=float) + observed = expm(A).toarray() + assert_array_almost_equal(observed, expected) + + def test_padecases_dtype_float(self): + for dtype in [np.float32, np.float64]: + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + A = scale * eye(3, dtype=dtype) + observed = expm(A) + expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype) + assert_array_almost_equal_nulp(observed, expected, nulp=100) + + def test_padecases_dtype_complex(self): + for dtype in [np.complex64, np.complex128]: + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + A = scale * eye(3, dtype=dtype) + observed = expm(A) + expected = exp(scale, dtype=dtype) * eye(3, dtype=dtype) + assert_array_almost_equal_nulp(observed, expected, nulp=100) + + def test_padecases_dtype_sparse_float(self): + # float32 and complex64 lead to errors in spsolve/UMFpack + dtype = np.float64 + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + a = scale * speye(3, 3, dtype=dtype, format='csc') + e = exp(scale, dtype=dtype) * eye(3, dtype=dtype) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + exact_onenorm = _expm(a, use_exact_onenorm=True).toarray() + inexact_onenorm = _expm(a, use_exact_onenorm=False).toarray() + assert_array_almost_equal_nulp(exact_onenorm, e, nulp=100) + assert_array_almost_equal_nulp(inexact_onenorm, e, nulp=100) + + def test_padecases_dtype_sparse_complex(self): + # float32 and complex64 lead to errors in spsolve/UMFpack + dtype = np.complex128 + for scale in [1e-2, 1e-1, 5e-1, 1, 10]: + a = scale * speye(3, 3, dtype=dtype, format='csc') + e = exp(scale) * eye(3, dtype=dtype) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + assert_array_almost_equal_nulp(expm(a).toarray(), e, nulp=100) + + def test_logm_consistency(self): + random.seed(1234) + for dtype in [np.float64, np.complex128]: + for n in range(1, 10): + for scale in [1e-4, 1e-3, 1e-2, 1e-1, 1, 1e1, 1e2]: + # make logm(A) be of a given scale + A = (eye(n) + random.rand(n, n) * scale).astype(dtype) + if np.iscomplexobj(A): + A = A + 1j * random.rand(n, n) * scale + assert_array_almost_equal(expm(logm(A)), A) + + def test_integer_matrix(self): + Q = np.array([ + [-3, 1, 1, 1], + [1, -3, 1, 1], + [1, 1, -3, 1], + [1, 1, 1, -3]]) + assert_allclose(expm(Q), expm(1.0 * Q)) + + def test_integer_matrix_2(self): + # Check for integer overflows + Q = np.array([[-500, 500, 0, 0], + [0, -550, 360, 190], + [0, 630, -630, 0], + [0, 0, 0, 0]], dtype=np.int16) + assert_allclose(expm(Q), expm(1.0 * Q)) + + Q = csc_matrix(Q) + assert_allclose(expm(Q).toarray(), expm(1.0 * Q).toarray()) + + def test_triangularity_perturbation(self): + # Experiment (1) of + # Awad H. Al-Mohy and Nicholas J. Higham (2012) + # Improved Inverse Scaling and Squaring Algorithms + # for the Matrix Logarithm. + A = np.array([ + [3.2346e-1, 3e4, 3e4, 3e4], + [0, 3.0089e-1, 3e4, 3e4], + [0, 0, 3.221e-1, 3e4], + [0, 0, 0, 3.0744e-1]], + dtype=float) + A_logm = np.array([ + [-1.12867982029050462e+00, 9.61418377142025565e+04, + -4.52485573953179264e+09, 2.92496941103871812e+14], + [0.00000000000000000e+00, -1.20101052953082288e+00, + 9.63469687211303099e+04, -4.68104828911105442e+09], + [0.00000000000000000e+00, 0.00000000000000000e+00, + -1.13289322264498393e+00, 9.53249183094775653e+04], + [0.00000000000000000e+00, 0.00000000000000000e+00, + 0.00000000000000000e+00, -1.17947533272554850e+00]], + dtype=float) + assert_allclose(expm(A_logm), A, rtol=1e-4) + + # Perturb the upper triangular matrix by tiny amounts, + # so that it becomes technically not upper triangular. + random.seed(1234) + tiny = 1e-17 + A_logm_perturbed = A_logm.copy() + A_logm_perturbed[1, 0] = tiny + with suppress_warnings() as sup: + sup.filter(RuntimeWarning, "Ill-conditioned.*") + A_expm_logm_perturbed = expm(A_logm_perturbed) + rtol = 1e-4 + atol = 100 * tiny + assert_(not np.allclose(A_expm_logm_perturbed, A, rtol=rtol, atol=atol)) + + def test_burkardt_1(self): + # This matrix is diagonal. + # The calculation of the matrix exponential is simple. + # + # This is the first of a series of matrix exponential tests + # collected by John Burkardt from the following sources. + # + # Alan Laub, + # Review of "Linear System Theory" by Joao Hespanha, + # SIAM Review, + # Volume 52, Number 4, December 2010, pages 779--781. + # + # Cleve Moler and Charles Van Loan, + # Nineteen Dubious Ways to Compute the Exponential of a Matrix, + # Twenty-Five Years Later, + # SIAM Review, + # Volume 45, Number 1, March 2003, pages 3--49. + # + # Cleve Moler, + # Cleve's Corner: A Balancing Act for the Matrix Exponential, + # 23 July 2012. + # + # Robert Ward, + # Numerical computation of the matrix exponential + # with accuracy estimate, + # SIAM Journal on Numerical Analysis, + # Volume 14, Number 4, September 1977, pages 600--610. + exp1 = np.exp(1) + exp2 = np.exp(2) + A = np.array([ + [1, 0], + [0, 2], + ], dtype=float) + desired = np.array([ + [exp1, 0], + [0, exp2], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_2(self): + # This matrix is symmetric. + # The calculation of the matrix exponential is straightforward. + A = np.array([ + [1, 3], + [3, 2], + ], dtype=float) + desired = np.array([ + [39.322809708033859, 46.166301438885753], + [46.166301438885768, 54.711576854329110], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_3(self): + # This example is due to Laub. + # This matrix is ill-suited for the Taylor series approach. + # As powers of A are computed, the entries blow up too quickly. + exp1 = np.exp(1) + exp39 = np.exp(39) + A = np.array([ + [0, 1], + [-39, -40], + ], dtype=float) + desired = np.array([ + [ + 39/(38*exp1) - 1/(38*exp39), + -np.expm1(-38) / (38*exp1)], + [ + 39*np.expm1(-38) / (38*exp1), + -1/(38*exp1) + 39/(38*exp39)], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_4(self): + # This example is due to Moler and Van Loan. + # The example will cause problems for the series summation approach, + # as well as for diagonal Pade approximations. + A = np.array([ + [-49, 24], + [-64, 31], + ], dtype=float) + U = np.array([[3, 1], [4, 2]], dtype=float) + V = np.array([[1, -1/2], [-2, 3/2]], dtype=float) + w = np.array([-17, -1], dtype=float) + desired = np.dot(U * np.exp(w), V) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_5(self): + # This example is due to Moler and Van Loan. + # This matrix is strictly upper triangular + # All powers of A are zero beyond some (low) limit. + # This example will cause problems for Pade approximations. + A = np.array([ + [0, 6, 0, 0], + [0, 0, 6, 0], + [0, 0, 0, 6], + [0, 0, 0, 0], + ], dtype=float) + desired = np.array([ + [1, 6, 18, 36], + [0, 1, 6, 18], + [0, 0, 1, 6], + [0, 0, 0, 1], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_6(self): + # This example is due to Moler and Van Loan. + # This matrix does not have a complete set of eigenvectors. + # That means the eigenvector approach will fail. + exp1 = np.exp(1) + A = np.array([ + [1, 1], + [0, 1], + ], dtype=float) + desired = np.array([ + [exp1, exp1], + [0, exp1], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_7(self): + # This example is due to Moler and Van Loan. + # This matrix is very close to example 5. + # Mathematically, it has a complete set of eigenvectors. + # Numerically, however, the calculation will be suspect. + exp1 = np.exp(1) + eps = np.spacing(1) + A = np.array([ + [1 + eps, 1], + [0, 1 - eps], + ], dtype=float) + desired = np.array([ + [exp1, exp1], + [0, exp1], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_8(self): + # This matrix was an example in Wikipedia. + exp4 = np.exp(4) + exp16 = np.exp(16) + A = np.array([ + [21, 17, 6], + [-5, -1, -6], + [4, 4, 16], + ], dtype=float) + desired = np.array([ + [13*exp16 - exp4, 13*exp16 - 5*exp4, 2*exp16 - 2*exp4], + [-9*exp16 + exp4, -9*exp16 + 5*exp4, -2*exp16 + 2*exp4], + [16*exp16, 16*exp16, 4*exp16], + ], dtype=float) * 0.25 + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_9(self): + # This matrix is due to the NAG Library. + # It is an example for function F01ECF. + A = np.array([ + [1, 2, 2, 2], + [3, 1, 1, 2], + [3, 2, 1, 2], + [3, 3, 3, 1], + ], dtype=float) + desired = np.array([ + [740.7038, 610.8500, 542.2743, 549.1753], + [731.2510, 603.5524, 535.0884, 542.2743], + [823.7630, 679.4257, 603.5524, 610.8500], + [998.4355, 823.7630, 731.2510, 740.7038], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_10(self): + # This is Ward's example #1. + # It is defective and nonderogatory. + A = np.array([ + [4, 2, 0], + [1, 4, 1], + [1, 1, 4], + ], dtype=float) + assert_allclose(sorted(scipy.linalg.eigvals(A)), (3, 3, 6)) + desired = np.array([ + [147.8666224463699, 183.7651386463682, 71.79703239999647], + [127.7810855231823, 183.7651386463682, 91.88256932318415], + [127.7810855231824, 163.6796017231806, 111.9681062463718], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_11(self): + # This is Ward's example #2. + # It is a symmetric matrix. + A = np.array([ + [29.87942128909879, 0.7815750847907159, -2.289519314033932], + [0.7815750847907159, 25.72656945571064, 8.680737820540137], + [-2.289519314033932, 8.680737820540137, 34.39400925519054], + ], dtype=float) + assert_allclose(scipy.linalg.eigvalsh(A), (20, 30, 40)) + desired = np.array([ + [ + 5.496313853692378E+15, + -1.823188097200898E+16, + -3.047577080858001E+16], + [ + -1.823188097200899E+16, + 6.060522870222108E+16, + 1.012918429302482E+17], + [ + -3.047577080858001E+16, + 1.012918429302482E+17, + 1.692944112408493E+17], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_12(self): + # This is Ward's example #3. + # Ward's algorithm has difficulty estimating the accuracy + # of its results. + A = np.array([ + [-131, 19, 18], + [-390, 56, 54], + [-387, 57, 52], + ], dtype=float) + assert_allclose(sorted(scipy.linalg.eigvals(A)), (-20, -2, -1)) + desired = np.array([ + [-1.509644158793135, 0.3678794391096522, 0.1353352811751005], + [-5.632570799891469, 1.471517758499875, 0.4060058435250609], + [-4.934938326088363, 1.103638317328798, 0.5413411267617766], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_burkardt_13(self): + # This is Ward's example #4. + # This is a version of the Forsythe matrix. + # The eigenvector problem is badly conditioned. + # Ward's algorithm has difficulty estimating the accuracy + # of its results for this problem. + # + # Check the construction of one instance of this family of matrices. + A4_actual = _burkardt_13_power(4, 1) + A4_desired = [[0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + [1e-4, 0, 0, 0]] + assert_allclose(A4_actual, A4_desired) + # Check the expm for a few instances. + for n in (2, 3, 4, 10): + # Approximate expm using Taylor series. + # This works well for this matrix family + # because each matrix in the summation, + # even before dividing by the factorial, + # is entrywise positive with max entry 10**(-floor(p/n)*n). + k = max(1, int(np.ceil(16/n))) + desired = np.zeros((n, n), dtype=float) + for p in range(n*k): + Ap = _burkardt_13_power(n, p) + assert_equal(np.min(Ap), 0) + assert_allclose(np.max(Ap), np.power(10, -np.floor(p/n)*n)) + desired += Ap / factorial(p) + actual = expm(_burkardt_13_power(n, 1)) + assert_allclose(actual, desired) + + def test_burkardt_14(self): + # This is Moler's example. + # This badly scaled matrix caused problems for MATLAB's expm(). + A = np.array([ + [0, 1e-8, 0], + [-(2e10 + 4e8/6.), -3, 2e10], + [200./3., 0, -200./3.], + ], dtype=float) + desired = np.array([ + [0.446849468283175, 1.54044157383952e-09, 0.462811453558774], + [-5743067.77947947, -0.0152830038686819, -4526542.71278401], + [0.447722977849494, 1.54270484519591e-09, 0.463480648837651], + ], dtype=float) + actual = expm(A) + assert_allclose(actual, desired) + + def test_pascal(self): + # Test pascal triangle. + # Nilpotent exponential, used to trigger a failure (gh-8029) + + for scale in [1.0, 1e-3, 1e-6]: + for n in range(0, 80, 3): + sc = scale ** np.arange(n, -1, -1) + if np.any(sc < 1e-300): + break + + A = np.diag(np.arange(1, n + 1), -1) * scale + B = expm(A) + + got = B + expected = binom(np.arange(n + 1)[:,None], + np.arange(n + 1)[None,:]) * sc[None,:] / sc[:,None] + atol = 1e-13 * abs(expected).max() + assert_allclose(got, expected, atol=atol) + + def test_matrix_input(self): + # Large np.matrix inputs should work, gh-5546 + A = np.zeros((200, 200)) + A[-1,0] = 1 + B0 = expm(A) + with suppress_warnings() as sup: + sup.filter(DeprecationWarning, "the matrix subclass.*") + sup.filter(PendingDeprecationWarning, "the matrix subclass.*") + B = expm(np.matrix(A)) + assert_allclose(B, B0) + + def test_exp_sinch_overflow(self): + # Check overflow in intermediate steps is fixed (gh-11839) + L = np.array([[1.0, -0.5, -0.5, 0.0, 0.0, 0.0, 0.0], + [0.0, 1.0, 0.0, -0.5, -0.5, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 0.0, -0.5, -0.5], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]) + + E0 = expm(-L) + E1 = expm(-2**11 * L) + E2 = E0 + for j in range(11): + E2 = E2 @ E2 + + assert_allclose(E1, E2) + + +class TestOperators: + + def test_product_operator(self): + random.seed(1234) + n = 5 + k = 2 + nsamples = 10 + for i in range(nsamples): + A = np.random.randn(n, n) + B = np.random.randn(n, n) + C = np.random.randn(n, n) + D = np.random.randn(n, k) + op = ProductOperator(A, B, C) + assert_allclose(op.matmat(D), A.dot(B).dot(C).dot(D)) + assert_allclose(op.T.matmat(D), (A.dot(B).dot(C)).T.dot(D)) + + def test_matrix_power_operator(self): + random.seed(1234) + n = 5 + k = 2 + p = 3 + nsamples = 10 + for i in range(nsamples): + A = np.random.randn(n, n) + B = np.random.randn(n, k) + op = MatrixPowerOperator(A, p) + assert_allclose(op.matmat(B), np.linalg.matrix_power(A, p).dot(B)) + assert_allclose(op.T.matmat(B), np.linalg.matrix_power(A, p).T.dot(B)) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_norm.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_norm.py new file mode 100644 index 0000000000000000000000000000000000000000..96c2f65da75b5e30e34dc2d4e695d1bb369b79b9 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_norm.py @@ -0,0 +1,141 @@ +"""Test functions for the sparse.linalg.norm module +""" + +import pytest +import numpy as np +from numpy.linalg import norm as npnorm +from numpy.testing import assert_allclose, assert_equal +from pytest import raises as assert_raises + +import scipy.sparse +from scipy.sparse.linalg import norm as spnorm + + +# https://github.com/scipy/scipy/issues/16031 +def test_sparray_norm(): + row = np.array([0, 0, 1, 1]) + col = np.array([0, 1, 2, 3]) + data = np.array([4, 5, 7, 9]) + test_arr = scipy.sparse.coo_array((data, (row, col)), shape=(2, 4)) + test_mat = scipy.sparse.coo_matrix((data, (row, col)), shape=(2, 4)) + assert_equal(spnorm(test_arr, ord=1, axis=0), np.array([4, 5, 7, 9])) + assert_equal(spnorm(test_mat, ord=1, axis=0), np.array([4, 5, 7, 9])) + assert_equal(spnorm(test_arr, ord=1, axis=1), np.array([9, 16])) + assert_equal(spnorm(test_mat, ord=1, axis=1), np.array([9, 16])) + + +class TestNorm: + def setup_method(self): + a = np.arange(9) - 4 + b = a.reshape((3, 3)) + self.b = scipy.sparse.csr_matrix(b) + + def test_matrix_norm(self): + + # Frobenius norm is the default + assert_allclose(spnorm(self.b), 7.745966692414834) + assert_allclose(spnorm(self.b, 'fro'), 7.745966692414834) + + assert_allclose(spnorm(self.b, np.inf), 9) + assert_allclose(spnorm(self.b, -np.inf), 2) + assert_allclose(spnorm(self.b, 1), 7) + assert_allclose(spnorm(self.b, -1), 6) + # Only floating or complex floating dtype supported by svds. + with pytest.warns(UserWarning, match="The problem size"): + assert_allclose(spnorm(self.b.astype(np.float64), 2), + 7.348469228349534) + + # _multi_svd_norm is not implemented for sparse matrix + assert_raises(NotImplementedError, spnorm, self.b, -2) + + def test_matrix_norm_axis(self): + for m, axis in ((self.b, None), (self.b, (0, 1)), (self.b.T, (1, 0))): + assert_allclose(spnorm(m, axis=axis), 7.745966692414834) + assert_allclose(spnorm(m, 'fro', axis=axis), 7.745966692414834) + assert_allclose(spnorm(m, np.inf, axis=axis), 9) + assert_allclose(spnorm(m, -np.inf, axis=axis), 2) + assert_allclose(spnorm(m, 1, axis=axis), 7) + assert_allclose(spnorm(m, -1, axis=axis), 6) + + def test_vector_norm(self): + v = [4.5825756949558398, 4.2426406871192848, 4.5825756949558398] + for m, a in (self.b, 0), (self.b.T, 1): + for axis in a, (a, ), a-2, (a-2, ): + assert_allclose(spnorm(m, 1, axis=axis), [7, 6, 7]) + assert_allclose(spnorm(m, np.inf, axis=axis), [4, 3, 4]) + assert_allclose(spnorm(m, axis=axis), v) + assert_allclose(spnorm(m, ord=2, axis=axis), v) + assert_allclose(spnorm(m, ord=None, axis=axis), v) + + def test_norm_exceptions(self): + m = self.b + assert_raises(TypeError, spnorm, m, None, 1.5) + assert_raises(TypeError, spnorm, m, None, [2]) + assert_raises(ValueError, spnorm, m, None, ()) + assert_raises(ValueError, spnorm, m, None, (0, 1, 2)) + assert_raises(ValueError, spnorm, m, None, (0, 0)) + assert_raises(ValueError, spnorm, m, None, (0, 2)) + assert_raises(ValueError, spnorm, m, None, (-3, 0)) + assert_raises(ValueError, spnorm, m, None, 2) + assert_raises(ValueError, spnorm, m, None, -3) + assert_raises(ValueError, spnorm, m, 'plate_of_shrimp', 0) + assert_raises(ValueError, spnorm, m, 'plate_of_shrimp', (0, 1)) + + +class TestVsNumpyNorm: + _sparse_types = ( + scipy.sparse.bsr_matrix, + scipy.sparse.coo_matrix, + scipy.sparse.csc_matrix, + scipy.sparse.csr_matrix, + scipy.sparse.dia_matrix, + scipy.sparse.dok_matrix, + scipy.sparse.lil_matrix, + ) + _test_matrices = ( + (np.arange(9) - 4).reshape((3, 3)), + [ + [1, 2, 3], + [-1, 1, 4]], + [ + [1, 0, 3], + [-1, 1, 4j]], + ) + + def test_sparse_matrix_norms(self): + for sparse_type in self._sparse_types: + for M in self._test_matrices: + S = sparse_type(M) + assert_allclose(spnorm(S), npnorm(M)) + assert_allclose(spnorm(S, 'fro'), npnorm(M, 'fro')) + assert_allclose(spnorm(S, np.inf), npnorm(M, np.inf)) + assert_allclose(spnorm(S, -np.inf), npnorm(M, -np.inf)) + assert_allclose(spnorm(S, 1), npnorm(M, 1)) + assert_allclose(spnorm(S, -1), npnorm(M, -1)) + + def test_sparse_matrix_norms_with_axis(self): + for sparse_type in self._sparse_types: + for M in self._test_matrices: + S = sparse_type(M) + for axis in None, (0, 1), (1, 0): + assert_allclose(spnorm(S, axis=axis), npnorm(M, axis=axis)) + for ord in 'fro', np.inf, -np.inf, 1, -1: + assert_allclose(spnorm(S, ord, axis=axis), + npnorm(M, ord, axis=axis)) + # Some numpy matrix norms are allergic to negative axes. + for axis in (-2, -1), (-1, -2), (1, -2): + assert_allclose(spnorm(S, axis=axis), npnorm(M, axis=axis)) + assert_allclose(spnorm(S, 'f', axis=axis), + npnorm(M, 'f', axis=axis)) + assert_allclose(spnorm(S, 'fro', axis=axis), + npnorm(M, 'fro', axis=axis)) + + def test_sparse_vector_norms(self): + for sparse_type in self._sparse_types: + for M in self._test_matrices: + S = sparse_type(M) + for axis in (0, 1, -1, -2, (0, ), (1, ), (-1, ), (-2, )): + assert_allclose(spnorm(S, axis=axis), npnorm(M, axis=axis)) + for ord in None, 2, np.inf, -np.inf, 1, 0.5, 0.42: + assert_allclose(spnorm(S, ord, axis=axis), + npnorm(M, ord, axis=axis)) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_onenormest.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_onenormest.py new file mode 100644 index 0000000000000000000000000000000000000000..907a456f0358e3a9cca4f6293e3806aef813ef4d --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_onenormest.py @@ -0,0 +1,252 @@ +"""Test functions for the sparse.linalg._onenormest module +""" + +import numpy as np +from numpy.testing import assert_allclose, assert_equal, assert_ +import pytest +import scipy.linalg +import scipy.sparse.linalg +from scipy.sparse.linalg._onenormest import _onenormest_core, _algorithm_2_2 + + +class MatrixProductOperator(scipy.sparse.linalg.LinearOperator): + """ + This is purely for onenormest testing. + """ + + def __init__(self, A, B): + if A.ndim != 2 or B.ndim != 2: + raise ValueError('expected ndarrays representing matrices') + if A.shape[1] != B.shape[0]: + raise ValueError('incompatible shapes') + self.A = A + self.B = B + self.ndim = 2 + self.shape = (A.shape[0], B.shape[1]) + + def _matvec(self, x): + return np.dot(self.A, np.dot(self.B, x)) + + def _rmatvec(self, x): + return np.dot(np.dot(x, self.A), self.B) + + def _matmat(self, X): + return np.dot(self.A, np.dot(self.B, X)) + + @property + def T(self): + return MatrixProductOperator(self.B.T, self.A.T) + + +class TestOnenormest: + + @pytest.mark.xslow + def test_onenormest_table_3_t_2(self): + # This will take multiple seconds if your computer is slow like mine. + # It is stochastic, so the tolerance could be too strict. + np.random.seed(1234) + t = 2 + n = 100 + itmax = 5 + nsamples = 5000 + observed = [] + expected = [] + nmult_list = [] + nresample_list = [] + for i in range(nsamples): + A = scipy.linalg.inv(np.random.randn(n, n)) + est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax) + observed.append(est) + expected.append(scipy.linalg.norm(A, 1)) + nmult_list.append(nmults) + nresample_list.append(nresamples) + observed = np.array(observed, dtype=float) + expected = np.array(expected, dtype=float) + relative_errors = np.abs(observed - expected) / expected + + # check the mean underestimation ratio + underestimation_ratio = observed / expected + assert_(0.99 < np.mean(underestimation_ratio) < 1.0) + + # check the max and mean required column resamples + assert_equal(np.max(nresample_list), 2) + assert_(0.05 < np.mean(nresample_list) < 0.2) + + # check the proportion of norms computed exactly correctly + nexact = np.count_nonzero(relative_errors < 1e-14) + proportion_exact = nexact / float(nsamples) + assert_(0.9 < proportion_exact < 0.95) + + # check the average number of matrix*vector multiplications + assert_(3.5 < np.mean(nmult_list) < 4.5) + + @pytest.mark.xslow + def test_onenormest_table_4_t_7(self): + # This will take multiple seconds if your computer is slow like mine. + # It is stochastic, so the tolerance could be too strict. + np.random.seed(1234) + t = 7 + n = 100 + itmax = 5 + nsamples = 5000 + observed = [] + expected = [] + nmult_list = [] + nresample_list = [] + for i in range(nsamples): + A = np.random.randint(-1, 2, size=(n, n)) + est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax) + observed.append(est) + expected.append(scipy.linalg.norm(A, 1)) + nmult_list.append(nmults) + nresample_list.append(nresamples) + observed = np.array(observed, dtype=float) + expected = np.array(expected, dtype=float) + relative_errors = np.abs(observed - expected) / expected + + # check the mean underestimation ratio + underestimation_ratio = observed / expected + assert_(0.90 < np.mean(underestimation_ratio) < 0.99) + + # check the required column resamples + assert_equal(np.max(nresample_list), 0) + + # check the proportion of norms computed exactly correctly + nexact = np.count_nonzero(relative_errors < 1e-14) + proportion_exact = nexact / float(nsamples) + assert_(0.15 < proportion_exact < 0.25) + + # check the average number of matrix*vector multiplications + assert_(3.5 < np.mean(nmult_list) < 4.5) + + def test_onenormest_table_5_t_1(self): + # "note that there is no randomness and hence only one estimate for t=1" + t = 1 + n = 100 + itmax = 5 + alpha = 1 - 1e-6 + A = -scipy.linalg.inv(np.identity(n) + alpha*np.eye(n, k=1)) + first_col = np.array([1] + [0]*(n-1)) + first_row = np.array([(-alpha)**i for i in range(n)]) + B = -scipy.linalg.toeplitz(first_col, first_row) + assert_allclose(A, B) + est, v, w, nmults, nresamples = _onenormest_core(B, B.T, t, itmax) + exact_value = scipy.linalg.norm(B, 1) + underest_ratio = est / exact_value + assert_allclose(underest_ratio, 0.05, rtol=1e-4) + assert_equal(nmults, 11) + assert_equal(nresamples, 0) + # check the non-underscored version of onenormest + est_plain = scipy.sparse.linalg.onenormest(B, t=t, itmax=itmax) + assert_allclose(est, est_plain) + + @pytest.mark.xslow + def test_onenormest_table_6_t_1(self): + #TODO this test seems to give estimates that match the table, + #TODO even though no attempt has been made to deal with + #TODO complex numbers in the one-norm estimation. + # This will take multiple seconds if your computer is slow like mine. + # It is stochastic, so the tolerance could be too strict. + np.random.seed(1234) + t = 1 + n = 100 + itmax = 5 + nsamples = 5000 + observed = [] + expected = [] + nmult_list = [] + nresample_list = [] + for i in range(nsamples): + A_inv = np.random.rand(n, n) + 1j * np.random.rand(n, n) + A = scipy.linalg.inv(A_inv) + est, v, w, nmults, nresamples = _onenormest_core(A, A.T, t, itmax) + observed.append(est) + expected.append(scipy.linalg.norm(A, 1)) + nmult_list.append(nmults) + nresample_list.append(nresamples) + observed = np.array(observed, dtype=float) + expected = np.array(expected, dtype=float) + relative_errors = np.abs(observed - expected) / expected + + # check the mean underestimation ratio + underestimation_ratio = observed / expected + underestimation_ratio_mean = np.mean(underestimation_ratio) + assert_(0.90 < underestimation_ratio_mean < 0.99) + + # check the required column resamples + max_nresamples = np.max(nresample_list) + assert_equal(max_nresamples, 0) + + # check the proportion of norms computed exactly correctly + nexact = np.count_nonzero(relative_errors < 1e-14) + proportion_exact = nexact / float(nsamples) + assert_(0.7 < proportion_exact < 0.8) + + # check the average number of matrix*vector multiplications + mean_nmult = np.mean(nmult_list) + assert_(4 < mean_nmult < 5) + + def _help_product_norm_slow(self, A, B): + # for profiling + C = np.dot(A, B) + return scipy.linalg.norm(C, 1) + + def _help_product_norm_fast(self, A, B): + # for profiling + t = 2 + itmax = 5 + D = MatrixProductOperator(A, B) + est, v, w, nmults, nresamples = _onenormest_core(D, D.T, t, itmax) + return est + + @pytest.mark.slow + def test_onenormest_linear_operator(self): + # Define a matrix through its product A B. + # Depending on the shapes of A and B, + # it could be easy to multiply this product by a small matrix, + # but it could be annoying to look at all of + # the entries of the product explicitly. + np.random.seed(1234) + n = 6000 + k = 3 + A = np.random.randn(n, k) + B = np.random.randn(k, n) + fast_estimate = self._help_product_norm_fast(A, B) + exact_value = self._help_product_norm_slow(A, B) + assert_(fast_estimate <= exact_value <= 3*fast_estimate, + f'fast: {fast_estimate:g}\nexact:{exact_value:g}') + + def test_returns(self): + np.random.seed(1234) + A = scipy.sparse.rand(50, 50, 0.1) + + s0 = scipy.linalg.norm(A.toarray(), 1) + s1, v = scipy.sparse.linalg.onenormest(A, compute_v=True) + s2, w = scipy.sparse.linalg.onenormest(A, compute_w=True) + s3, v2, w2 = scipy.sparse.linalg.onenormest(A, compute_w=True, compute_v=True) + + assert_allclose(s1, s0, rtol=1e-9) + assert_allclose(np.linalg.norm(A.dot(v), 1), s0*np.linalg.norm(v, 1), rtol=1e-9) + assert_allclose(A.dot(v), w, rtol=1e-9) + + +class TestAlgorithm_2_2: + + def test_randn_inv(self): + np.random.seed(1234) + n = 20 + nsamples = 100 + for i in range(nsamples): + + # Choose integer t uniformly between 1 and 3 inclusive. + t = np.random.randint(1, 4) + + # Choose n uniformly between 10 and 40 inclusive. + n = np.random.randint(10, 41) + + # Sample the inverse of a matrix with random normal entries. + A = scipy.linalg.inv(np.random.randn(n, n)) + + # Compute the 1-norm bounds. + g, ind = _algorithm_2_2(A, A.T, t) + diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_propack.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_propack.py new file mode 100644 index 0000000000000000000000000000000000000000..2dac7133997ac92d65b72c7bd83e844d2f02d802 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_propack.py @@ -0,0 +1,166 @@ +import os +import pytest + +import numpy as np +from numpy.testing import assert_allclose +from pytest import raises as assert_raises +from scipy.sparse.linalg._svdp import _svdp +from scipy.sparse import csr_matrix, csc_matrix + + +# dtype_flavour to tolerance +TOLS = { + np.float32: 1e-4, + np.float64: 1e-8, + np.complex64: 1e-4, + np.complex128: 1e-8, +} + + +def is_complex_type(dtype): + return np.dtype(dtype).kind == "c" + + +_dtypes = [] +for dtype_flavour in TOLS.keys(): + marks = [] + if is_complex_type(dtype_flavour): + marks = [pytest.mark.slow] + _dtypes.append(pytest.param(dtype_flavour, marks=marks, + id=dtype_flavour.__name__)) +_dtypes = tuple(_dtypes) # type: ignore[assignment] + + +def generate_matrix(constructor, n, m, f, + dtype=float, rseed=0, **kwargs): + """Generate a random sparse matrix""" + rng = np.random.RandomState(rseed) + if is_complex_type(dtype): + M = (- 5 + 10 * rng.rand(n, m) + - 5j + 10j * rng.rand(n, m)).astype(dtype) + else: + M = (-5 + 10 * rng.rand(n, m)).astype(dtype) + M[M.real > 10 * f - 5] = 0 + return constructor(M, **kwargs) + + +def assert_orthogonal(u1, u2, rtol, atol): + """Check that the first k rows of u1 and u2 are orthogonal""" + A = abs(np.dot(u1.conj().T, u2)) + assert_allclose(A, np.eye(u1.shape[1], u2.shape[1]), rtol=rtol, atol=atol) + + +def check_svdp(n, m, constructor, dtype, k, irl_mode, which, f=0.8): + tol = TOLS[dtype] + + M = generate_matrix(np.asarray, n, m, f, dtype) + Msp = constructor(M) + + u1, sigma1, vt1 = np.linalg.svd(M, full_matrices=False) + u2, sigma2, vt2, _ = _svdp(Msp, k=k, which=which, irl_mode=irl_mode, + tol=tol) + + # check the which + if which.upper() == 'SM': + u1 = np.roll(u1, k, 1) + vt1 = np.roll(vt1, k, 0) + sigma1 = np.roll(sigma1, k) + + # check that singular values agree + assert_allclose(sigma1[:k], sigma2, rtol=tol, atol=tol) + + # check that singular vectors are orthogonal + assert_orthogonal(u1, u2, rtol=tol, atol=tol) + assert_orthogonal(vt1.T, vt2.T, rtol=tol, atol=tol) + + +@pytest.mark.parametrize('ctor', (np.array, csr_matrix, csc_matrix)) +@pytest.mark.parametrize('dtype', _dtypes) +@pytest.mark.parametrize('irl', (True, False)) +@pytest.mark.parametrize('which', ('LM', 'SM')) +def test_svdp(ctor, dtype, irl, which): + np.random.seed(0) + n, m, k = 10, 20, 3 + if which == 'SM' and not irl: + message = "`which`='SM' requires irl_mode=True" + with assert_raises(ValueError, match=message): + check_svdp(n, m, ctor, dtype, k, irl, which) + else: + check_svdp(n, m, ctor, dtype, k, irl, which) + + +@pytest.mark.xslow +@pytest.mark.parametrize('dtype', _dtypes) +@pytest.mark.parametrize('irl', (False, True)) +@pytest.mark.timeout(120) # True, complex64 > 60 s: prerel deps cov 64bit blas +def test_examples(dtype, irl): + # Note: atol for complex64 bumped from 1e-4 to 1e-3 due to test failures + # with BLIS, Netlib, and MKL+AVX512 - see + # https://github.com/conda-forge/scipy-feedstock/pull/198#issuecomment-999180432 + atol = { + np.float32: 1.3e-4, + np.float64: 1e-9, + np.complex64: 1e-3, + np.complex128: 1e-9, + }[dtype] + + path_prefix = os.path.dirname(__file__) + # Test matrices from `illc1850.coord` and `mhd1280b.cua` distributed with + # PROPACK 2.1: http://sun.stanford.edu/~rmunk/PROPACK/ + relative_path = "propack_test_data.npz" + filename = os.path.join(path_prefix, relative_path) + with np.load(filename, allow_pickle=True) as data: + if is_complex_type(dtype): + A = data['A_complex'].item().astype(dtype) + else: + A = data['A_real'].item().astype(dtype) + + k = 200 + u, s, vh, _ = _svdp(A, k, irl_mode=irl, random_state=0) + + # complex example matrix has many repeated singular values, so check only + # beginning non-repeated singular vectors to avoid permutations + sv_check = 27 if is_complex_type(dtype) else k + u = u[:, :sv_check] + vh = vh[:sv_check, :] + s = s[:sv_check] + + # Check orthogonality of singular vectors + assert_allclose(np.eye(u.shape[1]), u.conj().T @ u, atol=atol) + assert_allclose(np.eye(vh.shape[0]), vh @ vh.conj().T, atol=atol) + + # Ensure the norm of the difference between the np.linalg.svd and + # PROPACK reconstructed matrices is small + u3, s3, vh3 = np.linalg.svd(A.todense()) + u3 = u3[:, :sv_check] + s3 = s3[:sv_check] + vh3 = vh3[:sv_check, :] + A3 = u3 @ np.diag(s3) @ vh3 + recon = u @ np.diag(s) @ vh + assert_allclose(np.linalg.norm(A3 - recon), 0, atol=atol) + + +@pytest.mark.parametrize('shifts', (None, -10, 0, 1, 10, 70)) +@pytest.mark.parametrize('dtype', _dtypes[:2]) +def test_shifts(shifts, dtype): + np.random.seed(0) + n, k = 70, 10 + A = np.random.random((n, n)) + if shifts is not None and ((shifts < 0) or (k > min(n-1-shifts, n))): + with pytest.raises(ValueError): + _svdp(A, k, shifts=shifts, kmax=5*k, irl_mode=True) + else: + _svdp(A, k, shifts=shifts, kmax=5*k, irl_mode=True) + + +@pytest.mark.slow +@pytest.mark.xfail() +def test_shifts_accuracy(): + np.random.seed(0) + n, k = 70, 10 + A = np.random.random((n, n)).astype(np.float64) + u1, s1, vt1, _ = _svdp(A, k, shifts=None, which='SM', irl_mode=True) + u2, s2, vt2, _ = _svdp(A, k, shifts=32, which='SM', irl_mode=True) + # shifts <= 32 doesn't agree with shifts > 32 + # Does agree when which='LM' instead of 'SM' + assert_allclose(s1, s2) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_pydata_sparse.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_pydata_sparse.py new file mode 100644 index 0000000000000000000000000000000000000000..b42448d0ef1ab92f9745b0be22b65bb3f280fcb4 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_pydata_sparse.py @@ -0,0 +1,243 @@ +import pytest + +import numpy as np +import scipy.sparse as sp +import scipy.sparse.linalg as splin + +from numpy.testing import assert_allclose, assert_equal + +try: + import sparse +except Exception: + sparse = None + +pytestmark = pytest.mark.skipif(sparse is None, + reason="pydata/sparse not installed") + + +msg = "pydata/sparse (0.15.1) does not implement necessary operations" + + +sparse_params = (pytest.param("COO"), + pytest.param("DOK", marks=[pytest.mark.xfail(reason=msg)])) + +scipy_sparse_classes = [ + sp.bsr_matrix, + sp.csr_matrix, + sp.coo_matrix, + sp.csc_matrix, + sp.dia_matrix, + sp.dok_matrix +] + + +@pytest.fixture(params=sparse_params) +def sparse_cls(request): + return getattr(sparse, request.param) + + +@pytest.fixture(params=scipy_sparse_classes) +def sp_sparse_cls(request): + return request.param + + +@pytest.fixture +def same_matrix(sparse_cls, sp_sparse_cls): + np.random.seed(1234) + A_dense = np.random.rand(9, 9) + return sp_sparse_cls(A_dense), sparse_cls(A_dense) + + +@pytest.fixture +def matrices(sparse_cls): + np.random.seed(1234) + A_dense = np.random.rand(9, 9) + A_dense = A_dense @ A_dense.T + A_sparse = sparse_cls(A_dense) + b = np.random.rand(9) + return A_dense, A_sparse, b + + +def test_isolve_gmres(matrices): + # Several of the iterative solvers use the same + # isolve.utils.make_system wrapper code, so test just one of them. + A_dense, A_sparse, b = matrices + x, info = splin.gmres(A_sparse, b, atol=1e-15) + assert info == 0 + assert isinstance(x, np.ndarray) + assert_allclose(A_sparse @ x, b) + + +def test_lsmr(matrices): + A_dense, A_sparse, b = matrices + res0 = splin.lsmr(A_dense, b) + res = splin.lsmr(A_sparse, b) + assert_allclose(res[0], res0[0], atol=1e-3) + + +# test issue 17012 +def test_lsmr_output_shape(): + x = splin.lsmr(A=np.ones((10, 1)), b=np.zeros(10), x0=np.ones(1))[0] + assert_equal(x.shape, (1,)) + + +def test_lsqr(matrices): + A_dense, A_sparse, b = matrices + res0 = splin.lsqr(A_dense, b) + res = splin.lsqr(A_sparse, b) + assert_allclose(res[0], res0[0], atol=1e-5) + + +def test_eigs(matrices): + A_dense, A_sparse, v0 = matrices + + M_dense = np.diag(v0**2) + M_sparse = A_sparse.__class__(M_dense) + + w_dense, v_dense = splin.eigs(A_dense, k=3, v0=v0) + w, v = splin.eigs(A_sparse, k=3, v0=v0) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + for M in [M_sparse, M_dense]: + w_dense, v_dense = splin.eigs(A_dense, M=M_dense, k=3, v0=v0) + w, v = splin.eigs(A_sparse, M=M, k=3, v0=v0) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + w_dense, v_dense = splin.eigsh(A_dense, M=M_dense, k=3, v0=v0) + w, v = splin.eigsh(A_sparse, M=M, k=3, v0=v0) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + +def test_svds(matrices): + A_dense, A_sparse, v0 = matrices + + u0, s0, vt0 = splin.svds(A_dense, k=2, v0=v0) + u, s, vt = splin.svds(A_sparse, k=2, v0=v0) + + assert_allclose(s, s0) + assert_allclose(np.abs(u), np.abs(u0)) + assert_allclose(np.abs(vt), np.abs(vt0)) + + +def test_lobpcg(matrices): + A_dense, A_sparse, x = matrices + X = x[:,None] + + w_dense, v_dense = splin.lobpcg(A_dense, X) + w, v = splin.lobpcg(A_sparse, X) + + assert_allclose(w, w_dense) + assert_allclose(v, v_dense) + + +def test_spsolve(matrices): + A_dense, A_sparse, b = matrices + b2 = np.random.rand(len(b), 3) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), b) + x = splin.spsolve(A_sparse, b) + assert isinstance(x, np.ndarray) + assert_allclose(x, x0) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), b) + x = splin.spsolve(A_sparse, b, use_umfpack=True) + assert isinstance(x, np.ndarray) + assert_allclose(x, x0) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), b2) + x = splin.spsolve(A_sparse, b2) + assert isinstance(x, np.ndarray) + assert_allclose(x, x0) + + x0 = splin.spsolve(sp.csc_matrix(A_dense), + sp.csc_matrix(A_dense)) + x = splin.spsolve(A_sparse, A_sparse) + assert isinstance(x, type(A_sparse)) + assert_allclose(x.todense(), x0.todense()) + + +def test_splu(matrices): + A_dense, A_sparse, b = matrices + n = len(b) + sparse_cls = type(A_sparse) + + lu = splin.splu(A_sparse) + + assert isinstance(lu.L, sparse_cls) + assert isinstance(lu.U, sparse_cls) + + _Pr_scipy = sp.csc_matrix((np.ones(n), (lu.perm_r, np.arange(n)))) + _Pc_scipy = sp.csc_matrix((np.ones(n), (np.arange(n), lu.perm_c))) + Pr = sparse_cls.from_scipy_sparse(_Pr_scipy) + Pc = sparse_cls.from_scipy_sparse(_Pc_scipy) + A2 = Pr.T @ lu.L @ lu.U @ Pc.T + + assert_allclose(A2.todense(), A_sparse.todense()) + + z = lu.solve(A_sparse.todense()) + assert_allclose(z, np.eye(n), atol=1e-10) + + +def test_spilu(matrices): + A_dense, A_sparse, b = matrices + sparse_cls = type(A_sparse) + + lu = splin.spilu(A_sparse) + + assert isinstance(lu.L, sparse_cls) + assert isinstance(lu.U, sparse_cls) + + z = lu.solve(A_sparse.todense()) + assert_allclose(z, np.eye(len(b)), atol=1e-3) + + +def test_spsolve_triangular(matrices): + A_dense, A_sparse, b = matrices + A_sparse = sparse.tril(A_sparse) + + x = splin.spsolve_triangular(A_sparse, b) + assert_allclose(A_sparse @ x, b) + + +def test_onenormest(matrices): + A_dense, A_sparse, b = matrices + est0 = splin.onenormest(A_dense) + est = splin.onenormest(A_sparse) + assert_allclose(est, est0) + + +def test_inv(matrices): + A_dense, A_sparse, b = matrices + x0 = splin.inv(sp.csc_matrix(A_dense)) + x = splin.inv(A_sparse) + assert_allclose(x.todense(), x0.todense()) + + +def test_expm(matrices): + A_dense, A_sparse, b = matrices + x0 = splin.expm(sp.csc_matrix(A_dense)) + x = splin.expm(A_sparse) + assert_allclose(x.todense(), x0.todense()) + + +def test_expm_multiply(matrices): + A_dense, A_sparse, b = matrices + x0 = splin.expm_multiply(A_dense, b) + x = splin.expm_multiply(A_sparse, b) + assert_allclose(x, x0) + + +def test_eq(same_matrix): + sp_sparse, pd_sparse = same_matrix + assert (sp_sparse == pd_sparse).all() + + +def test_ne(same_matrix): + sp_sparse, pd_sparse = same_matrix + assert not (sp_sparse != pd_sparse).any() diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_special_sparse_arrays.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_special_sparse_arrays.py new file mode 100644 index 0000000000000000000000000000000000000000..d9d1c4001af6697233380edf0047409a41847834 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/linalg/tests/test_special_sparse_arrays.py @@ -0,0 +1,337 @@ +import pytest +import numpy as np +from numpy.testing import assert_array_equal, assert_allclose + +from scipy.sparse import diags, csgraph +from scipy.linalg import eigh + +from scipy.sparse.linalg import LaplacianNd +from scipy.sparse.linalg._special_sparse_arrays import Sakurai +from scipy.sparse.linalg._special_sparse_arrays import MikotaPair + +INT_DTYPES = [np.int8, np.int16, np.int32, np.int64] +REAL_DTYPES = [np.float32, np.float64] +COMPLEX_DTYPES = [np.complex64, np.complex128] +ALLDTYPES = INT_DTYPES + REAL_DTYPES + COMPLEX_DTYPES + + +class TestLaplacianNd: + """ + LaplacianNd tests + """ + + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_1d_specific_shape(self, bc): + lap = LaplacianNd(grid_shape=(6, ), boundary_conditions=bc) + lapa = lap.toarray() + if bc == 'neumann': + a = np.array( + [ + [-1, 1, 0, 0, 0, 0], + [1, -2, 1, 0, 0, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 0, 0, 1, -2, 1], + [0, 0, 0, 0, 1, -1], + ] + ) + elif bc == 'dirichlet': + a = np.array( + [ + [-2, 1, 0, 0, 0, 0], + [1, -2, 1, 0, 0, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 0, 0, 1, -2, 1], + [0, 0, 0, 0, 1, -2], + ] + ) + else: + a = np.array( + [ + [-2, 1, 0, 0, 0, 1], + [1, -2, 1, 0, 0, 0], + [0, 1, -2, 1, 0, 0], + [0, 0, 1, -2, 1, 0], + [0, 0, 0, 1, -2, 1], + [1, 0, 0, 0, 1, -2], + ] + ) + assert_array_equal(a, lapa) + + def test_1d_with_graph_laplacian(self): + n = 6 + G = diags(np.ones(n - 1), 1, format='dia') + Lf = csgraph.laplacian(G, symmetrized=True, form='function') + La = csgraph.laplacian(G, symmetrized=True, form='array') + grid_shape = (n,) + bc = 'neumann' + lap = LaplacianNd(grid_shape, boundary_conditions=bc) + assert_array_equal(lap(np.eye(n)), -Lf(np.eye(n))) + assert_array_equal(lap.toarray(), -La.toarray()) + # https://github.com/numpy/numpy/issues/24351 + assert_array_equal(lap.tosparse().toarray(), -La.toarray()) + + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_eigenvalues(self, grid_shape, bc): + lap = LaplacianNd(grid_shape, boundary_conditions=bc, dtype=np.float64) + L = lap.toarray() + eigvals = eigh(L, eigvals_only=True) + n = np.prod(grid_shape) + eigenvalues = lap.eigenvalues() + dtype = eigenvalues.dtype + atol = n * n * np.finfo(dtype).eps + # test the default ``m = None`` + assert_allclose(eigenvalues, eigvals, atol=atol) + # test every ``m > 0`` + for m in np.arange(1, n + 1): + assert_array_equal(lap.eigenvalues(m), eigenvalues[-m:]) + + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_eigenvectors(self, grid_shape, bc): + lap = LaplacianNd(grid_shape, boundary_conditions=bc, dtype=np.float64) + n = np.prod(grid_shape) + eigenvalues = lap.eigenvalues() + eigenvectors = lap.eigenvectors() + dtype = eigenvectors.dtype + atol = n * n * max(np.finfo(dtype).eps, np.finfo(np.double).eps) + # test the default ``m = None`` every individual eigenvector + for i in np.arange(n): + r = lap.toarray() @ eigenvectors[:, i] - eigenvectors[:, i] * eigenvalues[i] + assert_allclose(r, np.zeros_like(r), atol=atol) + # test every ``m > 0`` + for m in np.arange(1, n + 1): + e = lap.eigenvalues(m) + ev = lap.eigenvectors(m) + r = lap.toarray() @ ev - ev @ np.diag(e) + assert_allclose(r, np.zeros_like(r), atol=atol) + + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_toarray_tosparse_consistency(self, grid_shape, bc): + lap = LaplacianNd(grid_shape, boundary_conditions=bc) + n = np.prod(grid_shape) + assert_array_equal(lap.toarray(), lap(np.eye(n))) + assert_array_equal(lap.tosparse().toarray(), lap.toarray()) + + @pytest.mark.parametrize('dtype', ALLDTYPES) + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_linearoperator_shape_dtype(self, grid_shape, bc, dtype): + lap = LaplacianNd(grid_shape, boundary_conditions=bc, dtype=dtype) + n = np.prod(grid_shape) + assert lap.shape == (n, n) + assert lap.dtype == dtype + assert_array_equal( + LaplacianNd( + grid_shape, boundary_conditions=bc, dtype=dtype + ).toarray(), + LaplacianNd(grid_shape, boundary_conditions=bc) + .toarray() + .astype(dtype), + ) + assert_array_equal( + LaplacianNd(grid_shape, boundary_conditions=bc, dtype=dtype) + .tosparse() + .toarray(), + LaplacianNd(grid_shape, boundary_conditions=bc) + .tosparse() + .toarray() + .astype(dtype), + ) + + @pytest.mark.parametrize('dtype', ALLDTYPES) + @pytest.mark.parametrize('grid_shape', [(6, ), (2, 3), (2, 3, 4)]) + @pytest.mark.parametrize('bc', ['neumann', 'dirichlet', 'periodic']) + def test_dot(self, grid_shape, bc, dtype): + """ Test the dot-product for type preservation and consistency. + """ + lap = LaplacianNd(grid_shape, boundary_conditions=bc) + n = np.prod(grid_shape) + x0 = np.arange(n) + x1 = x0.reshape((-1, 1)) + x2 = np.arange(2 * n).reshape((n, 2)) + input_set = [x0, x1, x2] + for x in input_set: + y = lap.dot(x.astype(dtype)) + assert x.shape == y.shape + assert y.dtype == dtype + if x.ndim == 2: + yy = lap.toarray() @ x.astype(dtype) + assert yy.dtype == dtype + np.array_equal(y, yy) + + def test_boundary_conditions_value_error(self): + with pytest.raises(ValueError, match="Unknown value 'robin'"): + LaplacianNd(grid_shape=(6, ), boundary_conditions='robin') + + +class TestSakurai: + """ + Sakurai tests + """ + + def test_specific_shape(self): + sak = Sakurai(6) + assert_array_equal(sak.toarray(), sak(np.eye(6))) + a = np.array( + [ + [ 5, -4, 1, 0, 0, 0], + [-4, 6, -4, 1, 0, 0], + [ 1, -4, 6, -4, 1, 0], + [ 0, 1, -4, 6, -4, 1], + [ 0, 0, 1, -4, 6, -4], + [ 0, 0, 0, 1, -4, 5] + ] + ) + + np.array_equal(a, sak.toarray()) + np.array_equal(sak.tosparse().toarray(), sak.toarray()) + ab = np.array( + [ + [ 1, 1, 1, 1, 1, 1], + [-4, -4, -4, -4, -4, -4], + [ 5, 6, 6, 6, 6, 5] + ] + ) + np.array_equal(ab, sak.tobanded()) + e = np.array( + [0.03922866, 0.56703972, 2.41789479, 5.97822974, + 10.54287655, 14.45473055] + ) + np.array_equal(e, sak.eigenvalues()) + np.array_equal(e[:2], sak.eigenvalues(2)) + + # `Sakurai` default `dtype` is `np.int8` as its entries are small integers + @pytest.mark.parametrize('dtype', ALLDTYPES) + def test_linearoperator_shape_dtype(self, dtype): + n = 7 + sak = Sakurai(n, dtype=dtype) + assert sak.shape == (n, n) + assert sak.dtype == dtype + assert_array_equal(sak.toarray(), Sakurai(n).toarray().astype(dtype)) + assert_array_equal(sak.tosparse().toarray(), + Sakurai(n).tosparse().toarray().astype(dtype)) + + @pytest.mark.parametrize('dtype', ALLDTYPES) + @pytest.mark.parametrize('argument_dtype', ALLDTYPES) + def test_dot(self, dtype, argument_dtype): + """ Test the dot-product for type preservation and consistency. + """ + result_dtype = np.promote_types(argument_dtype, dtype) + n = 5 + sak = Sakurai(n) + x0 = np.arange(n) + x1 = x0.reshape((-1, 1)) + x2 = np.arange(2 * n).reshape((n, 2)) + input_set = [x0, x1, x2] + for x in input_set: + y = sak.dot(x.astype(argument_dtype)) + assert x.shape == y.shape + assert np.can_cast(y.dtype, result_dtype) + if x.ndim == 2: + ya = sak.toarray() @ x.astype(argument_dtype) + np.array_equal(y, ya) + assert np.can_cast(ya.dtype, result_dtype) + ys = sak.tosparse() @ x.astype(argument_dtype) + np.array_equal(y, ys) + assert np.can_cast(ys.dtype, result_dtype) + +class TestMikotaPair: + """ + MikotaPair tests + """ + # both MikotaPair `LinearOperator`s share the same dtype + # while `MikotaK` `dtype` can be as small as its default `np.int32` + # since its entries are integers, the `MikotaM` involves inverses + # so its smallest still accurate `dtype` is `np.float32` + tested_types = REAL_DTYPES + COMPLEX_DTYPES + + def test_specific_shape(self): + n = 6 + mik = MikotaPair(n) + mik_k = mik.k + mik_m = mik.m + assert_array_equal(mik_k.toarray(), mik_k(np.eye(n))) + assert_array_equal(mik_m.toarray(), mik_m(np.eye(n))) + + k = np.array( + [ + [11, -5, 0, 0, 0, 0], + [-5, 9, -4, 0, 0, 0], + [ 0, -4, 7, -3, 0, 0], + [ 0, 0, -3, 5, -2, 0], + [ 0, 0, 0, -2, 3, -1], + [ 0, 0, 0, 0, -1, 1] + ] + ) + np.array_equal(k, mik_k.toarray()) + np.array_equal(mik_k.tosparse().toarray(), k) + kb = np.array( + [ + [ 0, -5, -4, -3, -2, -1], + [11, 9, 7, 5, 3, 1] + ] + ) + np.array_equal(kb, mik_k.tobanded()) + + minv = np.arange(1, n + 1) + np.array_equal(np.diag(1. / minv), mik_m.toarray()) + np.array_equal(mik_m.tosparse().toarray(), mik_m.toarray()) + np.array_equal(1. / minv, mik_m.tobanded()) + + e = np.array([ 1, 4, 9, 16, 25, 36]) + np.array_equal(e, mik.eigenvalues()) + np.array_equal(e[:2], mik.eigenvalues(2)) + + @pytest.mark.parametrize('dtype', tested_types) + def test_linearoperator_shape_dtype(self, dtype): + n = 7 + mik = MikotaPair(n, dtype=dtype) + mik_k = mik.k + mik_m = mik.m + assert mik_k.shape == (n, n) + assert mik_k.dtype == dtype + assert mik_m.shape == (n, n) + assert mik_m.dtype == dtype + mik_default_dtype = MikotaPair(n) + mikd_k = mik_default_dtype.k + mikd_m = mik_default_dtype.m + assert mikd_k.shape == (n, n) + assert mikd_k.dtype == np.float64 + assert mikd_m.shape == (n, n) + assert mikd_m.dtype == np.float64 + assert_array_equal(mik_k.toarray(), + mikd_k.toarray().astype(dtype)) + assert_array_equal(mik_k.tosparse().toarray(), + mikd_k.tosparse().toarray().astype(dtype)) + + @pytest.mark.parametrize('dtype', tested_types) + @pytest.mark.parametrize('argument_dtype', ALLDTYPES) + def test_dot(self, dtype, argument_dtype): + """ Test the dot-product for type preservation and consistency. + """ + result_dtype = np.promote_types(argument_dtype, dtype) + n = 5 + mik = MikotaPair(n, dtype=dtype) + mik_k = mik.k + mik_m = mik.m + x0 = np.arange(n) + x1 = x0.reshape((-1, 1)) + x2 = np.arange(2 * n).reshape((n, 2)) + lo_set = [mik_k, mik_m] + input_set = [x0, x1, x2] + for lo in lo_set: + for x in input_set: + y = lo.dot(x.astype(argument_dtype)) + assert x.shape == y.shape + assert np.can_cast(y.dtype, result_dtype) + if x.ndim == 2: + ya = lo.toarray() @ x.astype(argument_dtype) + np.array_equal(y, ya) + assert np.can_cast(ya.dtype, result_dtype) + ys = lo.tosparse() @ x.astype(argument_dtype) + np.array_equal(y, ys) + assert np.can_cast(ys.dtype, result_dtype) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/sparsetools.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/sparsetools.py new file mode 100644 index 0000000000000000000000000000000000000000..404e431d89d479520d2198ae73b9eab7b23a80f7 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/sparsetools.py @@ -0,0 +1,17 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__: list[str] = [] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="sparsetools", + private_modules=["_sparsetools"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/spfuncs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/spfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..911969e414d4a1d3888900ad8392b5fc2177c850 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/spfuncs.py @@ -0,0 +1,17 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__: list[str] = [] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="spfuncs", + private_modules=["_spfuncs"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/sputils.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/sputils.py new file mode 100644 index 0000000000000000000000000000000000000000..4ddd27a43889609b0642bd7579e13c8e3c460a8b --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/sputils.py @@ -0,0 +1,17 @@ +# This file is not meant for public use and will be removed in SciPy v2.0.0. +# Use the `scipy.sparse` namespace for importing the functions +# included below. + +from scipy._lib.deprecation import _sub_module_deprecation + +__all__: list[str] = [] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + return _sub_module_deprecation(sub_package="sparse", module="sputils", + private_modules=["_sputils"], all=__all__, + attribute=name) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__init__.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/__init__.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96c7392944264589b51913c4be13ee1e308e9cdb Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_arithmetic1d.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_arithmetic1d.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7117441497d4379d49cf4e65c1a971007530392c Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_arithmetic1d.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_array_api.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_array_api.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df492b41284b3d970a4c3a92531f7493b98afe4b Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_array_api.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_common1d.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_common1d.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d4fb8236d893ea99d7506c3fd02548ecb9f0fe4 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_common1d.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_construct.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_construct.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b19c091d2a6df5ab7665d3f71c29d4651ddcf29 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_construct.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_coo.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_coo.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb16822f2d6e8a4122e86175134de414df9aa346 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_coo.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_csc.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_csc.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a6ce71364ca17f31efc0296926d27e8beab317f Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_csc.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_csr.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_csr.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a565d9efe49636d270aa1abb8143548004ed545 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_csr.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_dok.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_dok.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aad27995f479e87cd6cd7a2a38f3791f4548691f Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_dok.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_extract.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_extract.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10099e842f5803761d154ecc9d43ef337f997522 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_extract.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_matrix_io.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_matrix_io.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f762d12df50b44c5a0ea9dd535485458bd6c06b4 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_matrix_io.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_minmax1d.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_minmax1d.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0199d59587f9d2faf92e303a4a8d8bb3109a2281 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_minmax1d.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_sparsetools.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_sparsetools.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd358b8638e0a1fbf73eef30909df2bc0e65ef51 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_sparsetools.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_spfuncs.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_spfuncs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24fc52b03d5adf22b15cd4b5474ed6242d7df83a Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_spfuncs.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_sputils.cpython-310.pyc b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_sputils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..093113c6ec07739a0ef273b009deba95e82f0819 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/__pycache__/test_sputils.cpython-310.pyc differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/data/csc_py2.npz b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/data/csc_py2.npz new file mode 100644 index 0000000000000000000000000000000000000000..83ee25757b61f6125eb181950a2ed5e6ce5623ef Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/data/csc_py2.npz differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/data/csc_py3.npz b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/data/csc_py3.npz new file mode 100644 index 0000000000000000000000000000000000000000..73d086fdcfc143dfc73a186c438b6412c3dbb901 Binary files /dev/null and b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/data/csc_py3.npz differ diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_arithmetic1d.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_arithmetic1d.py new file mode 100644 index 0000000000000000000000000000000000000000..3d5d2ee2f1bc2c3fef03c71fc0206cd06f3f8618 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_arithmetic1d.py @@ -0,0 +1,338 @@ +"""Test of 1D arithmetic operations""" + +import pytest + +import numpy as np +from numpy.testing import assert_equal, assert_allclose + +from scipy.sparse import coo_array, csr_array +from scipy.sparse._sputils import isscalarlike + + +spcreators = [coo_array, csr_array] +math_dtypes = [np.int64, np.float64, np.complex128] + + +def toarray(a): + if isinstance(a, np.ndarray) or isscalarlike(a): + return a + return a.toarray() + +@pytest.fixture +def dat1d(): + return np.array([3, 0, 1, 0], 'd') + + +@pytest.fixture +def datsp_math_dtypes(dat1d): + dat_dtypes = {dtype: dat1d.astype(dtype) for dtype in math_dtypes} + return { + sp: [(dtype, dat, sp(dat)) for dtype, dat in dat_dtypes.items()] + for sp in spcreators + } + + +@pytest.mark.parametrize("spcreator", spcreators) +class TestArithmetic1D: + def test_empty_arithmetic(self, spcreator): + shape = (5,) + for mytype in [ + np.dtype('int32'), + np.dtype('float32'), + np.dtype('float64'), + np.dtype('complex64'), + np.dtype('complex128'), + ]: + a = spcreator(shape, dtype=mytype) + b = a + a + c = 2 * a + assert isinstance(a @ a.tocsr(), np.ndarray) + assert isinstance(a @ a.tocoo(), np.ndarray) + for m in [a, b, c]: + assert m @ m == a.toarray() @ a.toarray() + assert m.dtype == mytype + assert toarray(m).dtype == mytype + + def test_abs(self, spcreator): + A = np.array([-1, 0, 17, 0, -5, 0, 1, -4, 0, 0, 0, 0], 'd') + assert_equal(abs(A), abs(spcreator(A)).toarray()) + + def test_round(self, spcreator): + A = np.array([-1.35, 0.56, 17.25, -5.98], 'd') + Asp = spcreator(A) + assert_equal(np.around(A, decimals=1), round(Asp, ndigits=1).toarray()) + + def test_elementwise_power(self, spcreator): + A = np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4], 'd') + Asp = spcreator(A) + assert_equal(np.power(A, 2), Asp.power(2).toarray()) + + # element-wise power function needs a scalar power + with pytest.raises(NotImplementedError, match='input is not scalar'): + spcreator(A).power(A) + + def test_real(self, spcreator): + D = np.array([1 + 3j, 2 - 4j]) + A = spcreator(D) + assert_equal(A.real.toarray(), D.real) + + def test_imag(self, spcreator): + D = np.array([1 + 3j, 2 - 4j]) + A = spcreator(D) + assert_equal(A.imag.toarray(), D.imag) + + def test_mul_scalar(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + assert_equal(dat * 2, (datsp * 2).toarray()) + assert_equal(dat * 17.3, (datsp * 17.3).toarray()) + + def test_rmul_scalar(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + assert_equal(2 * dat, (2 * datsp).toarray()) + assert_equal(17.3 * dat, (17.3 * datsp).toarray()) + + def test_sub(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + assert_equal((datsp - datsp).toarray(), np.zeros(4)) + assert_equal((datsp - 0).toarray(), dat) + + A = spcreator([1, -4, 0, 2], dtype='d') + assert_equal((datsp - A).toarray(), dat - A.toarray()) + assert_equal((A - datsp).toarray(), A.toarray() - dat) + + # test broadcasting + assert_equal(datsp.toarray() - dat[0], dat - dat[0]) + + def test_add0(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + # Adding 0 to a sparse matrix + assert_equal((datsp + 0).toarray(), dat) + # use sum (which takes 0 as a starting value) + sumS = sum([k * datsp for k in range(1, 3)]) + sumD = sum([k * dat for k in range(1, 3)]) + assert_allclose(sumS.toarray(), sumD) + + def test_elementwise_multiply(self, spcreator): + # real/real + A = np.array([4, 0, 9]) + B = np.array([0, 7, -1]) + Asp = spcreator(A) + Bsp = spcreator(B) + assert_allclose(Asp.multiply(Bsp).toarray(), A * B) # sparse/sparse + assert_allclose(Asp.multiply(B).toarray(), A * B) # sparse/dense + + # complex/complex + C = np.array([1 - 2j, 0 + 5j, -1 + 0j]) + D = np.array([5 + 2j, 7 - 3j, -2 + 1j]) + Csp = spcreator(C) + Dsp = spcreator(D) + assert_allclose(Csp.multiply(Dsp).toarray(), C * D) # sparse/sparse + assert_allclose(Csp.multiply(D).toarray(), C * D) # sparse/dense + + # real/complex + assert_allclose(Asp.multiply(Dsp).toarray(), A * D) # sparse/sparse + assert_allclose(Asp.multiply(D).toarray(), A * D) # sparse/dense + + def test_elementwise_multiply_broadcast(self, spcreator): + A = np.array([4]) + B = np.array([[-9]]) + C = np.array([1, -1, 0]) + D = np.array([[7, 9, -9]]) + E = np.array([[3], [2], [1]]) + F = np.array([[8, 6, 3], [-4, 3, 2], [6, 6, 6]]) + G = [1, 2, 3] + H = np.ones((3, 4)) + J = H.T + K = np.array([[0]]) + L = np.array([[[1, 2], [0, 1]]]) + + # Some arrays can't be cast as spmatrices (A, C, L) so leave + # them out. + Asp = spcreator(A) + Csp = spcreator(C) + Gsp = spcreator(G) + # 2d arrays + Bsp = spcreator(B) + Dsp = spcreator(D) + Esp = spcreator(E) + Fsp = spcreator(F) + Hsp = spcreator(H) + Hspp = spcreator(H[0, None]) + Jsp = spcreator(J) + Jspp = spcreator(J[:, 0, None]) + Ksp = spcreator(K) + + matrices = [A, B, C, D, E, F, G, H, J, K, L] + spmatrices = [Asp, Bsp, Csp, Dsp, Esp, Fsp, Gsp, Hsp, Hspp, Jsp, Jspp, Ksp] + sp1dmatrices = [Asp, Csp, Gsp] + + # sparse/sparse + for i in sp1dmatrices: + for j in spmatrices: + try: + dense_mult = i.toarray() * j.toarray() + except ValueError: + with pytest.raises(ValueError, match='inconsistent shapes'): + i.multiply(j) + continue + sp_mult = i.multiply(j) + assert_allclose(sp_mult.toarray(), dense_mult) + + # sparse/dense + for i in sp1dmatrices: + for j in matrices: + try: + dense_mult = i.toarray() * j + except TypeError: + continue + except ValueError: + matchme = 'broadcast together|inconsistent shapes' + with pytest.raises(ValueError, match=matchme): + i.multiply(j) + continue + sp_mult = i.multiply(j) + assert_allclose(toarray(sp_mult), dense_mult) + + def test_elementwise_divide(self, spcreator, dat1d): + datsp = spcreator(dat1d) + expected = np.array([1, np.nan, 1, np.nan]) + actual = datsp / datsp + # need assert_array_equal to handle nan values + np.testing.assert_array_equal(actual, expected) + + denom = spcreator([1, 0, 0, 4], dtype='d') + expected = [3, np.nan, np.inf, 0] + np.testing.assert_array_equal(datsp / denom, expected) + + # complex + A = np.array([1 - 2j, 0 + 5j, -1 + 0j]) + B = np.array([5 + 2j, 7 - 3j, -2 + 1j]) + Asp = spcreator(A) + Bsp = spcreator(B) + assert_allclose(Asp / Bsp, A / B) + + # integer + A = np.array([1, 2, 3]) + B = np.array([0, 1, 2]) + Asp = spcreator(A) + Bsp = spcreator(B) + with np.errstate(divide='ignore'): + assert_equal(Asp / Bsp, A / B) + + # mismatching sparsity patterns + A = np.array([0, 1]) + B = np.array([1, 0]) + Asp = spcreator(A) + Bsp = spcreator(B) + with np.errstate(divide='ignore', invalid='ignore'): + assert_equal(Asp / Bsp, A / B) + + def test_pow(self, spcreator): + A = np.array([1, 0, 2, 0]) + B = spcreator(A) + + # unusual exponents + with pytest.raises(ValueError, match='negative integer powers'): + B**-1 + with pytest.raises(NotImplementedError, match='zero power'): + B**0 + + for exponent in [1, 2, 3, 2.2]: + ret_sp = B**exponent + ret_np = A**exponent + assert_equal(ret_sp.toarray(), ret_np) + assert_equal(ret_sp.dtype, ret_np.dtype) + + def test_dot_scalar(self, spcreator, dat1d): + A = spcreator(dat1d) + scalar = 10 + actual = A.dot(scalar) + expected = A * scalar + + assert_allclose(actual.toarray(), expected.toarray()) + + def test_matmul(self, spcreator): + Msp = spcreator([2, 0, 3.0]) + B = spcreator(np.array([[0, 1], [1, 0], [0, 2]], 'd')) + col = np.array([[1, 2, 3]]).T + + # check sparse @ dense 2d column + assert_allclose(Msp @ col, Msp.toarray() @ col) + + # check sparse1d @ sparse2d, sparse1d @ dense2d, dense1d @ sparse2d + assert_allclose((Msp @ B).toarray(), (Msp @ B).toarray()) + assert_allclose(Msp.toarray() @ B, (Msp @ B).toarray()) + assert_allclose(Msp @ B.toarray(), (Msp @ B).toarray()) + + # check sparse1d @ dense1d, sparse1d @ sparse1d + V = np.array([0, 0, 1]) + assert_allclose(Msp @ V, Msp.toarray() @ V) + + Vsp = spcreator(V) + Msp_Vsp = Msp @ Vsp + assert isinstance(Msp_Vsp, np.ndarray) + assert Msp_Vsp.shape == () + + # output is 0-dim ndarray + assert_allclose(np.array(3), Msp_Vsp) + assert_allclose(np.array(3), Msp.toarray() @ Vsp) + assert_allclose(np.array(3), Msp @ Vsp.toarray()) + assert_allclose(np.array(3), Msp.toarray() @ Vsp.toarray()) + + # check error on matrix-scalar + with pytest.raises(ValueError, match='Scalar operands are not allowed'): + Msp @ 1 + with pytest.raises(ValueError, match='Scalar operands are not allowed'): + 1 @ Msp + + def test_sub_dense(self, spcreator, datsp_math_dtypes): + # subtracting a dense matrix to/from a sparse matrix + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + # Manually add to avoid upcasting from scalar + # multiplication. + sum1 = (dat + dat + dat) - datsp + assert_equal(sum1, dat + dat) + sum2 = (datsp + datsp + datsp) - dat + assert_equal(sum2, dat + dat) + + def test_size_zero_matrix_arithmetic(self, spcreator): + # Test basic matrix arithmetic with shapes like 0, (1, 0), (0, 3), etc. + mat = np.array([]) + a = mat.reshape(0) + d = mat.reshape((1, 0)) + f = np.ones([5, 5]) + + asp = spcreator(a) + dsp = spcreator(d) + # bad shape for addition + with pytest.raises(ValueError, match='inconsistent shapes'): + asp.__add__(dsp) + + # matrix product. + assert_equal(asp.dot(asp), np.dot(a, a)) + + # bad matrix products + with pytest.raises(ValueError, match='dimension mismatch'): + asp.dot(f) + + # elemente-wise multiplication + assert_equal(asp.multiply(asp).toarray(), np.multiply(a, a)) + + assert_equal(asp.multiply(a).toarray(), np.multiply(a, a)) + + assert_equal(asp.multiply(6).toarray(), np.multiply(a, 6)) + + # bad element-wise multiplication + with pytest.raises(ValueError, match='inconsistent shapes'): + asp.multiply(f) + + # Addition + assert_equal(asp.__add__(asp).toarray(), a.__add__(a)) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_array_api.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_array_api.py new file mode 100644 index 0000000000000000000000000000000000000000..a3be7b868b1ae7a92d75da616d2f9665e745d4a4 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_array_api.py @@ -0,0 +1,565 @@ +import pytest +import numpy as np +import numpy.testing as npt +import scipy.sparse +import scipy.sparse.linalg as spla + + +sparray_types = ('bsr', 'coo', 'csc', 'csr', 'dia', 'dok', 'lil') + +sparray_classes = [ + getattr(scipy.sparse, f'{T}_array') for T in sparray_types +] + +A = np.array([ + [0, 1, 2, 0], + [2, 0, 0, 3], + [1, 4, 0, 0] +]) + +B = np.array([ + [0, 1], + [2, 0] +]) + +X = np.array([ + [1, 0, 0, 1], + [2, 1, 2, 0], + [0, 2, 1, 0], + [0, 0, 1, 2] +], dtype=float) + + +sparrays = [sparray(A) for sparray in sparray_classes] +square_sparrays = [sparray(B) for sparray in sparray_classes] +eig_sparrays = [sparray(X) for sparray in sparray_classes] + +parametrize_sparrays = pytest.mark.parametrize( + "A", sparrays, ids=sparray_types +) +parametrize_square_sparrays = pytest.mark.parametrize( + "B", square_sparrays, ids=sparray_types +) +parametrize_eig_sparrays = pytest.mark.parametrize( + "X", eig_sparrays, ids=sparray_types +) + + +@parametrize_sparrays +def test_sum(A): + assert not isinstance(A.sum(axis=0), np.matrix), \ + "Expected array, got matrix" + assert A.sum(axis=0).shape == (4,) + assert A.sum(axis=1).shape == (3,) + + +@parametrize_sparrays +def test_mean(A): + assert not isinstance(A.mean(axis=1), np.matrix), \ + "Expected array, got matrix" + + +@parametrize_sparrays +def test_min_max(A): + # Some formats don't support min/max operations, so we skip those here. + if hasattr(A, 'min'): + assert not isinstance(A.min(axis=1), np.matrix), \ + "Expected array, got matrix" + if hasattr(A, 'max'): + assert not isinstance(A.max(axis=1), np.matrix), \ + "Expected array, got matrix" + if hasattr(A, 'argmin'): + assert not isinstance(A.argmin(axis=1), np.matrix), \ + "Expected array, got matrix" + if hasattr(A, 'argmax'): + assert not isinstance(A.argmax(axis=1), np.matrix), \ + "Expected array, got matrix" + + +@parametrize_sparrays +def test_todense(A): + assert not isinstance(A.todense(), np.matrix), \ + "Expected array, got matrix" + + +@parametrize_sparrays +def test_indexing(A): + if A.__class__.__name__[:3] in ('dia', 'coo', 'bsr'): + return + + with pytest.raises(NotImplementedError): + A[1, :] + + with pytest.raises(NotImplementedError): + A[:, 1] + + with pytest.raises(NotImplementedError): + A[1, [1, 2]] + + with pytest.raises(NotImplementedError): + A[[1, 2], 1] + + assert isinstance(A[[0]], scipy.sparse.sparray), \ + "Expected sparse array, got sparse matrix" + assert isinstance(A[1, [[1, 2]]], scipy.sparse.sparray), \ + "Expected ndarray, got sparse array" + assert isinstance(A[[[1, 2]], 1], scipy.sparse.sparray), \ + "Expected ndarray, got sparse array" + assert isinstance(A[:, [1, 2]], scipy.sparse.sparray), \ + "Expected sparse array, got something else" + + +@parametrize_sparrays +def test_dense_addition(A): + X = np.random.random(A.shape) + assert not isinstance(A + X, np.matrix), "Expected array, got matrix" + + +@parametrize_sparrays +def test_sparse_addition(A): + assert isinstance((A + A), scipy.sparse.sparray), "Expected array, got matrix" + + +@parametrize_sparrays +def test_elementwise_mul(A): + assert np.all((A * A).todense() == A.power(2).todense()) + + +@parametrize_sparrays +def test_elementwise_rmul(A): + with pytest.raises(TypeError): + None * A + + with pytest.raises(ValueError): + np.eye(3) * scipy.sparse.csr_array(np.arange(6).reshape(2, 3)) + + assert np.all((2 * A) == (A.todense() * 2)) + + assert np.all((A.todense() * A) == (A.todense() ** 2)) + + +@parametrize_sparrays +def test_matmul(A): + assert np.all((A @ A.T).todense() == A.dot(A.T).todense()) + + +@parametrize_sparrays +def test_power_operator(A): + assert isinstance((A**2), scipy.sparse.sparray), "Expected array, got matrix" + + # https://github.com/scipy/scipy/issues/15948 + npt.assert_equal((A**2).todense(), (A.todense())**2) + + # power of zero is all ones (dense) so helpful msg exception + with pytest.raises(NotImplementedError, match="zero power"): + A**0 + + +@parametrize_sparrays +def test_sparse_divide(A): + assert isinstance(A / A, np.ndarray) + +@parametrize_sparrays +def test_sparse_dense_divide(A): + with pytest.warns(RuntimeWarning): + assert isinstance((A / A.todense()), scipy.sparse.sparray) + +@parametrize_sparrays +def test_dense_divide(A): + assert isinstance((A / 2), scipy.sparse.sparray), "Expected array, got matrix" + + +@parametrize_sparrays +def test_no_A_attr(A): + with pytest.raises(AttributeError): + A.A + + +@parametrize_sparrays +def test_no_H_attr(A): + with pytest.raises(AttributeError): + A.H + + +@parametrize_sparrays +def test_getrow_getcol(A): + assert isinstance(A._getcol(0), scipy.sparse.sparray) + assert isinstance(A._getrow(0), scipy.sparse.sparray) + + +# -- linalg -- + +@parametrize_sparrays +def test_as_linearoperator(A): + L = spla.aslinearoperator(A) + npt.assert_allclose(L * [1, 2, 3, 4], A @ [1, 2, 3, 4]) + + +@parametrize_square_sparrays +def test_inv(B): + if B.__class__.__name__[:3] != 'csc': + return + + C = spla.inv(B) + + assert isinstance(C, scipy.sparse.sparray) + npt.assert_allclose(C.todense(), np.linalg.inv(B.todense())) + + +@parametrize_square_sparrays +def test_expm(B): + if B.__class__.__name__[:3] != 'csc': + return + + Bmat = scipy.sparse.csc_matrix(B) + + C = spla.expm(B) + + assert isinstance(C, scipy.sparse.sparray) + npt.assert_allclose( + C.todense(), + spla.expm(Bmat).todense() + ) + + +@parametrize_square_sparrays +def test_expm_multiply(B): + if B.__class__.__name__[:3] != 'csc': + return + + npt.assert_allclose( + spla.expm_multiply(B, np.array([1, 2])), + spla.expm(B) @ [1, 2] + ) + + +@parametrize_sparrays +def test_norm(A): + C = spla.norm(A) + npt.assert_allclose(C, np.linalg.norm(A.todense())) + + +@parametrize_square_sparrays +def test_onenormest(B): + C = spla.onenormest(B) + npt.assert_allclose(C, np.linalg.norm(B.todense(), 1)) + + +@parametrize_square_sparrays +def test_spsolve(B): + if B.__class__.__name__[:3] not in ('csc', 'csr'): + return + + npt.assert_allclose( + spla.spsolve(B, [1, 2]), + np.linalg.solve(B.todense(), [1, 2]) + ) + + +@pytest.mark.parametrize("fmt",["csr","csc"]) +def test_spsolve_triangular(fmt): + arr = [ + [1, 0, 0, 0], + [2, 1, 0, 0], + [3, 2, 1, 0], + [4, 3, 2, 1], + ] + if fmt == "csr": + X = scipy.sparse.csr_array(arr) + else: + X = scipy.sparse.csc_array(arr) + spla.spsolve_triangular(X, [1, 2, 3, 4]) + + +@parametrize_square_sparrays +def test_factorized(B): + if B.__class__.__name__[:3] != 'csc': + return + + LU = spla.factorized(B) + npt.assert_allclose( + LU(np.array([1, 2])), + np.linalg.solve(B.todense(), [1, 2]) + ) + + +@parametrize_square_sparrays +@pytest.mark.parametrize( + "solver", + ["bicg", "bicgstab", "cg", "cgs", "gmres", "lgmres", "minres", "qmr", + "gcrotmk", "tfqmr"] +) +def test_solvers(B, solver): + if solver == "minres": + kwargs = {} + else: + kwargs = {'atol': 1e-5} + + x, info = getattr(spla, solver)(B, np.array([1, 2]), **kwargs) + assert info >= 0 # no errors, even if perhaps did not converge fully + npt.assert_allclose(x, [1, 1], atol=1e-1) + + +@parametrize_sparrays +@pytest.mark.parametrize( + "solver", + ["lsqr", "lsmr"] +) +def test_lstsqr(A, solver): + x, *_ = getattr(spla, solver)(A, [1, 2, 3]) + npt.assert_allclose(A @ x, [1, 2, 3]) + + +@parametrize_eig_sparrays +def test_eigs(X): + e, v = spla.eigs(X, k=1) + npt.assert_allclose( + X @ v, + e[0] * v + ) + + +@parametrize_eig_sparrays +def test_eigsh(X): + X = X + X.T + e, v = spla.eigsh(X, k=1) + npt.assert_allclose( + X @ v, + e[0] * v + ) + + +@parametrize_eig_sparrays +def test_svds(X): + u, s, vh = spla.svds(X, k=3) + u2, s2, vh2 = np.linalg.svd(X.todense()) + s = np.sort(s) + s2 = np.sort(s2[:3]) + npt.assert_allclose(s, s2, atol=1e-3) + + +def test_splu(): + X = scipy.sparse.csc_array([ + [1, 0, 0, 0], + [2, 1, 0, 0], + [3, 2, 1, 0], + [4, 3, 2, 1], + ]) + LU = spla.splu(X) + npt.assert_allclose( + LU.solve(np.array([1, 2, 3, 4])), + np.asarray([1, 0, 0, 0], dtype=np.float64), + rtol=1e-14, atol=3e-16 + ) + + +def test_spilu(): + X = scipy.sparse.csc_array([ + [1, 0, 0, 0], + [2, 1, 0, 0], + [3, 2, 1, 0], + [4, 3, 2, 1], + ]) + LU = spla.spilu(X) + npt.assert_allclose( + LU.solve(np.array([1, 2, 3, 4])), + np.asarray([1, 0, 0, 0], dtype=np.float64), + rtol=1e-14, atol=3e-16 + ) + + +@pytest.mark.parametrize( + "cls,indices_attrs", + [ + ( + scipy.sparse.csr_array, + ["indices", "indptr"], + ), + ( + scipy.sparse.csc_array, + ["indices", "indptr"], + ), + ( + scipy.sparse.coo_array, + ["row", "col"], + ), + ] +) +@pytest.mark.parametrize("expected_dtype", [np.int64, np.int32]) +def test_index_dtype_compressed(cls, indices_attrs, expected_dtype): + input_array = scipy.sparse.coo_array(np.arange(9).reshape(3, 3)) + coo_tuple = ( + input_array.data, + ( + input_array.row.astype(expected_dtype), + input_array.col.astype(expected_dtype), + ) + ) + + result = cls(coo_tuple) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + result = cls(coo_tuple, shape=(3, 3)) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + if issubclass(cls, scipy.sparse._compressed._cs_matrix): + input_array_csr = input_array.tocsr() + csr_tuple = ( + input_array_csr.data, + input_array_csr.indices.astype(expected_dtype), + input_array_csr.indptr.astype(expected_dtype), + ) + + result = cls(csr_tuple) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + result = cls(csr_tuple, shape=(3, 3)) + for attr in indices_attrs: + assert getattr(result, attr).dtype == expected_dtype + + +def test_default_is_matrix_diags(): + m = scipy.sparse.diags([0, 1, 2]) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_eye(): + m = scipy.sparse.eye(3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_spdiags(): + m = scipy.sparse.spdiags([1, 2, 3], 0, 3, 3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_identity(): + m = scipy.sparse.identity(3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_kron_dense(): + m = scipy.sparse.kron( + np.array([[1, 2], [3, 4]]), np.array([[4, 3], [2, 1]]) + ) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_kron_sparse(): + m = scipy.sparse.kron( + np.array([[1, 2], [3, 4]]), np.array([[1, 0], [0, 0]]) + ) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_kronsum(): + m = scipy.sparse.kronsum( + np.array([[1, 0], [0, 1]]), np.array([[0, 1], [1, 0]]) + ) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_random(): + m = scipy.sparse.random(3, 3) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_default_is_matrix_rand(): + m = scipy.sparse.rand(3, 3) + assert not isinstance(m, scipy.sparse.sparray) + + +@pytest.mark.parametrize("fn", (scipy.sparse.hstack, scipy.sparse.vstack)) +def test_default_is_matrix_stacks(fn): + """Same idea as `test_default_construction_fn_matrices`, but for the + stacking creation functions.""" + A = scipy.sparse.coo_matrix(np.eye(2)) + B = scipy.sparse.coo_matrix([[0, 1], [1, 0]]) + m = fn([A, B]) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_blocks_default_construction_fn_matrices(): + """Same idea as `test_default_construction_fn_matrices`, but for the block + creation function""" + A = scipy.sparse.coo_matrix(np.eye(2)) + B = scipy.sparse.coo_matrix([[2], [0]]) + C = scipy.sparse.coo_matrix([[3]]) + + # block diag + m = scipy.sparse.block_diag((A, B, C)) + assert not isinstance(m, scipy.sparse.sparray) + + # bmat + m = scipy.sparse.bmat([[A, None], [None, C]]) + assert not isinstance(m, scipy.sparse.sparray) + + +def test_format_property(): + for fmt in sparray_types: + arr_cls = getattr(scipy.sparse, f"{fmt}_array") + M = arr_cls([[1, 2]]) + assert M.format == fmt + assert M._format == fmt + with pytest.raises(AttributeError): + M.format = "qqq" + + +def test_issparse(): + m = scipy.sparse.eye(3) + a = scipy.sparse.csr_array(m) + assert not isinstance(m, scipy.sparse.sparray) + assert isinstance(a, scipy.sparse.sparray) + + # Both sparse arrays and sparse matrices should be sparse + assert scipy.sparse.issparse(a) + assert scipy.sparse.issparse(m) + + # ndarray and array_likes are not sparse + assert not scipy.sparse.issparse(a.todense()) + assert not scipy.sparse.issparse(m.todense()) + + +def test_isspmatrix(): + m = scipy.sparse.eye(3) + a = scipy.sparse.csr_array(m) + assert not isinstance(m, scipy.sparse.sparray) + assert isinstance(a, scipy.sparse.sparray) + + # Should only be true for sparse matrices, not sparse arrays + assert not scipy.sparse.isspmatrix(a) + assert scipy.sparse.isspmatrix(m) + + # ndarray and array_likes are not sparse + assert not scipy.sparse.isspmatrix(a.todense()) + assert not scipy.sparse.isspmatrix(m.todense()) + + +@pytest.mark.parametrize( + ("fmt", "fn"), + ( + ("bsr", scipy.sparse.isspmatrix_bsr), + ("coo", scipy.sparse.isspmatrix_coo), + ("csc", scipy.sparse.isspmatrix_csc), + ("csr", scipy.sparse.isspmatrix_csr), + ("dia", scipy.sparse.isspmatrix_dia), + ("dok", scipy.sparse.isspmatrix_dok), + ("lil", scipy.sparse.isspmatrix_lil), + ), +) +def test_isspmatrix_format(fmt, fn): + m = scipy.sparse.eye(3, format=fmt) + a = scipy.sparse.csr_array(m).asformat(fmt) + assert not isinstance(m, scipy.sparse.sparray) + assert isinstance(a, scipy.sparse.sparray) + + # Should only be true for sparse matrices, not sparse arrays + assert not fn(a) + assert fn(m) + + # ndarray and array_likes are not sparse + assert not fn(a.todense()) + assert not fn(m.todense()) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_base.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_base.py new file mode 100644 index 0000000000000000000000000000000000000000..1aa76b8ff26efea45b36ad41e99ad8b6c8a127b2 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_base.py @@ -0,0 +1,5210 @@ +# +# Authors: Travis Oliphant, Ed Schofield, Robert Cimrman, Nathan Bell, and others + +""" Test functions for sparse matrices. Each class in the "Matrix class +based tests" section become subclasses of the classes in the "Generic +tests" section. This is done by the functions in the "Tailored base +class for generic tests" section. + +""" + + +import contextlib +import functools +import operator +import platform +import itertools +import sys + +import pytest +from pytest import raises as assert_raises + +import numpy as np +from numpy import (arange, zeros, array, dot, asarray, + vstack, ndarray, transpose, diag, kron, inf, conjugate, + int8) + +import random +from numpy.testing import (assert_equal, assert_array_equal, + assert_array_almost_equal, assert_almost_equal, assert_, + assert_allclose, suppress_warnings) + +import scipy.linalg + +import scipy.sparse as sparse +from scipy.sparse import (csc_matrix, csr_matrix, dok_matrix, + coo_matrix, lil_matrix, dia_matrix, bsr_matrix, + eye, issparse, SparseEfficiencyWarning, sparray) +from scipy.sparse._base import _formats +from scipy.sparse._sputils import (supported_dtypes, isscalarlike, + get_index_dtype, asmatrix, matrix) +from scipy.sparse.linalg import splu, expm, inv + +from scipy._lib.decorator import decorator +from scipy._lib._util import ComplexWarning + + +IS_COLAB = ('google.colab' in sys.modules) + + +def assert_in(member, collection, msg=None): + message = msg if msg is not None else f"{member!r} not found in {collection!r}" + assert_(member in collection, msg=message) + + +def assert_array_equal_dtype(x, y, **kwargs): + assert_(x.dtype == y.dtype) + assert_array_equal(x, y, **kwargs) + + +NON_ARRAY_BACKED_FORMATS = frozenset(['dok']) + +def sparse_may_share_memory(A, B): + # Checks if A and B have any numpy array sharing memory. + + def _underlying_arrays(x): + # Given any object (e.g. a sparse array), returns all numpy arrays + # stored in any attribute. + + arrays = [] + for a in x.__dict__.values(): + if isinstance(a, (np.ndarray, np.generic)): + arrays.append(a) + return arrays + + for a in _underlying_arrays(A): + for b in _underlying_arrays(B): + if np.may_share_memory(a, b): + return True + return False + + +sup_complex = suppress_warnings() +sup_complex.filter(ComplexWarning) + + +def with_64bit_maxval_limit(maxval_limit=None, random=False, fixed_dtype=None, + downcast_maxval=None, assert_32bit=False): + """ + Monkeypatch the maxval threshold at which scipy.sparse switches to + 64-bit index arrays, or make it (pseudo-)random. + + """ + if maxval_limit is None: + maxval_limit = np.int64(10) + else: + # Ensure we use numpy scalars rather than Python scalars (matters for + # NEP 50 casting rule changes) + maxval_limit = np.int64(maxval_limit) + + if assert_32bit: + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + tp = get_index_dtype(arrays, maxval, check_contents) + assert_equal(np.iinfo(tp).max, np.iinfo(np.int32).max) + assert_(tp == np.int32 or tp == np.intc) + return tp + elif fixed_dtype is not None: + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + return fixed_dtype + elif random: + counter = np.random.RandomState(seed=1234) + + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + return (np.int32, np.int64)[counter.randint(2)] + else: + def new_get_index_dtype(arrays=(), maxval=None, check_contents=False): + dtype = np.int32 + if maxval is not None: + if maxval > maxval_limit: + dtype = np.int64 + for arr in arrays: + arr = np.asarray(arr) + if arr.dtype > np.int32: + if check_contents: + if arr.size == 0: + # a bigger type not needed + continue + elif np.issubdtype(arr.dtype, np.integer): + maxval = arr.max() + minval = arr.min() + if minval >= -maxval_limit and maxval <= maxval_limit: + # a bigger type not needed + continue + dtype = np.int64 + return dtype + + if downcast_maxval is not None: + def new_downcast_intp_index(arr): + if arr.max() > downcast_maxval: + raise AssertionError("downcast limited") + return arr.astype(np.intp) + + @decorator + def deco(func, *a, **kw): + backup = [] + modules = [scipy.sparse._bsr, scipy.sparse._coo, scipy.sparse._csc, + scipy.sparse._csr, scipy.sparse._dia, scipy.sparse._dok, + scipy.sparse._lil, scipy.sparse._sputils, + scipy.sparse._compressed, scipy.sparse._construct] + try: + for mod in modules: + backup.append((mod, 'get_index_dtype', + getattr(mod, 'get_index_dtype', None))) + setattr(mod, 'get_index_dtype', new_get_index_dtype) + if downcast_maxval is not None: + backup.append((mod, 'downcast_intp_index', + getattr(mod, 'downcast_intp_index', None))) + setattr(mod, 'downcast_intp_index', new_downcast_intp_index) + return func(*a, **kw) + finally: + for mod, name, oldfunc in backup: + if oldfunc is not None: + setattr(mod, name, oldfunc) + + return deco + + +def toarray(a): + if isinstance(a, np.ndarray) or isscalarlike(a): + return a + return a.toarray() + + +class BinopTester: + # Custom type to test binary operations on sparse matrices. + + def __add__(self, mat): + return "matrix on the right" + + def __mul__(self, mat): + return "matrix on the right" + + def __sub__(self, mat): + return "matrix on the right" + + def __radd__(self, mat): + return "matrix on the left" + + def __rmul__(self, mat): + return "matrix on the left" + + def __rsub__(self, mat): + return "matrix on the left" + + def __matmul__(self, mat): + return "matrix on the right" + + def __rmatmul__(self, mat): + return "matrix on the left" + +class BinopTester_with_shape: + # Custom type to test binary operations on sparse matrices + # with object which has shape attribute. + def __init__(self,shape): + self._shape = shape + + def shape(self): + return self._shape + + def ndim(self): + return len(self._shape) + + def __add__(self, mat): + return "matrix on the right" + + def __mul__(self, mat): + return "matrix on the right" + + def __sub__(self, mat): + return "matrix on the right" + + def __radd__(self, mat): + return "matrix on the left" + + def __rmul__(self, mat): + return "matrix on the left" + + def __rsub__(self, mat): + return "matrix on the left" + + def __matmul__(self, mat): + return "matrix on the right" + + def __rmatmul__(self, mat): + return "matrix on the left" + +class ComparisonTester: + # Custom type to test comparison operations on sparse matrices. + def __eq__(self, other): + return "eq" + + def __ne__(self, other): + return "ne" + + def __lt__(self, other): + return "lt" + + def __le__(self, other): + return "le" + + def __gt__(self, other): + return "gt" + + def __ge__(self, other): + return "ge" + + +#------------------------------------------------------------------------------ +# Generic tests +#------------------------------------------------------------------------------ + + +# TODO test prune +# TODO test has_sorted_indices +class _TestCommon: + """test common functionality shared by all sparse formats""" + math_dtypes = supported_dtypes + + @classmethod + def init_class(cls): + # Canonical data. + cls.dat = array([[1, 0, 0, 2], [3, 0, 1, 0], [0, 2, 0, 0]], 'd') + cls.datsp = cls.spcreator(cls.dat) + + # Some sparse and dense matrices with data for every supported dtype. + # This set union is a workaround for numpy#6295, which means that + # two np.int64 dtypes don't hash to the same value. + cls.checked_dtypes = set(supported_dtypes).union(cls.math_dtypes) + cls.dat_dtypes = {} + cls.datsp_dtypes = {} + for dtype in cls.checked_dtypes: + cls.dat_dtypes[dtype] = cls.dat.astype(dtype) + cls.datsp_dtypes[dtype] = cls.spcreator(cls.dat.astype(dtype)) + + # Check that the original data is equivalent to the + # corresponding dat_dtypes & datsp_dtypes. + assert_equal(cls.dat, cls.dat_dtypes[np.float64]) + assert_equal(cls.datsp.toarray(), + cls.datsp_dtypes[np.float64].toarray()) + + def test_bool(self): + def check(dtype): + datsp = self.datsp_dtypes[dtype] + + assert_raises(ValueError, bool, datsp) + assert_(self.spcreator([[1]])) + assert_(not self.spcreator([[0]])) + + if isinstance(self, TestDOK): + pytest.skip("Cannot create a rank <= 2 DOK matrix.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_bool_rollover(self): + # bool's underlying dtype is 1 byte, check that it does not + # rollover True -> False at 256. + dat = array([[True, False]]) + datsp = self.spcreator(dat) + + for _ in range(10): + datsp = datsp + datsp + dat = dat + dat + assert_array_equal(dat, datsp.toarray()) + + def test_eq(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datbsr = bsr_matrix(dat) + datcsr = csr_matrix(dat) + datcsc = csc_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat == dat2, (datsp == datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat == dat2, (datbsr == datsp2).toarray()) + assert_array_equal_dtype(dat == dat2, (datcsr == datsp2).toarray()) + assert_array_equal_dtype(dat == dat2, (datcsc == datsp2).toarray()) + assert_array_equal_dtype(dat == dat2, (datlil == datsp2).toarray()) + # sparse/dense + assert_array_equal_dtype(dat == datsp2, datsp2 == dat) + # sparse/scalar + assert_array_equal_dtype(dat == 0, (datsp == 0).toarray()) + assert_array_equal_dtype(dat == 1, (datsp == 1).toarray()) + assert_array_equal_dtype(dat == np.nan, + (datsp == np.nan).toarray()) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_ne(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat != dat2, (datsp != datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat != dat2, (datbsr != datsp2).toarray()) + assert_array_equal_dtype(dat != dat2, (datcsc != datsp2).toarray()) + assert_array_equal_dtype(dat != dat2, (datcsr != datsp2).toarray()) + assert_array_equal_dtype(dat != dat2, (datlil != datsp2).toarray()) + # sparse/dense + assert_array_equal_dtype(dat != datsp2, datsp2 != dat) + # sparse/scalar + assert_array_equal_dtype(dat != 0, (datsp != 0).toarray()) + assert_array_equal_dtype(dat != 1, (datsp != 1).toarray()) + assert_array_equal_dtype(0 != dat, (0 != datsp).toarray()) + assert_array_equal_dtype(1 != dat, (1 != datsp).toarray()) + assert_array_equal_dtype(dat != np.nan, + (datsp != np.nan).toarray()) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_lt(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat < dat2, (datsp < datsp2).toarray()) + assert_array_equal_dtype(datcomplex < dat2, + (datspcomplex < datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat < dat2, (datbsr < datsp2).toarray()) + assert_array_equal_dtype(dat < dat2, (datcsc < datsp2).toarray()) + assert_array_equal_dtype(dat < dat2, (datcsr < datsp2).toarray()) + assert_array_equal_dtype(dat < dat2, (datlil < datsp2).toarray()) + + assert_array_equal_dtype(dat2 < dat, (datsp2 < datbsr).toarray()) + assert_array_equal_dtype(dat2 < dat, (datsp2 < datcsc).toarray()) + assert_array_equal_dtype(dat2 < dat, (datsp2 < datcsr).toarray()) + assert_array_equal_dtype(dat2 < dat, (datsp2 < datlil).toarray()) + # sparse/dense + assert_array_equal_dtype(dat < dat2, datsp < dat2) + assert_array_equal_dtype(datcomplex < dat2, datspcomplex < dat2) + # sparse/scalar + for val in [2, 1, 0, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp < val).toarray(), dat < val) + assert_array_equal_dtype((val < datsp).toarray(), val < dat) + + with np.errstate(invalid='ignore'): + assert_array_equal_dtype((datsp < np.nan).toarray(), + dat < np.nan) + + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat < datsp2, datsp < dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_gt(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat > dat2, (datsp > datsp2).toarray()) + assert_array_equal_dtype(datcomplex > dat2, + (datspcomplex > datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype(dat > dat2, (datbsr > datsp2).toarray()) + assert_array_equal_dtype(dat > dat2, (datcsc > datsp2).toarray()) + assert_array_equal_dtype(dat > dat2, (datcsr > datsp2).toarray()) + assert_array_equal_dtype(dat > dat2, (datlil > datsp2).toarray()) + + assert_array_equal_dtype(dat2 > dat, (datsp2 > datbsr).toarray()) + assert_array_equal_dtype(dat2 > dat, (datsp2 > datcsc).toarray()) + assert_array_equal_dtype(dat2 > dat, (datsp2 > datcsr).toarray()) + assert_array_equal_dtype(dat2 > dat, (datsp2 > datlil).toarray()) + # sparse/dense + assert_array_equal_dtype(dat > dat2, datsp > dat2) + assert_array_equal_dtype(datcomplex > dat2, datspcomplex > dat2) + # sparse/scalar + for val in [2, 1, 0, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp > val).toarray(), dat > val) + assert_array_equal_dtype((val > datsp).toarray(), val > dat) + + with np.errstate(invalid='ignore'): + assert_array_equal_dtype((datsp > np.nan).toarray(), + dat > np.nan) + + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat > datsp2, datsp > dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_le(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat <= dat2, (datsp <= datsp2).toarray()) + assert_array_equal_dtype(datcomplex <= dat2, + (datspcomplex <= datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype((datbsr <= datsp2).toarray(), dat <= dat2) + assert_array_equal_dtype((datcsc <= datsp2).toarray(), dat <= dat2) + assert_array_equal_dtype((datcsr <= datsp2).toarray(), dat <= dat2) + assert_array_equal_dtype((datlil <= datsp2).toarray(), dat <= dat2) + + assert_array_equal_dtype((datsp2 <= datbsr).toarray(), dat2 <= dat) + assert_array_equal_dtype((datsp2 <= datcsc).toarray(), dat2 <= dat) + assert_array_equal_dtype((datsp2 <= datcsr).toarray(), dat2 <= dat) + assert_array_equal_dtype((datsp2 <= datlil).toarray(), dat2 <= dat) + # sparse/dense + assert_array_equal_dtype(datsp <= dat2, dat <= dat2) + assert_array_equal_dtype(datspcomplex <= dat2, datcomplex <= dat2) + # sparse/scalar + for val in [2, 1, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp <= val).toarray(), dat <= val) + assert_array_equal_dtype((val <= datsp).toarray(), val <= dat) + + # data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat <= datsp2, datsp <= dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_ge(self): + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + @sup_complex + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + datcomplex = dat.astype(complex) + datcomplex[:,0] = 1 + 1j + datspcomplex = self.spcreator(datcomplex) + datbsr = bsr_matrix(dat) + datcsc = csc_matrix(dat) + datcsr = csr_matrix(dat) + datlil = lil_matrix(dat) + + # sparse/sparse + assert_array_equal_dtype(dat >= dat2, (datsp >= datsp2).toarray()) + assert_array_equal_dtype(datcomplex >= dat2, + (datspcomplex >= datsp2).toarray()) + # mix sparse types + assert_array_equal_dtype((datbsr >= datsp2).toarray(), dat >= dat2) + assert_array_equal_dtype((datcsc >= datsp2).toarray(), dat >= dat2) + assert_array_equal_dtype((datcsr >= datsp2).toarray(), dat >= dat2) + assert_array_equal_dtype((datlil >= datsp2).toarray(), dat >= dat2) + + assert_array_equal_dtype((datsp2 >= datbsr).toarray(), dat2 >= dat) + assert_array_equal_dtype((datsp2 >= datcsc).toarray(), dat2 >= dat) + assert_array_equal_dtype((datsp2 >= datcsr).toarray(), dat2 >= dat) + assert_array_equal_dtype((datsp2 >= datlil).toarray(), dat2 >= dat) + # sparse/dense + assert_array_equal_dtype(datsp >= dat2, dat >= dat2) + assert_array_equal_dtype(datspcomplex >= dat2, datcomplex >= dat2) + # sparse/scalar + for val in [2, 1, -1, -2]: + val = np.int64(val) # avoid Python scalar (due to NEP 50 changes) + assert_array_equal_dtype((datsp >= val).toarray(), dat >= val) + assert_array_equal_dtype((val >= datsp).toarray(), val >= dat) + + # dense data + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + dat2 = dat.copy() + dat2[:,0] = 0 + datsp2 = self.spcreator(dat2) + + # dense rhs + assert_array_equal_dtype(dat >= datsp2, datsp >= dat2) + + if not isinstance(self, (TestBSR, TestCSC, TestCSR)): + pytest.skip("Bool comparisons only implemented for BSR, CSC, and CSR.") + for dtype in self.checked_dtypes: + check(dtype) + + def test_empty(self): + # create empty matrices + assert_equal(self.spcreator((3, 3)).toarray(), zeros((3, 3))) + assert_equal(self.spcreator((3, 3)).nnz, 0) + assert_equal(self.spcreator((3, 3)).count_nonzero(), 0) + + def test_count_nonzero(self): + expected = np.count_nonzero(self.datsp.toarray()) + assert_equal(self.datsp.count_nonzero(), expected) + assert_equal(self.datsp.T.count_nonzero(), expected) + + def test_invalid_shapes(self): + assert_raises(ValueError, self.spcreator, (-1,3)) + assert_raises(ValueError, self.spcreator, (3,-1)) + assert_raises(ValueError, self.spcreator, (-1,-1)) + + def test_repr(self): + datsp = self.spcreator([[1, 0, 0], [0, 0, 0], [0, 0, -2]]) + extra = ( + "(1 diagonals) " if datsp.format == "dia" + else "(blocksize=1x1) " if datsp.format == "bsr" + else "" + ) + _, fmt = _formats[datsp.format] + expected = ( + f"<{fmt} sparse matrix of dtype '{datsp.dtype}'\n" + f"\twith {datsp.nnz} stored elements {extra}and shape {datsp.shape}>" + ) + assert repr(datsp) == expected + + def test_str(self): + datsp = self.spcreator([[1, 0, 0], [0, 0, 0], [0, 0, -2]]) + if datsp.nnz != 2: + return + extra = ( + "(1 diagonals) " if datsp.format == "dia" + else "(blocksize=1x1) " if datsp.format == "bsr" + else "" + ) + _, fmt = _formats[datsp.format] + expected = ( + f"<{fmt} sparse matrix of dtype '{datsp.dtype}'\n" + f"\twith {datsp.nnz} stored elements {extra}and shape {datsp.shape}>" + "\n Coords\tValues" + "\n (0, 0)\t1" + "\n (2, 2)\t-2" + ) + assert str(datsp) == expected + + def test_empty_arithmetic(self): + # Test manipulating empty matrices. Fails in SciPy SVN <= r1768 + shape = (5, 5) + for mytype in [np.dtype('int32'), np.dtype('float32'), + np.dtype('float64'), np.dtype('complex64'), + np.dtype('complex128')]: + a = self.spcreator(shape, dtype=mytype) + b = a + a + c = 2 * a + d = a @ a.tocsc() + e = a @ a.tocsr() + f = a @ a.tocoo() + for m in [a,b,c,d,e,f]: + assert_equal(m.toarray(), a.toarray()@a.toarray()) + # These fail in all revisions <= r1768: + assert_equal(m.dtype,mytype) + assert_equal(m.toarray().dtype,mytype) + + def test_abs(self): + A = array([[-1, 0, 17], [0, -5, 0], [1, -4, 0], [0, 0, 0]], 'd') + assert_equal(abs(A), abs(self.spcreator(A)).toarray()) + + def test_round(self): + decimal = 1 + A = array([[-1.35, 0.56], [17.25, -5.98]], 'd') + assert_equal(np.around(A, decimals=decimal), + round(self.spcreator(A), ndigits=decimal).toarray()) + + def test_elementwise_power(self): + A = array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]], 'd') + assert_equal(np.power(A, 2), self.spcreator(A).power(2).toarray()) + + #it's element-wise power function, input has to be a scalar + assert_raises(NotImplementedError, self.spcreator(A).power, A) + + def test_neg(self): + A = array([[-1, 0, 17], [0, -5, 0], [1, -4, 0], [0, 0, 0]], 'd') + assert_equal(-A, (-self.spcreator(A)).toarray()) + + # see gh-5843 + A = array([[True, False, False], [False, False, True]]) + assert_raises(NotImplementedError, self.spcreator(A).__neg__) + + def test_real(self): + D = array([[1 + 3j, 2 - 4j]]) + A = self.spcreator(D) + assert_equal(A.real.toarray(), D.real) + + def test_imag(self): + D = array([[1 + 3j, 2 - 4j]]) + A = self.spcreator(D) + assert_equal(A.imag.toarray(), D.imag) + + def test_diagonal(self): + # Does the matrix's .diagonal() method work? + mats = [] + mats.append([[1,0,2]]) + mats.append([[1],[0],[2]]) + mats.append([[0,1],[0,2],[0,3]]) + mats.append([[0,0,1],[0,0,2],[0,3,0]]) + mats.append([[1,0],[0,0]]) + + mats.append(kron(mats[0],[[1,2]])) + mats.append(kron(mats[0],[[1],[2]])) + mats.append(kron(mats[1],[[1,2],[3,4]])) + mats.append(kron(mats[2],[[1,2],[3,4]])) + mats.append(kron(mats[3],[[1,2],[3,4]])) + mats.append(kron(mats[3],[[1,2,3,4]])) + + for m in mats: + rows, cols = array(m).shape + sparse_mat = self.spcreator(m) + for k in range(-rows-1, cols+2): + assert_equal(sparse_mat.diagonal(k=k), diag(m, k=k)) + # Test for k beyond boundaries(issue #11949) + assert_equal(sparse_mat.diagonal(k=10), diag(m, k=10)) + assert_equal(sparse_mat.diagonal(k=-99), diag(m, k=-99)) + + # Test all-zero matrix. + assert_equal(self.spcreator((40, 16130)).diagonal(), np.zeros(40)) + # Test empty matrix + # https://github.com/scipy/scipy/issues/11949 + assert_equal(self.spcreator((0, 0)).diagonal(), np.empty(0)) + assert_equal(self.spcreator((15, 0)).diagonal(), np.empty(0)) + assert_equal(self.spcreator((0, 5)).diagonal(10), np.empty(0)) + + def test_trace(self): + # For square matrix + A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + B = self.spcreator(A) + for k in range(-2, 3): + assert_equal(A.trace(offset=k), B.trace(offset=k)) + + # For rectangular matrix + A = np.array([[1, 2, 3], [4, 5, 6]]) + B = self.spcreator(A) + for k in range(-1, 3): + assert_equal(A.trace(offset=k), B.trace(offset=k)) + + def test_reshape(self): + # This first example is taken from the lil_matrix reshaping test. + x = self.spcreator([[1, 0, 7], [0, 0, 0], [0, 3, 0], [0, 0, 5]]) + for order in ['C', 'F']: + for s in [(12, 1), (1, 12)]: + assert_array_equal(x.reshape(s, order=order).toarray(), + x.toarray().reshape(s, order=order)) + + # This example is taken from the stackoverflow answer at + # https://stackoverflow.com/q/16511879 + x = self.spcreator([[0, 10, 0, 0], [0, 0, 0, 0], [0, 20, 30, 40]]) + y = x.reshape((2, 6)) # Default order is 'C' + desired = [[0, 10, 0, 0, 0, 0], [0, 0, 0, 20, 30, 40]] + assert_array_equal(y.toarray(), desired) + + # Reshape with negative indexes + y = x.reshape((2, -1)) + assert_array_equal(y.toarray(), desired) + y = x.reshape((-1, 6)) + assert_array_equal(y.toarray(), desired) + assert_raises(ValueError, x.reshape, (-1, -1)) + + # Reshape with star args + y = x.reshape(2, 6) + assert_array_equal(y.toarray(), desired) + assert_raises(TypeError, x.reshape, 2, 6, not_an_arg=1) + + # Reshape with same size is noop unless copy=True + y = x.reshape((3, 4)) + assert_(y is x) + y = x.reshape((3, 4), copy=True) + assert_(y is not x) + + # Ensure reshape did not alter original size + assert_array_equal(x.shape, (3, 4)) + + # Reshape in place + x.shape = (2, 6) + assert_array_equal(x.toarray(), desired) + + # Reshape to bad ndim + assert_raises(ValueError, x.reshape, (x.size,)) + assert_raises(ValueError, x.reshape, (1, x.size, 1)) + + @pytest.mark.slow + def test_setdiag_comprehensive(self): + def dense_setdiag(a, v, k): + v = np.asarray(v) + if k >= 0: + n = min(a.shape[0], a.shape[1] - k) + if v.ndim != 0: + n = min(n, len(v)) + v = v[:n] + i = np.arange(0, n) + j = np.arange(k, k + n) + a[i,j] = v + elif k < 0: + dense_setdiag(a.T, v, -k) + + def check_setdiag(a, b, k): + # Check setting diagonal using a scalar, a vector of + # correct length, and too short or too long vectors + for r in [-1, len(np.diag(a, k)), 2, 30]: + if r < 0: + v = np.random.choice(range(1, 20)) + else: + v = np.random.randint(1, 20, size=r) + + dense_setdiag(a, v, k) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structu") + b.setdiag(v, k) + + # check that dense_setdiag worked + d = np.diag(a, k) + if np.asarray(v).ndim == 0: + assert_array_equal(d, v, err_msg="%s %d" % (msg, r)) + else: + n = min(len(d), len(v)) + assert_array_equal(d[:n], v[:n], err_msg="%s %d" % (msg, r)) + # check that sparse setdiag worked + assert_array_equal(b.toarray(), a, err_msg="%s %d" % (msg, r)) + + # comprehensive test + np.random.seed(1234) + shapes = [(0,5), (5,0), (1,5), (5,1), (5,5)] + for dtype in [np.int8, np.float64]: + for m,n in shapes: + ks = np.arange(-m+1, n-1) + for k in ks: + msg = repr((dtype, m, n, k)) + a = np.zeros((m, n), dtype=dtype) + b = self.spcreator((m, n), dtype=dtype) + + check_setdiag(a, b, k) + + # check overwriting etc + for k2 in np.random.choice(ks, size=min(len(ks), 5)): + check_setdiag(a, b, k2) + + def test_setdiag(self): + # simple test cases + m = self.spcreator(np.eye(3)) + m2 = self.spcreator((4, 4)) + values = [3, 2, 1] + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + assert_raises(ValueError, m.setdiag, values, k=4) + m.setdiag(values) + assert_array_equal(m.diagonal(), values) + m.setdiag(values, k=1) + assert_array_equal(m.toarray(), np.array([[3, 3, 0], + [0, 2, 2], + [0, 0, 1]])) + m.setdiag(values, k=-2) + assert_array_equal(m.toarray(), np.array([[3, 3, 0], + [0, 2, 2], + [3, 0, 1]])) + m.setdiag((9,), k=2) + assert_array_equal(m.toarray()[0,2], 9) + m.setdiag((9,), k=-2) + assert_array_equal(m.toarray()[2,0], 9) + # test short values on an empty matrix + m2.setdiag([1], k=2) + assert_array_equal(m2.toarray()[0], [0, 0, 1, 0]) + # test overwriting that same diagonal + m2.setdiag([1, 1], k=2) + assert_array_equal(m2.toarray()[:2], [[0, 0, 1, 0], + [0, 0, 0, 1]]) + + def test_nonzero(self): + A = array([[1, 0, 1],[0, 1, 1],[0, 0, 1]]) + Asp = self.spcreator(A) + + A_nz = {tuple(ij) for ij in transpose(A.nonzero())} + Asp_nz = {tuple(ij) for ij in transpose(Asp.nonzero())} + + assert_equal(A_nz, Asp_nz) + + def test_numpy_nonzero(self): + # See gh-5987 + A = array([[1, 0, 1], [0, 1, 1], [0, 0, 1]]) + Asp = self.spcreator(A) + + A_nz = {tuple(ij) for ij in transpose(np.nonzero(A))} + Asp_nz = {tuple(ij) for ij in transpose(np.nonzero(Asp))} + + assert_equal(A_nz, Asp_nz) + + def test_getrow(self): + assert_array_equal(self.datsp.getrow(1).toarray(), self.dat[[1], :]) + assert_array_equal(self.datsp.getrow(-1).toarray(), self.dat[[-1], :]) + + def test_getcol(self): + assert_array_equal(self.datsp.getcol(1).toarray(), self.dat[:, [1]]) + assert_array_equal(self.datsp.getcol(-1).toarray(), self.dat[:, [-1]]) + + def test_sum(self): + np.random.seed(1234) + dat_1 = matrix([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + dat_2 = np.random.rand(5, 5) + dat_3 = np.array([[]]) + dat_4 = np.zeros((40, 40)) + dat_5 = sparse.rand(5, 5, density=1e-2).toarray() + matrices = [dat_1, dat_2, dat_3, dat_4, dat_5] + + def check(dtype, j): + dat = matrix(matrices[j], dtype=dtype) + datsp = self.spcreator(dat, dtype=dtype) + with np.errstate(over='ignore'): + assert_array_almost_equal(dat.sum(), datsp.sum()) + assert_equal(dat.sum().dtype, datsp.sum().dtype) + assert_(np.isscalar(datsp.sum(axis=None))) + assert_array_almost_equal(dat.sum(axis=None), + datsp.sum(axis=None)) + assert_equal(dat.sum(axis=None).dtype, + datsp.sum(axis=None).dtype) + assert_array_almost_equal(dat.sum(axis=0), datsp.sum(axis=0)) + assert_equal(dat.sum(axis=0).dtype, datsp.sum(axis=0).dtype) + assert_array_almost_equal(dat.sum(axis=1), datsp.sum(axis=1)) + assert_equal(dat.sum(axis=1).dtype, datsp.sum(axis=1).dtype) + assert_array_almost_equal(dat.sum(axis=-2), datsp.sum(axis=-2)) + assert_equal(dat.sum(axis=-2).dtype, datsp.sum(axis=-2).dtype) + assert_array_almost_equal(dat.sum(axis=-1), datsp.sum(axis=-1)) + assert_equal(dat.sum(axis=-1).dtype, datsp.sum(axis=-1).dtype) + + for dtype in self.checked_dtypes: + for j in range(len(matrices)): + check(dtype, j) + + def test_sum_invalid_params(self): + out = np.zeros((1, 3)) + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + assert_raises(ValueError, datsp.sum, axis=3) + assert_raises(TypeError, datsp.sum, axis=(0, 1)) + assert_raises(TypeError, datsp.sum, axis=1.5) + assert_raises(ValueError, datsp.sum, axis=1, out=out) + + def test_sum_dtype(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + def check(dtype): + dat_mean = dat.mean(dtype=dtype) + datsp_mean = datsp.mean(dtype=dtype) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + for dtype in self.checked_dtypes: + check(dtype) + + def test_sum_out(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_out = array([[0]]) + datsp_out = matrix([[0]]) + + dat.sum(out=dat_out, keepdims=True) + datsp.sum(out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + dat_out = np.zeros((3, 1)) + datsp_out = asmatrix(np.zeros((3, 1))) + + dat.sum(axis=1, out=dat_out, keepdims=True) + datsp.sum(axis=1, out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + def test_numpy_sum(self): + # See gh-5987 + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_mean = np.sum(dat) + datsp_mean = np.sum(datsp) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + def test_mean(self): + def check(dtype): + dat = array([[0, 1, 2], + [3, 4, 5], + [6, 7, 9]], dtype=dtype) + datsp = self.spcreator(dat, dtype=dtype) + + assert_array_almost_equal(dat.mean(), datsp.mean()) + assert_equal(dat.mean().dtype, datsp.mean().dtype) + assert_(np.isscalar(datsp.mean(axis=None))) + assert_array_almost_equal( + dat.mean(axis=None, keepdims=True), datsp.mean(axis=None) + ) + assert_equal(dat.mean(axis=None).dtype, datsp.mean(axis=None).dtype) + assert_array_almost_equal( + dat.mean(axis=0, keepdims=True), datsp.mean(axis=0) + ) + assert_equal(dat.mean(axis=0).dtype, datsp.mean(axis=0).dtype) + assert_array_almost_equal( + dat.mean(axis=1, keepdims=True), datsp.mean(axis=1) + ) + assert_equal(dat.mean(axis=1).dtype, datsp.mean(axis=1).dtype) + assert_array_almost_equal( + dat.mean(axis=-2, keepdims=True), datsp.mean(axis=-2) + ) + assert_equal(dat.mean(axis=-2).dtype, datsp.mean(axis=-2).dtype) + assert_array_almost_equal( + dat.mean(axis=-1, keepdims=True), datsp.mean(axis=-1) + ) + assert_equal(dat.mean(axis=-1).dtype, datsp.mean(axis=-1).dtype) + + for dtype in self.checked_dtypes: + check(dtype) + + def test_mean_invalid_params(self): + out = asmatrix(np.zeros((1, 3))) + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + assert_raises(ValueError, datsp.mean, axis=3) + assert_raises(TypeError, datsp.mean, axis=(0, 1)) + assert_raises(TypeError, datsp.mean, axis=1.5) + assert_raises(ValueError, datsp.mean, axis=1, out=out) + + def test_mean_dtype(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + def check(dtype): + dat_mean = dat.mean(dtype=dtype) + datsp_mean = datsp.mean(dtype=dtype) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + for dtype in self.checked_dtypes: + check(dtype) + + def test_mean_out(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_out = array([[0]]) + datsp_out = matrix([[0]]) + + dat.mean(out=dat_out, keepdims=True) + datsp.mean(out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + dat_out = np.zeros((3, 1)) + datsp_out = matrix(np.zeros((3, 1))) + + dat.mean(axis=1, out=dat_out, keepdims=True) + datsp.mean(axis=1, out=datsp_out) + assert_array_almost_equal(dat_out, datsp_out) + + def test_numpy_mean(self): + # See gh-5987 + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + dat_mean = np.mean(dat) + datsp_mean = np.mean(datsp) + + assert_array_almost_equal(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + def test_expm(self): + M = array([[1, 0, 2], [0, 0, 3], [-4, 5, 6]], float) + sM = self.spcreator(M, shape=(3,3), dtype=float) + Mexp = scipy.linalg.expm(M) + + N = array([[3., 0., 1.], [0., 2., 0.], [0., 0., 0.]]) + sN = self.spcreator(N, shape=(3,3), dtype=float) + Nexp = scipy.linalg.expm(N) + + with suppress_warnings() as sup: + sup.filter( + SparseEfficiencyWarning, + "splu converted its input to CSC format", + ) + sup.filter( + SparseEfficiencyWarning, + "spsolve is more efficient when sparse b is in the CSC matrix format", + ) + sup.filter( + SparseEfficiencyWarning, + "spsolve requires A be CSC or CSR matrix format", + ) + sMexp = expm(sM).toarray() + sNexp = expm(sN).toarray() + + assert_array_almost_equal((sMexp - Mexp), zeros((3, 3))) + assert_array_almost_equal((sNexp - Nexp), zeros((3, 3))) + + def test_inv(self): + def check(dtype): + M = array([[1, 0, 2], [0, 0, 3], [-4, 5, 6]], dtype) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "spsolve requires A be CSC or CSR matrix format",) + sup.filter(SparseEfficiencyWarning, + "spsolve is more efficient when sparse b " + "is in the CSC matrix format",) + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format",) + sM = self.spcreator(M, shape=(3,3), dtype=dtype) + sMinv = inv(sM) + assert_array_almost_equal(sMinv.dot(sM).toarray(), np.eye(3)) + assert_raises(TypeError, inv, M) + for dtype in [float]: + check(dtype) + + @sup_complex + def test_from_array(self): + A = array([[1,0,0],[2,3,4],[0,5,0],[0,0,0]]) + assert_array_equal(self.spcreator(A).toarray(), A) + + A = array([[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]]) + assert_array_equal(self.spcreator(A).toarray(), A) + assert_array_equal(self.spcreator(A, dtype='int16').toarray(),A.astype('int16')) + + @sup_complex + def test_from_matrix(self): + A = matrix([[1, 0, 0], [2, 3, 4], [0, 5, 0], [0, 0, 0]]) + assert_array_equal(self.spcreator(A).todense(), A) + + A = matrix([[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]]) + assert_array_equal(self.spcreator(A).todense(), A) + assert_array_equal( + self.spcreator(A, dtype='int16').todense(), A.astype('int16') + ) + + @sup_complex + def test_from_list(self): + A = [[1,0,0],[2,3,4],[0,5,0],[0,0,0]] + assert_array_equal(self.spcreator(A).toarray(), A) + + A = [[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]] + assert_array_equal(self.spcreator(A).toarray(), array(A)) + assert_array_equal( + self.spcreator(A, dtype='int16').toarray(), array(A).astype('int16') + ) + + @sup_complex + def test_from_sparse(self): + D = array([[1,0,0],[2,3,4],[0,5,0],[0,0,0]]) + S = csr_matrix(D) + assert_array_equal(self.spcreator(S).toarray(), D) + S = self.spcreator(D) + assert_array_equal(self.spcreator(S).toarray(), D) + + D = array([[1.0 + 3j, 0, 0], + [0, 2.0 + 5, 0], + [0, 0, 0]]) + S = csr_matrix(D) + assert_array_equal(self.spcreator(S).toarray(), D) + assert_array_equal(self.spcreator(S, dtype='int16').toarray(), + D.astype('int16')) + S = self.spcreator(D) + assert_array_equal(self.spcreator(S).toarray(), D) + assert_array_equal(self.spcreator(S, dtype='int16').toarray(), + D.astype('int16')) + + # def test_array(self): + # """test array(A) where A is in sparse format""" + # assert_equal( array(self.datsp), self.dat ) + + def test_todense(self): + # Check C- or F-contiguous (default). + chk = self.datsp.todense() + assert isinstance(chk, np.matrix) + assert_array_equal(chk, self.dat) + assert_(chk.flags.c_contiguous != chk.flags.f_contiguous) + # Check C-contiguous (with arg). + chk = self.datsp.todense(order='C') + assert_array_equal(chk, self.dat) + assert_(chk.flags.c_contiguous) + assert_(not chk.flags.f_contiguous) + # Check F-contiguous (with arg). + chk = self.datsp.todense(order='F') + assert_array_equal(chk, self.dat) + assert_(not chk.flags.c_contiguous) + assert_(chk.flags.f_contiguous) + # Check with out argument (array). + out = np.zeros(self.datsp.shape, dtype=self.datsp.dtype) + chk = self.datsp.todense(out=out) + assert_array_equal(self.dat, out) + assert_array_equal(self.dat, chk) + assert np.may_share_memory(chk, out) + # Check with out array (matrix). + out = asmatrix(np.zeros(self.datsp.shape, dtype=self.datsp.dtype)) + chk = self.datsp.todense(out=out) + assert_array_equal(self.dat, out) + assert_array_equal(self.dat, chk) + assert np.may_share_memory(chk, out) + a = array([[1.,2.,3.]]) + dense_dot_dense = a @ self.dat + check = a @ self.datsp.todense() + assert_array_equal(dense_dot_dense, check) + b = array([[1.,2.,3.,4.]]).T + dense_dot_dense = self.dat @ b + check2 = self.datsp.todense() @ b + assert_array_equal(dense_dot_dense, check2) + # Check bool data works. + spbool = self.spcreator(self.dat, dtype=bool) + matbool = self.dat.astype(bool) + assert_array_equal(spbool.todense(), matbool) + + def test_toarray(self): + # Check C- or F-contiguous (default). + dat = asarray(self.dat) + chk = self.datsp.toarray() + assert_array_equal(chk, dat) + assert_(chk.flags.c_contiguous != chk.flags.f_contiguous) + # Check C-contiguous (with arg). + chk = self.datsp.toarray(order='C') + assert_array_equal(chk, dat) + assert_(chk.flags.c_contiguous) + assert_(not chk.flags.f_contiguous) + # Check F-contiguous (with arg). + chk = self.datsp.toarray(order='F') + assert_array_equal(chk, dat) + assert_(not chk.flags.c_contiguous) + assert_(chk.flags.f_contiguous) + # Check with output arg. + out = np.zeros(self.datsp.shape, dtype=self.datsp.dtype) + self.datsp.toarray(out=out) + assert_array_equal(chk, dat) + # Check that things are fine when we don't initialize with zeros. + out[...] = 1. + self.datsp.toarray(out=out) + assert_array_equal(chk, dat) + a = array([1.,2.,3.]) + dense_dot_dense = dot(a, dat) + check = dot(a, self.datsp.toarray()) + assert_array_equal(dense_dot_dense, check) + b = array([1.,2.,3.,4.]) + dense_dot_dense = dot(dat, b) + check2 = dot(self.datsp.toarray(), b) + assert_array_equal(dense_dot_dense, check2) + # Check bool data works. + spbool = self.spcreator(self.dat, dtype=bool) + arrbool = dat.astype(bool) + assert_array_equal(spbool.toarray(), arrbool) + + @sup_complex + def test_astype(self): + D = array([[2.0 + 3j, 0, 0], + [0, 4.0 + 5j, 0], + [0, 0, 0]]) + S = self.spcreator(D) + + for x in supported_dtypes: + # Check correctly casted + D_casted = D.astype(x) + for copy in (True, False): + S_casted = S.astype(x, copy=copy) + assert_equal(S_casted.dtype, D_casted.dtype) # correct type + assert_equal(S_casted.toarray(), D_casted) # correct values + assert_equal(S_casted.format, S.format) # format preserved + # Check correctly copied + assert_(S_casted.astype(x, copy=False) is S_casted) + S_copied = S_casted.astype(x, copy=True) + assert_(S_copied is not S_casted) + + def check_equal_but_not_same_array_attribute(attribute): + a = getattr(S_casted, attribute) + b = getattr(S_copied, attribute) + assert_array_equal(a, b) + assert_(a is not b) + i = (0,) * b.ndim + b_i = b[i] + b[i] = not b[i] + assert_(a[i] != b[i]) + b[i] = b_i + + if S_casted.format in ('csr', 'csc', 'bsr'): + for attribute in ('indices', 'indptr', 'data'): + check_equal_but_not_same_array_attribute(attribute) + elif S_casted.format == 'coo': + for attribute in ('row', 'col', 'data'): + check_equal_but_not_same_array_attribute(attribute) + elif S_casted.format == 'dia': + for attribute in ('offsets', 'data'): + check_equal_but_not_same_array_attribute(attribute) + + @sup_complex + def test_astype_immutable(self): + D = array([[2.0 + 3j, 0, 0], + [0, 4.0 + 5j, 0], + [0, 0, 0]]) + S = self.spcreator(D) + if hasattr(S, 'data'): + S.data.flags.writeable = False + if S.format in ('csr', 'csc', 'bsr'): + S.indptr.flags.writeable = False + S.indices.flags.writeable = False + for x in supported_dtypes: + D_casted = D.astype(x) + S_casted = S.astype(x) + assert_equal(S_casted.dtype, D_casted.dtype) + + + def test_asfptype(self): + A = self.spcreator(arange(6,dtype='int32').reshape(2,3)) + + assert_equal(A.dtype, np.dtype('int32')) + assert_equal(A.asfptype().dtype, np.dtype('float64')) + assert_equal(A.asfptype().format, A.format) + assert_equal(A.astype('int16').asfptype().dtype, np.dtype('float32')) + assert_equal(A.astype('complex128').asfptype().dtype, np.dtype('complex128')) + + B = A.asfptype() + C = B.asfptype() + assert_(B is C) + + def test_mul_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal(dat*2, (datsp*2).toarray()) + assert_array_equal(dat*17.3, (datsp*17.3).toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + def test_rmul_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal(2*dat, (2*datsp).toarray()) + assert_array_equal(17.3*dat, (17.3*datsp).toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + # github issue #15210 + def test_rmul_scalar_type_error(self): + datsp = self.datsp_dtypes[np.float64] + with assert_raises(TypeError): + None * datsp + + def test_add(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + a = dat.copy() + a[0,2] = 2.0 + b = datsp + c = b + a + assert_array_equal(c, b.toarray() + a) + + c = b + b.tocsr() + assert_array_equal(c.toarray(), + b.toarray() + b.toarray()) + + # test broadcasting + c = b + a[0] + assert_array_equal(c, b.toarray() + a[0]) + + for dtype in self.math_dtypes: + check(dtype) + + def test_radd(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + a = dat.copy() + a[0,2] = 2.0 + b = datsp + c = a + b + assert_array_equal(c, a + b.toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + def test_sub(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal((datsp - datsp).toarray(), np.zeros((3, 4))) + assert_array_equal((datsp - 0).toarray(), dat) + + A = self.spcreator( + np.array([[1, 0, 0, 4], [-1, 0, 0, 0], [0, 8, 0, -5]], 'd') + ) + assert_array_equal((datsp - A).toarray(), dat - A.toarray()) + assert_array_equal((A - datsp).toarray(), A.toarray() - dat) + + # test broadcasting + assert_array_equal(datsp - dat[0], dat - dat[0]) + + for dtype in self.math_dtypes: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + check(dtype) + + def test_rsub(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + assert_array_equal((dat - datsp),[[0,0,0,0],[0,0,0,0],[0,0,0,0]]) + assert_array_equal((datsp - dat),[[0,0,0,0],[0,0,0,0],[0,0,0,0]]) + assert_array_equal((0 - datsp).toarray(), -dat) + + A = self.spcreator(matrix([[1,0,0,4],[-1,0,0,0],[0,8,0,-5]],'d')) + assert_array_equal((dat - A), dat - A.toarray()) + assert_array_equal((A - dat), A.toarray() - dat) + assert_array_equal(A.toarray() - datsp, A.toarray() - dat) + assert_array_equal(datsp - A.toarray(), dat - A.toarray()) + + # test broadcasting + assert_array_equal(dat[0] - datsp, dat[0] - dat) + + for dtype in self.math_dtypes: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + check(dtype) + + def test_add0(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # Adding 0 to a sparse matrix + assert_array_equal((datsp + 0).toarray(), dat) + # use sum (which takes 0 as a starting value) + sumS = sum([k * datsp for k in range(1, 3)]) + sumD = sum([k * dat for k in range(1, 3)]) + assert_almost_equal(sumS.toarray(), sumD) + + for dtype in self.math_dtypes: + check(dtype) + + def test_elementwise_multiply(self): + # real/real + A = array([[4,0,9],[2,-3,5]]) + B = array([[0,7,0],[0,-4,0]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + assert_almost_equal(Asp.multiply(Bsp).toarray(), A*B) # sparse/sparse + assert_almost_equal(Asp.multiply(B).toarray(), A*B) # sparse/dense + + # complex/complex + C = array([[1-2j,0+5j,-1+0j],[4-3j,-3+6j,5]]) + D = array([[5+2j,7-3j,-2+1j],[0-1j,-4+2j,9]]) + Csp = self.spcreator(C) + Dsp = self.spcreator(D) + assert_almost_equal(Csp.multiply(Dsp).toarray(), C*D) # sparse/sparse + assert_almost_equal(Csp.multiply(D).toarray(), C*D) # sparse/dense + + # real/complex + assert_almost_equal(Asp.multiply(Dsp).toarray(), A*D) # sparse/sparse + assert_almost_equal(Asp.multiply(D).toarray(), A*D) # sparse/dense + + def test_elementwise_multiply_broadcast(self): + A = array([4]) + B = array([[-9]]) + C = array([1,-1,0]) + D = array([[7,9,-9]]) + E = array([[3],[2],[1]]) + F = array([[8,6,3],[-4,3,2],[6,6,6]]) + G = [1, 2, 3] + H = np.ones((3, 4)) + J = H.T + K = array([[0]]) + L = array([[[1,2],[0,1]]]) + + # Some arrays can't be cast as spmatrices (A,C,L) so leave + # them out. + Bsp = self.spcreator(B) + Dsp = self.spcreator(D) + Esp = self.spcreator(E) + Fsp = self.spcreator(F) + Hsp = self.spcreator(H) + Hspp = self.spcreator(H[0,None]) + Jsp = self.spcreator(J) + Jspp = self.spcreator(J[:,0,None]) + Ksp = self.spcreator(K) + + matrices = [A, B, C, D, E, F, G, H, J, K, L] + spmatrices = [Bsp, Dsp, Esp, Fsp, Hsp, Hspp, Jsp, Jspp, Ksp] + + # sparse/sparse + for i in spmatrices: + for j in spmatrices: + try: + dense_mult = i.toarray() * j.toarray() + except ValueError: + assert_raises(ValueError, i.multiply, j) + continue + sp_mult = i.multiply(j) + assert_almost_equal(sp_mult.toarray(), dense_mult) + + # sparse/dense + for i in spmatrices: + for j in matrices: + try: + dense_mult = i.toarray() * j + except TypeError: + continue + except ValueError: + assert_raises(ValueError, i.multiply, j) + continue + sp_mult = i.multiply(j) + if issparse(sp_mult): + assert_almost_equal(sp_mult.toarray(), dense_mult) + else: + assert_almost_equal(sp_mult, dense_mult) + + def test_elementwise_divide(self): + expected = [[1,np.nan,np.nan,1], + [1,np.nan,1,np.nan], + [np.nan,1,np.nan,np.nan]] + assert_array_equal(toarray(self.datsp / self.datsp), expected) + + denom = self.spcreator(matrix([[1,0,0,4],[-1,0,0,0],[0,8,0,-5]],'d')) + expected = [[1,np.nan,np.nan,0.5], + [-3,np.nan,inf,np.nan], + [np.nan,0.25,np.nan,0]] + assert_array_equal(toarray(self.datsp / denom), expected) + + # complex + A = array([[1-2j,0+5j,-1+0j],[4-3j,-3+6j,5]]) + B = array([[5+2j,7-3j,-2+1j],[0-1j,-4+2j,9]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + assert_almost_equal(toarray(Asp / Bsp), A/B) + + # integer + A = array([[1,2,3],[-3,2,1]]) + B = array([[0,1,2],[0,-2,3]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + with np.errstate(divide='ignore'): + assert_array_equal(toarray(Asp / Bsp), A / B) + + # mismatching sparsity patterns + A = array([[0,1],[1,0]]) + B = array([[1,0],[1,0]]) + Asp = self.spcreator(A) + Bsp = self.spcreator(B) + with np.errstate(divide='ignore', invalid='ignore'): + assert_array_equal(np.array(toarray(Asp / Bsp)), A / B) + + def test_pow(self): + A = array([[1, 0, 2, 0], [0, 3, 4, 0], [0, 5, 0, 0], [0, 6, 7, 8]]) + B = self.spcreator(A) + + for exponent in [0,1,2,3]: + ret_sp = B**exponent + ret_np = np.linalg.matrix_power(A, exponent) + assert_array_equal(ret_sp.toarray(), ret_np) + assert_equal(ret_sp.dtype, ret_np.dtype) + + # invalid exponents + for exponent in [-1, 2.2, 1 + 3j]: + assert_raises(ValueError, B.__pow__, exponent) + + # nonsquare matrix + B = self.spcreator(A[:3,:]) + assert_raises(TypeError, B.__pow__, 1) + + def test_rmatvec(self): + M = self.spcreator(matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + assert_array_almost_equal([1,2,3,4] @ M, dot([1,2,3,4], M.toarray())) + row = array([[1,2,3,4]]) + assert_array_almost_equal(row @ M, row @ M.toarray()) + + def test_small_multiplication(self): + # test that A*x works for x with shape () (1,) (1,1) and (1,0) + A = self.spcreator([[1],[2],[3]]) + + assert_(issparse(A * array(1))) + assert_equal((A * array(1)).toarray(), [[1], [2], [3]]) + + assert_equal(A @ array([1]), array([1, 2, 3])) + assert_equal(A @ array([[1]]), array([[1], [2], [3]])) + assert_equal(A @ np.ones((1, 1)), array([[1], [2], [3]])) + assert_equal(A @ np.ones((1, 0)), np.ones((3, 0))) + + def test_start_vs_at_sign_for_sparray_and_spmatrix(self): + # test that * is matmul for spmatrix and mul for sparray + A = self.spcreator([[1],[2],[3]]) + + if isinstance(A, sparray): + assert_array_almost_equal(A * np.ones((3,1)), A) + assert_array_almost_equal(A * array([[1]]), A) + assert_array_almost_equal(A * np.ones((3,1)), A) + else: + assert_equal(A * array([1]), array([1, 2, 3])) + assert_equal(A * array([[1]]), array([[1], [2], [3]])) + assert_equal(A * np.ones((1, 0)), np.ones((3, 0))) + + def test_binop_custom_type(self): + # Non-regression test: previously, binary operations would raise + # NotImplementedError instead of returning NotImplemented + # (https://docs.python.org/library/constants.html#NotImplemented) + # so overloading Custom + matrix etc. didn't work. + A = self.spcreator([[1], [2], [3]]) + B = BinopTester() + assert_equal(A + B, "matrix on the left") + assert_equal(A - B, "matrix on the left") + assert_equal(A * B, "matrix on the left") + assert_equal(B + A, "matrix on the right") + assert_equal(B - A, "matrix on the right") + assert_equal(B * A, "matrix on the right") + + assert_equal(A @ B, "matrix on the left") + assert_equal(B @ A, "matrix on the right") + + def test_binop_custom_type_with_shape(self): + A = self.spcreator([[1], [2], [3]]) + B = BinopTester_with_shape((3,1)) + assert_equal(A + B, "matrix on the left") + assert_equal(A - B, "matrix on the left") + assert_equal(A * B, "matrix on the left") + assert_equal(B + A, "matrix on the right") + assert_equal(B - A, "matrix on the right") + assert_equal(B * A, "matrix on the right") + + assert_equal(A @ B, "matrix on the left") + assert_equal(B @ A, "matrix on the right") + + def test_mul_custom_type(self): + class Custom: + def __init__(self, scalar): + self.scalar = scalar + + def __rmul__(self, other): + return other * self.scalar + + scalar = 2 + A = self.spcreator([[1],[2],[3]]) + c = Custom(scalar) + A_scalar = A * scalar + A_c = A * c + assert_array_equal_dtype(A_scalar.toarray(), A_c.toarray()) + assert_equal(A_scalar.format, A_c.format) + + def test_comparisons_custom_type(self): + A = self.spcreator([[1], [2], [3]]) + B = ComparisonTester() + assert_equal(A == B, "eq") + assert_equal(A != B, "ne") + assert_equal(A > B, "lt") + assert_equal(A >= B, "le") + assert_equal(A < B, "gt") + assert_equal(A <= B, "ge") + + def test_dot_scalar(self): + M = self.spcreator(array([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + scalar = 10 + actual = M.dot(scalar) + expected = M * scalar + + assert_allclose(actual.toarray(), expected.toarray()) + + def test_matmul(self): + M = self.spcreator(array([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + B = self.spcreator(array([[0,1],[1,0],[0,2]],'d')) + col = array([[1,2,3]]).T + + matmul = operator.matmul + # check matrix-vector + assert_array_almost_equal(matmul(M, col), M.toarray() @ col) + + # check matrix-matrix + assert_array_almost_equal(matmul(M, B).toarray(), (M @ B).toarray()) + assert_array_almost_equal(matmul(M.toarray(), B), (M @ B).toarray()) + assert_array_almost_equal(matmul(M, B.toarray()), (M @ B).toarray()) + if not isinstance(M, sparray): + assert_array_almost_equal(matmul(M, B).toarray(), (M * B).toarray()) + assert_array_almost_equal(matmul(M.toarray(), B), (M * B).toarray()) + assert_array_almost_equal(matmul(M, B.toarray()), (M * B).toarray()) + + # check error on matrix-scalar + assert_raises(ValueError, matmul, M, 1) + assert_raises(ValueError, matmul, 1, M) + + def test_matvec(self): + M = self.spcreator(matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]])) + col = array([[1,2,3]]).T + + assert_array_almost_equal(M @ col, M.toarray() @ col) + + # check result dimensions (ticket #514) + assert_equal((M @ array([1,2,3])).shape,(4,)) + assert_equal((M @ array([[1],[2],[3]])).shape,(4,1)) + assert_equal((M @ matrix([[1],[2],[3]])).shape,(4,1)) + + # check result type + assert_(isinstance(M @ array([1,2,3]), ndarray)) + assert_(isinstance(M @ matrix([1,2,3]).T, np.matrix)) + + # ensure exception is raised for improper dimensions + bad_vecs = [array([1,2]), array([1,2,3,4]), array([[1],[2]]), + matrix([1,2,3]), matrix([[1],[2]])] + for x in bad_vecs: + assert_raises(ValueError, M.__mul__, x) + + # The current relationship between sparse matrix products and array + # products is as follows: + assert_array_almost_equal(M@array([1,2,3]), dot(M.toarray(),[1,2,3])) + assert_array_almost_equal(M@[[1],[2],[3]], asmatrix(dot(M.toarray(),[1,2,3])).T) + # Note that the result of M * x is dense if x has a singleton dimension. + + # Currently M.matvec(asarray(col)) is rank-1, whereas M.matvec(col) + # is rank-2. Is this desirable? + + def test_matmat_sparse(self): + a = matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]]) + a2 = array([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]]) + b = matrix([[0,1],[1,0],[0,2]],'d') + asp = self.spcreator(a) + bsp = self.spcreator(b) + assert_array_almost_equal((asp @ bsp).toarray(), a @ b) + assert_array_almost_equal(asp @ b, a @ b) + assert_array_almost_equal(a @ bsp, a @ b) + assert_array_almost_equal(a2 @ bsp, a @ b) + + # Now try performing cross-type multplication: + csp = bsp.tocsc() + c = b + want = a @ c + assert_array_almost_equal((asp @ csp).toarray(), want) + assert_array_almost_equal(asp @ c, want) + + assert_array_almost_equal(a @ csp, want) + assert_array_almost_equal(a2 @ csp, want) + csp = bsp.tocsr() + assert_array_almost_equal((asp @ csp).toarray(), want) + assert_array_almost_equal(asp @ c, want) + + assert_array_almost_equal(a @ csp, want) + assert_array_almost_equal(a2 @ csp, want) + csp = bsp.tocoo() + assert_array_almost_equal((asp @ csp).toarray(), want) + assert_array_almost_equal(asp @ c, want) + + assert_array_almost_equal(a @ csp, want) + assert_array_almost_equal(a2 @ csp, want) + + # Test provided by Andy Fraser, 2006-03-26 + L = 30 + frac = .3 + random.seed(0) # make runs repeatable + A = zeros((L,2)) + for i in range(L): + for j in range(2): + r = random.random() + if r < frac: + A[i,j] = r/frac + + A = self.spcreator(A) + B = A @ A.T + assert_array_almost_equal(B.toarray(), A.toarray() @ A.T.toarray()) + assert_array_almost_equal(B.toarray(), A.toarray() @ A.toarray().T) + + # check dimension mismatch 2x2 times 3x2 + A = self.spcreator([[1,2],[3,4]]) + B = self.spcreator([[1,2],[3,4],[5,6]]) + assert_raises(ValueError, A.__matmul__, B) + if isinstance(A, sparray): + assert_raises(ValueError, A.__mul__, B) + + def test_matmat_dense(self): + a = matrix([[3,0,0],[0,1,0],[2,0,3.0],[2,3,0]]) + asp = self.spcreator(a) + + # check both array and matrix types + bs = [array([[1,2],[3,4],[5,6]]), matrix([[1,2],[3,4],[5,6]])] + + for b in bs: + result = asp @ b + assert_(isinstance(result, type(b))) + assert_equal(result.shape, (4,2)) + assert_equal(result, dot(a,b)) + + def test_sparse_format_conversions(self): + A = sparse.kron([[1,0,2],[0,3,4],[5,0,0]], [[1,2],[0,3]]) + D = A.toarray() + A = self.spcreator(A) + + for format in ['bsr','coo','csc','csr','dia','dok','lil']: + a = A.asformat(format) + assert_equal(a.format,format) + assert_array_equal(a.toarray(), D) + + b = self.spcreator(D+3j).asformat(format) + assert_equal(b.format,format) + assert_array_equal(b.toarray(), D+3j) + + c = eval(format + '_matrix')(A) + assert_equal(c.format,format) + assert_array_equal(c.toarray(), D) + + for format in ['array', 'dense']: + a = A.asformat(format) + assert_array_equal(a, D) + + b = self.spcreator(D+3j).asformat(format) + assert_array_equal(b, D+3j) + + def test_tobsr(self): + x = array([[1,0,2,0],[0,0,0,0],[0,0,4,5]]) + y = array([[0,1,2],[3,0,5]]) + A = kron(x,y) + Asp = self.spcreator(A) + for format in ['bsr']: + fn = getattr(Asp, 'to' + format) + + for X in [1, 2, 3, 6]: + for Y in [1, 2, 3, 4, 6, 12]: + assert_equal(fn(blocksize=(X, Y)).toarray(), A) + + def test_transpose(self): + dat_1 = self.dat + dat_2 = np.array([[]]) + matrices = [dat_1, dat_2] + + def check(dtype, j): + dat = array(matrices[j], dtype=dtype) + datsp = self.spcreator(dat) + + a = datsp.transpose() + b = dat.transpose() + + assert_array_equal(a.toarray(), b) + assert_array_equal(a.transpose().toarray(), dat) + assert_array_equal(datsp.transpose(axes=(1, 0)).toarray(), b) + assert_equal(a.dtype, b.dtype) + + # See gh-5987 + empty = self.spcreator((3, 4)) + assert_array_equal(np.transpose(empty).toarray(), + np.transpose(zeros((3, 4)))) + assert_array_equal(empty.T.toarray(), zeros((4, 3))) + assert_raises(ValueError, empty.transpose, axes=0) + + for dtype in self.checked_dtypes: + for j in range(len(matrices)): + check(dtype, j) + + def test_add_dense(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # adding a dense matrix to a sparse matrix + sum1 = dat + datsp + assert_array_equal(sum1, dat + dat) + sum2 = datsp + dat + assert_array_equal(sum2, dat + dat) + + for dtype in self.math_dtypes: + check(dtype) + + def test_sub_dense(self): + # subtracting a dense matrix to/from a sparse matrix + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # Behavior is different for bool. + if dat.dtype == bool: + sum1 = dat - datsp + assert_array_equal(sum1, dat - dat) + sum2 = datsp - dat + assert_array_equal(sum2, dat - dat) + else: + # Manually add to avoid upcasting from scalar + # multiplication. + sum1 = (dat + dat + dat) - datsp + assert_array_equal(sum1, dat + dat) + sum2 = (datsp + datsp + datsp) - dat + assert_array_equal(sum2, dat + dat) + + for dtype in self.math_dtypes: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + check(dtype) + + def test_maximum_minimum(self): + A_dense = np.array([[1, 0, 3], [0, 4, 5], [0, 0, 0]]) + B_dense = np.array([[1, 1, 2], [0, 3, 6], [1, -1, 0]]) + + A_dense_cpx = np.array([[1, 0, 3], [0, 4+2j, 5], [0, 1j, -1j]]) + + def check(dtype, dtype2, btype): + if np.issubdtype(dtype, np.complexfloating): + A = self.spcreator(A_dense_cpx.astype(dtype)) + else: + A = self.spcreator(A_dense.astype(dtype)) + if btype == 'scalar': + B = dtype2.type(1) + elif btype == 'scalar2': + B = dtype2.type(-1) + elif btype == 'dense': + B = B_dense.astype(dtype2) + elif btype == 'sparse': + B = self.spcreator(B_dense.astype(dtype2)) + else: + raise ValueError() + + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "Taking maximum .minimum. with > 0 .< 0. number " + "results to a dense matrix") + + max_s = A.maximum(B) + min_s = A.minimum(B) + + max_d = np.maximum(toarray(A), toarray(B)) + assert_array_equal(toarray(max_s), max_d) + assert_equal(max_s.dtype, max_d.dtype) + + min_d = np.minimum(toarray(A), toarray(B)) + assert_array_equal(toarray(min_s), min_d) + assert_equal(min_s.dtype, min_d.dtype) + + for dtype in self.math_dtypes: + for dtype2 in [np.int8, np.float64, np.complex128]: + for btype in ['scalar', 'scalar2', 'dense', 'sparse']: + check(np.dtype(dtype), np.dtype(dtype2), btype) + + def test_copy(self): + # Check whether the copy=True and copy=False keywords work + A = self.datsp + + # check that copy preserves format + assert_equal(A.copy().format, A.format) + assert_equal(A.__class__(A,copy=True).format, A.format) + assert_equal(A.__class__(A,copy=False).format, A.format) + + assert_equal(A.copy().toarray(), A.toarray()) + assert_equal(A.__class__(A, copy=True).toarray(), A.toarray()) + assert_equal(A.__class__(A, copy=False).toarray(), A.toarray()) + + # check that XXX_matrix.toXXX() works + toself = getattr(A,'to' + A.format) + assert_(toself() is A) + assert_(toself(copy=False) is A) + assert_equal(toself(copy=True).format, A.format) + assert_equal(toself(copy=True).toarray(), A.toarray()) + + # check whether the data is copied? + assert_(not sparse_may_share_memory(A.copy(), A)) + + # test that __iter__ is compatible with NumPy matrix + def test_iterator(self): + B = matrix(np.arange(50).reshape(5, 10)) + A = self.spcreator(B) + + for x, y in zip(A, B): + assert_equal(x.toarray(), y) + + def test_size_zero_matrix_arithmetic(self): + # Test basic matrix arithmetic with shapes like (0,0), (10,0), + # (0, 3), etc. + mat = array([]) + a = mat.reshape((0, 0)) + b = mat.reshape((0, 1)) + c = mat.reshape((0, 5)) + d = mat.reshape((1, 0)) + e = mat.reshape((5, 0)) + f = np.ones([5, 5]) + + asp = self.spcreator(a) + bsp = self.spcreator(b) + csp = self.spcreator(c) + dsp = self.spcreator(d) + esp = self.spcreator(e) + fsp = self.spcreator(f) + + # matrix product. + assert_array_equal(asp.dot(asp).toarray(), np.dot(a, a)) + assert_array_equal(bsp.dot(dsp).toarray(), np.dot(b, d)) + assert_array_equal(dsp.dot(bsp).toarray(), np.dot(d, b)) + assert_array_equal(csp.dot(esp).toarray(), np.dot(c, e)) + assert_array_equal(csp.dot(fsp).toarray(), np.dot(c, f)) + assert_array_equal(esp.dot(csp).toarray(), np.dot(e, c)) + assert_array_equal(dsp.dot(csp).toarray(), np.dot(d, c)) + assert_array_equal(fsp.dot(esp).toarray(), np.dot(f, e)) + + # bad matrix products + assert_raises(ValueError, dsp.dot, e) + assert_raises(ValueError, asp.dot, d) + + # elemente-wise multiplication + assert_array_equal(asp.multiply(asp).toarray(), np.multiply(a, a)) + assert_array_equal(bsp.multiply(bsp).toarray(), np.multiply(b, b)) + assert_array_equal(dsp.multiply(dsp).toarray(), np.multiply(d, d)) + + assert_array_equal(asp.multiply(a).toarray(), np.multiply(a, a)) + assert_array_equal(bsp.multiply(b).toarray(), np.multiply(b, b)) + assert_array_equal(dsp.multiply(d).toarray(), np.multiply(d, d)) + + assert_array_equal(asp.multiply(6).toarray(), np.multiply(a, 6)) + assert_array_equal(bsp.multiply(6).toarray(), np.multiply(b, 6)) + assert_array_equal(dsp.multiply(6).toarray(), np.multiply(d, 6)) + + # bad element-wise multiplication + assert_raises(ValueError, asp.multiply, c) + assert_raises(ValueError, esp.multiply, c) + + # Addition + assert_array_equal(asp.__add__(asp).toarray(), a.__add__(a)) + assert_array_equal(bsp.__add__(bsp).toarray(), b.__add__(b)) + assert_array_equal(dsp.__add__(dsp).toarray(), d.__add__(d)) + + # bad addition + assert_raises(ValueError, asp.__add__, dsp) + assert_raises(ValueError, bsp.__add__, asp) + + def test_size_zero_conversions(self): + mat = array([]) + a = mat.reshape((0, 0)) + b = mat.reshape((0, 5)) + c = mat.reshape((5, 0)) + + for m in [a, b, c]: + spm = self.spcreator(m) + assert_array_equal(spm.tocoo().toarray(), m) + assert_array_equal(spm.tocsr().toarray(), m) + assert_array_equal(spm.tocsc().toarray(), m) + assert_array_equal(spm.tolil().toarray(), m) + assert_array_equal(spm.todok().toarray(), m) + assert_array_equal(spm.tobsr().toarray(), m) + + def test_pickle(self): + import pickle + sup = suppress_warnings() + sup.filter(SparseEfficiencyWarning) + + @sup + def check(): + datsp = self.datsp.copy() + for protocol in range(pickle.HIGHEST_PROTOCOL): + sploaded = pickle.loads(pickle.dumps(datsp, protocol=protocol)) + assert_equal(datsp.shape, sploaded.shape) + assert_array_equal(datsp.toarray(), sploaded.toarray()) + assert_equal(datsp.format, sploaded.format) + # Hacky check for class member equality. This assumes that + # all instance variables are one of: + # 1. Plain numpy ndarrays + # 2. Tuples of ndarrays + # 3. Types that support equality comparison with == + for key, val in datsp.__dict__.items(): + if isinstance(val, np.ndarray): + assert_array_equal(val, sploaded.__dict__[key]) + elif (isinstance(val, tuple) and val + and isinstance(val[0], np.ndarray)): + assert_array_equal(val, sploaded.__dict__[key]) + else: + assert_(val == sploaded.__dict__[key]) + check() + + def test_unary_ufunc_overrides(self): + def check(name): + if name == "sign": + pytest.skip("sign conflicts with comparison op " + "support on Numpy") + if self.spcreator in (dok_matrix, lil_matrix): + pytest.skip("Unary ops not implemented for dok/lil") + ufunc = getattr(np, name) + + X = self.spcreator(np.arange(20).reshape(4, 5) / 20.) + X0 = ufunc(X.toarray()) + + X2 = ufunc(X) + assert_array_equal(X2.toarray(), X0) + + for name in ["sin", "tan", "arcsin", "arctan", "sinh", "tanh", + "arcsinh", "arctanh", "rint", "sign", "expm1", "log1p", + "deg2rad", "rad2deg", "floor", "ceil", "trunc", "sqrt", + "abs"]: + check(name) + + def test_resize(self): + # resize(shape) resizes the matrix in-place + D = np.array([[1, 0, 3, 4], + [2, 0, 0, 0], + [3, 0, 0, 0]]) + S = self.spcreator(D) + assert_(S.resize((3, 2)) is None) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [3, 0]]) + S.resize((2, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0]]) + S.resize((3, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [0, 0]]) + S.resize((3, 3)) + assert_array_equal(S.toarray(), [[1, 0, 0], + [2, 0, 0], + [0, 0, 0]]) + # test no-op + S.resize((3, 3)) + assert_array_equal(S.toarray(), [[1, 0, 0], + [2, 0, 0], + [0, 0, 0]]) + + # test *args + S.resize(3, 2) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [0, 0]]) + + for bad_shape in [1, (-1, 2), (2, -1), (1, 2, 3)]: + assert_raises(ValueError, S.resize, bad_shape) + + def test_constructor1_base(self): + A = self.datsp + + self_format = A.format + + C = A.__class__(A, copy=False) + assert_array_equal_dtype(A.toarray(), C.toarray()) + if self_format not in NON_ARRAY_BACKED_FORMATS: + assert_(sparse_may_share_memory(A, C)) + + C = A.__class__(A, dtype=A.dtype, copy=False) + assert_array_equal_dtype(A.toarray(), C.toarray()) + if self_format not in NON_ARRAY_BACKED_FORMATS: + assert_(sparse_may_share_memory(A, C)) + + C = A.__class__(A, dtype=np.float32, copy=False) + assert_array_equal(A.toarray(), C.toarray()) + + C = A.__class__(A, copy=True) + assert_array_equal_dtype(A.toarray(), C.toarray()) + assert_(not sparse_may_share_memory(A, C)) + + for other_format in ['csr', 'csc', 'coo', 'dia', 'dok', 'lil']: + if other_format == self_format: + continue + B = A.asformat(other_format) + C = A.__class__(B, copy=False) + assert_array_equal_dtype(A.toarray(), C.toarray()) + + C = A.__class__(B, copy=True) + assert_array_equal_dtype(A.toarray(), C.toarray()) + assert_(not sparse_may_share_memory(B, C)) + + +class _TestInplaceArithmetic: + def test_inplace_dense(self): + a = np.ones((3, 4)) + b = self.spcreator(a) + + x = a.copy() + y = a.copy() + x += a + y += b + assert_array_equal(x, y) + + x = a.copy() + y = a.copy() + x -= a + y -= b + assert_array_equal(x, y) + + x = a.copy() + y = a.copy() + if isinstance(b, sparray): + assert_raises(ValueError, operator.imul, x, b.T) + x = x * a + y *= b + else: + # This is matrix product, from __rmul__ + assert_raises(ValueError, operator.imul, x, b) + x = x.dot(a.T) + y *= b.T + assert_array_equal(x, y) + + # Matrix (non-elementwise) floor division is not defined + assert_raises(TypeError, operator.ifloordiv, x, b) + + def test_imul_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + # Avoid implicit casting. + if np.can_cast(int, dtype, casting='same_kind'): + a = datsp.copy() + a *= 2 + b = dat.copy() + b *= 2 + assert_array_equal(b, a.toarray()) + + if np.can_cast(float, dtype, casting='same_kind'): + a = datsp.copy() + a *= 17.3 + b = dat.copy() + b *= 17.3 + assert_array_equal(b, a.toarray()) + + for dtype in self.math_dtypes: + check(dtype) + + def test_idiv_scalar(self): + def check(dtype): + dat = self.dat_dtypes[dtype] + datsp = self.datsp_dtypes[dtype] + + if np.can_cast(int, dtype, casting='same_kind'): + a = datsp.copy() + a /= 2 + b = dat.copy() + b /= 2 + assert_array_equal(b, a.toarray()) + + if np.can_cast(float, dtype, casting='same_kind'): + a = datsp.copy() + a /= 17.3 + b = dat.copy() + b /= 17.3 + assert_array_equal(b, a.toarray()) + + for dtype in self.math_dtypes: + # /= should only be used with float dtypes to avoid implicit + # casting. + if not np.can_cast(dtype, np.dtype(int)): + check(dtype) + + def test_inplace_success(self): + # Inplace ops should work even if a specialized version is not + # implemented, falling back to x = x y + a = self.spcreator(np.eye(5)) + b = self.spcreator(np.eye(5)) + bp = self.spcreator(np.eye(5)) + + b += a + bp = bp + a + assert_allclose(b.toarray(), bp.toarray()) + + b *= a + bp = bp * a + assert_allclose(b.toarray(), bp.toarray()) + + b -= a + bp = bp - a + assert_allclose(b.toarray(), bp.toarray()) + + assert_raises(TypeError, operator.ifloordiv, a, b) + + +class _TestGetSet: + def test_getelement(self): + def check(dtype): + D = array([[1,0,0], + [4,3,0], + [0,2,0], + [0,0,0]], dtype=dtype) + A = self.spcreator(D) + + M,N = D.shape + + for i in range(-M, M): + for j in range(-N, N): + assert_equal(A[i,j], D[i,j]) + + assert_equal(type(A[1,1]), dtype) + + for ij in [(0,3),(-1,3),(4,0),(4,3),(4,-1), (1, 2, 3)]: + assert_raises((IndexError, TypeError), A.__getitem__, ij) + + for dtype in supported_dtypes: + check(np.dtype(dtype)) + + def test_setelement(self): + def check(dtype): + A = self.spcreator((3,4), dtype=dtype) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[0, 0] = dtype.type(0) # bug 870 + A[1, 2] = dtype.type(4.0) + A[0, 1] = dtype.type(3) + A[2, 0] = dtype.type(2.0) + A[0,-1] = dtype.type(8) + A[-1,-2] = dtype.type(7) + A[0, 1] = dtype.type(5) + + if dtype != np.bool_: + assert_array_equal( + A.toarray(), + [ + [0, 5, 0, 8], + [0, 0, 4, 0], + [2, 0, 7, 0] + ] + ) + + for ij in [(0,4),(-1,4),(3,0),(3,4),(3,-1)]: + assert_raises(IndexError, A.__setitem__, ij, 123.0) + + for v in [[1,2,3], array([1,2,3])]: + assert_raises(ValueError, A.__setitem__, (0,0), v) + + if (not np.issubdtype(dtype, np.complexfloating) and + dtype != np.bool_): + for v in [3j]: + assert_raises(TypeError, A.__setitem__, (0,0), v) + + for dtype in supported_dtypes: + check(np.dtype(dtype)) + + def test_negative_index_assignment(self): + # Regression test for github issue 4428. + + def check(dtype): + A = self.spcreator((3, 10), dtype=dtype) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[0, -4] = 1 + assert_equal(A[0, -4], 1) + + for dtype in self.math_dtypes: + check(np.dtype(dtype)) + + def test_scalar_assign_2(self): + n, m = (5, 10) + + def _test_set(i, j, nitems): + msg = f"{i!r} ; {j!r} ; {nitems!r}" + A = self.spcreator((n, m)) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[i, j] = 1 + assert_almost_equal(A.sum(), nitems, err_msg=msg) + assert_almost_equal(A[i, j], 1, err_msg=msg) + + # [i,j] + for i, j in [(2, 3), (-1, 8), (-1, -2), (array(-1), -2), (-1, array(-2)), + (array(-1), array(-2))]: + _test_set(i, j, 1) + + def test_index_scalar_assign(self): + A = self.spcreator((5, 5)) + B = np.zeros((5, 5)) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + for C in [A, B]: + C[0,1] = 1 + C[3,0] = 4 + C[3,0] = 9 + assert_array_equal(A.toarray(), B) + + +class _TestSolve: + def test_solve(self): + # Test whether the lu_solve command segfaults, as reported by Nils + # Wagner for a 64-bit machine, 02 March 2005 (EJS) + n = 20 + np.random.seed(0) # make tests repeatable + A = zeros((n,n), dtype=complex) + x = np.random.rand(n) + y = np.random.rand(n-1)+1j*np.random.rand(n-1) + r = np.random.rand(n) + for i in range(len(x)): + A[i,i] = x[i] + for i in range(len(y)): + A[i,i+1] = y[i] + A[i+1,i] = conjugate(y[i]) + A = self.spcreator(A) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, + "splu converted its input to CSC format") + x = splu(A).solve(r) + assert_almost_equal(A @ x,r) + + +class _TestSlicing: + def test_dtype_preservation(self): + assert_equal(self.spcreator((1,10), dtype=np.int16)[0,1:5].dtype, np.int16) + assert_equal(self.spcreator((1,10), dtype=np.int32)[0,1:5].dtype, np.int32) + assert_equal(self.spcreator((1,10), dtype=np.float32)[0,1:5].dtype, np.float32) + assert_equal(self.spcreator((1,10), dtype=np.float64)[0,1:5].dtype, np.float64) + + def test_dtype_preservation_empty_slice(self): + # This should be parametrized with pytest, but something in the parent + # class creation used in this file breaks pytest.mark.parametrize. + for dt in [np.int16, np.int32, np.float32, np.float64]: + A = self.spcreator((3, 2), dtype=dt) + assert_equal(A[:, 0:0:2].dtype, dt) + assert_equal(A[0:0:2, :].dtype, dt) + assert_equal(A[0, 0:0:2].dtype, dt) + assert_equal(A[0:0:2, 0].dtype, dt) + + def test_get_horiz_slice(self): + B = asmatrix(arange(50.).reshape(5,10)) + A = self.spcreator(B) + assert_array_equal(B[1, :], A[1, :].toarray()) + assert_array_equal(B[1, 2:5], A[1, 2:5].toarray()) + + C = matrix([[1, 2, 1], [4, 0, 6], [0, 0, 0], [0, 0, 1]]) + D = self.spcreator(C) + assert_array_equal(C[1, 1:3], D[1, 1:3].toarray()) + + # Now test slicing when a row contains only zeros + E = matrix([[1, 2, 1], [4, 0, 0], [0, 0, 0], [0, 0, 1]]) + F = self.spcreator(E) + assert_array_equal(E[1, 1:3], F[1, 1:3].toarray()) + assert_array_equal(E[2, -2:], F[2, -2:].toarray()) + + # The following should raise exceptions: + assert_raises(IndexError, A.__getitem__, (slice(None), 11)) + assert_raises(IndexError, A.__getitem__, (6, slice(3, 7))) + + def test_get_vert_slice(self): + B = arange(50.).reshape(5, 10) + A = self.spcreator(B) + assert_array_equal(B[2:5, [0]], A[2:5, 0].toarray()) + assert_array_equal(B[:, [1]], A[:, 1].toarray()) + + C = array([[1, 2, 1], [4, 0, 6], [0, 0, 0], [0, 0, 1]]) + D = self.spcreator(C) + assert_array_equal(C[1:3, [1]], D[1:3, 1].toarray()) + assert_array_equal(C[:, [2]], D[:, 2].toarray()) + + # Now test slicing when a column contains only zeros + E = array([[1, 0, 1], [4, 0, 0], [0, 0, 0], [0, 0, 1]]) + F = self.spcreator(E) + assert_array_equal(E[:, [1]], F[:, 1].toarray()) + assert_array_equal(E[-2:, [2]], F[-2:, 2].toarray()) + + # The following should raise exceptions: + assert_raises(IndexError, A.__getitem__, (slice(None), 11)) + assert_raises(IndexError, A.__getitem__, (6, slice(3, 7))) + + def test_get_slices(self): + B = arange(50.).reshape(5, 10) + A = self.spcreator(B) + assert_array_equal(A[2:5, 0:3].toarray(), B[2:5, 0:3]) + assert_array_equal(A[1:, :-1].toarray(), B[1:, :-1]) + assert_array_equal(A[:-1, 1:].toarray(), B[:-1, 1:]) + + # Now test slicing when a column contains only zeros + E = array([[1, 0, 1], [4, 0, 0], [0, 0, 0], [0, 0, 1]]) + F = self.spcreator(E) + assert_array_equal(E[1:2, 1:2], F[1:2, 1:2].toarray()) + assert_array_equal(E[:, 1:], F[:, 1:].toarray()) + + def test_non_unit_stride_2d_indexing(self): + # Regression test -- used to silently ignore the stride. + v0 = np.random.rand(50, 50) + try: + v = self.spcreator(v0)[0:25:2, 2:30:3] + except ValueError: + # if unsupported + raise pytest.skip("feature not implemented") + + assert_array_equal(v.toarray(), v0[0:25:2, 2:30:3]) + + def test_slicing_2(self): + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + # [i,j] + assert_equal(A[2,3], B[2,3]) + assert_equal(A[-1,8], B[-1,8]) + assert_equal(A[-1,-2],B[-1,-2]) + assert_equal(A[array(-1),-2],B[-1,-2]) + assert_equal(A[-1,array(-2)],B[-1,-2]) + assert_equal(A[array(-1),array(-2)],B[-1,-2]) + + # [i,1:2] + assert_equal(A[2, :].toarray(), B[2, :]) + assert_equal(A[2, 5:-2].toarray(), B[2, 5:-2]) + assert_equal(A[array(2), 5:-2].toarray(), B[2, 5:-2]) + + # [1:2,j] + assert_equal(A[:, 2].toarray(), B[:, 2]) + assert_equal(A[3:4, 9].toarray(), B[3:4, 9]) + assert_equal(A[1:4, -5].toarray(), B[1:4, -5]) + assert_equal(A[2:-1, 3].toarray(), B[2:-1, 3]) + assert_equal(A[2:-1, array(3)].toarray(), B[2:-1, 3]) + + # [1:2,1:2] + assert_equal(A[1:2, 1:2].toarray(), B[1:2, 1:2]) + assert_equal(A[4:, 3:].toarray(), B[4:, 3:]) + assert_equal(A[:4, :5].toarray(), B[:4, :5]) + assert_equal(A[2:-1, :5].toarray(), B[2:-1, :5]) + + # [i] + assert_equal(A[1, :].toarray(), B[1, :]) + assert_equal(A[-2, :].toarray(), B[-2, :]) + assert_equal(A[array(-2), :].toarray(), B[-2, :]) + + # [1:2] + assert_equal(A[1:4].toarray(), B[1:4]) + assert_equal(A[1:-2].toarray(), B[1:-2]) + + # Check bug reported by Robert Cimrman: + # http://thread.gmane.org/gmane.comp.python.scientific.devel/7986 (dead link) + s = slice(int8(2),int8(4),None) + assert_equal(A[s, :].toarray(), B[2:4, :]) + assert_equal(A[:, s].toarray(), B[:, 2:4]) + + def test_slicing_3(self): + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + s_ = np.s_ + slices = [s_[:2], s_[1:2], s_[3:], s_[3::2], + s_[15:20], s_[3:2], + s_[8:3:-1], s_[4::-2], s_[:5:-1], + 0, 1, s_[:], s_[1:5], -1, -2, -5, + array(-1), np.int8(-3)] + + def check_1(a): + x = A[a] + y = B[a] + if y.shape == (): + assert_equal(x, y, repr(a)) + else: + if x.size == 0 and y.size == 0: + pass + else: + assert_array_equal(x.toarray(), y, repr(a)) + + for j, a in enumerate(slices): + check_1(a) + + def check_2(a, b): + # Indexing np.matrix with 0-d arrays seems to be broken, + # as they seem not to be treated as scalars. + # https://github.com/numpy/numpy/issues/3110 + if isinstance(a, np.ndarray): + ai = int(a) + else: + ai = a + if isinstance(b, np.ndarray): + bi = int(b) + else: + bi = b + + x = A[a, b] + y = B[ai, bi] + + if y.shape == (): + assert_equal(x, y, repr((a, b))) + else: + if x.size == 0 and y.size == 0: + pass + else: + assert_array_equal(x.toarray(), y, repr((a, b))) + + for i, a in enumerate(slices): + for j, b in enumerate(slices): + check_2(a, b) + + # Check out of bounds etc. systematically + extra_slices = [] + for a, b, c in itertools.product(*([(None, 0, 1, 2, 5, 15, + -1, -2, 5, -15)]*3)): + if c == 0: + continue + extra_slices.append(slice(a, b, c)) + + for a in extra_slices: + check_2(a, a) + check_2(a, -2) + check_2(-2, a) + + def test_ellipsis_slicing(self): + b = asmatrix(arange(50).reshape(5,10)) + a = self.spcreator(b) + + assert_array_equal(a[...].toarray(), b[...].A) + assert_array_equal(a[...,].toarray(), b[...,].A) + + assert_array_equal(a[1, ...].toarray(), b[1, ...].A) + assert_array_equal(a[..., 1].toarray(), b[..., 1].A) + assert_array_equal(a[1:, ...].toarray(), b[1:, ...].A) + assert_array_equal(a[..., 1:].toarray(), b[..., 1:].A) + + assert_array_equal(a[1:, 1, ...].toarray(), b[1:, 1, ...].A) + assert_array_equal(a[1, ..., 1:].toarray(), b[1, ..., 1:].A) + # These return ints + assert_equal(a[1, 1, ...], b[1, 1, ...]) + assert_equal(a[1, ..., 1], b[1, ..., 1]) + + def test_multiple_ellipsis_slicing(self): + a = self.spcreator(arange(6).reshape(3, 2)) + + with pytest.raises(IndexError, + match='an index can only have a single ellipsis'): + a[..., ...] + with pytest.raises(IndexError, + match='an index can only have a single ellipsis'): + a[..., 1, ...] + + +class _TestSlicingAssign: + def test_slice_scalar_assign(self): + A = self.spcreator((5, 5)) + B = np.zeros((5, 5)) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + for C in [A, B]: + C[0:1,1] = 1 + C[3:0,0] = 4 + C[3:4,0] = 9 + C[0,4:] = 1 + C[3::-1,4:] = 9 + assert_array_equal(A.toarray(), B) + + def test_slice_assign_2(self): + n, m = (5, 10) + + def _test_set(i, j): + msg = f"i={i!r}; j={j!r}" + A = self.spcreator((n, m)) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[i, j] = 1 + B = np.zeros((n, m)) + B[i, j] = 1 + assert_array_almost_equal(A.toarray(), B, err_msg=msg) + # [i,1:2] + for i, j in [(2, slice(3)), (2, slice(None, 10, 4)), (2, slice(5, -2)), + (array(2), slice(5, -2))]: + _test_set(i, j) + + def test_self_self_assignment(self): + # Tests whether a row of one lil_matrix can be assigned to + # another. + B = self.spcreator((4,3)) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + B[0,0] = 2 + B[1,2] = 7 + B[2,1] = 3 + B[3,0] = 10 + + A = B / 10 + B[0,:] = A[0,:] + assert_array_equal(A[0,:].toarray(), B[0,:].toarray()) + + A = B / 10 + B[:,:] = A[:1,:1] + assert_array_equal(np.zeros((4,3)) + A[0,0], B.toarray()) + + A = B / 10 + B[:-1,0] = A[0,:].T + assert_array_equal(A[0,:].toarray().T, B[:-1,0].toarray()) + + def test_slice_assignment(self): + B = self.spcreator((4,3)) + expected = array([[10,0,0], + [0,0,6], + [0,14,0], + [0,0,0]]) + block = [[1,0],[0,4]] + + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + B[0,0] = 5 + B[1,2] = 3 + B[2,1] = 7 + B[:,:] = B+B + assert_array_equal(B.toarray(), expected) + + B[:2,:2] = csc_matrix(array(block)) + assert_array_equal(B.toarray()[:2, :2], block) + + def test_sparsity_modifying_assignment(self): + B = self.spcreator((4,3)) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + B[0,0] = 5 + B[1,2] = 3 + B[2,1] = 7 + B[3,0] = 10 + B[:3] = csr_matrix(np.eye(3)) + + expected = array([[1,0,0],[0,1,0],[0,0,1],[10,0,0]]) + assert_array_equal(B.toarray(), expected) + + def test_set_slice(self): + A = self.spcreator((5,10)) + B = array(zeros((5, 10), float)) + s_ = np.s_ + slices = [s_[:2], s_[1:2], s_[3:], s_[3::2], + s_[8:3:-1], s_[4::-2], s_[:5:-1], + 0, 1, s_[:], s_[1:5], -1, -2, -5, + array(-1), np.int8(-3)] + + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + for j, a in enumerate(slices): + A[a] = j + B[a] = j + assert_array_equal(A.toarray(), B, repr(a)) + + for i, a in enumerate(slices): + for j, b in enumerate(slices): + A[a,b] = 10*i + 1000*(j+1) + B[a,b] = 10*i + 1000*(j+1) + assert_array_equal(A.toarray(), B, repr((a, b))) + + A[0, 1:10:2] = range(1, 10, 2) + B[0, 1:10:2] = range(1, 10, 2) + assert_array_equal(A.toarray(), B) + A[1:5:2, 0] = np.arange(1, 5, 2)[:, None] + B[1:5:2, 0] = np.arange(1, 5, 2)[:] + assert_array_equal(A.toarray(), B) + + # The next commands should raise exceptions + assert_raises(ValueError, A.__setitem__, (0, 0), list(range(100))) + assert_raises(ValueError, A.__setitem__, (0, 0), arange(100)) + assert_raises(ValueError, A.__setitem__, (0, slice(None)), + list(range(100))) + assert_raises(ValueError, A.__setitem__, (slice(None), 1), + list(range(100))) + assert_raises(ValueError, A.__setitem__, (slice(None), 1), A.copy()) + assert_raises(ValueError, A.__setitem__, + ([[1, 2, 3], [0, 3, 4]], [1, 2, 3]), [1, 2, 3, 4]) + assert_raises(ValueError, A.__setitem__, + ([[1, 2, 3], [0, 3, 4], [4, 1, 3]], + [[1, 2, 4], [0, 1, 3]]), [2, 3, 4]) + assert_raises(ValueError, A.__setitem__, (slice(4), 0), + [[1, 2], [3, 4]]) + + def test_assign_empty(self): + A = self.spcreator(np.ones((2, 3))) + B = self.spcreator((1, 2)) + A[1, :2] = B + assert_array_equal(A.toarray(), [[1, 1, 1], [0, 0, 1]]) + + def test_assign_1d_slice(self): + A = self.spcreator(np.ones((3, 3))) + x = np.zeros(3) + A[:, 0] = x + A[1, :] = x + assert_array_equal(A.toarray(), [[0, 1, 1], [0, 0, 0], [0, 1, 1]]) + + +class _TestFancyIndexing: + """Tests fancy indexing features. The tests for any matrix formats + that implement these features should derive from this class. + """ + + def test_dtype_preservation_empty_index(self): + # This should be parametrized with pytest, but something in the parent + # class creation used in this file breaks pytest.mark.parametrize. + for dt in [np.int16, np.int32, np.float32, np.float64]: + A = self.spcreator((3, 2), dtype=dt) + assert_equal(A[:, [False, False]].dtype, dt) + assert_equal(A[[False, False, False], :].dtype, dt) + assert_equal(A[:, []].dtype, dt) + assert_equal(A[[], :].dtype, dt) + + def test_bad_index(self): + A = self.spcreator(np.zeros([5, 5])) + assert_raises((IndexError, ValueError, TypeError), A.__getitem__, "foo") + assert_raises((IndexError, ValueError, TypeError), A.__getitem__, (2, "foo")) + assert_raises((IndexError, ValueError), A.__getitem__, + ([1, 2, 3], [1, 2, 3, 4])) + + def test_fancy_indexing(self): + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + # [i] + assert_equal(A[[1, 3]].toarray(), B[[1, 3]]) + + # [i,[1,2]] + assert_equal(A[3, [1, 3]].toarray(), B[3, [1, 3]]) + assert_equal(A[-1, [2, -5]].toarray(), B[-1, [2, -5]]) + assert_equal(A[array(-1), [2, -5]].toarray(), B[-1, [2, -5]]) + assert_equal(A[-1, array([2, -5])].toarray(), B[-1, [2, -5]]) + assert_equal(A[array(-1), array([2, -5])].toarray(), B[-1, [2, -5]]) + + # [1:2,[1,2]] + assert_equal(A[:, [2, 8, 3, -1]].toarray(), B[:, [2, 8, 3, -1]]) + assert_equal(A[3:4, [9]].toarray(), B[3:4, [9]]) + assert_equal(A[1:4, [-1, -5]].toarray(), B[1:4, [-1, -5]]) + assert_equal(A[1:4, array([-1, -5])].toarray(), B[1:4, [-1, -5]]) + + # [[1,2],j] + assert_equal(A[[1, 3], 3].toarray(), B[[1, 3], 3]) + assert_equal(A[[2, -5], -4].toarray(), B[[2, -5], -4]) + assert_equal(A[array([2, -5]), -4].toarray(), B[[2, -5], -4]) + assert_equal(A[[2, -5], array(-4)].toarray(), B[[2, -5], -4]) + assert_equal(A[array([2, -5]), array(-4)].toarray(), B[[2, -5], -4]) + + # [[1,2],1:2] + assert_equal(A[[1, 3], :].toarray(), B[[1, 3], :]) + assert_equal(A[[2, -5], 8:-1].toarray(), B[[2, -5], 8:-1]) + assert_equal(A[array([2, -5]), 8:-1].toarray(), B[[2, -5], 8:-1]) + + # [[1,2],[1,2]] + assert_equal(toarray(A[[1, 3], [2, 4]]), B[[1, 3], [2, 4]]) + assert_equal(toarray(A[[-1, -3], [2, -4]]), B[[-1, -3], [2, -4]]) + assert_equal( + toarray(A[array([-1, -3]), [2, -4]]), B[[-1, -3], [2, -4]] + ) + assert_equal( + toarray(A[[-1, -3], array([2, -4])]), B[[-1, -3], [2, -4]] + ) + assert_equal( + toarray(A[array([-1, -3]), array([2, -4])]), B[[-1, -3], [2, -4]] + ) + + # [[[1],[2]],[1,2]] + assert_equal(A[[[1], [3]], [2, 4]].toarray(), B[[[1], [3]], [2, 4]]) + assert_equal( + A[[[-1], [-3], [-2]], [2, -4]].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + assert_equal( + A[array([[-1], [-3], [-2]]), [2, -4]].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + assert_equal( + A[[[-1], [-3], [-2]], array([2, -4])].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + assert_equal( + A[array([[-1], [-3], [-2]]), array([2, -4])].toarray(), + B[[[-1], [-3], [-2]], [2, -4]] + ) + + # [[1,2]] + assert_equal(A[[1, 3]].toarray(), B[[1, 3]]) + assert_equal(A[[-1, -3]].toarray(), B[[-1, -3]]) + assert_equal(A[array([-1, -3])].toarray(), B[[-1, -3]]) + + # [[1,2],:][:,[1,2]] + assert_equal( + A[[1, 3], :][:, [2, 4]].toarray(), B[[1, 3], :][:, [2, 4]] + ) + assert_equal( + A[[-1, -3], :][:, [2, -4]].toarray(), B[[-1, -3], :][:, [2, -4]] + ) + assert_equal( + A[array([-1, -3]), :][:, array([2, -4])].toarray(), + B[[-1, -3], :][:, [2, -4]] + ) + + # [:,[1,2]][[1,2],:] + assert_equal( + A[:, [1, 3]][[2, 4], :].toarray(), B[:, [1, 3]][[2, 4], :] + ) + assert_equal( + A[:, [-1, -3]][[2, -4], :].toarray(), B[:, [-1, -3]][[2, -4], :] + ) + assert_equal( + A[:, array([-1, -3])][array([2, -4]), :].toarray(), + B[:, [-1, -3]][[2, -4], :] + ) + + # Check bug reported by Robert Cimrman: + # http://thread.gmane.org/gmane.comp.python.scientific.devel/7986 (dead link) + s = slice(int8(2),int8(4),None) + assert_equal(A[s, :].toarray(), B[2:4, :]) + assert_equal(A[:, s].toarray(), B[:, 2:4]) + + # Regression for gh-4917: index with tuple of 2D arrays + i = np.array([[1]], dtype=int) + assert_equal(A[i, i].toarray(), B[i, i]) + + # Regression for gh-4917: index with tuple of empty nested lists + assert_equal(A[[[]], [[]]].toarray(), B[[[]], [[]]]) + + def test_fancy_indexing_randomized(self): + np.random.seed(1234) # make runs repeatable + + NUM_SAMPLES = 50 + M = 6 + N = 4 + + D = asmatrix(np.random.rand(M,N)) + D = np.multiply(D, D > 0.5) + + I = np.random.randint(-M + 1, M, size=NUM_SAMPLES) + J = np.random.randint(-N + 1, N, size=NUM_SAMPLES) + + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + I_bad = I + M + J_bad = J - N + + assert_raises(IndexError, S.__getitem__, (I_bad,J)) + assert_raises(IndexError, S.__getitem__, (I,J_bad)) + + def test_missized_masking(self): + M, N = 5, 10 + + B = asmatrix(arange(M * N).reshape(M, N)) + A = self.spcreator(B) + + # Content of mask shouldn't matter, only its size + row_long = np.ones(M + 1, dtype=bool) + row_short = np.ones(M - 1, dtype=bool) + col_long = np.ones(N + 2, dtype=bool) + col_short = np.ones(N - 2, dtype=bool) + + with pytest.raises( + IndexError, + match=rf"boolean row index has incorrect length: {M + 1} instead of {M}" + ): + _ = A[row_long, :] + with pytest.raises( + IndexError, + match=rf"boolean row index has incorrect length: {M - 1} instead of {M}" + ): + _ = A[row_short, :] + + for i, j in itertools.product( + (row_long, row_short, slice(None)), + (col_long, col_short, slice(None)), + ): + if isinstance(i, slice) and isinstance(j, slice): + continue + with pytest.raises( + IndexError, + match=r"boolean \w+ index has incorrect length" + ): + _ = A[i, j] + + def test_fancy_indexing_boolean(self): + np.random.seed(1234) # make runs repeatable + + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + I = np.array(np.random.randint(0, 2, size=5), dtype=bool) + J = np.array(np.random.randint(0, 2, size=10), dtype=bool) + X = np.array(np.random.randint(0, 2, size=(5, 10)), dtype=bool) + + assert_equal(toarray(A[I]), B[I]) + assert_equal(toarray(A[:, J]), B[:, J]) + assert_equal(toarray(A[X]), B[X]) + assert_equal(toarray(A[B > 9]), B[B > 9]) + + I = np.array([True, False, True, True, False]) + J = np.array([False, True, True, False, True, + False, False, False, False, False]) + + assert_equal(toarray(A[I, J]), B[I, J]) + + Z1 = np.zeros((6, 11), dtype=bool) + Z2 = np.zeros((6, 11), dtype=bool) + Z2[0,-1] = True + Z3 = np.zeros((6, 11), dtype=bool) + Z3[-1,0] = True + + assert_raises(IndexError, A.__getitem__, Z1) + assert_raises(IndexError, A.__getitem__, Z2) + assert_raises(IndexError, A.__getitem__, Z3) + assert_raises((IndexError, ValueError), A.__getitem__, (X, 1)) + + def test_fancy_indexing_sparse_boolean(self): + np.random.seed(1234) # make runs repeatable + + B = asmatrix(arange(50).reshape(5,10)) + A = self.spcreator(B) + + X = np.array(np.random.randint(0, 2, size=(5, 10)), dtype=bool) + + Xsp = csr_matrix(X) + + assert_equal(toarray(A[Xsp]), B[X]) + assert_equal(toarray(A[A > 9]), B[B > 9]) + + Z = np.array(np.random.randint(0, 2, size=(5, 11)), dtype=bool) + Y = np.array(np.random.randint(0, 2, size=(6, 10)), dtype=bool) + + Zsp = csr_matrix(Z) + Ysp = csr_matrix(Y) + + assert_raises(IndexError, A.__getitem__, Zsp) + assert_raises(IndexError, A.__getitem__, Ysp) + assert_raises((IndexError, ValueError), A.__getitem__, (Xsp, 1)) + + def test_fancy_indexing_regression_3087(self): + mat = self.spcreator(array([[1, 0, 0], [0,1,0], [1,0,0]])) + desired_cols = np.ravel(mat.sum(0)) > 0 + assert_equal(mat[:, desired_cols].toarray(), [[1, 0], [0, 1], [1, 0]]) + + def test_fancy_indexing_seq_assign(self): + mat = self.spcreator(array([[1, 0], [0, 1]])) + assert_raises(ValueError, mat.__setitem__, (0, 0), np.array([1,2])) + + def test_fancy_indexing_2d_assign(self): + # regression test for gh-10695 + mat = self.spcreator(array([[1, 0], [2, 3]])) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + mat[[0, 1], [1, 1]] = mat[[1, 0], [0, 0]] + assert_equal(toarray(mat), array([[1, 2], [2, 1]])) + + def test_fancy_indexing_empty(self): + B = asmatrix(arange(50).reshape(5,10)) + B[1,:] = 0 + B[:,2] = 0 + B[3,6] = 0 + A = self.spcreator(B) + + K = np.array([False, False, False, False, False]) + assert_equal(toarray(A[K]), B[K]) + K = np.array([], dtype=int) + assert_equal(toarray(A[K]), B[K]) + assert_equal(toarray(A[K, K]), B[K, K]) + J = np.array([0, 1, 2, 3, 4], dtype=int)[:,None] + assert_equal(toarray(A[K, J]), B[K, J]) + assert_equal(toarray(A[J, K]), B[J, K]) + + +@contextlib.contextmanager +def check_remains_sorted(X): + """Checks that sorted indices property is retained through an operation + """ + if not hasattr(X, 'has_sorted_indices') or not X.has_sorted_indices: + yield + return + yield + indices = X.indices.copy() + X.has_sorted_indices = False + X.sort_indices() + assert_array_equal(indices, X.indices, + 'Expected sorted indices, found unsorted') + + +class _TestFancyIndexingAssign: + def test_bad_index_assign(self): + A = self.spcreator(np.zeros([5, 5])) + assert_raises((IndexError, ValueError, TypeError), A.__setitem__, "foo", 2) + assert_raises((IndexError, ValueError, TypeError), A.__setitem__, (2, "foo"), 5) + + def test_fancy_indexing_set(self): + n, m = (5, 10) + + def _test_set_slice(i, j): + A = self.spcreator((n, m)) + B = asmatrix(np.zeros((n, m))) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + B[i, j] = 1 + with check_remains_sorted(A): + A[i, j] = 1 + assert_array_almost_equal(A.toarray(), B) + # [1:2,1:2] + for i, j in [((2, 3, 4), slice(None, 10, 4)), + (np.arange(3), slice(5, -2)), + (slice(2, 5), slice(5, -2))]: + _test_set_slice(i, j) + for i, j in [(np.arange(3), np.arange(3)), ((0, 3, 4), (1, 2, 4))]: + _test_set_slice(i, j) + + def test_fancy_assignment_dtypes(self): + def check(dtype): + A = self.spcreator((5, 5), dtype=dtype) + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[[0,1],[0,1]] = dtype.type(1) + assert_equal(A.sum(), dtype.type(1)*2) + A[0:2,0:2] = dtype.type(1.0) + assert_equal(A.sum(), dtype.type(1)*4) + A[2,2] = dtype.type(1.0) + assert_equal(A.sum(), dtype.type(1)*4 + dtype.type(1)) + + for dtype in supported_dtypes: + check(np.dtype(dtype)) + + def test_sequence_assignment(self): + A = self.spcreator((4,3)) + B = self.spcreator(eye(3,4)) + + i0 = [0,1,2] + i1 = (0,1,2) + i2 = array(i0) + + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + with check_remains_sorted(A): + A[0,i0] = B[i0,0].T + A[1,i1] = B[i1,1].T + A[2,i2] = B[i2,2].T + assert_array_equal(A.toarray(), B.T.toarray()) + + # column slice + A = self.spcreator((2,3)) + with check_remains_sorted(A): + A[1,1:3] = [10,20] + assert_array_equal(A.toarray(), [[0, 0, 0], [0, 10, 20]]) + + # row slice + A = self.spcreator((3,2)) + with check_remains_sorted(A): + A[1:3,1] = [[10],[20]] + assert_array_equal(A.toarray(), [[0, 0], [0, 10], [0, 20]]) + + # both slices + A = self.spcreator((3,3)) + B = asmatrix(np.zeros((3,3))) + with check_remains_sorted(A): + for C in [A, B]: + C[[0,1,2], [0,1,2]] = [4,5,6] + assert_array_equal(A.toarray(), B) + + # both slices (2) + A = self.spcreator((4, 3)) + with check_remains_sorted(A): + A[(1, 2, 3), (0, 1, 2)] = [1, 2, 3] + assert_almost_equal(A.sum(), 6) + B = asmatrix(np.zeros((4, 3))) + B[(1, 2, 3), (0, 1, 2)] = [1, 2, 3] + assert_array_equal(A.toarray(), B) + + def test_fancy_assign_empty(self): + B = asmatrix(arange(50).reshape(5,10)) + B[1,:] = 0 + B[:,2] = 0 + B[3,6] = 0 + A = self.spcreator(B) + + K = np.array([False, False, False, False, False]) + A[K] = 42 + assert_equal(toarray(A), B) + + K = np.array([], dtype=int) + A[K] = 42 + assert_equal(toarray(A), B) + A[K,K] = 42 + assert_equal(toarray(A), B) + + J = np.array([0, 1, 2, 3, 4], dtype=int)[:,None] + A[K,J] = 42 + assert_equal(toarray(A), B) + A[J,K] = 42 + assert_equal(toarray(A), B) + + +class _TestFancyMultidim: + def test_fancy_indexing_ndarray(self): + sets = [ + (np.array([[1], [2], [3]]), np.array([3, 4, 2])), + (np.array([[1], [2], [3]]), np.array([[3, 4, 2]])), + (np.array([[1, 2, 3]]), np.array([[3], [4], [2]])), + (np.array([1, 2, 3]), np.array([[3], [4], [2]])), + (np.array([[1, 2, 3], [3, 4, 2]]), + np.array([[5, 6, 3], [2, 3, 1]])) + ] + # These inputs generate 3-D outputs + # (np.array([[[1], [2], [3]], [[3], [4], [2]]]), + # np.array([[[5], [6], [3]], [[2], [3], [1]]])), + + for I, J in sets: + np.random.seed(1234) + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + I_bad = I + 5 + J_bad = J + 7 + + assert_raises(IndexError, S.__getitem__, (I_bad,J)) + assert_raises(IndexError, S.__getitem__, (I,J_bad)) + + # This would generate 3-D arrays -- not supported + assert_raises(IndexError, S.__getitem__, ([I, I], slice(None))) + assert_raises(IndexError, S.__getitem__, (slice(None), [J, J])) + + +class _TestFancyMultidimAssign: + def test_fancy_assign_ndarray(self): + np.random.seed(1234) + + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + X = np.random.rand(2, 3) + + I = np.array([[1, 2, 3], [3, 4, 2]]) + J = np.array([[5, 6, 3], [2, 3, 1]]) + + with check_remains_sorted(S): + S[I,J] = X + D[I,J] = X + assert_equal(S.toarray(), D) + + I_bad = I + 5 + J_bad = J + 7 + + C = [1, 2, 3] + + with check_remains_sorted(S): + S[I,J] = C + D[I,J] = C + assert_equal(S.toarray(), D) + + with check_remains_sorted(S): + S[I,J] = 3 + D[I,J] = 3 + assert_equal(S.toarray(), D) + + assert_raises(IndexError, S.__setitem__, (I_bad,J), C) + assert_raises(IndexError, S.__setitem__, (I,J_bad), C) + + def test_fancy_indexing_multidim_set(self): + n, m = (5, 10) + + def _test_set_slice(i, j): + A = self.spcreator((n, m)) + with check_remains_sorted(A), suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[i, j] = 1 + B = asmatrix(np.zeros((n, m))) + B[i, j] = 1 + assert_array_almost_equal(A.toarray(), B) + # [[[1, 2], [1, 2]], [1, 2]] + for i, j in [(np.array([[1, 2], [1, 3]]), [1, 3]), + (np.array([0, 4]), [[0, 3], [1, 2]]), + ([[1, 2, 3], [0, 2, 4]], [[0, 4, 3], [4, 1, 2]])]: + _test_set_slice(i, j) + + def test_fancy_assign_list(self): + np.random.seed(1234) + + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + X = np.random.rand(2, 3) + + I = [[1, 2, 3], [3, 4, 2]] + J = [[5, 6, 3], [2, 3, 1]] + + S[I,J] = X + D[I,J] = X + assert_equal(S.toarray(), D) + + I_bad = [[ii + 5 for ii in i] for i in I] + J_bad = [[jj + 7 for jj in j] for j in J] + C = [1, 2, 3] + + S[I,J] = C + D[I,J] = C + assert_equal(S.toarray(), D) + + S[I,J] = 3 + D[I,J] = 3 + assert_equal(S.toarray(), D) + + assert_raises(IndexError, S.__setitem__, (I_bad,J), C) + assert_raises(IndexError, S.__setitem__, (I,J_bad), C) + + def test_fancy_assign_slice(self): + np.random.seed(1234) + + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + I = [1, 2, 3, 3, 4, 2] + J = [5, 6, 3, 2, 3, 1] + + I_bad = [ii + 5 for ii in I] + J_bad = [jj + 7 for jj in J] + + C1 = [1, 2, 3, 4, 5, 6, 7] + C2 = np.arange(5)[:, None] + assert_raises(IndexError, S.__setitem__, (I_bad, slice(None)), C1) + assert_raises(IndexError, S.__setitem__, (slice(None), J_bad), C2) + + +class _TestArithmetic: + """ + Test real/complex arithmetic + """ + def __arith_init(self): + # these can be represented exactly in FP (so arithmetic should be exact) + self.__A = array([[-1.5, 6.5, 0, 2.25, 0, 0], + [3.125, -7.875, 0.625, 0, 0, 0], + [0, 0, -0.125, 1.0, 0, 0], + [0, 0, 8.375, 0, 0, 0]], 'float64') + self.__B = array([[0.375, 0, 0, 0, -5, 2.5], + [14.25, -3.75, 0, 0, -0.125, 0], + [0, 7.25, 0, 0, 0, 0], + [18.5, -0.0625, 0, 0, 0, 0]], 'complex128') + self.__B.imag = array([[1.25, 0, 0, 0, 6, -3.875], + [2.25, 4.125, 0, 0, 0, 2.75], + [0, 4.125, 0, 0, 0, 0], + [-0.0625, 0, 0, 0, 0, 0]], 'float64') + + # fractions are all x/16ths + assert_array_equal((self.__A*16).astype('int32'),16*self.__A) + assert_array_equal((self.__B.real*16).astype('int32'),16*self.__B.real) + assert_array_equal((self.__B.imag*16).astype('int32'),16*self.__B.imag) + + self.__Asp = self.spcreator(self.__A) + self.__Bsp = self.spcreator(self.__B) + + @pytest.mark.fail_slow(5) + def test_add_sub(self): + self.__arith_init() + + # basic tests + assert_array_equal( + (self.__Asp + self.__Bsp).toarray(), self.__A + self.__B + ) + + # check conversions + for x in supported_dtypes: + with np.errstate(invalid="ignore"): + A = self.__A.astype(x) + Asp = self.spcreator(A) + for y in supported_dtypes: + if not np.issubdtype(y, np.complexfloating): + with np.errstate(invalid="ignore"): + B = self.__B.real.astype(y) + else: + B = self.__B.astype(y) + Bsp = self.spcreator(B) + + # addition + D1 = A + B + S1 = Asp + Bsp + + assert_equal(S1.dtype,D1.dtype) + assert_array_equal(S1.toarray(), D1) + assert_array_equal(Asp + B,D1) # check sparse + dense + assert_array_equal(A + Bsp,D1) # check dense + sparse + + # subtraction + if np.dtype('bool') in [x, y]: + # boolean array subtraction deprecated in 1.9.0 + continue + + D1 = A - B + S1 = Asp - Bsp + + assert_equal(S1.dtype,D1.dtype) + assert_array_equal(S1.toarray(), D1) + assert_array_equal(Asp - B,D1) # check sparse - dense + assert_array_equal(A - Bsp,D1) # check dense - sparse + + def test_mu(self): + self.__arith_init() + + # basic tests + assert_array_equal((self.__Asp @ self.__Bsp.T).toarray(), + self.__A @ self.__B.T) + + for x in supported_dtypes: + with np.errstate(invalid="ignore"): + A = self.__A.astype(x) + Asp = self.spcreator(A) + for y in supported_dtypes: + if np.issubdtype(y, np.complexfloating): + B = self.__B.astype(y) + else: + with np.errstate(invalid="ignore"): + B = self.__B.real.astype(y) + Bsp = self.spcreator(B) + + D1 = A @ B.T + S1 = Asp @ Bsp.T + + assert_allclose(S1.toarray(), D1, + atol=1e-14*abs(D1).max()) + assert_equal(S1.dtype,D1.dtype) + + +class _TestMinMax: + def test_minmax(self): + for dtype in [np.float32, np.float64, np.int32, np.int64, np.complex128]: + D = np.arange(20, dtype=dtype).reshape(5,4) + + X = self.spcreator(D) + assert_equal(X.min(), 0) + assert_equal(X.max(), 19) + assert_equal(X.min().dtype, dtype) + assert_equal(X.max().dtype, dtype) + + D *= -1 + X = self.spcreator(D) + assert_equal(X.min(), -19) + assert_equal(X.max(), 0) + + D += 5 + X = self.spcreator(D) + assert_equal(X.min(), -14) + assert_equal(X.max(), 5) + + # try a fully dense matrix + X = self.spcreator(np.arange(1, 10).reshape(3, 3)) + assert_equal(X.min(), 1) + assert_equal(X.min().dtype, X.dtype) + + X = -X + assert_equal(X.max(), -1) + + # and a fully sparse matrix + Z = self.spcreator(np.zeros((1, 1))) + assert_equal(Z.min(), 0) + assert_equal(Z.max(), 0) + assert_equal(Z.max().dtype, Z.dtype) + + # another test + D = np.arange(20, dtype=float).reshape(5,4) + D[0:2, :] = 0 + X = self.spcreator(D) + assert_equal(X.min(), 0) + assert_equal(X.max(), 19) + + # zero-size matrices + for D in [np.zeros((0, 0)), np.zeros((0, 10)), np.zeros((10, 0))]: + X = self.spcreator(D) + assert_raises(ValueError, X.min) + assert_raises(ValueError, X.max) + + def test_minmax_axis(self): + D = np.arange(50).reshape(5, 10) + # completely empty rows, leaving some completely full: + D[1, :] = 0 + # empty at end for reduceat: + D[:, 9] = 0 + # partial rows/cols: + D[3, 3] = 0 + # entries on either side of 0: + D[2, 2] = -1 + X = self.spcreator(D) + + axes = [-2, -1, 0, 1] + for axis in axes: + assert_array_equal( + X.max(axis=axis).toarray(), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + X.min(axis=axis).toarray(), D.min(axis=axis, keepdims=True) + ) + + # full matrix + D = np.arange(1, 51).reshape(10, 5) + X = self.spcreator(D) + for axis in axes: + assert_array_equal( + X.max(axis=axis).toarray(), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + X.min(axis=axis).toarray(), D.min(axis=axis, keepdims=True) + ) + + # empty matrix + D = np.zeros((10, 5)) + X = self.spcreator(D) + for axis in axes: + assert_array_equal( + X.max(axis=axis).toarray(), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + X.min(axis=axis).toarray(), D.min(axis=axis, keepdims=True) + ) + + axes_even = [0, -2] + axes_odd = [1, -1] + + # zero-size matrices + D = np.zeros((0, 10)) + X = self.spcreator(D) + for axis in axes_even: + assert_raises(ValueError, X.min, axis=axis) + assert_raises(ValueError, X.max, axis=axis) + for axis in axes_odd: + assert_array_equal(np.zeros((0, 1)), X.min(axis=axis).toarray()) + assert_array_equal(np.zeros((0, 1)), X.max(axis=axis).toarray()) + + D = np.zeros((10, 0)) + X = self.spcreator(D) + for axis in axes_odd: + assert_raises(ValueError, X.min, axis=axis) + assert_raises(ValueError, X.max, axis=axis) + for axis in axes_even: + assert_array_equal(np.zeros((1, 0)), X.min(axis=axis).toarray()) + assert_array_equal(np.zeros((1, 0)), X.max(axis=axis).toarray()) + + def test_nanminmax(self): + D = matrix(np.arange(50).reshape(5,10), dtype=float) + D[1, :] = 0 + D[:, 9] = 0 + D[3, 3] = 0 + D[2, 2] = -1 + D[4, 2] = np.nan + D[1, 4] = np.nan + X = self.spcreator(D) + + X_nan_maximum = X.nanmax() + assert np.isscalar(X_nan_maximum) + assert X_nan_maximum == np.nanmax(D) + + X_nan_minimum = X.nanmin() + assert np.isscalar(X_nan_minimum) + assert X_nan_minimum == np.nanmin(D) + + axes = [-2, -1, 0, 1] + for axis in axes: + X_nan_maxima = X.nanmax(axis=axis) + assert isinstance(X_nan_maxima, coo_matrix) + assert_allclose(X_nan_maxima.toarray(), + np.nanmax(D, axis=axis)) + + X_nan_minima = X.nanmin(axis=axis) + assert isinstance(X_nan_minima, coo_matrix) + assert_allclose(X_nan_minima.toarray(), + np.nanmin(D, axis=axis)) + + def test_minmax_invalid_params(self): + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + for fname in ('min', 'max'): + func = getattr(datsp, fname) + assert_raises(ValueError, func, axis=3) + assert_raises(TypeError, func, axis=(0, 1)) + assert_raises(TypeError, func, axis=1.5) + assert_raises(ValueError, func, axis=1, out=1) + + def test_numpy_minmax(self): + # See gh-5987 + # xref gh-7460 in 'numpy' + from scipy.sparse import _data + + dat = array([[0, 1, 2], + [3, -4, 5], + [-6, 7, 9]]) + datsp = self.spcreator(dat) + + # We are only testing sparse matrices who have + # implemented 'min' and 'max' because they are + # the ones with the compatibility issues with + # the 'numpy' implementation. + if isinstance(datsp, _data._minmax_mixin): + assert_array_equal(np.min(datsp), np.min(dat)) + assert_array_equal(np.max(datsp), np.max(dat)) + + def test_argmax(self): + from scipy.sparse import _data + D1 = np.array([ + [-1, 5, 2, 3], + [0, 0, -1, -2], + [-1, -2, -3, -4], + [1, 2, 3, 4], + [1, 2, 0, 0], + ]) + D2 = D1.transpose() + # Non-regression test cases for gh-16929. + D3 = np.array([[4, 3], [7, 5]]) + D4 = np.array([[4, 3], [7, 0]]) + D5 = np.array([[5, 5, 3], [4, 9, 10], [3, 4, 9]]) + + for D in [D1, D2, D3, D4, D5]: + mat = self.spcreator(D) + if not isinstance(mat, _data._minmax_mixin): + continue + + assert_equal(mat.argmax(), np.argmax(D)) + assert_equal(mat.argmin(), np.argmin(D)) + + assert_equal(mat.argmax(axis=0), + asmatrix(np.argmax(D, axis=0))) + assert_equal(mat.argmin(axis=0), + asmatrix(np.argmin(D, axis=0))) + + assert_equal(mat.argmax(axis=1), + asmatrix(np.argmax(D, axis=1).reshape(-1, 1))) + assert_equal(mat.argmin(axis=1), + asmatrix(np.argmin(D, axis=1).reshape(-1, 1))) + + D1 = np.empty((0, 5)) + D2 = np.empty((5, 0)) + + for axis in [None, 0]: + mat = self.spcreator(D1) + assert_raises(ValueError, mat.argmax, axis=axis) + assert_raises(ValueError, mat.argmin, axis=axis) + + for axis in [None, 1]: + mat = self.spcreator(D2) + assert_raises(ValueError, mat.argmax, axis=axis) + assert_raises(ValueError, mat.argmin, axis=axis) + + +class _TestGetNnzAxis: + def test_getnnz_axis(self): + dat = array([[0, 2], + [3, 5], + [-6, 9]]) + bool_dat = dat.astype(bool) + datsp = self.spcreator(dat) + + accepted_return_dtypes = (np.int32, np.int64) + + assert_array_equal(bool_dat.sum(axis=None), datsp.getnnz(axis=None)) + assert_array_equal(bool_dat.sum(), datsp.getnnz()) + assert_array_equal(bool_dat.sum(axis=0), datsp.getnnz(axis=0)) + assert_in(datsp.getnnz(axis=0).dtype, accepted_return_dtypes) + assert_array_equal(bool_dat.sum(axis=1), datsp.getnnz(axis=1)) + assert_in(datsp.getnnz(axis=1).dtype, accepted_return_dtypes) + assert_array_equal(bool_dat.sum(axis=-2), datsp.getnnz(axis=-2)) + assert_in(datsp.getnnz(axis=-2).dtype, accepted_return_dtypes) + assert_array_equal(bool_dat.sum(axis=-1), datsp.getnnz(axis=-1)) + assert_in(datsp.getnnz(axis=-1).dtype, accepted_return_dtypes) + + assert_raises(ValueError, datsp.getnnz, axis=2) + + +#------------------------------------------------------------------------------ +# Tailored base class for generic tests +#------------------------------------------------------------------------------ + +def _possibly_unimplemented(cls, require=True): + """ + Construct a class that either runs tests as usual (require=True), + or each method skips if it encounters a common error. + """ + if require: + return cls + else: + def wrap(fc): + @functools.wraps(fc) + def wrapper(*a, **kw): + try: + return fc(*a, **kw) + except (NotImplementedError, TypeError, ValueError, + IndexError, AttributeError): + raise pytest.skip("feature not implemented") + + return wrapper + + new_dict = dict(cls.__dict__) + for name, func in cls.__dict__.items(): + if name.startswith('test_'): + new_dict[name] = wrap(func) + return type(cls.__name__ + "NotImplemented", + cls.__bases__, + new_dict) + + +def sparse_test_class(getset=True, slicing=True, slicing_assign=True, + fancy_indexing=True, fancy_assign=True, + fancy_multidim_indexing=True, fancy_multidim_assign=True, + minmax=True, nnz_axis=True): + """ + Construct a base class, optionally converting some of the tests in + the suite to check that the feature is not implemented. + """ + bases = (_TestCommon, + _possibly_unimplemented(_TestGetSet, getset), + _TestSolve, + _TestInplaceArithmetic, + _TestArithmetic, + _possibly_unimplemented(_TestSlicing, slicing), + _possibly_unimplemented(_TestSlicingAssign, slicing_assign), + _possibly_unimplemented(_TestFancyIndexing, fancy_indexing), + _possibly_unimplemented(_TestFancyIndexingAssign, + fancy_assign), + _possibly_unimplemented(_TestFancyMultidim, + fancy_indexing and fancy_multidim_indexing), + _possibly_unimplemented(_TestFancyMultidimAssign, + fancy_multidim_assign and fancy_assign), + _possibly_unimplemented(_TestMinMax, minmax), + _possibly_unimplemented(_TestGetNnzAxis, nnz_axis)) + + # check that test names do not clash + names = {} + for cls in bases: + for name in cls.__dict__: + if not name.startswith('test_'): + continue + old_cls = names.get(name) + if old_cls is not None: + raise ValueError(f"Test class {cls.__name__} overloads test " + f"{name} defined in {old_cls.__name__}") + names[name] = cls + + return type("TestBase", bases, {}) + + +#------------------------------------------------------------------------------ +# Matrix class based tests +#------------------------------------------------------------------------------ + +class TestCSR(sparse_test_class()): + @classmethod + def spcreator(cls, *args, **kwargs): + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + return csr_matrix(*args, **kwargs) + math_dtypes = [np.bool_, np.int_, np.float64, np.complex128] + + def test_constructor1(self): + b = array([[0, 4, 0], + [3, 0, 0], + [0, 2, 0]], 'd') + bsp = csr_matrix(b) + assert_array_almost_equal(bsp.data,[4,3,2]) + assert_array_equal(bsp.indices,[1,0,1]) + assert_array_equal(bsp.indptr,[0,1,2,3]) + assert_equal(bsp.getnnz(),3) + assert_equal(bsp.format,'csr') + assert_array_equal(bsp.toarray(), b) + + def test_constructor2(self): + b = zeros((6,6),'d') + b[3,4] = 5 + bsp = csr_matrix(b) + assert_array_almost_equal(bsp.data,[5]) + assert_array_equal(bsp.indices,[4]) + assert_array_equal(bsp.indptr,[0,0,0,0,1,1,1]) + assert_array_almost_equal(bsp.toarray(), b) + + def test_constructor3(self): + b = array([[1, 0], + [0, 2], + [3, 0]], 'd') + bsp = csr_matrix(b) + assert_array_almost_equal(bsp.data,[1,2,3]) + assert_array_equal(bsp.indices,[0,1,0]) + assert_array_equal(bsp.indptr,[0,1,2,3]) + assert_array_almost_equal(bsp.toarray(), b) + + def test_constructor4(self): + # using (data, ij) format + row = array([2, 3, 1, 3, 0, 1, 3, 0, 2, 1, 2]) + col = array([0, 1, 0, 0, 1, 1, 2, 2, 2, 2, 1]) + data = array([6., 10., 3., 9., 1., 4., + 11., 2., 8., 5., 7.]) + + ij = vstack((row,col)) + csr = csr_matrix((data,ij),(4,3)) + assert_array_equal(arange(12).reshape(4, 3), csr.toarray()) + + # using Python lists and a specified dtype + csr = csr_matrix(([2**63 + 1, 1], ([0, 1], [0, 1])), dtype=np.uint64) + dense = array([[2**63 + 1, 0], [0, 1]], dtype=np.uint64) + assert_array_equal(dense, csr.toarray()) + + # with duplicates (should sum the duplicates) + csr = csr_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + assert csr.nnz == 2 + + def test_constructor5(self): + # infer dimensions from arrays + indptr = array([0,1,3,3]) + indices = array([0,5,1,2]) + data = array([1,2,3,4]) + csr = csr_matrix((data, indices, indptr)) + assert_array_equal(csr.shape,(3,6)) + + def test_constructor6(self): + # infer dimensions and dtype from lists + indptr = [0, 1, 3, 3] + indices = [0, 5, 1, 2] + data = [1, 2, 3, 4] + csr = csr_matrix((data, indices, indptr)) + assert_array_equal(csr.shape, (3,6)) + assert_(np.issubdtype(csr.dtype, np.signedinteger)) + + def test_constructor_smallcol(self): + # int64 indices not required + data = arange(6) + 1 + col = array([1, 2, 1, 0, 0, 2], dtype=np.int64) + ptr = array([0, 2, 4, 6], dtype=np.int64) + + a = csr_matrix((data, col, ptr), shape=(3, 3)) + + b = array([[0, 1, 2], + [4, 3, 0], + [5, 0, 6]], 'd') + + assert_equal(a.indptr.dtype, np.dtype(np.int32)) + assert_equal(a.indices.dtype, np.dtype(np.int32)) + assert_array_equal(a.toarray(), b) + + def test_constructor_largecol(self): + # int64 indices required + data = arange(6) + 1 + large = np.iinfo(np.int32).max + 100 + col = array([0, 1, 2, large, large+1, large+2], dtype=np.int64) + ptr = array([0, 2, 4, 6], dtype=np.int64) + + a = csr_matrix((data, col, ptr)) + + assert_equal(a.indptr.dtype, np.dtype(np.int64)) + assert_equal(a.indices.dtype, np.dtype(np.int64)) + assert_array_equal(a.shape, (3, max(col)+1)) + + def test_sort_indices(self): + data = arange(5) + indices = array([7, 2, 1, 5, 4]) + indptr = array([0, 3, 5]) + asp = csr_matrix((data, indices, indptr), shape=(2,10)) + bsp = asp.copy() + asp.sort_indices() + assert_array_equal(asp.indices,[1, 2, 7, 4, 5]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_eliminate_zeros(self): + data = array([1, 0, 0, 0, 2, 0, 3, 0]) + indices = array([1, 2, 3, 4, 5, 6, 7, 8]) + indptr = array([0, 3, 8]) + asp = csr_matrix((data, indices, indptr), shape=(2,10)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_array_equal(asp.nnz, 3) + assert_array_equal(asp.data,[1, 2, 3]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_ufuncs(self): + X = csr_matrix(np.arange(20).reshape(4, 5) / 20.) + for f in ["sin", "tan", "arcsin", "arctan", "sinh", "tanh", + "arcsinh", "arctanh", "rint", "sign", "expm1", "log1p", + "deg2rad", "rad2deg", "floor", "ceil", "trunc", "sqrt"]: + assert_equal(hasattr(csr_matrix, f), True) + X2 = getattr(X, f)() + assert_equal(X.shape, X2.shape) + assert_array_equal(X.indices, X2.indices) + assert_array_equal(X.indptr, X2.indptr) + assert_array_equal(X2.toarray(), getattr(np, f)(X.toarray())) + + def test_unsorted_arithmetic(self): + data = arange(5) + indices = array([7, 2, 1, 5, 4]) + indptr = array([0, 3, 5]) + asp = csr_matrix((data, indices, indptr), shape=(2,10)) + data = arange(6) + indices = array([8, 1, 5, 7, 2, 4]) + indptr = array([0, 2, 6]) + bsp = csr_matrix((data, indices, indptr), shape=(2,10)) + assert_equal((asp + bsp).toarray(), asp.toarray() + bsp.toarray()) + + def test_fancy_indexing_broadcast(self): + # broadcasting indexing mode is supported + I = np.array([[1], [2], [3]]) + J = np.array([3, 4, 2]) + + np.random.seed(1234) + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + def test_has_sorted_indices(self): + "Ensure has_sorted_indices memoizes sorted state for sort_indices" + sorted_inds = np.array([0, 1]) + unsorted_inds = np.array([1, 0]) + data = np.array([1, 1]) + indptr = np.array([0, 2]) + M = csr_matrix((data, sorted_inds, indptr)).copy() + assert_equal(True, M.has_sorted_indices) + assert isinstance(M.has_sorted_indices, bool) + + M = csr_matrix((data, unsorted_inds, indptr)).copy() + assert_equal(False, M.has_sorted_indices) + + # set by sorting + M.sort_indices() + assert_equal(True, M.has_sorted_indices) + assert_array_equal(M.indices, sorted_inds) + + M = csr_matrix((data, unsorted_inds, indptr)).copy() + # set manually (although underlyingly unsorted) + M.has_sorted_indices = True + assert_equal(True, M.has_sorted_indices) + assert_array_equal(M.indices, unsorted_inds) + + # ensure sort bypassed when has_sorted_indices == True + M.sort_indices() + assert_array_equal(M.indices, unsorted_inds) + + def test_has_canonical_format(self): + "Ensure has_canonical_format memoizes state for sum_duplicates" + + M = csr_matrix((np.array([2]), np.array([0]), np.array([0, 1]))) + assert_equal(True, M.has_canonical_format) + + indices = np.array([0, 0]) # contains duplicate + data = np.array([1, 1]) + indptr = np.array([0, 2]) + + M = csr_matrix((data, indices, indptr)).copy() + assert_equal(False, M.has_canonical_format) + assert isinstance(M.has_canonical_format, bool) + + # set by deduplicating + M.sum_duplicates() + assert_equal(True, M.has_canonical_format) + assert_equal(1, len(M.indices)) + + M = csr_matrix((data, indices, indptr)).copy() + # set manually (although underlyingly duplicated) + M.has_canonical_format = True + assert_equal(True, M.has_canonical_format) + assert_equal(2, len(M.indices)) # unaffected content + + # ensure deduplication bypassed when has_canonical_format == True + M.sum_duplicates() + assert_equal(2, len(M.indices)) # unaffected content + + def test_scalar_idx_dtype(self): + # Check that index dtype takes into account all parameters + # passed to sparsetools, including the scalar ones + indptr = np.zeros(2, dtype=np.int32) + indices = np.zeros(0, dtype=np.int32) + vals = np.zeros(0) + a = csr_matrix((vals, indices, indptr), shape=(1, 2**31-1)) + b = csr_matrix((vals, indices, indptr), shape=(1, 2**31)) + ij = np.zeros((2, 0), dtype=np.int32) + c = csr_matrix((vals, ij), shape=(1, 2**31-1)) + d = csr_matrix((vals, ij), shape=(1, 2**31)) + e = csr_matrix((1, 2**31-1)) + f = csr_matrix((1, 2**31)) + assert_equal(a.indptr.dtype, np.int32) + assert_equal(b.indptr.dtype, np.int64) + assert_equal(c.indptr.dtype, np.int32) + assert_equal(d.indptr.dtype, np.int64) + assert_equal(e.indptr.dtype, np.int32) + assert_equal(f.indptr.dtype, np.int64) + + # These shouldn't fail + for x in [a, b, c, d, e, f]: + x + x + + def test_binop_explicit_zeros(self): + # Check that binary ops don't introduce spurious explicit zeros. + # See gh-9619 for context. + a = csr_matrix([[0, 1, 0]]) + b = csr_matrix([[1, 1, 0]]) + assert (a + b).nnz == 2 + assert a.multiply(b).nnz == 1 + + +TestCSR.init_class() + + +class TestCSC(sparse_test_class()): + @classmethod + def spcreator(cls, *args, **kwargs): + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + return csc_matrix(*args, **kwargs) + math_dtypes = [np.bool_, np.int_, np.float64, np.complex128] + + def test_constructor1(self): + b = array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 2, 0, 3]], 'd') + bsp = csc_matrix(b) + assert_array_almost_equal(bsp.data,[1,2,1,3]) + assert_array_equal(bsp.indices,[0,2,1,2]) + assert_array_equal(bsp.indptr,[0,1,2,3,4]) + assert_equal(bsp.getnnz(),4) + assert_equal(bsp.shape,b.shape) + assert_equal(bsp.format,'csc') + + def test_constructor2(self): + b = zeros((6,6),'d') + b[2,4] = 5 + bsp = csc_matrix(b) + assert_array_almost_equal(bsp.data,[5]) + assert_array_equal(bsp.indices,[2]) + assert_array_equal(bsp.indptr,[0,0,0,0,0,1,1]) + + def test_constructor3(self): + b = array([[1, 0], [0, 0], [0, 2]], 'd') + bsp = csc_matrix(b) + assert_array_almost_equal(bsp.data,[1,2]) + assert_array_equal(bsp.indices,[0,2]) + assert_array_equal(bsp.indptr,[0,1,2]) + + def test_constructor4(self): + # using (data, ij) format + row = array([2, 3, 1, 3, 0, 1, 3, 0, 2, 1, 2]) + col = array([0, 1, 0, 0, 1, 1, 2, 2, 2, 2, 1]) + data = array([6., 10., 3., 9., 1., 4., 11., 2., 8., 5., 7.]) + + ij = vstack((row,col)) + csc = csc_matrix((data,ij),(4,3)) + assert_array_equal(arange(12).reshape(4, 3), csc.toarray()) + + # with duplicates (should sum the duplicates) + csc = csc_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + assert csc.nnz == 2 + + def test_constructor5(self): + # infer dimensions from arrays + indptr = array([0,1,3,3]) + indices = array([0,5,1,2]) + data = array([1,2,3,4]) + csc = csc_matrix((data, indices, indptr)) + assert_array_equal(csc.shape,(6,3)) + + def test_constructor6(self): + # infer dimensions and dtype from lists + indptr = [0, 1, 3, 3] + indices = [0, 5, 1, 2] + data = [1, 2, 3, 4] + csc = csc_matrix((data, indices, indptr)) + assert_array_equal(csc.shape,(6,3)) + assert_(np.issubdtype(csc.dtype, np.signedinteger)) + + def test_eliminate_zeros(self): + data = array([1, 0, 0, 0, 2, 0, 3, 0]) + indices = array([1, 2, 3, 4, 5, 6, 7, 8]) + indptr = array([0, 3, 8]) + asp = csc_matrix((data, indices, indptr), shape=(10,2)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_array_equal(asp.nnz, 3) + assert_array_equal(asp.data,[1, 2, 3]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_sort_indices(self): + data = arange(5) + row = array([7, 2, 1, 5, 4]) + ptr = [0, 3, 5] + asp = csc_matrix((data, row, ptr), shape=(10,2)) + bsp = asp.copy() + asp.sort_indices() + assert_array_equal(asp.indices,[1, 2, 7, 4, 5]) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_ufuncs(self): + X = csc_matrix(np.arange(21).reshape(7, 3) / 21.) + for f in ["sin", "tan", "arcsin", "arctan", "sinh", "tanh", + "arcsinh", "arctanh", "rint", "sign", "expm1", "log1p", + "deg2rad", "rad2deg", "floor", "ceil", "trunc", "sqrt"]: + assert_equal(hasattr(csr_matrix, f), True) + X2 = getattr(X, f)() + assert_equal(X.shape, X2.shape) + assert_array_equal(X.indices, X2.indices) + assert_array_equal(X.indptr, X2.indptr) + assert_array_equal(X2.toarray(), getattr(np, f)(X.toarray())) + + def test_unsorted_arithmetic(self): + data = arange(5) + indices = array([7, 2, 1, 5, 4]) + indptr = array([0, 3, 5]) + asp = csc_matrix((data, indices, indptr), shape=(10,2)) + data = arange(6) + indices = array([8, 1, 5, 7, 2, 4]) + indptr = array([0, 2, 6]) + bsp = csc_matrix((data, indices, indptr), shape=(10,2)) + assert_equal((asp + bsp).toarray(), asp.toarray() + bsp.toarray()) + + def test_fancy_indexing_broadcast(self): + # broadcasting indexing mode is supported + I = np.array([[1], [2], [3]]) + J = np.array([3, 4, 2]) + + np.random.seed(1234) + D = asmatrix(np.random.rand(5, 7)) + S = self.spcreator(D) + + SIJ = S[I,J] + if issparse(SIJ): + SIJ = SIJ.toarray() + assert_equal(SIJ, D[I,J]) + + def test_scalar_idx_dtype(self): + # Check that index dtype takes into account all parameters + # passed to sparsetools, including the scalar ones + indptr = np.zeros(2, dtype=np.int32) + indices = np.zeros(0, dtype=np.int32) + vals = np.zeros(0) + a = csc_matrix((vals, indices, indptr), shape=(2**31-1, 1)) + b = csc_matrix((vals, indices, indptr), shape=(2**31, 1)) + ij = np.zeros((2, 0), dtype=np.int32) + c = csc_matrix((vals, ij), shape=(2**31-1, 1)) + d = csc_matrix((vals, ij), shape=(2**31, 1)) + e = csr_matrix((1, 2**31-1)) + f = csr_matrix((1, 2**31)) + assert_equal(a.indptr.dtype, np.int32) + assert_equal(b.indptr.dtype, np.int64) + assert_equal(c.indptr.dtype, np.int32) + assert_equal(d.indptr.dtype, np.int64) + assert_equal(e.indptr.dtype, np.int32) + assert_equal(f.indptr.dtype, np.int64) + + # These shouldn't fail + for x in [a, b, c, d, e, f]: + x + x + + +TestCSC.init_class() + + +class TestDOK(sparse_test_class(minmax=False, nnz_axis=False)): + spcreator = dok_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_mult(self): + A = dok_matrix((10,10)) + A[0,3] = 10 + A[5,6] = 20 + D = A*A.T + E = A*A.T.conjugate() + assert_array_equal(D.toarray(), E.toarray()) + + def test_add_nonzero(self): + A = self.spcreator((3,2)) + A[0,1] = -10 + A[2,0] = 20 + A = A + 10 + B = array([[10, 0], [10, 10], [30, 10]]) + assert_array_equal(A.toarray(), B) + + A = A + 1j + B = B + 1j + assert_array_equal(A.toarray(), B) + + def test_dok_divide_scalar(self): + A = self.spcreator((3,2)) + A[0,1] = -10 + A[2,0] = 20 + + assert_array_equal((A/1j).toarray(), A.toarray()/1j) + assert_array_equal((A/9).toarray(), A.toarray()/9) + + def test_convert(self): + # Test provided by Andrew Straw. Fails in SciPy <= r1477. + (m, n) = (6, 7) + a = dok_matrix((m, n)) + + # set a few elements, but none in the last column + a[2,1] = 1 + a[0,2] = 2 + a[3,1] = 3 + a[1,5] = 4 + a[4,3] = 5 + a[4,2] = 6 + + # assert that the last column is all zeros + assert_array_equal(a.toarray()[:,n-1], zeros(m,)) + + # make sure it still works for CSC format + csc = a.tocsc() + assert_array_equal(csc.toarray()[:,n-1], zeros(m,)) + + # now test CSR + (m, n) = (n, m) + b = a.transpose() + assert_equal(b.shape, (m, n)) + # assert that the last row is all zeros + assert_array_equal(b.toarray()[m-1,:], zeros(n,)) + + # make sure it still works for CSR format + csr = b.tocsr() + assert_array_equal(csr.toarray()[m-1,:], zeros(n,)) + + def test_ctor(self): + # Empty ctor + assert_raises(TypeError, dok_matrix) + + # Dense ctor + b = array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 2, 0, 3]], 'd') + A = dok_matrix(b) + assert_equal(b.dtype, A.dtype) + assert_equal(A.toarray(), b) + + # Sparse ctor + c = csr_matrix(b) + assert_equal(A.toarray(), c.toarray()) + + data = [[0, 1, 2], [3, 0, 0]] + d = dok_matrix(data, dtype=np.float32) + assert_equal(d.dtype, np.float32) + da = d.toarray() + assert_equal(da.dtype, np.float32) + assert_array_equal(da, data) + + def test_ticket1160(self): + # Regression test for ticket #1160. + a = dok_matrix((3,3)) + a[0,0] = 0 + # This assert would fail, because the above assignment would + # incorrectly call __set_item__ even though the value was 0. + assert_((0,0) not in a.keys(), "Unexpected entry (0,0) in keys") + + # Slice assignments were also affected. + b = dok_matrix((3,3)) + b[:,0] = 0 + assert_(len(b.keys()) == 0, "Unexpected entries in keys") + + +TestDOK.init_class() + + +class TestLIL(sparse_test_class(minmax=False)): + spcreator = lil_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_dot(self): + A = zeros((10, 10), np.complex128) + A[0, 3] = 10 + A[5, 6] = 20j + + B = lil_matrix((10, 10), dtype=np.complex128) + B[0, 3] = 10 + B[5, 6] = 20j + + # TODO: properly handle this assertion on ppc64le + if platform.machine() != 'ppc64le': + assert_array_equal(A @ A.T, (B * B.T).toarray()) + + assert_array_equal(A @ A.conjugate().T, (B * B.conjugate().T).toarray()) + + def test_scalar_mul(self): + x = lil_matrix((3, 3)) + x[0, 0] = 2 + + x = x*2 + assert_equal(x[0, 0], 4) + + x = x*0 + assert_equal(x[0, 0], 0) + + def test_truediv_scalar(self): + A = self.spcreator((3, 2)) + A[0, 1] = -10 + A[2, 0] = 20 + + assert_array_equal((A / 1j).toarray(), A.toarray() / 1j) + assert_array_equal((A / 9).toarray(), A.toarray() / 9) + + def test_inplace_ops(self): + A = lil_matrix([[0, 2, 3], [4, 0, 6]]) + B = lil_matrix([[0, 1, 0], [0, 2, 3]]) + + data = {'add': (B, A + B), + 'sub': (B, A - B), + 'mul': (3, A * 3)} + + for op, (other, expected) in data.items(): + result = A.copy() + getattr(result, '__i%s__' % op)(other) + + assert_array_equal(result.toarray(), expected.toarray()) + + # Ticket 1604. + A = lil_matrix((1, 3), dtype=np.dtype('float64')) + B = array([0.1, 0.1, 0.1]) + A[0, :] += B + assert_array_equal(A[0, :].toarray().squeeze(), B) + + def test_lil_iteration(self): + row_data = [[1, 2, 3], [4, 5, 6]] + B = lil_matrix(array(row_data)) + for r, row in enumerate(B): + assert_array_equal(row.toarray(), array(row_data[r], ndmin=2)) + + def test_lil_from_csr(self): + # Tests whether a lil_matrix can be constructed from a + # csr_matrix. + B = lil_matrix((10, 10)) + B[0, 3] = 10 + B[5, 6] = 20 + B[8, 3] = 30 + B[3, 8] = 40 + B[8, 9] = 50 + C = B.tocsr() + D = lil_matrix(C) + assert_array_equal(C.toarray(), D.toarray()) + + def test_fancy_indexing_lil(self): + M = asmatrix(arange(25).reshape(5, 5)) + A = lil_matrix(M) + + assert_equal(A[array([1, 2, 3]), 2:3].toarray(), + M[array([1, 2, 3]), 2:3]) + + def test_point_wise_multiply(self): + l = lil_matrix((4, 3)) + l[0, 0] = 1 + l[1, 1] = 2 + l[2, 2] = 3 + l[3, 1] = 4 + + m = lil_matrix((4, 3)) + m[0, 0] = 1 + m[0, 1] = 2 + m[2, 2] = 3 + m[3, 1] = 4 + m[3, 2] = 4 + + assert_array_equal(l.multiply(m).toarray(), + m.multiply(l).toarray()) + + assert_array_equal(l.multiply(m).toarray(), + [[1, 0, 0], + [0, 0, 0], + [0, 0, 9], + [0, 16, 0]]) + + def test_lil_multiply_removal(self): + # Ticket #1427. + a = lil_matrix(np.ones((3, 3))) + a *= 2. + a[0, :] = 0 + + +TestLIL.init_class() + + +class TestCOO(sparse_test_class(getset=False, + slicing=False, slicing_assign=False, + fancy_indexing=False, fancy_assign=False)): + spcreator = coo_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_constructor1(self): + # unsorted triplet format + row = array([2, 3, 1, 3, 0, 1, 3, 0, 2, 1, 2]) + col = array([0, 1, 0, 0, 1, 1, 2, 2, 2, 2, 1]) + data = array([6., 10., 3., 9., 1., 4., 11., 2., 8., 5., 7.]) + + coo = coo_matrix((data,(row,col)),(4,3)) + assert_array_equal(arange(12).reshape(4, 3), coo.toarray()) + + # using Python lists and a specified dtype + coo = coo_matrix(([2**63 + 1, 1], ([0, 1], [0, 1])), dtype=np.uint64) + dense = array([[2**63 + 1, 0], [0, 1]], dtype=np.uint64) + assert_array_equal(dense, coo.toarray()) + + def test_constructor2(self): + # unsorted triplet format with duplicates (which are summed) + row = array([0,1,2,2,2,2,0,0,2,2]) + col = array([0,2,0,2,1,1,1,0,0,2]) + data = array([2,9,-4,5,7,0,-1,2,1,-5]) + coo = coo_matrix((data,(row,col)),(3,3)) + + mat = array([[4, -1, 0], [0, 0, 9], [-3, 7, 0]]) + + assert_array_equal(mat, coo.toarray()) + + def test_constructor3(self): + # empty matrix + coo = coo_matrix((4,3)) + + assert_array_equal(coo.shape,(4,3)) + assert_array_equal(coo.row,[]) + assert_array_equal(coo.col,[]) + assert_array_equal(coo.data,[]) + assert_array_equal(coo.toarray(), zeros((4, 3))) + + def test_constructor4(self): + # from dense matrix + mat = array([[0,1,0,0], + [7,0,3,0], + [0,4,0,0]]) + coo = coo_matrix(mat) + assert_array_equal(coo.toarray(), mat) + + # upgrade rank 1 arrays to row matrix + mat = array([0,1,0,0]) + coo = coo_matrix(mat) + assert_array_equal(coo.toarray(), mat.reshape(1, -1)) + + # error if second arg interpreted as shape (gh-9919) + with pytest.raises(TypeError, match=r'object cannot be interpreted'): + coo_matrix([0, 11, 22, 33], ([0, 1, 2, 3], [0, 0, 0, 0])) + + # error if explicit shape arg doesn't match the dense matrix + with pytest.raises(ValueError, match=r'inconsistent shapes'): + coo_matrix([0, 11, 22, 33], shape=(4, 4)) + + def test_constructor_data_ij_dtypeNone(self): + data = [1] + coo = coo_matrix((data, ([0], [0])), dtype=None) + assert coo.dtype == np.array(data).dtype + + @pytest.mark.xfail(run=False, reason='COO does not have a __getitem__') + def test_iterator(self): + pass + + def test_todia_all_zeros(self): + zeros = [[0, 0]] + dia = coo_matrix(zeros).todia() + assert_array_equal(dia.toarray(), zeros) + + def test_sum_duplicates(self): + coo = coo_matrix((4,3)) + coo.sum_duplicates() + coo = coo_matrix(([1,2], ([1,0], [1,0]))) + coo.sum_duplicates() + assert_array_equal(coo.toarray(), [[2,0],[0,1]]) + coo = coo_matrix(([1,2], ([1,1], [1,1]))) + coo.sum_duplicates() + assert_array_equal(coo.toarray(), [[0,0],[0,3]]) + assert_array_equal(coo.row, [1]) + assert_array_equal(coo.col, [1]) + assert_array_equal(coo.data, [3]) + + def test_todok_duplicates(self): + coo = coo_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + dok = coo.todok() + assert_array_equal(dok.toarray(), coo.toarray()) + + def test_tocompressed_duplicates(self): + coo = coo_matrix(([1,1,1,1], ([0,2,2,0], [0,1,1,0]))) + csr = coo.tocsr() + assert_equal(csr.nnz + 2, coo.nnz) + csc = coo.tocsc() + assert_equal(csc.nnz + 2, coo.nnz) + + def test_eliminate_zeros(self): + data = array([1, 0, 0, 0, 2, 0, 3, 0]) + row = array([0, 0, 0, 1, 1, 1, 1, 1]) + col = array([1, 2, 3, 4, 5, 6, 7, 8]) + asp = coo_matrix((data, (row, col)), shape=(2,10)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_((asp.data != 0).all()) + assert_array_equal(asp.toarray(), bsp.toarray()) + + def test_reshape_copy(self): + arr = [[0, 10, 0, 0], [0, 0, 0, 0], [0, 20, 30, 40]] + new_shape = (2, 6) + x = coo_matrix(arr) + + y = x.reshape(new_shape) + assert_(y.data is x.data) + + y = x.reshape(new_shape, copy=False) + assert_(y.data is x.data) + + y = x.reshape(new_shape, copy=True) + assert_(not np.may_share_memory(y.data, x.data)) + + def test_large_dimensions_reshape(self): + # Test that reshape is immune to integer overflow when number of elements + # exceeds 2^31-1 + mat1 = coo_matrix(([1], ([3000000], [1000])), (3000001, 1001)) + mat2 = coo_matrix(([1], ([1000], [3000000])), (1001, 3000001)) + + # assert_array_equal is slow for big matrices because it expects dense + # Using __ne__ and nnz instead + assert_((mat1.reshape((1001, 3000001), order='C') != mat2).nnz == 0) + assert_((mat2.reshape((3000001, 1001), order='F') != mat1).nnz == 0) + + +TestCOO.init_class() + + +class TestDIA(sparse_test_class(getset=False, slicing=False, slicing_assign=False, + fancy_indexing=False, fancy_assign=False, + minmax=False, nnz_axis=False)): + spcreator = dia_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_constructor1(self): + D = array([[1, 0, 3, 0], + [1, 2, 0, 4], + [0, 2, 3, 0], + [0, 0, 3, 4]]) + data = np.array([[1,2,3,4]]).repeat(3,axis=0) + offsets = np.array([0,-1,2]) + assert_equal(dia_matrix((data, offsets), shape=(4, 4)).toarray(), D) + + @pytest.mark.xfail(run=False, reason='DIA does not have a __getitem__') + def test_iterator(self): + pass + + @with_64bit_maxval_limit(3) + def test_setdiag_dtype(self): + m = dia_matrix(np.eye(3)) + assert_equal(m.offsets.dtype, np.int32) + m.setdiag((3,), k=2) + assert_equal(m.offsets.dtype, np.int32) + + m = dia_matrix(np.eye(4)) + assert_equal(m.offsets.dtype, np.int64) + m.setdiag((3,), k=3) + assert_equal(m.offsets.dtype, np.int64) + + @pytest.mark.skip(reason='DIA stores extra zeros') + def test_getnnz_axis(self): + pass + + def test_convert_gh14555(self): + # regression test for gh-14555 + m = dia_matrix(([[1, 1, 0]], [-1]), shape=(4, 2)) + expected = m.toarray() + assert_array_equal(m.tocsc().toarray(), expected) + assert_array_equal(m.tocsr().toarray(), expected) + + def test_tocoo_gh10050(self): + # regression test for gh-10050 + m = dia_matrix([[1, 2], [3, 4]]).tocoo() + flat_inds = np.ravel_multi_index((m.row, m.col), m.shape) + inds_are_sorted = np.all(np.diff(flat_inds) > 0) + assert m.has_canonical_format == inds_are_sorted + + def test_tocoo_tocsr_tocsc_gh19245(self): + # test index_dtype with tocoo, tocsr, tocsc + data = np.array([[1, 2, 3, 4]]).repeat(3, axis=0) + offsets = np.array([0, -1, 2], dtype=np.int32) + dia = sparse.dia_array((data, offsets), shape=(4, 4)) + + coo = dia.tocoo() + assert coo.col.dtype == np.int32 + csr = dia.tocsr() + assert csr.indices.dtype == np.int32 + csc = dia.tocsc() + assert csc.indices.dtype == np.int32 + + def test_mul_scalar(self): + # repro for gh-20434 + m = dia_matrix([[1, 2], [0, 4]]) + res = m * 3 + assert isinstance(res, dia_matrix) + assert_array_equal(res.toarray(), [[3, 6], [0, 12]]) + + res2 = m.multiply(3) + assert isinstance(res2, dia_matrix) + assert_array_equal(res2.toarray(), [[3, 6], [0, 12]]) + + +TestDIA.init_class() + + +class TestBSR(sparse_test_class(getset=False, + slicing=False, slicing_assign=False, + fancy_indexing=False, fancy_assign=False, + nnz_axis=False)): + spcreator = bsr_matrix + math_dtypes = [np.int_, np.float64, np.complex128] + + def test_constructor1(self): + # check native BSR format constructor + indptr = array([0,2,2,4]) + indices = array([0,2,2,3]) + data = zeros((4,2,3)) + + data[0] = array([[0, 1, 2], + [3, 0, 5]]) + data[1] = array([[0, 2, 4], + [6, 0, 10]]) + data[2] = array([[0, 4, 8], + [12, 0, 20]]) + data[3] = array([[0, 5, 10], + [15, 0, 25]]) + + A = kron([[1,0,2,0],[0,0,0,0],[0,0,4,5]], [[0,1,2],[3,0,5]]) + Asp = bsr_matrix((data,indices,indptr),shape=(6,12)) + assert_equal(Asp.toarray(), A) + + # infer shape from arrays + Asp = bsr_matrix((data,indices,indptr)) + assert_equal(Asp.toarray(), A) + + def test_constructor2(self): + # construct from dense + + # test zero mats + for shape in [(1,1), (5,1), (1,10), (10,4), (3,7), (2,1)]: + A = zeros(shape) + assert_equal(bsr_matrix(A).toarray(), A) + A = zeros((4,6)) + assert_equal(bsr_matrix(A, blocksize=(2, 2)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 3)).toarray(), A) + + A = kron([[1,0,2,0],[0,0,0,0],[0,0,4,5]], [[0,1,2],[3,0,5]]) + assert_equal(bsr_matrix(A).toarray(), A) + assert_equal(bsr_matrix(A, shape=(6, 12)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(1, 1)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 3)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 6)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(2, 12)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(3, 12)).toarray(), A) + assert_equal(bsr_matrix(A, blocksize=(6, 12)).toarray(), A) + + A = kron([[1,0,2,0],[0,1,0,0],[0,0,0,0]], [[0,1,2],[3,0,5]]) + assert_equal(bsr_matrix(A, blocksize=(2, 3)).toarray(), A) + + def test_constructor3(self): + # construct from coo-like (data,(row,col)) format + arg = ([1,2,3], ([0,1,1], [0,0,1])) + A = array([[1,0],[2,3]]) + assert_equal(bsr_matrix(arg, blocksize=(2, 2)).toarray(), A) + + def test_constructor4(self): + # regression test for gh-6292: bsr_matrix((data, indices, indptr)) was + # trying to compare an int to a None + n = 8 + data = np.ones((n, n, 1), dtype=np.int8) + indptr = np.array([0, n], dtype=np.int32) + indices = np.arange(n, dtype=np.int32) + bsr_matrix((data, indices, indptr), blocksize=(n, 1), copy=False) + + def test_constructor5(self): + # check for validations introduced in gh-13400 + n = 8 + data_1dim = np.ones(n) + data = np.ones((n, n, n)) + indptr = np.array([0, n]) + indices = np.arange(n) + + with assert_raises(ValueError): + # data ndim check + bsr_matrix((data_1dim, indices, indptr)) + + with assert_raises(ValueError): + # invalid blocksize + bsr_matrix((data, indices, indptr), blocksize=(1, 1, 1)) + + with assert_raises(ValueError): + # mismatching blocksize + bsr_matrix((data, indices, indptr), blocksize=(1, 1)) + + def test_default_dtype(self): + # As a numpy array, `values` has shape (2, 2, 1). + values = [[[1], [1]], [[1], [1]]] + indptr = np.array([0, 2], dtype=np.int32) + indices = np.array([0, 1], dtype=np.int32) + b = bsr_matrix((values, indices, indptr), blocksize=(2, 1)) + assert b.dtype == np.array(values).dtype + + def test_bsr_tocsr(self): + # check native conversion from BSR to CSR + indptr = array([0, 2, 2, 4]) + indices = array([0, 2, 2, 3]) + data = zeros((4, 2, 3)) + + data[0] = array([[0, 1, 2], + [3, 0, 5]]) + data[1] = array([[0, 2, 4], + [6, 0, 10]]) + data[2] = array([[0, 4, 8], + [12, 0, 20]]) + data[3] = array([[0, 5, 10], + [15, 0, 25]]) + + A = kron([[1, 0, 2, 0], [0, 0, 0, 0], [0, 0, 4, 5]], + [[0, 1, 2], [3, 0, 5]]) + Absr = bsr_matrix((data, indices, indptr), shape=(6, 12)) + Acsr = Absr.tocsr() + Acsr_via_coo = Absr.tocoo().tocsr() + assert_equal(Acsr.toarray(), A) + assert_equal(Acsr.toarray(), Acsr_via_coo.toarray()) + + def test_eliminate_zeros(self): + data = kron([1, 0, 0, 0, 2, 0, 3, 0], [[1,1],[1,1]]).T + data = data.reshape(-1,2,2) + indices = array([1, 2, 3, 4, 5, 6, 7, 8]) + indptr = array([0, 3, 8]) + asp = bsr_matrix((data, indices, indptr), shape=(4,20)) + bsp = asp.copy() + asp.eliminate_zeros() + assert_array_equal(asp.nnz, 3*4) + assert_array_equal(asp.toarray(), bsp.toarray()) + + # github issue #9687 + def test_eliminate_zeros_all_zero(self): + np.random.seed(0) + m = bsr_matrix(np.random.random((12, 12)), blocksize=(2, 3)) + + # eliminate some blocks, but not all + m.data[m.data <= 0.9] = 0 + m.eliminate_zeros() + assert_equal(m.nnz, 66) + assert_array_equal(m.data.shape, (11, 2, 3)) + + # eliminate all remaining blocks + m.data[m.data <= 1.0] = 0 + m.eliminate_zeros() + assert_equal(m.nnz, 0) + assert_array_equal(m.data.shape, (0, 2, 3)) + assert_array_equal(m.toarray(), np.zeros((12, 12))) + + # test fast path + m.eliminate_zeros() + assert_equal(m.nnz, 0) + assert_array_equal(m.data.shape, (0, 2, 3)) + assert_array_equal(m.toarray(), np.zeros((12, 12))) + + def test_bsr_matvec(self): + A = bsr_matrix(arange(2*3*4*5).reshape(2*4,3*5), blocksize=(4,5)) + x = arange(A.shape[1]).reshape(-1,1) + assert_equal(A*x, A.toarray() @ x) + + def test_bsr_matvecs(self): + A = bsr_matrix(arange(2*3*4*5).reshape(2*4,3*5), blocksize=(4,5)) + x = arange(A.shape[1]*6).reshape(-1,6) + assert_equal(A*x, A.toarray() @ x) + + @pytest.mark.xfail(run=False, reason='BSR does not have a __getitem__') + def test_iterator(self): + pass + + @pytest.mark.xfail(run=False, reason='BSR does not have a __setitem__') + def test_setdiag(self): + pass + + def test_resize_blocked(self): + # test resize() with non-(1,1) blocksize + D = np.array([[1, 0, 3, 4], + [2, 0, 0, 0], + [3, 0, 0, 0]]) + S = self.spcreator(D, blocksize=(1, 2)) + assert_(S.resize((3, 2)) is None) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [3, 0]]) + S.resize((2, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0]]) + S.resize((3, 2)) + assert_array_equal(S.toarray(), [[1, 0], + [2, 0], + [0, 0]]) + S.resize((3, 4)) + assert_array_equal(S.toarray(), [[1, 0, 0, 0], + [2, 0, 0, 0], + [0, 0, 0, 0]]) + assert_raises(ValueError, S.resize, (2, 3)) + + @pytest.mark.xfail(run=False, reason='BSR does not have a __setitem__') + def test_setdiag_comprehensive(self): + pass + + @pytest.mark.skipif(IS_COLAB, reason="exceeds memory limit") + def test_scalar_idx_dtype(self): + # Check that index dtype takes into account all parameters + # passed to sparsetools, including the scalar ones + indptr = np.zeros(2, dtype=np.int32) + indices = np.zeros(0, dtype=np.int32) + vals = np.zeros((0, 1, 1)) + a = bsr_matrix((vals, indices, indptr), shape=(1, 2**31-1)) + b = bsr_matrix((vals, indices, indptr), shape=(1, 2**31)) + c = bsr_matrix((1, 2**31-1)) + d = bsr_matrix((1, 2**31)) + assert_equal(a.indptr.dtype, np.int32) + assert_equal(b.indptr.dtype, np.int64) + assert_equal(c.indptr.dtype, np.int32) + assert_equal(d.indptr.dtype, np.int64) + + try: + vals2 = np.zeros((0, 1, 2**31-1)) + vals3 = np.zeros((0, 1, 2**31)) + e = bsr_matrix((vals2, indices, indptr), shape=(1, 2**31-1)) + f = bsr_matrix((vals3, indices, indptr), shape=(1, 2**31)) + assert_equal(e.indptr.dtype, np.int32) + assert_equal(f.indptr.dtype, np.int64) + except (MemoryError, ValueError): + # May fail on 32-bit Python + e = 0 + f = 0 + + # These shouldn't fail + for x in [a, b, c, d, e, f]: + x + x + + +TestBSR.init_class() + + +#------------------------------------------------------------------------------ +# Tests for non-canonical representations (with duplicates, unsorted indices) +#------------------------------------------------------------------------------ + +def _same_sum_duplicate(data, *inds, **kwargs): + """Duplicates entries to produce the same matrix""" + indptr = kwargs.pop('indptr', None) + if np.issubdtype(data.dtype, np.bool_) or \ + np.issubdtype(data.dtype, np.unsignedinteger): + if indptr is None: + return (data,) + inds + else: + return (data,) + inds + (indptr,) + + zeros_pos = (data == 0).nonzero() + + # duplicate data + data = data.repeat(2, axis=0) + data[::2] -= 1 + data[1::2] = 1 + + # don't spoil all explicit zeros + if zeros_pos[0].size > 0: + pos = tuple(p[0] for p in zeros_pos) + pos1 = (2*pos[0],) + pos[1:] + pos2 = (2*pos[0]+1,) + pos[1:] + data[pos1] = 0 + data[pos2] = 0 + + inds = tuple(indices.repeat(2) for indices in inds) + + if indptr is None: + return (data,) + inds + else: + return (data,) + inds + (indptr * 2,) + + +class _NonCanonicalMixin: + def spcreator(self, D, sorted_indices=False, **kwargs): + """Replace D with a non-canonical equivalent: containing + duplicate elements and explicit zeros""" + construct = super().spcreator + M = construct(D, **kwargs) + + zero_pos = (M.toarray() == 0).nonzero() + has_zeros = (zero_pos[0].size > 0) + if has_zeros: + k = zero_pos[0].size//2 + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + M = self._insert_explicit_zero(M, zero_pos[0][k], zero_pos[1][k]) + + arg1 = self._arg1_for_noncanonical(M, sorted_indices) + if 'shape' not in kwargs: + kwargs['shape'] = M.shape + NC = construct(arg1, **kwargs) + + # check that result is valid + if NC.dtype in [np.float32, np.complex64]: + # For single-precision floats, the differences between M and NC + # that are introduced by the extra operations involved in the + # construction of NC necessitate a more lenient tolerance level + # than the default. + rtol = 1e-05 + else: + rtol = 1e-07 + assert_allclose(NC.toarray(), M.toarray(), rtol=rtol) + + # check that at least one explicit zero + if has_zeros: + assert_((NC.data == 0).any()) + # TODO check that NC has duplicates (which are not explicit zeros) + + return NC + + @pytest.mark.skip(reason='bool(matrix) counts explicit zeros') + def test_bool(self): + pass + + @pytest.mark.skip(reason='getnnz-axis counts explicit zeros') + def test_getnnz_axis(self): + pass + + @pytest.mark.skip(reason='nnz counts explicit zeros') + def test_empty(self): + pass + + +class _NonCanonicalCompressedMixin(_NonCanonicalMixin): + def _arg1_for_noncanonical(self, M, sorted_indices=False): + """Return non-canonical constructor arg1 equivalent to M""" + data, indices, indptr = _same_sum_duplicate(M.data, M.indices, + indptr=M.indptr) + if not sorted_indices: + for start, stop in zip(indptr, indptr[1:]): + indices[start:stop] = indices[start:stop][::-1].copy() + data[start:stop] = data[start:stop][::-1].copy() + return data, indices, indptr + + def _insert_explicit_zero(self, M, i, j): + M[i,j] = 0 + return M + + +class _NonCanonicalCSMixin(_NonCanonicalCompressedMixin): + def test_getelement(self): + def check(dtype, sorted_indices): + D = array([[1,0,0], + [4,3,0], + [0,2,0], + [0,0,0]], dtype=dtype) + A = self.spcreator(D, sorted_indices=sorted_indices) + + M,N = D.shape + + for i in range(-M, M): + for j in range(-N, N): + assert_equal(A[i,j], D[i,j]) + + for ij in [(0,3),(-1,3),(4,0),(4,3),(4,-1), (1, 2, 3)]: + assert_raises((IndexError, TypeError), A.__getitem__, ij) + + for dtype in supported_dtypes: + for sorted_indices in [False, True]: + check(np.dtype(dtype), sorted_indices) + + def test_setitem_sparse(self): + D = np.eye(3) + A = self.spcreator(D) + B = self.spcreator([[1,2,3]]) + + D[1,:] = B.toarray() + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[1,:] = B + assert_array_equal(A.toarray(), D) + + D[:,2] = B.toarray().ravel() + with suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[:,2] = B.T + assert_array_equal(A.toarray(), D) + + @pytest.mark.xfail(run=False, reason='inverse broken with non-canonical matrix') + def test_inv(self): + pass + + @pytest.mark.xfail(run=False, reason='solve broken with non-canonical matrix') + def test_solve(self): + pass + + +class TestCSRNonCanonical(_NonCanonicalCSMixin, TestCSR): + pass + + +class TestCSCNonCanonical(_NonCanonicalCSMixin, TestCSC): + pass + + +class TestBSRNonCanonical(_NonCanonicalCompressedMixin, TestBSR): + def _insert_explicit_zero(self, M, i, j): + x = M.tocsr() + x[i,j] = 0 + return x.tobsr(blocksize=M.blocksize) + + @pytest.mark.xfail(run=False, reason='diagonal broken with non-canonical BSR') + def test_diagonal(self): + pass + + @pytest.mark.xfail(run=False, reason='expm broken with non-canonical BSR') + def test_expm(self): + pass + + +class TestCOONonCanonical(_NonCanonicalMixin, TestCOO): + def _arg1_for_noncanonical(self, M, sorted_indices=None): + """Return non-canonical constructor arg1 equivalent to M""" + data, row, col = _same_sum_duplicate(M.data, M.row, M.col) + return data, (row, col) + + def _insert_explicit_zero(self, M, i, j): + M.data = np.r_[M.data.dtype.type(0), M.data] + M.row = np.r_[M.row.dtype.type(i), M.row] + M.col = np.r_[M.col.dtype.type(j), M.col] + return M + + def test_setdiag_noncanonical(self): + m = self.spcreator(np.eye(3)) + m.sum_duplicates() + m.setdiag([3, 2], k=1) + m.sum_duplicates() + assert_(np.all(np.diff(m.col) >= 0)) + + +def cases_64bit(): + TEST_CLASSES = [TestBSR, TestCOO, TestCSC, TestCSR, TestDIA, + # lil/dok->other conversion operations have get_index_dtype + TestDOK, TestLIL + ] + + # The following features are missing, so skip the tests: + SKIP_TESTS = { + 'test_expm': 'expm for 64-bit indices not available', + 'test_inv': 'linsolve for 64-bit indices not available', + 'test_solve': 'linsolve for 64-bit indices not available', + 'test_scalar_idx_dtype': 'test implemented in base class', + 'test_large_dimensions_reshape': 'test actually requires 64-bit to work', + 'test_constructor_smallcol': 'test verifies int32 indexes', + 'test_constructor_largecol': 'test verifies int64 indexes', + 'test_tocoo_tocsr_tocsc_gh19245': 'test verifies int32 indexes', + } + + for cls in TEST_CLASSES: + for method_name in sorted(dir(cls)): + method = getattr(cls, method_name) + if (method_name.startswith('test_') and + not getattr(method, 'slow', False)): + marks = [] + + msg = SKIP_TESTS.get(method_name) + if bool(msg): + marks += [pytest.mark.skip(reason=msg)] + + markers = getattr(method, 'pytestmark', []) + for mark in markers: + if mark.name in ('skipif', 'skip', 'xfail', 'xslow'): + marks.append(mark) + + yield pytest.param(cls, method_name, marks=marks) + + +class Test64Bit: + MAT_CLASSES = [bsr_matrix, coo_matrix, csc_matrix, csr_matrix, dia_matrix] + + def _create_some_matrix(self, mat_cls, m, n): + return mat_cls(np.random.rand(m, n)) + + def _compare_index_dtype(self, m, dtype): + dtype = np.dtype(dtype) + if isinstance(m, (csc_matrix, csr_matrix, bsr_matrix)): + return (m.indices.dtype == dtype) and (m.indptr.dtype == dtype) + elif isinstance(m, coo_matrix): + return (m.row.dtype == dtype) and (m.col.dtype == dtype) + elif isinstance(m, dia_matrix): + return (m.offsets.dtype == dtype) + else: + raise ValueError(f"matrix {m!r} has no integer indices") + + def test_decorator_maxval_limit(self): + # Test that the with_64bit_maxval_limit decorator works + + @with_64bit_maxval_limit(maxval_limit=10) + def check(mat_cls): + m = mat_cls(np.random.rand(10, 1)) + assert_(self._compare_index_dtype(m, np.int32)) + m = mat_cls(np.random.rand(11, 1)) + assert_(self._compare_index_dtype(m, np.int64)) + + for mat_cls in self.MAT_CLASSES: + check(mat_cls) + + def test_decorator_maxval_random(self): + # Test that the with_64bit_maxval_limit decorator works (2) + + @with_64bit_maxval_limit(random=True) + def check(mat_cls): + seen_32 = False + seen_64 = False + for k in range(100): + m = self._create_some_matrix(mat_cls, 9, 9) + seen_32 = seen_32 or self._compare_index_dtype(m, np.int32) + seen_64 = seen_64 or self._compare_index_dtype(m, np.int64) + if seen_32 and seen_64: + break + else: + raise AssertionError("both 32 and 64 bit indices not seen") + + for mat_cls in self.MAT_CLASSES: + check(mat_cls) + + def _check_resiliency(self, cls, method_name, **kw): + # Resiliency test, to check that sparse matrices deal reasonably + # with varying index data types. + + @with_64bit_maxval_limit(**kw) + def check(cls, method_name): + instance = cls() + if hasattr(instance, 'setup_method'): + instance.setup_method() + try: + getattr(instance, method_name)() + finally: + if hasattr(instance, 'teardown_method'): + instance.teardown_method() + + check(cls, method_name) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_limit_10(self, cls, method_name): + self._check_resiliency(cls, method_name, maxval_limit=10) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_random(self, cls, method_name): + # bsr_matrix.eliminate_zeros relies on csr_matrix constructor + # not making copies of index arrays --- this is not + # necessarily true when we pick the index data type randomly + self._check_resiliency(cls, method_name, random=True) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_all_32(self, cls, method_name): + self._check_resiliency(cls, method_name, fixed_dtype=np.int32) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_resiliency_all_64(self, cls, method_name): + self._check_resiliency(cls, method_name, fixed_dtype=np.int64) + + @pytest.mark.parametrize('cls,method_name', cases_64bit()) + def test_no_64(self, cls, method_name): + self._check_resiliency(cls, method_name, assert_32bit=True) + + def test_downcast_intp(self): + # Check that bincount and ufunc.reduceat intp downcasts are + # dealt with. The point here is to trigger points in the code + # that can fail on 32-bit systems when using 64-bit indices, + # due to use of functions that only work with intp-size + # indices. + + @with_64bit_maxval_limit(fixed_dtype=np.int64, + downcast_maxval=1) + def check_limited(): + # These involve indices larger than `downcast_maxval` + a = csc_matrix([[1, 2], [3, 4], [5, 6]]) + assert_raises(AssertionError, a.getnnz, axis=1) + assert_raises(AssertionError, a.sum, axis=0) + + a = csr_matrix([[1, 2, 3], [3, 4, 6]]) + assert_raises(AssertionError, a.getnnz, axis=0) + + a = coo_matrix([[1, 2, 3], [3, 4, 5]]) + assert_raises(AssertionError, a.getnnz, axis=0) + + @with_64bit_maxval_limit(fixed_dtype=np.int64) + def check_unlimited(): + # These involve indices larger than `downcast_maxval` + a = csc_matrix([[1, 2], [3, 4], [5, 6]]) + a.getnnz(axis=1) + a.sum(axis=0) + + a = csr_matrix([[1, 2, 3], [3, 4, 6]]) + a.getnnz(axis=0) + + a = coo_matrix([[1, 2, 3], [3, 4, 5]]) + a.getnnz(axis=0) + + check_limited() + check_unlimited() diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_common1d.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_common1d.py new file mode 100644 index 0000000000000000000000000000000000000000..1d6ca6041858cc230a5139bd8572e9498569bcd9 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_common1d.py @@ -0,0 +1,450 @@ +"""Test of 1D aspects of sparse array classes""" + +import pytest + +import numpy as np +from numpy.testing import assert_equal, assert_allclose + +from scipy.sparse import ( + bsr_array, csc_array, dia_array, lil_array, + coo_array, csr_array, dok_array, SparseEfficiencyWarning, + ) +from scipy.sparse._sputils import supported_dtypes, matrix +from scipy._lib._util import ComplexWarning + + +sup_complex = np.testing.suppress_warnings() +sup_complex.filter(ComplexWarning) + + +spcreators = [coo_array, csr_array, dok_array] +math_dtypes = [np.int64, np.float64, np.complex128] + + +@pytest.fixture +def dat1d(): + return np.array([3, 0, 1, 0], 'd') + + +@pytest.fixture +def datsp_math_dtypes(dat1d): + dat_dtypes = {dtype: dat1d.astype(dtype) for dtype in math_dtypes} + return { + spcreator: [(dtype, dat, spcreator(dat)) for dtype, dat in dat_dtypes.items()] + for spcreator in spcreators + } + + +# Test init with 1D dense input +# sparrays which do not plan to support 1D +@pytest.mark.parametrize("spcreator", [bsr_array, csc_array, dia_array, lil_array]) +def test_no_1d_support_in_init(spcreator): + with pytest.raises(ValueError, match="arrays don't support 1D input"): + spcreator([0, 1, 2, 3]) + + +# Main tests class +@pytest.mark.parametrize("spcreator", spcreators) +class TestCommon1D: + """test common functionality shared by 1D sparse formats""" + + def test_create_empty(self, spcreator): + assert_equal(spcreator((3,)).toarray(), np.zeros(3)) + assert_equal(spcreator((3,)).nnz, 0) + assert_equal(spcreator((3,)).count_nonzero(), 0) + + def test_invalid_shapes(self, spcreator): + with pytest.raises(ValueError, match='elements cannot be negative'): + spcreator((-3,)) + + def test_repr(self, spcreator, dat1d): + repr(spcreator(dat1d)) + + def test_str(self, spcreator, dat1d): + str(spcreator(dat1d)) + + def test_neg(self, spcreator): + A = np.array([-1, 0, 17, 0, -5, 0, 1, -4, 0, 0, 0, 0], 'd') + assert_equal(-A, (-spcreator(A)).toarray()) + + def test_1d_supported_init(self, spcreator): + A = spcreator([0, 1, 2, 3]) + assert A.ndim == 1 + + def test_reshape_1d_tofrom_row_or_column(self, spcreator): + # add a dimension 1d->2d + x = spcreator([1, 0, 7, 0, 0, 0, 0, -3, 0, 0, 0, 5]) + y = x.reshape(1, 12) + desired = [[1, 0, 7, 0, 0, 0, 0, -3, 0, 0, 0, 5]] + assert_equal(y.toarray(), desired) + + # remove a size-1 dimension 2d->1d + x = spcreator(desired) + y = x.reshape(12) + assert_equal(y.toarray(), desired[0]) + y2 = x.reshape((12,)) + assert y.shape == y2.shape + + # make a 2d column into 1d. 2d->1d + y = x.T.reshape(12) + assert_equal(y.toarray(), desired[0]) + + def test_reshape(self, spcreator): + x = spcreator([1, 0, 7, 0, 0, 0, 0, -3, 0, 0, 0, 5]) + y = x.reshape((4, 3)) + desired = [[1, 0, 7], [0, 0, 0], [0, -3, 0], [0, 0, 5]] + assert_equal(y.toarray(), desired) + + y = x.reshape((12,)) + assert y is x + + y = x.reshape(12) + assert_equal(y.toarray(), x.toarray()) + + def test_sum(self, spcreator): + np.random.seed(1234) + dat_1 = np.array([0, 1, 2, 3, -4, 5, -6, 7, 9]) + dat_2 = np.random.rand(5) + dat_3 = np.array([]) + dat_4 = np.zeros((40,)) + arrays = [dat_1, dat_2, dat_3, dat_4] + + for dat in arrays: + datsp = spcreator(dat) + with np.errstate(over='ignore'): + assert np.isscalar(datsp.sum()) + assert_allclose(dat.sum(), datsp.sum()) + assert_allclose(dat.sum(axis=None), datsp.sum(axis=None)) + assert_allclose(dat.sum(axis=0), datsp.sum(axis=0)) + assert_allclose(dat.sum(axis=-1), datsp.sum(axis=-1)) + + # test `out` parameter + datsp.sum(axis=0, out=np.zeros(())) + + def test_sum_invalid_params(self, spcreator): + out = np.zeros((3,)) # wrong size for out + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + with pytest.raises(ValueError, match='axis must be None, -1 or 0'): + datsp.sum(axis=1) + with pytest.raises(TypeError, match='Tuples are not accepted'): + datsp.sum(axis=(0, 1)) + with pytest.raises(TypeError, match='axis must be an integer'): + datsp.sum(axis=1.5) + with pytest.raises(ValueError, match='dimensions do not match'): + datsp.sum(axis=0, out=out) + + def test_numpy_sum(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + dat_sum = np.sum(dat) + datsp_sum = np.sum(datsp) + + assert_allclose(dat_sum, datsp_sum) + + def test_mean(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + assert_allclose(dat.mean(), datsp.mean()) + assert np.isscalar(datsp.mean(axis=None)) + assert_allclose(dat.mean(axis=None), datsp.mean(axis=None)) + assert_allclose(dat.mean(axis=0), datsp.mean(axis=0)) + assert_allclose(dat.mean(axis=-1), datsp.mean(axis=-1)) + + with pytest.raises(ValueError, match='axis'): + datsp.mean(axis=1) + with pytest.raises(ValueError, match='axis'): + datsp.mean(axis=-2) + + def test_mean_invalid_params(self, spcreator): + out = np.asarray(np.zeros((1, 3))) + dat = np.array([[0, 1, 2], [3, -4, 5], [-6, 7, 9]]) + + datsp = spcreator(dat) + with pytest.raises(ValueError, match='axis out of range'): + datsp.mean(axis=3) + with pytest.raises(TypeError, match='Tuples are not accepted'): + datsp.mean(axis=(0, 1)) + with pytest.raises(TypeError, match='axis must be an integer'): + datsp.mean(axis=1.5) + with pytest.raises(ValueError, match='dimensions do not match'): + datsp.mean(axis=1, out=out) + + def test_sum_dtype(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + for dtype in supported_dtypes: + dat_sum = dat.sum(dtype=dtype) + datsp_sum = datsp.sum(dtype=dtype) + + assert_allclose(dat_sum, datsp_sum) + assert_equal(dat_sum.dtype, datsp_sum.dtype) + + def test_mean_dtype(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + for dtype in supported_dtypes: + dat_mean = dat.mean(dtype=dtype) + datsp_mean = datsp.mean(dtype=dtype) + + assert_allclose(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + def test_mean_out(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + dat_out = np.array([0]) + datsp_out = np.array([0]) + + dat.mean(out=dat_out, keepdims=True) + datsp.mean(out=datsp_out) + assert_allclose(dat_out, datsp_out) + + dat.mean(axis=0, out=dat_out, keepdims=True) + datsp.mean(axis=0, out=datsp_out) + assert_allclose(dat_out, datsp_out) + + def test_numpy_mean(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + + dat_mean = np.mean(dat) + datsp_mean = np.mean(datsp) + + assert_allclose(dat_mean, datsp_mean) + assert_equal(dat_mean.dtype, datsp_mean.dtype) + + @sup_complex + def test_from_array(self, spcreator): + A = np.array([2, 3, 4]) + assert_equal(spcreator(A).toarray(), A) + + A = np.array([1.0 + 3j, 0, -1]) + assert_equal(spcreator(A).toarray(), A) + assert_equal(spcreator(A, dtype='int16').toarray(), A.astype('int16')) + + @sup_complex + def test_from_list(self, spcreator): + A = [2, 3, 4] + assert_equal(spcreator(A).toarray(), A) + + A = [1.0 + 3j, 0, -1] + assert_equal(spcreator(A).toarray(), np.array(A)) + assert_equal( + spcreator(A, dtype='int16').toarray(), np.array(A).astype('int16') + ) + + @sup_complex + def test_from_sparse(self, spcreator): + D = np.array([1, 0, 0]) + S = coo_array(D) + assert_equal(spcreator(S).toarray(), D) + S = spcreator(D) + assert_equal(spcreator(S).toarray(), D) + + D = np.array([1.0 + 3j, 0, -1]) + S = coo_array(D) + assert_equal(spcreator(S).toarray(), D) + assert_equal(spcreator(S, dtype='int16').toarray(), D.astype('int16')) + S = spcreator(D) + assert_equal(spcreator(S).toarray(), D) + assert_equal(spcreator(S, dtype='int16').toarray(), D.astype('int16')) + + def test_toarray(self, spcreator, dat1d): + datsp = spcreator(dat1d) + # Check C- or F-contiguous (default). + chk = datsp.toarray() + assert_equal(chk, dat1d) + assert chk.flags.c_contiguous == chk.flags.f_contiguous + + # Check C-contiguous (with arg). + chk = datsp.toarray(order='C') + assert_equal(chk, dat1d) + assert chk.flags.c_contiguous + assert chk.flags.f_contiguous + + # Check F-contiguous (with arg). + chk = datsp.toarray(order='F') + assert_equal(chk, dat1d) + assert chk.flags.c_contiguous + assert chk.flags.f_contiguous + + # Check with output arg. + out = np.zeros(datsp.shape, dtype=datsp.dtype) + datsp.toarray(out=out) + assert_equal(out, dat1d) + + # Check that things are fine when we don't initialize with zeros. + out[...] = 1.0 + datsp.toarray(out=out) + assert_equal(out, dat1d) + + # np.dot does not work with sparse matrices (unless scalars) + # so this is testing whether dat1d matches datsp.toarray() + a = np.array([1.0, 2.0, 3.0, 4.0]) + dense_dot_dense = np.dot(a, dat1d) + check = np.dot(a, datsp.toarray()) + assert_equal(dense_dot_dense, check) + + b = np.array([1.0, 2.0, 3.0, 4.0]) + dense_dot_dense = np.dot(dat1d, b) + check = np.dot(datsp.toarray(), b) + assert_equal(dense_dot_dense, check) + + # Check bool data works. + spbool = spcreator(dat1d, dtype=bool) + arrbool = dat1d.astype(bool) + assert_equal(spbool.toarray(), arrbool) + + def test_add(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + a = dat.copy() + a[0] = 2.0 + b = datsp + c = b + a + assert_equal(c, b.toarray() + a) + + # test broadcasting + # Note: cant add nonzero scalar to sparray. Can add len 1 array + c = b + a[0:1] + assert_equal(c, b.toarray() + a[0]) + + def test_radd(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + a = dat.copy() + a[0] = 2.0 + b = datsp + c = a + b + assert_equal(c, a + b.toarray()) + + def test_rsub(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + if dtype == np.dtype('bool'): + # boolean array subtraction deprecated in 1.9.0 + continue + + assert_equal((dat - datsp), [0, 0, 0, 0]) + assert_equal((datsp - dat), [0, 0, 0, 0]) + assert_equal((0 - datsp).toarray(), -dat) + + A = spcreator([1, -4, 0, 2], dtype='d') + assert_equal((dat - A), dat - A.toarray()) + assert_equal((A - dat), A.toarray() - dat) + assert_equal(A.toarray() - datsp, A.toarray() - dat) + assert_equal(datsp - A.toarray(), dat - A.toarray()) + + # test broadcasting + assert_equal(dat[:1] - datsp, dat[:1] - dat) + + def test_matvec(self, spcreator): + A = np.array([2, 0, 3.0]) + Asp = spcreator(A) + col = np.array([[1, 2, 3]]).T + + assert_allclose(Asp @ col, Asp.toarray() @ col) + + assert (A @ np.array([1, 2, 3])).shape == () + assert Asp @ np.array([1, 2, 3]) == 11 + assert (Asp @ np.array([1, 2, 3])).shape == () + assert (Asp @ np.array([[1], [2], [3]])).shape == () + # check result type + assert isinstance(Asp @ matrix([[1, 2, 3]]).T, np.ndarray) + assert (Asp @ np.array([[1, 2, 3]]).T).shape == () + + # ensure exception is raised for improper dimensions + bad_vecs = [np.array([1, 2]), np.array([1, 2, 3, 4]), np.array([[1], [2]])] + for x in bad_vecs: + with pytest.raises(ValueError, match='dimension mismatch'): + Asp.__matmul__(x) + + # The current relationship between sparse matrix products and array + # products is as follows: + dot_result = np.dot(Asp.toarray(), [1, 2, 3]) + assert_allclose(Asp @ np.array([1, 2, 3]), dot_result) + assert_allclose(Asp @ [[1], [2], [3]], dot_result.T) + # Note that the result of Asp @ x is dense if x has a singleton dimension. + + def test_rmatvec(self, spcreator, dat1d): + M = spcreator(dat1d) + assert_allclose([1, 2, 3, 4] @ M, np.dot([1, 2, 3, 4], M.toarray())) + row = np.array([[1, 2, 3, 4]]) + assert_allclose(row @ M, row @ M.toarray()) + + def test_transpose(self, spcreator, dat1d): + for A in [dat1d, np.array([])]: + B = spcreator(A) + assert_equal(B.toarray(), A) + assert_equal(B.transpose().toarray(), A) + assert_equal(B.dtype, A.dtype) + + def test_add_dense_to_sparse(self, spcreator, datsp_math_dtypes): + for dtype, dat, datsp in datsp_math_dtypes[spcreator]: + sum1 = dat + datsp + assert_equal(sum1, dat + dat) + sum2 = datsp + dat + assert_equal(sum2, dat + dat) + + def test_iterator(self, spcreator): + # test that __iter__ is compatible with NumPy + B = np.arange(5) + A = spcreator(B) + + if A.format not in ['coo', 'dia', 'bsr']: + for x, y in zip(A, B): + assert_equal(x, y) + + def test_resize(self, spcreator): + # resize(shape) resizes the matrix in-place + D = np.array([1, 0, 3, 4]) + S = spcreator(D) + assert S.resize((3,)) is None + assert_equal(S.toarray(), [1, 0, 3]) + S.resize((5,)) + assert_equal(S.toarray(), [1, 0, 3, 0, 0]) + + +@pytest.mark.parametrize("spcreator", [csr_array, dok_array]) +class TestGetSet1D: + def test_getelement(self, spcreator): + D = np.array([4, 3, 0]) + A = spcreator(D) + + N = D.shape[0] + for j in range(-N, N): + assert_equal(A[j], D[j]) + + for ij in [3, -4]: + with pytest.raises( + (IndexError, TypeError), match='index value out of bounds' + ): + A.__getitem__(ij) + + # single element tuples unwrapped + assert A[(0,)] == 4 + + with pytest.raises(IndexError, match='index value out of bounds'): + A.__getitem__((4,)) + + def test_setelement(self, spcreator): + dtype = np.float64 + A = spcreator((12,), dtype=dtype) + with np.testing.suppress_warnings() as sup: + sup.filter(SparseEfficiencyWarning, "Changing the sparsity structure") + A[0] = dtype(0) + A[1] = dtype(3) + A[8] = dtype(9.0) + A[-2] = dtype(7) + A[5] = 9 + + A[-9,] = dtype(8) + A[1,] = dtype(5) # overwrite using 1-tuple index + + for ij in [13, -14, (13,), (14,)]: + with pytest.raises(IndexError, match='index value out of bounds'): + A.__setitem__(ij, 123.0) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_construct.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_construct.py new file mode 100644 index 0000000000000000000000000000000000000000..9a3e80d8465d550c62219f080e5247f6c750ae25 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_construct.py @@ -0,0 +1,836 @@ +"""test sparse matrix construction functions""" + +import numpy as np +from numpy import array +from numpy.testing import (assert_equal, assert_, + assert_array_equal, assert_array_almost_equal_nulp) +import pytest +from pytest import raises as assert_raises +from scipy._lib._testutils import check_free_memory +from scipy._lib._util import check_random_state + +from scipy.sparse import (csr_matrix, coo_matrix, + csr_array, coo_array, + csc_array, bsr_array, + dia_array, dok_array, + lil_array, csc_matrix, + bsr_matrix, dia_matrix, + lil_matrix, sparray, spmatrix, + _construct as construct) +from scipy.sparse._construct import rand as sprand + +sparse_formats = ['csr','csc','coo','bsr','dia','lil','dok'] + +#TODO check whether format=XXX is respected + + +def _sprandn(m, n, density=0.01, format="coo", dtype=None, random_state=None): + # Helper function for testing. + random_state = check_random_state(random_state) + data_rvs = random_state.standard_normal + return construct.random(m, n, density, format, dtype, + random_state, data_rvs) + + +def _sprandn_array(m, n, density=0.01, format="coo", dtype=None, random_state=None): + # Helper function for testing. + random_state = check_random_state(random_state) + data_sampler = random_state.standard_normal + return construct.random_array((m, n), density=density, format=format, dtype=dtype, + random_state=random_state, data_sampler=data_sampler) + + +class TestConstructUtils: + + @pytest.mark.parametrize("cls", [ + csc_array, csr_array, coo_array, bsr_array, + dia_array, dok_array, lil_array + ]) + def test_singleton_array_constructor(self, cls): + with pytest.raises( + ValueError, + match=( + 'scipy sparse array classes do not support ' + 'instantiation from a scalar' + ) + ): + cls(0) + + @pytest.mark.parametrize("cls", [ + csc_matrix, csr_matrix, coo_matrix, + bsr_matrix, dia_matrix, lil_matrix + ]) + def test_singleton_matrix_constructor(self, cls): + """ + This test is for backwards compatibility post scipy 1.13. + The behavior observed here is what is to be expected + with the older matrix classes. This test comes with the + exception of dok_matrix, which was not working pre scipy1.12 + (unlike the rest of these). + """ + assert cls(0).shape == (1, 1) + + def test_spdiags(self): + diags1 = array([[1, 2, 3, 4, 5]]) + diags2 = array([[1, 2, 3, 4, 5], + [6, 7, 8, 9,10]]) + diags3 = array([[1, 2, 3, 4, 5], + [6, 7, 8, 9,10], + [11,12,13,14,15]]) + + cases = [] + cases.append((diags1, 0, 1, 1, [[1]])) + cases.append((diags1, [0], 1, 1, [[1]])) + cases.append((diags1, [0], 2, 1, [[1],[0]])) + cases.append((diags1, [0], 1, 2, [[1,0]])) + cases.append((diags1, [1], 1, 2, [[0,2]])) + cases.append((diags1,[-1], 1, 2, [[0,0]])) + cases.append((diags1, [0], 2, 2, [[1,0],[0,2]])) + cases.append((diags1,[-1], 2, 2, [[0,0],[1,0]])) + cases.append((diags1, [3], 2, 2, [[0,0],[0,0]])) + cases.append((diags1, [0], 3, 4, [[1,0,0,0],[0,2,0,0],[0,0,3,0]])) + cases.append((diags1, [1], 3, 4, [[0,2,0,0],[0,0,3,0],[0,0,0,4]])) + cases.append((diags1, [2], 3, 5, [[0,0,3,0,0],[0,0,0,4,0],[0,0,0,0,5]])) + + cases.append((diags2, [0,2], 3, 3, [[1,0,8],[0,2,0],[0,0,3]])) + cases.append((diags2, [-1,0], 3, 4, [[6,0,0,0],[1,7,0,0],[0,2,8,0]])) + cases.append((diags2, [2,-3], 6, 6, [[0,0,3,0,0,0], + [0,0,0,4,0,0], + [0,0,0,0,5,0], + [6,0,0,0,0,0], + [0,7,0,0,0,0], + [0,0,8,0,0,0]])) + + cases.append((diags3, [-1,0,1], 6, 6, [[6,12, 0, 0, 0, 0], + [1, 7,13, 0, 0, 0], + [0, 2, 8,14, 0, 0], + [0, 0, 3, 9,15, 0], + [0, 0, 0, 4,10, 0], + [0, 0, 0, 0, 5, 0]])) + cases.append((diags3, [-4,2,-1], 6, 5, [[0, 0, 8, 0, 0], + [11, 0, 0, 9, 0], + [0,12, 0, 0,10], + [0, 0,13, 0, 0], + [1, 0, 0,14, 0], + [0, 2, 0, 0,15]])) + cases.append((diags3, [-1, 1, 2], len(diags3[0]), len(diags3[0]), + [[0, 7, 13, 0, 0], + [1, 0, 8, 14, 0], + [0, 2, 0, 9, 15], + [0, 0, 3, 0, 10], + [0, 0, 0, 4, 0]])) + + for d, o, m, n, result in cases: + if len(d[0]) == m and m == n: + assert_equal(construct.spdiags(d, o).toarray(), result) + assert_equal(construct.spdiags(d, o, m, n).toarray(), result) + assert_equal(construct.spdiags(d, o, (m, n)).toarray(), result) + + def test_diags(self): + a = array([1, 2, 3, 4, 5]) + b = array([6, 7, 8, 9, 10]) + c = array([11, 12, 13, 14, 15]) + + cases = [] + cases.append((a[:1], 0, (1, 1), [[1]])) + cases.append(([a[:1]], [0], (1, 1), [[1]])) + cases.append(([a[:1]], [0], (2, 1), [[1],[0]])) + cases.append(([a[:1]], [0], (1, 2), [[1,0]])) + cases.append(([a[:1]], [1], (1, 2), [[0,1]])) + cases.append(([a[:2]], [0], (2, 2), [[1,0],[0,2]])) + cases.append(([a[:1]],[-1], (2, 2), [[0,0],[1,0]])) + cases.append(([a[:3]], [0], (3, 4), [[1,0,0,0],[0,2,0,0],[0,0,3,0]])) + cases.append(([a[:3]], [1], (3, 4), [[0,1,0,0],[0,0,2,0],[0,0,0,3]])) + cases.append(([a[:1]], [-2], (3, 5), [[0,0,0,0,0],[0,0,0,0,0],[1,0,0,0,0]])) + cases.append(([a[:2]], [-1], (3, 5), [[0,0,0,0,0],[1,0,0,0,0],[0,2,0,0,0]])) + cases.append(([a[:3]], [0], (3, 5), [[1,0,0,0,0],[0,2,0,0,0],[0,0,3,0,0]])) + cases.append(([a[:3]], [1], (3, 5), [[0,1,0,0,0],[0,0,2,0,0],[0,0,0,3,0]])) + cases.append(([a[:3]], [2], (3, 5), [[0,0,1,0,0],[0,0,0,2,0],[0,0,0,0,3]])) + cases.append(([a[:2]], [3], (3, 5), [[0,0,0,1,0],[0,0,0,0,2],[0,0,0,0,0]])) + cases.append(([a[:1]], [4], (3, 5), [[0,0,0,0,1],[0,0,0,0,0],[0,0,0,0,0]])) + cases.append(([a[:1]], [-4], (5, 3), [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[1,0,0]])) + cases.append(([a[:2]], [-3], (5, 3), [[0,0,0],[0,0,0],[0,0,0],[1,0,0],[0,2,0]])) + cases.append(([a[:3]], [-2], (5, 3), [[0,0,0],[0,0,0],[1,0,0],[0,2,0],[0,0,3]])) + cases.append(([a[:3]], [-1], (5, 3), [[0,0,0],[1,0,0],[0,2,0],[0,0,3],[0,0,0]])) + cases.append(([a[:3]], [0], (5, 3), [[1,0,0],[0,2,0],[0,0,3],[0,0,0],[0,0,0]])) + cases.append(([a[:2]], [1], (5, 3), [[0,1,0],[0,0,2],[0,0,0],[0,0,0],[0,0,0]])) + cases.append(([a[:1]], [2], (5, 3), [[0,0,1],[0,0,0],[0,0,0],[0,0,0],[0,0,0]])) + + cases.append(([a[:3],b[:1]], [0,2], (3, 3), [[1,0,6],[0,2,0],[0,0,3]])) + cases.append(([a[:2],b[:3]], [-1,0], (3, 4), [[6,0,0,0],[1,7,0,0],[0,2,8,0]])) + cases.append(([a[:4],b[:3]], [2,-3], (6, 6), [[0,0,1,0,0,0], + [0,0,0,2,0,0], + [0,0,0,0,3,0], + [6,0,0,0,0,4], + [0,7,0,0,0,0], + [0,0,8,0,0,0]])) + + cases.append(([a[:4],b,c[:4]], [-1,0,1], (5, 5), [[6,11, 0, 0, 0], + [1, 7,12, 0, 0], + [0, 2, 8,13, 0], + [0, 0, 3, 9,14], + [0, 0, 0, 4,10]])) + cases.append(([a[:2],b[:3],c], [-4,2,-1], (6, 5), [[0, 0, 6, 0, 0], + [11, 0, 0, 7, 0], + [0,12, 0, 0, 8], + [0, 0,13, 0, 0], + [1, 0, 0,14, 0], + [0, 2, 0, 0,15]])) + + # too long arrays are OK + cases.append(([a], [0], (1, 1), [[1]])) + cases.append(([a[:3],b], [0,2], (3, 3), [[1, 0, 6], [0, 2, 0], [0, 0, 3]])) + cases.append(( + np.array([[1, 2, 3], [4, 5, 6]]), + [0,-1], + (3, 3), + [[1, 0, 0], [4, 2, 0], [0, 5, 3]] + )) + + # scalar case: broadcasting + cases.append(([1,-2,1], [1,0,-1], (3, 3), [[-2, 1, 0], + [1, -2, 1], + [0, 1, -2]])) + + for d, o, shape, result in cases: + err_msg = f"{d!r} {o!r} {shape!r} {result!r}" + assert_equal(construct.diags(d, offsets=o, shape=shape).toarray(), + result, err_msg=err_msg) + + if (shape[0] == shape[1] + and hasattr(d[0], '__len__') + and len(d[0]) <= max(shape)): + # should be able to find the shape automatically + assert_equal(construct.diags(d, offsets=o).toarray(), result, + err_msg=err_msg) + + def test_diags_default(self): + a = array([1, 2, 3, 4, 5]) + assert_equal(construct.diags(a).toarray(), np.diag(a)) + + def test_diags_default_bad(self): + a = array([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]]) + assert_raises(ValueError, construct.diags, a) + + def test_diags_bad(self): + a = array([1, 2, 3, 4, 5]) + b = array([6, 7, 8, 9, 10]) + c = array([11, 12, 13, 14, 15]) + + cases = [] + cases.append(([a[:0]], 0, (1, 1))) + cases.append(([a[:4],b,c[:3]], [-1,0,1], (5, 5))) + cases.append(([a[:2],c,b[:3]], [-4,2,-1], (6, 5))) + cases.append(([a[:2],c,b[:3]], [-4,2,-1], None)) + cases.append(([], [-4,2,-1], None)) + cases.append(([1], [-5], (4, 4))) + cases.append(([a], 0, None)) + + for d, o, shape in cases: + assert_raises(ValueError, construct.diags, d, offsets=o, shape=shape) + + assert_raises(TypeError, construct.diags, [[None]], offsets=[0]) + + def test_diags_vs_diag(self): + # Check that + # + # diags([a, b, ...], [i, j, ...]) == diag(a, i) + diag(b, j) + ... + # + + np.random.seed(1234) + + for n_diags in [1, 2, 3, 4, 5, 10]: + n = 1 + n_diags//2 + np.random.randint(0, 10) + + offsets = np.arange(-n+1, n-1) + np.random.shuffle(offsets) + offsets = offsets[:n_diags] + + diagonals = [np.random.rand(n - abs(q)) for q in offsets] + + mat = construct.diags(diagonals, offsets=offsets) + dense_mat = sum([np.diag(x, j) for x, j in zip(diagonals, offsets)]) + + assert_array_almost_equal_nulp(mat.toarray(), dense_mat) + + if len(offsets) == 1: + mat = construct.diags(diagonals[0], offsets=offsets[0]) + dense_mat = np.diag(diagonals[0], offsets[0]) + assert_array_almost_equal_nulp(mat.toarray(), dense_mat) + + def test_diags_dtype(self): + x = construct.diags([2.2], offsets=[0], shape=(2, 2), dtype=int) + assert_equal(x.dtype, int) + assert_equal(x.toarray(), [[2, 0], [0, 2]]) + + def test_diags_one_diagonal(self): + d = list(range(5)) + for k in range(-5, 6): + assert_equal(construct.diags(d, offsets=k).toarray(), + construct.diags([d], offsets=[k]).toarray()) + + def test_diags_empty(self): + x = construct.diags([]) + assert_equal(x.shape, (0, 0)) + + @pytest.mark.parametrize("identity", [construct.identity, construct.eye_array]) + def test_identity(self, identity): + assert_equal(identity(1).toarray(), [[1]]) + assert_equal(identity(2).toarray(), [[1,0],[0,1]]) + + I = identity(3, dtype='int8', format='dia') + assert_equal(I.dtype, np.dtype('int8')) + assert_equal(I.format, 'dia') + + for fmt in sparse_formats: + I = identity(3, format=fmt) + assert_equal(I.format, fmt) + assert_equal(I.toarray(), [[1,0,0],[0,1,0],[0,0,1]]) + + @pytest.mark.parametrize("eye", [construct.eye, construct.eye_array]) + def test_eye(self, eye): + assert_equal(eye(1,1).toarray(), [[1]]) + assert_equal(eye(2,3).toarray(), [[1,0,0],[0,1,0]]) + assert_equal(eye(3,2).toarray(), [[1,0],[0,1],[0,0]]) + assert_equal(eye(3,3).toarray(), [[1,0,0],[0,1,0],[0,0,1]]) + + assert_equal(eye(3,3,dtype='int16').dtype, np.dtype('int16')) + + for m in [3, 5]: + for n in [3, 5]: + for k in range(-5,6): + # scipy.sparse.eye deviates from np.eye here. np.eye will + # create arrays of all 0's when the diagonal offset is + # greater than the size of the array. For sparse arrays + # this makes less sense, especially as it results in dia + # arrays with negative diagonals. Therefore sp.sparse.eye + # validates that diagonal offsets fall within the shape of + # the array. See gh-18555. + if (k > 0 and k > n) or (k < 0 and abs(k) > m): + with pytest.raises( + ValueError, match="Offset.*out of bounds" + ): + eye(m, n, k=k) + + else: + assert_equal( + eye(m, n, k=k).toarray(), + np.eye(m, n, k=k) + ) + if m == n: + assert_equal( + eye(m, k=k).toarray(), + np.eye(m, n, k=k) + ) + + @pytest.mark.parametrize("eye", [construct.eye, construct.eye_array]) + def test_eye_one(self, eye): + assert_equal(eye(1).toarray(), [[1]]) + assert_equal(eye(2).toarray(), [[1,0],[0,1]]) + + I = eye(3, dtype='int8', format='dia') + assert_equal(I.dtype, np.dtype('int8')) + assert_equal(I.format, 'dia') + + for fmt in sparse_formats: + I = eye(3, format=fmt) + assert_equal(I.format, fmt) + assert_equal(I.toarray(), [[1,0,0],[0,1,0],[0,0,1]]) + + def test_eye_array_vs_matrix(self): + assert isinstance(construct.eye_array(3), sparray) + assert not isinstance(construct.eye(3), sparray) + + def test_kron(self): + cases = [] + + cases.append(array([[0]])) + cases.append(array([[-1]])) + cases.append(array([[4]])) + cases.append(array([[10]])) + cases.append(array([[0],[0]])) + cases.append(array([[0,0]])) + cases.append(array([[1,2],[3,4]])) + cases.append(array([[0,2],[5,0]])) + cases.append(array([[0,2,-6],[8,0,14]])) + cases.append(array([[5,4],[0,0],[6,0]])) + cases.append(array([[5,4,4],[1,0,0],[6,0,8]])) + cases.append(array([[0,1,0,2,0,5,8]])) + cases.append(array([[0.5,0.125,0,3.25],[0,2.5,0,0]])) + + # test all cases with some formats + for a in cases: + ca = csr_array(a) + for b in cases: + cb = csr_array(b) + expected = np.kron(a, b) + for fmt in sparse_formats[1:4]: + result = construct.kron(ca, cb, format=fmt) + assert_equal(result.format, fmt) + assert_array_equal(result.toarray(), expected) + assert isinstance(result, sparray) + + # test one case with all formats + a = cases[-1] + b = cases[-3] + ca = csr_array(a) + cb = csr_array(b) + + expected = np.kron(a, b) + for fmt in sparse_formats: + result = construct.kron(ca, cb, format=fmt) + assert_equal(result.format, fmt) + assert_array_equal(result.toarray(), expected) + assert isinstance(result, sparray) + + # check that spmatrix returned when both inputs are spmatrix + result = construct.kron(csr_matrix(a), csr_matrix(b), format=fmt) + assert_equal(result.format, fmt) + assert_array_equal(result.toarray(), expected) + assert isinstance(result, spmatrix) + + def test_kron_ndim_exceptions(self): + with pytest.raises(ValueError, match='requires 2D input'): + construct.kron([[0], [1]], csr_array([0, 1])) + with pytest.raises(ValueError, match='requires 2D input'): + construct.kron(csr_array([0, 1]), [[0], [1]]) + # no exception if sparse arrays are not input (spmatrix inferred) + construct.kron([[0], [1]], [0, 1]) + + def test_kron_large(self): + n = 2**16 + a = construct.diags_array([1], shape=(1, n), offsets=n-1) + b = construct.diags_array([1], shape=(n, 1), offsets=1-n) + + construct.kron(a, a) + construct.kron(b, b) + + def test_kronsum(self): + cases = [] + + cases.append(array([[0]])) + cases.append(array([[-1]])) + cases.append(array([[4]])) + cases.append(array([[10]])) + cases.append(array([[1,2],[3,4]])) + cases.append(array([[0,2],[5,0]])) + cases.append(array([[0,2,-6],[8,0,14],[0,3,0]])) + cases.append(array([[1,0,0],[0,5,-1],[4,-2,8]])) + + # test all cases with default format + for a in cases: + for b in cases: + result = construct.kronsum(csr_array(a), csr_array(b)).toarray() + expected = (np.kron(np.eye(b.shape[0]), a) + + np.kron(b, np.eye(a.shape[0]))) + assert_array_equal(result, expected) + + # check that spmatrix returned when both inputs are spmatrix + result = construct.kronsum(csr_matrix(a), csr_matrix(b)).toarray() + assert_array_equal(result, expected) + + def test_kronsum_ndim_exceptions(self): + with pytest.raises(ValueError, match='requires 2D input'): + construct.kronsum([[0], [1]], csr_array([0, 1])) + with pytest.raises(ValueError, match='requires 2D input'): + construct.kronsum(csr_array([0, 1]), [[0], [1]]) + # no exception if sparse arrays are not input (spmatrix inferred) + construct.kronsum([[0, 1], [1, 0]], [2]) + + @pytest.mark.parametrize("coo_cls", [coo_matrix, coo_array]) + def test_vstack(self, coo_cls): + A = coo_cls([[1,2],[3,4]]) + B = coo_cls([[5,6]]) + + expected = array([[1, 2], + [3, 4], + [5, 6]]) + assert_equal(construct.vstack([A, B]).toarray(), expected) + assert_equal(construct.vstack([A, B], dtype=np.float32).dtype, + np.float32) + + assert_equal(construct.vstack([A.todok(), B.todok()]).toarray(), expected) + + assert_equal(construct.vstack([A.tocsr(), B.tocsr()]).toarray(), + expected) + result = construct.vstack([A.tocsr(), B.tocsr()], + format="csr", dtype=np.float32) + assert_equal(result.dtype, np.float32) + assert_equal(result.indices.dtype, np.int32) + assert_equal(result.indptr.dtype, np.int32) + + assert_equal(construct.vstack([A.tocsc(), B.tocsc()]).toarray(), + expected) + result = construct.vstack([A.tocsc(), B.tocsc()], + format="csc", dtype=np.float32) + assert_equal(result.dtype, np.float32) + assert_equal(result.indices.dtype, np.int32) + assert_equal(result.indptr.dtype, np.int32) + + def test_vstack_matrix_or_array(self): + A = [[1,2],[3,4]] + B = [[5,6]] + assert isinstance(construct.vstack([coo_array(A), coo_array(B)]), sparray) + assert isinstance(construct.vstack([coo_array(A), coo_matrix(B)]), sparray) + assert isinstance(construct.vstack([coo_matrix(A), coo_array(B)]), sparray) + assert isinstance(construct.vstack([coo_matrix(A), coo_matrix(B)]), spmatrix) + + def test_vstack_1d_with_2d(self): + # fixes gh-21064 + arr = csr_array([[1, 0, 0], [0, 1, 0]]) + arr1d = csr_array([1, 0, 0]) + arr1dcoo = coo_array([1, 0, 0]) + assert construct.vstack([arr, np.array([0, 0, 0])]).shape == (3, 3) + assert construct.hstack([arr1d, np.array([[0]])]).shape == (1, 4) + assert construct.hstack([arr1d, arr1d]).shape == (1, 6) + assert construct.vstack([arr1d, arr1d]).shape == (2, 3) + + # check csr specialty stacking code like _stack_along_minor_axis + assert construct.hstack([arr, arr]).shape == (2, 6) + assert construct.hstack([arr1d, arr1d]).shape == (1, 6) + + assert construct.hstack([arr1d, arr1dcoo]).shape == (1, 6) + assert construct.vstack([arr, arr1dcoo]).shape == (3, 3) + assert construct.vstack([arr1d, arr1dcoo]).shape == (2, 3) + + with pytest.raises(ValueError, match="incompatible row dimensions"): + construct.hstack([arr, np.array([0, 0])]) + with pytest.raises(ValueError, match="incompatible column dimensions"): + construct.vstack([arr, np.array([0, 0])]) + + @pytest.mark.parametrize("coo_cls", [coo_matrix, coo_array]) + def test_hstack(self, coo_cls): + A = coo_cls([[1,2],[3,4]]) + B = coo_cls([[5],[6]]) + + expected = array([[1, 2, 5], + [3, 4, 6]]) + assert_equal(construct.hstack([A, B]).toarray(), expected) + assert_equal(construct.hstack([A, B], dtype=np.float32).dtype, + np.float32) + + assert_equal(construct.hstack([A.todok(), B.todok()]).toarray(), expected) + + assert_equal(construct.hstack([A.tocsc(), B.tocsc()]).toarray(), + expected) + assert_equal(construct.hstack([A.tocsc(), B.tocsc()], + dtype=np.float32).dtype, + np.float32) + assert_equal(construct.hstack([A.tocsr(), B.tocsr()]).toarray(), + expected) + assert_equal(construct.hstack([A.tocsr(), B.tocsr()], + dtype=np.float32).dtype, + np.float32) + + def test_hstack_matrix_or_array(self): + A = [[1,2],[3,4]] + B = [[5],[6]] + assert isinstance(construct.hstack([coo_array(A), coo_array(B)]), sparray) + assert isinstance(construct.hstack([coo_array(A), coo_matrix(B)]), sparray) + assert isinstance(construct.hstack([coo_matrix(A), coo_array(B)]), sparray) + assert isinstance(construct.hstack([coo_matrix(A), coo_matrix(B)]), spmatrix) + + @pytest.mark.parametrize("block_array", (construct.bmat, construct.block_array)) + def test_block_creation(self, block_array): + + A = coo_array([[1, 2], [3, 4]]) + B = coo_array([[5],[6]]) + C = coo_array([[7]]) + D = coo_array((0, 0)) + + expected = array([[1, 2, 5], + [3, 4, 6], + [0, 0, 7]]) + assert_equal(block_array([[A, B], [None, C]]).toarray(), expected) + E = csr_array((1, 2), dtype=np.int32) + assert_equal(block_array([[A.tocsr(), B.tocsr()], + [E, C.tocsr()]]).toarray(), + expected) + assert_equal(block_array([[A.tocsc(), B.tocsc()], + [E.tocsc(), C.tocsc()]]).toarray(), + expected) + + expected = array([[1, 2, 0], + [3, 4, 0], + [0, 0, 7]]) + assert_equal(block_array([[A, None], [None, C]]).toarray(), expected) + assert_equal(block_array([[A.tocsr(), E.T.tocsr()], + [E, C.tocsr()]]).toarray(), + expected) + assert_equal(block_array([[A.tocsc(), E.T.tocsc()], + [E.tocsc(), C.tocsc()]]).toarray(), + expected) + + Z = csr_array((1, 1), dtype=np.int32) + expected = array([[0, 5], + [0, 6], + [7, 0]]) + assert_equal(block_array([[None, B], [C, None]]).toarray(), expected) + assert_equal(block_array([[E.T.tocsr(), B.tocsr()], + [C.tocsr(), Z]]).toarray(), + expected) + assert_equal(block_array([[E.T.tocsc(), B.tocsc()], + [C.tocsc(), Z.tocsc()]]).toarray(), + expected) + + expected = np.empty((0, 0)) + assert_equal(block_array([[None, None]]).toarray(), expected) + assert_equal(block_array([[None, D], [D, None]]).toarray(), + expected) + + # test bug reported in gh-5976 + expected = array([[7]]) + assert_equal(block_array([[None, D], [C, None]]).toarray(), + expected) + + # test failure cases + with assert_raises(ValueError) as excinfo: + block_array([[A], [B]]) + excinfo.match(r'Got blocks\[1,0\]\.shape\[1\] == 1, expected 2') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsr()], [B.tocsr()]]) + excinfo.match(r'incompatible dimensions for axis 1') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsc()], [B.tocsc()]]) + excinfo.match(r'Mismatching dimensions along axis 1: ({1, 2}|{2, 1})') + + with assert_raises(ValueError) as excinfo: + block_array([[A, C]]) + excinfo.match(r'Got blocks\[0,1\]\.shape\[0\] == 1, expected 2') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsr(), C.tocsr()]]) + excinfo.match(r'Mismatching dimensions along axis 0: ({1, 2}|{2, 1})') + + with assert_raises(ValueError) as excinfo: + block_array([[A.tocsc(), C.tocsc()]]) + excinfo.match(r'incompatible dimensions for axis 0') + + def test_block_return_type(self): + block = construct.block_array + + # csr format ensures we hit _compressed_sparse_stack + # shape of F,G ensure we hit _stack_along_minor_axis + # list version ensure we hit the path with neither helper function + Fl, Gl = [[1, 2],[3, 4]], [[7], [5]] + Fm, Gm = csr_matrix(Fl), csr_matrix(Gl) + assert isinstance(block([[None, Fl], [Gl, None]], format="csr"), sparray) + assert isinstance(block([[None, Fm], [Gm, None]], format="csr"), sparray) + assert isinstance(block([[Fm, Gm]], format="csr"), sparray) + + def test_bmat_return_type(self): + """This can be removed after sparse matrix is removed""" + bmat = construct.bmat + # check return type. if any input _is_array output array, else matrix + Fl, Gl = [[1, 2],[3, 4]], [[7], [5]] + Fm, Gm = csr_matrix(Fl), csr_matrix(Gl) + Fa, Ga = csr_array(Fl), csr_array(Gl) + assert isinstance(bmat([[Fa, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Fm, Gm]], format="csr"), spmatrix) + assert isinstance(bmat([[None, Fa], [Ga, None]], format="csr"), sparray) + assert isinstance(bmat([[None, Fm], [Ga, None]], format="csr"), sparray) + assert isinstance(bmat([[None, Fm], [Gm, None]], format="csr"), spmatrix) + assert isinstance(bmat([[None, Fl], [Gl, None]], format="csr"), spmatrix) + + # type returned by _compressed_sparse_stack (all csr) + assert isinstance(bmat([[Ga, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Gm, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Ga, Gm]], format="csr"), sparray) + assert isinstance(bmat([[Gm, Gm]], format="csr"), spmatrix) + # shape is 2x2 so no _stack_along_minor_axis + assert isinstance(bmat([[Fa, Fm]], format="csr"), sparray) + assert isinstance(bmat([[Fm, Fm]], format="csr"), spmatrix) + + # type returned by _compressed_sparse_stack (all csc) + assert isinstance(bmat([[Gm.tocsc(), Ga.tocsc()]], format="csc"), sparray) + assert isinstance(bmat([[Gm.tocsc(), Gm.tocsc()]], format="csc"), spmatrix) + # shape is 2x2 so no _stack_along_minor_axis + assert isinstance(bmat([[Fa.tocsc(), Fm.tocsc()]], format="csr"), sparray) + assert isinstance(bmat([[Fm.tocsc(), Fm.tocsc()]], format="csr"), spmatrix) + + # type returned when mixed input + assert isinstance(bmat([[Gl, Ga]], format="csr"), sparray) + assert isinstance(bmat([[Gm.tocsc(), Ga]], format="csr"), sparray) + assert isinstance(bmat([[Gm.tocsc(), Gm]], format="csr"), spmatrix) + assert isinstance(bmat([[Gm, Gm]], format="csc"), spmatrix) + + @pytest.mark.slow + @pytest.mark.xfail_on_32bit("Can't create large array for test") + def test_concatenate_int32_overflow(self): + """ test for indptr overflow when concatenating matrices """ + check_free_memory(30000) + + n = 33000 + A = csr_array(np.ones((n, n), dtype=bool)) + B = A.copy() + C = construct._compressed_sparse_stack((A, B), axis=0, + return_spmatrix=False) + + assert_(np.all(np.equal(np.diff(C.indptr), n))) + assert_equal(C.indices.dtype, np.int64) + assert_equal(C.indptr.dtype, np.int64) + + def test_block_diag_basic(self): + """ basic test for block_diag """ + A = coo_array([[1,2],[3,4]]) + B = coo_array([[5],[6]]) + C = coo_array([[7]]) + + expected = array([[1, 2, 0, 0], + [3, 4, 0, 0], + [0, 0, 5, 0], + [0, 0, 6, 0], + [0, 0, 0, 7]]) + + assert_equal(construct.block_diag((A, B, C)).toarray(), expected) + + def test_block_diag_scalar_1d_args(self): + """ block_diag with scalar and 1d arguments """ + # one 1d matrix and a scalar + assert_array_equal(construct.block_diag([[2,3], 4]).toarray(), + [[2, 3, 0], [0, 0, 4]]) + # 1d sparse arrays + A = coo_array([1,0,3]) + B = coo_array([0,4]) + assert_array_equal(construct.block_diag([A, B]).toarray(), + [[1, 0, 3, 0, 0], [0, 0, 0, 0, 4]]) + + + def test_block_diag_1(self): + """ block_diag with one matrix """ + assert_equal(construct.block_diag([[1, 0]]).toarray(), + array([[1, 0]])) + assert_equal(construct.block_diag([[[1, 0]]]).toarray(), + array([[1, 0]])) + assert_equal(construct.block_diag([[[1], [0]]]).toarray(), + array([[1], [0]])) + # just on scalar + assert_equal(construct.block_diag([1]).toarray(), + array([[1]])) + + def test_block_diag_sparse_arrays(self): + """ block_diag with sparse arrays """ + + A = coo_array([[1, 2, 3]], shape=(1, 3)) + B = coo_array([[4, 5]], shape=(1, 2)) + assert_equal(construct.block_diag([A, B]).toarray(), + array([[1, 2, 3, 0, 0], [0, 0, 0, 4, 5]])) + + A = coo_array([[1], [2], [3]], shape=(3, 1)) + B = coo_array([[4], [5]], shape=(2, 1)) + assert_equal(construct.block_diag([A, B]).toarray(), + array([[1, 0], [2, 0], [3, 0], [0, 4], [0, 5]])) + + def test_block_diag_return_type(self): + A, B = coo_array([[1, 2, 3]]), coo_matrix([[2, 3, 4]]) + assert isinstance(construct.block_diag([A, A]), sparray) + assert isinstance(construct.block_diag([A, B]), sparray) + assert isinstance(construct.block_diag([B, A]), sparray) + assert isinstance(construct.block_diag([B, B]), spmatrix) + + def test_random_sampling(self): + # Simple sanity checks for sparse random sampling. + for f in sprand, _sprandn: + for t in [np.float32, np.float64, np.longdouble, + np.int32, np.int64, np.complex64, np.complex128]: + x = f(5, 10, density=0.1, dtype=t) + assert_equal(x.dtype, t) + assert_equal(x.shape, (5, 10)) + assert_equal(x.nnz, 5) + + x1 = f(5, 10, density=0.1, random_state=4321) + assert_equal(x1.dtype, np.float64) + + x2 = f(5, 10, density=0.1, + random_state=np.random.RandomState(4321)) + + assert_array_equal(x1.data, x2.data) + assert_array_equal(x1.row, x2.row) + assert_array_equal(x1.col, x2.col) + + for density in [0.0, 0.1, 0.5, 1.0]: + x = f(5, 10, density=density) + assert_equal(x.nnz, int(density * np.prod(x.shape))) + + for fmt in ['coo', 'csc', 'csr', 'lil']: + x = f(5, 10, format=fmt) + assert_equal(x.format, fmt) + + assert_raises(ValueError, lambda: f(5, 10, 1.1)) + assert_raises(ValueError, lambda: f(5, 10, -0.1)) + + def test_rand(self): + # Simple distributional checks for sparse.rand. + random_states = [None, 4321, np.random.RandomState()] + try: + gen = np.random.default_rng() + random_states.append(gen) + except AttributeError: + pass + + for random_state in random_states: + x = sprand(10, 20, density=0.5, dtype=np.float64, + random_state=random_state) + assert_(np.all(np.less_equal(0, x.data))) + assert_(np.all(np.less_equal(x.data, 1))) + + def test_randn(self): + # Simple distributional checks for sparse.randn. + # Statistically, some of these should be negative + # and some should be greater than 1. + random_states = [None, 4321, np.random.RandomState()] + try: + gen = np.random.default_rng() + random_states.append(gen) + except AttributeError: + pass + + for rs in random_states: + x = _sprandn(10, 20, density=0.5, dtype=np.float64, random_state=rs) + assert_(np.any(np.less(x.data, 0))) + assert_(np.any(np.less(1, x.data))) + x = _sprandn_array(10, 20, density=0.5, dtype=np.float64, random_state=rs) + assert_(np.any(np.less(x.data, 0))) + assert_(np.any(np.less(1, x.data))) + + def test_random_accept_str_dtype(self): + # anything that np.dtype can convert to a dtype should be accepted + # for the dtype + construct.random(10, 10, dtype='d') + construct.random_array((10, 10), dtype='d') + + def test_random_sparse_matrix_returns_correct_number_of_non_zero_elements(self): + # A 10 x 10 matrix, with density of 12.65%, should have 13 nonzero elements. + # 10 x 10 x 0.1265 = 12.65, which should be rounded up to 13, not 12. + sparse_matrix = construct.random(10, 10, density=0.1265) + assert_equal(sparse_matrix.count_nonzero(),13) + # check random_array + sparse_array = construct.random_array((10, 10), density=0.1265) + assert_equal(sparse_array.count_nonzero(),13) + assert isinstance(sparse_array, sparray) + # check big size + shape = (2**33, 2**33) + sparse_array = construct.random_array(shape, density=2.7105e-17) + assert_equal(sparse_array.count_nonzero(),2000) + + +def test_diags_array(): + """Tests of diags_array that do not rely on diags wrapper.""" + diag = np.arange(1, 5) + + assert_array_equal(construct.diags_array(diag).toarray(), np.diag(diag)) + + assert_array_equal( + construct.diags_array(diag, offsets=2).toarray(), np.diag(diag, k=2) + ) + + assert_array_equal( + construct.diags_array(diag, offsets=2, shape=(4, 4)).toarray(), + np.diag(diag, k=2)[:4, :4] + ) + + # Offset outside bounds when shape specified + with pytest.raises(ValueError, match=".*out of bounds"): + construct.diags(np.arange(1, 5), 5, shape=(4, 4)) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_coo.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_coo.py new file mode 100644 index 0000000000000000000000000000000000000000..ec00e11229132c830a64e4b1477f55ab684eda10 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_coo.py @@ -0,0 +1,274 @@ +import numpy as np +from numpy.testing import assert_equal +import pytest +from scipy.sparse import coo_array + + +def test_shape_constructor(): + empty1d = coo_array((3,)) + assert empty1d.shape == (3,) + assert_equal(empty1d.toarray(), np.zeros((3,))) + + empty2d = coo_array((3, 2)) + assert empty2d.shape == (3, 2) + assert_equal(empty2d.toarray(), np.zeros((3, 2))) + + with pytest.raises(TypeError, match='invalid input format'): + coo_array((3, 2, 2)) + + +def test_dense_constructor(): + res1d = coo_array([1, 2, 3]) + assert res1d.shape == (3,) + assert_equal(res1d.toarray(), np.array([1, 2, 3])) + + res2d = coo_array([[1, 2, 3], [4, 5, 6]]) + assert res2d.shape == (2, 3) + assert_equal(res2d.toarray(), np.array([[1, 2, 3], [4, 5, 6]])) + + with pytest.raises(ValueError, match='shape must be a 1- or 2-tuple'): + coo_array([[[3]], [[4]]]) + + +def test_dense_constructor_with_shape(): + res1d = coo_array([1, 2, 3], shape=(3,)) + assert res1d.shape == (3,) + assert_equal(res1d.toarray(), np.array([1, 2, 3])) + + res2d = coo_array([[1, 2, 3], [4, 5, 6]], shape=(2, 3)) + assert res2d.shape == (2, 3) + assert_equal(res2d.toarray(), np.array([[1, 2, 3], [4, 5, 6]])) + + with pytest.raises(ValueError, match='shape must be a 1- or 2-tuple'): + coo_array([[[3]], [[4]]], shape=(2, 1, 1)) + + +def test_dense_constructor_with_inconsistent_shape(): + with pytest.raises(ValueError, match='inconsistent shapes'): + coo_array([1, 2, 3], shape=(4,)) + + with pytest.raises(ValueError, match='inconsistent shapes'): + coo_array([1, 2, 3], shape=(3, 1)) + + with pytest.raises(ValueError, match='inconsistent shapes'): + coo_array([[1, 2, 3]], shape=(3,)) + + with pytest.raises(ValueError, + match='axis 0 index 2 exceeds matrix dimension 2'): + coo_array(([1], ([2],)), shape=(2,)) + + with pytest.raises(ValueError, match='negative axis 0 index: -1'): + coo_array(([1], ([-1],))) + + +def test_1d_sparse_constructor(): + empty1d = coo_array((3,)) + res = coo_array(empty1d) + assert res.shape == (3,) + assert_equal(res.toarray(), np.zeros((3,))) + + +def test_1d_tuple_constructor(): + res = coo_array(([9,8], ([1,2],))) + assert res.shape == (3,) + assert_equal(res.toarray(), np.array([0, 9, 8])) + + +def test_1d_tuple_constructor_with_shape(): + res = coo_array(([9,8], ([1,2],)), shape=(4,)) + assert res.shape == (4,) + assert_equal(res.toarray(), np.array([0, 9, 8, 0])) + +def test_non_subscriptability(): + coo_2d = coo_array((2, 2)) + + with pytest.raises(TypeError, + match="'coo_array' object does not support item assignment"): + coo_2d[0, 0] = 1 + + with pytest.raises(TypeError, + match="'coo_array' object is not subscriptable"): + coo_2d[0, :] + +def test_reshape(): + arr1d = coo_array([1, 0, 3]) + assert arr1d.shape == (3,) + + col_vec = arr1d.reshape((3, 1)) + assert col_vec.shape == (3, 1) + assert_equal(col_vec.toarray(), np.array([[1], [0], [3]])) + + row_vec = arr1d.reshape((1, 3)) + assert row_vec.shape == (1, 3) + assert_equal(row_vec.toarray(), np.array([[1, 0, 3]])) + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]) + assert arr2d.shape == (2, 3) + + flat = arr2d.reshape((6,)) + assert flat.shape == (6,) + assert_equal(flat.toarray(), np.array([1, 2, 0, 0, 0, 3])) + + +def test_nnz(): + arr1d = coo_array([1, 0, 3]) + assert arr1d.shape == (3,) + assert arr1d.nnz == 2 + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]) + assert arr2d.shape == (2, 3) + assert arr2d.nnz == 3 + + +def test_transpose(): + arr1d = coo_array([1, 0, 3]).T + assert arr1d.shape == (3,) + assert_equal(arr1d.toarray(), np.array([1, 0, 3])) + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]).T + assert arr2d.shape == (3, 2) + assert_equal(arr2d.toarray(), np.array([[1, 0], [2, 0], [0, 3]])) + + +def test_transpose_with_axis(): + arr1d = coo_array([1, 0, 3]).transpose(axes=(0,)) + assert arr1d.shape == (3,) + assert_equal(arr1d.toarray(), np.array([1, 0, 3])) + + arr2d = coo_array([[1, 2, 0], [0, 0, 3]]).transpose(axes=(0, 1)) + assert arr2d.shape == (2, 3) + assert_equal(arr2d.toarray(), np.array([[1, 2, 0], [0, 0, 3]])) + + with pytest.raises(ValueError, match="axes don't match matrix dimensions"): + coo_array([1, 0, 3]).transpose(axes=(0, 1)) + + with pytest.raises(ValueError, match="repeated axis in transpose"): + coo_array([[1, 2, 0], [0, 0, 3]]).transpose(axes=(1, 1)) + + +def test_1d_row_and_col(): + res = coo_array([1, -2, -3]) + assert_equal(res.col, np.array([0, 1, 2])) + assert_equal(res.row, np.zeros_like(res.col)) + assert res.row.dtype == res.col.dtype + assert res.row.flags.writeable is False + + res.col = [1, 2, 3] + assert len(res.coords) == 1 + assert_equal(res.col, np.array([1, 2, 3])) + assert res.row.dtype == res.col.dtype + + with pytest.raises(ValueError, match="cannot set row attribute"): + res.row = [1, 2, 3] + + +def test_1d_toformats(): + res = coo_array([1, -2, -3]) + for f in [res.tobsr, res.tocsc, res.todia, res.tolil]: + with pytest.raises(ValueError, match='Cannot convert'): + f() + for f in [res.tocoo, res.tocsr, res.todok]: + assert_equal(f().toarray(), res.toarray()) + + +@pytest.mark.parametrize('arg', [1, 2, 4, 5, 8]) +def test_1d_resize(arg: int): + den = np.array([1, -2, -3]) + res = coo_array(den) + den.resize(arg, refcheck=False) + res.resize(arg) + assert res.shape == den.shape + assert_equal(res.toarray(), den) + + +@pytest.mark.parametrize('arg', zip([1, 2, 3, 4], [1, 2, 3, 4])) +def test_1d_to_2d_resize(arg: tuple[int, int]): + den = np.array([1, 0, 3]) + res = coo_array(den) + + den.resize(arg, refcheck=False) + res.resize(arg) + assert res.shape == den.shape + assert_equal(res.toarray(), den) + + +@pytest.mark.parametrize('arg', [1, 4, 6, 8]) +def test_2d_to_1d_resize(arg: int): + den = np.array([[1, 0, 3], [4, 0, 0]]) + res = coo_array(den) + den.resize(arg, refcheck=False) + res.resize(arg) + assert res.shape == den.shape + assert_equal(res.toarray(), den) + + +def test_sum_duplicates(): + arr1d = coo_array(([2, 2, 2], ([1, 0, 1],))) + assert arr1d.nnz == 3 + assert_equal(arr1d.toarray(), np.array([2, 4])) + arr1d.sum_duplicates() + assert arr1d.nnz == 2 + assert_equal(arr1d.toarray(), np.array([2, 4])) + + +def test_eliminate_zeros(): + arr1d = coo_array(([0, 0, 1], ([1, 0, 1],))) + assert arr1d.nnz == 3 + assert arr1d.count_nonzero() == 1 + assert_equal(arr1d.toarray(), np.array([0, 1])) + arr1d.eliminate_zeros() + assert arr1d.nnz == 1 + assert arr1d.count_nonzero() == 1 + assert_equal(arr1d.toarray(), np.array([0, 1])) + assert_equal(arr1d.col, np.array([1])) + assert_equal(arr1d.row, np.array([0])) + + +def test_1d_add_dense(): + den_a = np.array([0, -2, -3, 0]) + den_b = np.array([0, 1, 2, 3]) + exp = den_a + den_b + res = coo_array(den_a) + den_b + assert type(res) == type(exp) + assert_equal(res, exp) + + +def test_1d_add_sparse(): + den_a = np.array([0, -2, -3, 0]) + den_b = np.array([0, 1, 2, 3]) + dense_sum = den_a + den_b + # this routes through CSR format + sparse_sum = coo_array(den_a) + coo_array(den_b) + assert_equal(dense_sum, sparse_sum.toarray()) + + +def test_1d_matmul_vector(): + den_a = np.array([0, -2, -3, 0]) + den_b = np.array([0, 1, 2, 3]) + exp = den_a @ den_b + res = coo_array(den_a) @ den_b + assert np.ndim(res) == 0 + assert_equal(res, exp) + + +def test_1d_matmul_multivector(): + den = np.array([0, -2, -3, 0]) + other = np.array([[0, 1, 2, 3], [3, 2, 1, 0]]).T + exp = den @ other + res = coo_array(den) @ other + assert type(res) == type(exp) + assert_equal(res, exp) + + +def test_2d_matmul_multivector(): + den = np.array([[0, 1, 2, 3], [3, 2, 1, 0]]) + arr2d = coo_array(den) + exp = den @ den.T + res = arr2d @ arr2d.T + assert_equal(res.toarray(), exp) + + +def test_1d_diagonal(): + den = np.array([0, -2, -3, 0]) + with pytest.raises(ValueError, match='diagonal requires two dimensions'): + coo_array(den).diagonal() diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_csc.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_csc.py new file mode 100644 index 0000000000000000000000000000000000000000..6313751e41899ae7c5daf01fbdbbacdc1f303fa1 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_csc.py @@ -0,0 +1,98 @@ +import numpy as np +from numpy.testing import assert_array_almost_equal, assert_ +from scipy.sparse import csr_matrix, csc_matrix, lil_matrix + +import pytest + + +def test_csc_getrow(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsc = csc_matrix(X) + + for i in range(N): + arr_row = X[i:i + 1, :] + csc_row = Xcsc.getrow(i) + + assert_array_almost_equal(arr_row, csc_row.toarray()) + assert_(type(csc_row) is csr_matrix) + + +def test_csc_getcol(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsc = csc_matrix(X) + + for i in range(N): + arr_col = X[:, i:i + 1] + csc_col = Xcsc.getcol(i) + + assert_array_almost_equal(arr_col, csc_col.toarray()) + assert_(type(csc_col) is csc_matrix) + +@pytest.mark.parametrize("matrix_input, axis, expected_shape", + [(csc_matrix([[1, 0], + [0, 0], + [0, 2]]), + 0, (0, 2)), + (csc_matrix([[1, 0], + [0, 0], + [0, 2]]), + 1, (3, 0)), + (csc_matrix([[1, 0], + [0, 0], + [0, 2]]), + 'both', (0, 0)), + (csc_matrix([[0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 2, 3, 0, 1]]), + 0, (0, 6))]) +def test_csc_empty_slices(matrix_input, axis, expected_shape): + # see gh-11127 for related discussion + slice_1 = matrix_input.toarray().shape[0] - 1 + slice_2 = slice_1 + slice_3 = slice_2 - 1 + + if axis == 0: + actual_shape_1 = matrix_input[slice_1:slice_2, :].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, :].toarray().shape + elif axis == 1: + actual_shape_1 = matrix_input[:, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[:, slice_1:slice_3].toarray().shape + elif axis == 'both': + actual_shape_1 = matrix_input[slice_1:slice_2, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, slice_1:slice_3].toarray().shape + + assert actual_shape_1 == expected_shape + assert actual_shape_1 == actual_shape_2 + + +@pytest.mark.parametrize('ax', (-2, -1, 0, 1, None)) +def test_argmax_overflow(ax): + # See gh-13646: Windows integer overflow for large sparse matrices. + dim = (100000, 100000) + A = lil_matrix(dim) + A[-2, -2] = 42 + A[-3, -3] = 0.1234 + A = csc_matrix(A) + idx = A.argmax(axis=ax) + + if ax is None: + # idx is a single flattened index + # that we need to convert to a 2d index pair; + # can't do this with np.unravel_index because + # the dimensions are too large + ii = idx % dim[0] + jj = idx // dim[0] + else: + # idx is an array of size of A.shape[ax]; + # check the max index to make sure no overflows + # we encountered + assert np.count_nonzero(idx) == A.nnz + ii, jj = np.max(idx), np.argmax(idx) + + assert A[ii, jj] == A[-2, -2] diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_csr.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_csr.py new file mode 100644 index 0000000000000000000000000000000000000000..94b64a77869458d3da7ad9f3dd993c809119ccd6 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_csr.py @@ -0,0 +1,190 @@ +import numpy as np +from numpy.testing import assert_array_almost_equal, assert_ +from scipy.sparse import csr_matrix, csc_matrix, csr_array, csc_array, hstack +from scipy import sparse +import pytest + + +def _check_csr_rowslice(i, sl, X, Xcsr): + np_slice = X[i, sl] + csr_slice = Xcsr[i, sl] + assert_array_almost_equal(np_slice, csr_slice.toarray()[0]) + assert_(type(csr_slice) is csr_matrix) + + +def test_csr_rowslice(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsr = csr_matrix(X) + + slices = [slice(None, None, None), + slice(None, None, -1), + slice(1, -2, 2), + slice(-2, 1, -2)] + + for i in range(N): + for sl in slices: + _check_csr_rowslice(i, sl, X, Xcsr) + + +def test_csr_getrow(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsr = csr_matrix(X) + + for i in range(N): + arr_row = X[i:i + 1, :] + csr_row = Xcsr.getrow(i) + + assert_array_almost_equal(arr_row, csr_row.toarray()) + assert_(type(csr_row) is csr_matrix) + + +def test_csr_getcol(): + N = 10 + np.random.seed(0) + X = np.random.random((N, N)) + X[X > 0.7] = 0 + Xcsr = csr_matrix(X) + + for i in range(N): + arr_col = X[:, i:i + 1] + csr_col = Xcsr.getcol(i) + + assert_array_almost_equal(arr_col, csr_col.toarray()) + assert_(type(csr_col) is csr_matrix) + +@pytest.mark.parametrize("matrix_input, axis, expected_shape", + [(csr_matrix([[1, 0, 0, 0], + [0, 0, 0, 0], + [0, 2, 3, 0]]), + 0, (0, 4)), + (csr_matrix([[1, 0, 0, 0], + [0, 0, 0, 0], + [0, 2, 3, 0]]), + 1, (3, 0)), + (csr_matrix([[1, 0, 0, 0], + [0, 0, 0, 0], + [0, 2, 3, 0]]), + 'both', (0, 0)), + (csr_matrix([[0, 1, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, 0, 2, 3, 0]]), + 0, (0, 5))]) +def test_csr_empty_slices(matrix_input, axis, expected_shape): + # see gh-11127 for related discussion + slice_1 = matrix_input.toarray().shape[0] - 1 + slice_2 = slice_1 + slice_3 = slice_2 - 1 + + if axis == 0: + actual_shape_1 = matrix_input[slice_1:slice_2, :].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, :].toarray().shape + elif axis == 1: + actual_shape_1 = matrix_input[:, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[:, slice_1:slice_3].toarray().shape + elif axis == 'both': + actual_shape_1 = matrix_input[slice_1:slice_2, slice_1:slice_2].toarray().shape + actual_shape_2 = matrix_input[slice_1:slice_3, slice_1:slice_3].toarray().shape + + assert actual_shape_1 == expected_shape + assert actual_shape_1 == actual_shape_2 + + +def test_csr_bool_indexing(): + data = csr_matrix([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) + list_indices1 = [False, True, False] + array_indices1 = np.array(list_indices1) + list_indices2 = [[False, True, False], [False, True, False], [False, True, False]] + array_indices2 = np.array(list_indices2) + list_indices3 = ([False, True, False], [False, True, False]) + array_indices3 = (np.array(list_indices3[0]), np.array(list_indices3[1])) + slice_list1 = data[list_indices1].toarray() + slice_array1 = data[array_indices1].toarray() + slice_list2 = data[list_indices2] + slice_array2 = data[array_indices2] + slice_list3 = data[list_indices3] + slice_array3 = data[array_indices3] + assert (slice_list1 == slice_array1).all() + assert (slice_list2 == slice_array2).all() + assert (slice_list3 == slice_array3).all() + + +def test_csr_hstack_int64(): + """ + Tests if hstack properly promotes to indices and indptr arrays to np.int64 + when using np.int32 during concatenation would result in either array + overflowing. + """ + max_int32 = np.iinfo(np.int32).max + + # First case: indices would overflow with int32 + data = [1.0] + row = [0] + + max_indices_1 = max_int32 - 1 + max_indices_2 = 3 + + # Individual indices arrays are representable with int32 + col_1 = [max_indices_1 - 1] + col_2 = [max_indices_2 - 1] + + X_1 = csr_matrix((data, (row, col_1))) + X_2 = csr_matrix((data, (row, col_2))) + + assert max(max_indices_1 - 1, max_indices_2 - 1) < max_int32 + assert X_1.indices.dtype == X_1.indptr.dtype == np.int32 + assert X_2.indices.dtype == X_2.indptr.dtype == np.int32 + + # ... but when concatenating their CSR matrices, the resulting indices + # array can't be represented with int32 and must be promoted to int64. + X_hs = hstack([X_1, X_2], format="csr") + + assert X_hs.indices.max() == max_indices_1 + max_indices_2 - 1 + assert max_indices_1 + max_indices_2 - 1 > max_int32 + assert X_hs.indices.dtype == X_hs.indptr.dtype == np.int64 + + # Even if the matrices are empty, we must account for their size + # contribution so that we may safely set the final elements. + X_1_empty = csr_matrix(X_1.shape) + X_2_empty = csr_matrix(X_2.shape) + X_hs_empty = hstack([X_1_empty, X_2_empty], format="csr") + + assert X_hs_empty.shape == X_hs.shape + assert X_hs_empty.indices.dtype == np.int64 + + # Should be just small enough to stay in int32 after stack. Note that + # we theoretically could support indices.max() == max_int32, but due to an + # edge-case in the underlying sparsetools code + # (namely the `coo_tocsr` routine), + # we require that max(X_hs_32.shape) < max_int32 as well. + # Hence we can only support max_int32 - 1. + col_3 = [max_int32 - max_indices_1 - 1] + X_3 = csr_matrix((data, (row, col_3))) + X_hs_32 = hstack([X_1, X_3], format="csr") + assert X_hs_32.indices.dtype == np.int32 + assert X_hs_32.indices.max() == max_int32 - 1 + +@pytest.mark.parametrize("cls", [csr_matrix, csr_array, csc_matrix, csc_array]) +def test_mixed_index_dtype_int_indexing(cls): + # https://github.com/scipy/scipy/issues/20182 + rng = np.random.default_rng(0) + base_mtx = cls(sparse.random(50, 50, random_state=rng, density=0.1)) + indptr_64bit = base_mtx.copy() + indices_64bit = base_mtx.copy() + indptr_64bit.indptr = base_mtx.indptr.astype(np.int64) + indices_64bit.indices = base_mtx.indices.astype(np.int64) + + for mtx in [base_mtx, indptr_64bit, indices_64bit]: + np.testing.assert_array_equal( + mtx[[1,2], :].toarray(), + base_mtx[[1, 2], :].toarray() + ) + np.testing.assert_array_equal( + mtx[:, [1, 2]].toarray(), + base_mtx[:, [1, 2]].toarray() + ) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_dok.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_dok.py new file mode 100644 index 0000000000000000000000000000000000000000..8823ce8a2dbcf2ff3609f4e5df266458226744c2 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_dok.py @@ -0,0 +1,210 @@ +import pytest +import numpy as np +from numpy.testing import assert_equal +import scipy as sp +from scipy.sparse import dok_array, dok_matrix + + +@pytest.fixture +def d(): + return {(0, 1): 1, (0, 2): 2} + +@pytest.fixture +def A(): + return np.array([[0, 1, 2], [0, 0, 0], [0, 0, 0]]) + +@pytest.fixture(params=[dok_array, dok_matrix]) +def Asp(request): + A = request.param((3, 3)) + A[(0, 1)] = 1 + A[(0, 2)] = 2 + yield A + +# Note: __iter__ and comparison dunders act like ndarrays for DOK, not dict. +# Dunders reversed, or, ror, ior work as dict for dok_matrix, raise for dok_array +# All other dict methods on DOK format act like dict methods (with extra checks). + +# Start of tests +################ +def test_dict_methods_covered(d, Asp): + d_methods = set(dir(d)) - {"__class_getitem__"} + asp_methods = set(dir(Asp)) + assert d_methods < asp_methods + +def test_clear(d, Asp): + assert d.items() == Asp.items() + d.clear() + Asp.clear() + assert d.items() == Asp.items() + +def test_copy(d, Asp): + assert d.items() == Asp.items() + dd = d.copy() + asp = Asp.copy() + assert dd.items() == asp.items() + assert asp.items() == Asp.items() + asp[(0, 1)] = 3 + assert Asp[(0, 1)] == 1 + +def test_fromkeys_default(): + # test with default value + edges = [(0, 2), (1, 0), (2, 1)] + Xdok = dok_array.fromkeys(edges) + X = [[0, 0, 1], [1, 0, 0], [0, 1, 0]] + assert_equal(Xdok.toarray(), X) + +def test_fromkeys_positional(): + # test with positional value + edges = [(0, 2), (1, 0), (2, 1)] + Xdok = dok_array.fromkeys(edges, -1) + X = [[0, 0, -1], [-1, 0, 0], [0, -1, 0]] + assert_equal(Xdok.toarray(), X) + +def test_fromkeys_iterator(): + it = ((a, a % 2) for a in range(4)) + Xdok = dok_array.fromkeys(it) + X = [[1, 0], [0, 1], [1, 0], [0, 1]] + assert_equal(Xdok.toarray(), X) + +def test_get(d, Asp): + assert Asp.get((0, 1)) == d.get((0, 1)) + assert Asp.get((0, 0), 99) == d.get((0, 0), 99) + with pytest.raises(IndexError, match="out of bounds"): + Asp.get((0, 4), 99) + +def test_items(d, Asp): + assert Asp.items() == d.items() + +def test_keys(d, Asp): + assert Asp.keys() == d.keys() + +def test_pop(d, Asp): + assert d.pop((0, 1)) == 1 + assert Asp.pop((0, 1)) == 1 + assert d.items() == Asp.items() + + assert Asp.pop((22, 21), None) is None + assert Asp.pop((22, 21), "other") == "other" + with pytest.raises(KeyError, match="(22, 21)"): + Asp.pop((22, 21)) + with pytest.raises(TypeError, match="got an unexpected keyword argument"): + Asp.pop((22, 21), default=5) + +def test_popitem(d, Asp): + assert d.popitem() == Asp.popitem() + assert d.items() == Asp.items() + +def test_setdefault(d, Asp): + assert Asp.setdefault((0, 1), 4) == 1 + assert Asp.setdefault((2, 2), 4) == 4 + d.setdefault((0, 1), 4) + d.setdefault((2, 2), 4) + assert d.items() == Asp.items() + +def test_update(d, Asp): + with pytest.raises(NotImplementedError): + Asp.update(Asp) + +def test_values(d, Asp): + # Note: dict.values are strange: d={1: 1}; d.values() == d.values() is False + # Using list(d.values()) makes them comparable. + assert list(Asp.values()) == list(d.values()) + +def test_dunder_getitem(d, Asp): + assert Asp[(0, 1)] == d[(0, 1)] + +def test_dunder_setitem(d, Asp): + Asp[(1, 1)] = 5 + d[(1, 1)] = 5 + assert d.items() == Asp.items() + +def test_dunder_delitem(d, Asp): + del Asp[(0, 1)] + del d[(0, 1)] + assert d.items() == Asp.items() + +def test_dunder_contains(d, Asp): + assert ((0, 1) in d) == ((0, 1) in Asp) + assert ((0, 0) in d) == ((0, 0) in Asp) + +def test_dunder_len(d, Asp): + assert len(d) == len(Asp) + +# Note: dunders reversed, or, ror, ior work as dict for dok_matrix, raise for dok_array +def test_dunder_reversed(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + list(reversed(Asp)) + else: + list(reversed(Asp)) == list(reversed(d)) + +def test_dunder_ior(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + Asp |= Asp + else: + dd = {(0, 0): 5} + Asp |= dd + assert Asp[(0, 0)] == 5 + d |= dd + assert d.items() == Asp.items() + dd |= Asp + assert dd.items() == Asp.items() + +def test_dunder_or(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + Asp | Asp + else: + assert d | d == Asp | d + assert d | d == Asp | Asp + +def test_dunder_ror(d, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(TypeError): + Asp | Asp + with pytest.raises(TypeError): + d | Asp + else: + assert Asp.__ror__(d) == Asp.__ror__(Asp) + assert d.__ror__(d) == Asp.__ror__(d) + assert d | Asp + +# Note: comparison dunders, e.g. ==, >=, etc follow np.array not dict +def test_dunder_eq(A, Asp): + with np.testing.suppress_warnings() as sup: + sup.filter(sp.sparse.SparseEfficiencyWarning) + assert (Asp == Asp).toarray().all() + assert (A == Asp).all() + +def test_dunder_ne(A, Asp): + assert not (Asp != Asp).toarray().any() + assert not (A != Asp).any() + +def test_dunder_lt(A, Asp): + assert not (Asp < Asp).toarray().any() + assert not (A < Asp).any() + +def test_dunder_gt(A, Asp): + assert not (Asp > Asp).toarray().any() + assert not (A > Asp).any() + +def test_dunder_le(A, Asp): + with np.testing.suppress_warnings() as sup: + sup.filter(sp.sparse.SparseEfficiencyWarning) + assert (Asp <= Asp).toarray().all() + assert (A <= Asp).all() + +def test_dunder_ge(A, Asp): + with np.testing.suppress_warnings() as sup: + sup.filter(sp.sparse.SparseEfficiencyWarning) + assert (Asp >= Asp).toarray().all() + assert (A >= Asp).all() + +# Note: iter dunder follows np.array not dict +def test_dunder_iter(A, Asp): + if isinstance(Asp, dok_array): + with pytest.raises(NotImplementedError): + [a.toarray() for a in Asp] + else: + assert all((a == asp).all() for a, asp in zip(A, Asp)) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_extract.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_extract.py new file mode 100644 index 0000000000000000000000000000000000000000..a7c9f68bb2bde76d74ca767abba3c99b89d6e771 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_extract.py @@ -0,0 +1,51 @@ +"""test sparse matrix construction functions""" + +from numpy.testing import assert_equal +from scipy.sparse import csr_matrix, csr_array, sparray + +import numpy as np +from scipy.sparse import _extract + + +class TestExtract: + def setup_method(self): + self.cases = [ + csr_array([[1,2]]), + csr_array([[1,0]]), + csr_array([[0,0]]), + csr_array([[1],[2]]), + csr_array([[1],[0]]), + csr_array([[0],[0]]), + csr_array([[1,2],[3,4]]), + csr_array([[0,1],[0,0]]), + csr_array([[0,0],[1,0]]), + csr_array([[0,0],[0,0]]), + csr_array([[1,2,0,0,3],[4,5,0,6,7],[0,0,8,9,0]]), + csr_array([[1,2,0,0,3],[4,5,0,6,7],[0,0,8,9,0]]).T, + ] + + def test_find(self): + for A in self.cases: + I,J,V = _extract.find(A) + B = csr_array((V,(I,J)), shape=A.shape) + assert_equal(A.toarray(), B.toarray()) + + def test_tril(self): + for A in self.cases: + B = A.toarray() + for k in [-3,-2,-1,0,1,2,3]: + assert_equal(_extract.tril(A,k=k).toarray(), np.tril(B,k=k)) + + def test_triu(self): + for A in self.cases: + B = A.toarray() + for k in [-3,-2,-1,0,1,2,3]: + assert_equal(_extract.triu(A,k=k).toarray(), np.triu(B,k=k)) + + def test_array_vs_matrix(self): + for A in self.cases: + assert isinstance(_extract.tril(A), sparray) + assert isinstance(_extract.triu(A), sparray) + M = csr_matrix(A) + assert not isinstance(_extract.tril(M), sparray) + assert not isinstance(_extract.triu(M), sparray) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_matrix_io.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_matrix_io.py new file mode 100644 index 0000000000000000000000000000000000000000..90b4ea64a8928073eb5dd3f1b2752379f57327d9 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_matrix_io.py @@ -0,0 +1,109 @@ +import os +import numpy as np +import tempfile + +from pytest import raises as assert_raises +from numpy.testing import assert_equal, assert_ + +from scipy.sparse import (sparray, csc_matrix, csr_matrix, bsr_matrix, dia_matrix, + coo_matrix, dok_matrix, csr_array, save_npz, load_npz) + + +DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') + + +def _save_and_load(matrix): + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + save_npz(tmpfile, matrix) + loaded_matrix = load_npz(tmpfile) + finally: + os.remove(tmpfile) + return loaded_matrix + +def _check_save_and_load(dense_matrix): + for matrix_class in [csc_matrix, csr_matrix, bsr_matrix, dia_matrix, coo_matrix]: + matrix = matrix_class(dense_matrix) + loaded_matrix = _save_and_load(matrix) + assert_(type(loaded_matrix) is matrix_class) + assert_(loaded_matrix.shape == dense_matrix.shape) + assert_(loaded_matrix.dtype == dense_matrix.dtype) + assert_equal(loaded_matrix.toarray(), dense_matrix) + +def test_save_and_load_random(): + N = 10 + np.random.seed(0) + dense_matrix = np.random.random((N, N)) + dense_matrix[dense_matrix > 0.7] = 0 + _check_save_and_load(dense_matrix) + +def test_save_and_load_empty(): + dense_matrix = np.zeros((4,6)) + _check_save_and_load(dense_matrix) + +def test_save_and_load_one_entry(): + dense_matrix = np.zeros((4,6)) + dense_matrix[1,2] = 1 + _check_save_and_load(dense_matrix) + +def test_sparray_vs_spmatrix(): + #save/load matrix + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + save_npz(tmpfile, csr_matrix([[1.2, 0, 0.9], [0, 0.3, 0]])) + loaded_matrix = load_npz(tmpfile) + finally: + os.remove(tmpfile) + + #save/load array + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + save_npz(tmpfile, csr_array([[1.2, 0, 0.9], [0, 0.3, 0]])) + loaded_array = load_npz(tmpfile) + finally: + os.remove(tmpfile) + + assert not isinstance(loaded_matrix, sparray) + assert isinstance(loaded_array, sparray) + assert_(loaded_matrix.dtype == loaded_array.dtype) + assert_equal(loaded_matrix.toarray(), loaded_array.toarray()) + +def test_malicious_load(): + class Executor: + def __reduce__(self): + return (assert_, (False, 'unexpected code execution')) + + fd, tmpfile = tempfile.mkstemp(suffix='.npz') + os.close(fd) + try: + np.savez(tmpfile, format=Executor()) + + # Should raise a ValueError, not execute code + assert_raises(ValueError, load_npz, tmpfile) + finally: + os.remove(tmpfile) + + +def test_py23_compatibility(): + # Try loading files saved on Python 2 and Python 3. They are not + # the same, since files saved with SciPy versions < 1.0.0 may + # contain unicode. + + a = load_npz(os.path.join(DATA_DIR, 'csc_py2.npz')) + b = load_npz(os.path.join(DATA_DIR, 'csc_py3.npz')) + c = csc_matrix([[0]]) + + assert_equal(a.toarray(), c.toarray()) + assert_equal(b.toarray(), c.toarray()) + +def test_implemented_error(): + # Attempts to save an unsupported type and checks that an + # NotImplementedError is raised. + + x = dok_matrix((2,3)) + x[0,1] = 1 + + assert_raises(NotImplementedError, save_npz, 'x.npz', x) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_minmax1d.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_minmax1d.py new file mode 100644 index 0000000000000000000000000000000000000000..dca3f44fa485070805995c2f76c0c511123ce355 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_minmax1d.py @@ -0,0 +1,128 @@ +"""Test of min-max 1D features of sparse array classes""" + +import pytest + +import numpy as np + +from numpy.testing import assert_equal, assert_array_equal + +from scipy.sparse import coo_array, csr_array, csc_array, bsr_array +from scipy.sparse import coo_matrix, csr_matrix, csc_matrix, bsr_matrix +from scipy.sparse._sputils import isscalarlike + + +def toarray(a): + if isinstance(a, np.ndarray) or isscalarlike(a): + return a + return a.toarray() + + +formats_for_minmax = [bsr_array, coo_array, csc_array, csr_array] +formats_for_minmax_supporting_1d = [coo_array, csr_array] + + +@pytest.mark.parametrize("spcreator", formats_for_minmax_supporting_1d) +class Test_MinMaxMixin1D: + def test_minmax(self, spcreator): + D = np.arange(5) + X = spcreator(D) + + assert_equal(X.min(), 0) + assert_equal(X.max(), 4) + assert_equal((-X).min(), -4) + assert_equal((-X).max(), 0) + + def test_minmax_axis(self, spcreator): + D = np.arange(50) + X = spcreator(D) + + for axis in [0, -1]: + assert_array_equal( + toarray(X.max(axis=axis)), D.max(axis=axis, keepdims=True) + ) + assert_array_equal( + toarray(X.min(axis=axis)), D.min(axis=axis, keepdims=True) + ) + for axis in [-2, 1]: + with pytest.raises(ValueError, match="axis out of range"): + X.min(axis=axis) + with pytest.raises(ValueError, match="axis out of range"): + X.max(axis=axis) + + def test_numpy_minmax(self, spcreator): + dat = np.array([0, 1, 2]) + datsp = spcreator(dat) + assert_array_equal(np.min(datsp), np.min(dat)) + assert_array_equal(np.max(datsp), np.max(dat)) + + + def test_argmax(self, spcreator): + D1 = np.array([-1, 5, 2, 3]) + D2 = np.array([0, 0, -1, -2]) + D3 = np.array([-1, -2, -3, -4]) + D4 = np.array([1, 2, 3, 4]) + D5 = np.array([1, 2, 0, 0]) + + for D in [D1, D2, D3, D4, D5]: + mat = spcreator(D) + + assert_equal(mat.argmax(), np.argmax(D)) + assert_equal(mat.argmin(), np.argmin(D)) + + assert_equal(mat.argmax(axis=0), np.argmax(D, axis=0)) + assert_equal(mat.argmin(axis=0), np.argmin(D, axis=0)) + + D6 = np.empty((0,)) + + for axis in [None, 0]: + mat = spcreator(D6) + with pytest.raises(ValueError, match="to an empty matrix"): + mat.argmin(axis=axis) + with pytest.raises(ValueError, match="to an empty matrix"): + mat.argmax(axis=axis) + + +@pytest.mark.parametrize("spcreator", formats_for_minmax) +class Test_ShapeMinMax2DWithAxis: + def test_minmax(self, spcreator): + dat = np.array([[-1, 5, 0, 3], [0, 0, -1, -2], [0, 0, 1, 2]]) + datsp = spcreator(dat) + + for (spminmax, npminmax) in [ + (datsp.min, np.min), + (datsp.max, np.max), + (datsp.nanmin, np.nanmin), + (datsp.nanmax, np.nanmax), + ]: + for ax, result_shape in [(0, (4,)), (1, (3,))]: + assert_equal(toarray(spminmax(axis=ax)), npminmax(dat, axis=ax)) + assert_equal(spminmax(axis=ax).shape, result_shape) + assert spminmax(axis=ax).format == "coo" + + for spminmax in [datsp.argmin, datsp.argmax]: + for ax in [0, 1]: + assert isinstance(spminmax(axis=ax), np.ndarray) + + # verify spmatrix behavior + spmat_form = { + 'coo': coo_matrix, + 'csr': csr_matrix, + 'csc': csc_matrix, + 'bsr': bsr_matrix, + } + datspm = spmat_form[datsp.format](dat) + + for spm, npm in [ + (datspm.min, np.min), + (datspm.max, np.max), + (datspm.nanmin, np.nanmin), + (datspm.nanmax, np.nanmax), + ]: + for ax, result_shape in [(0, (1, 4)), (1, (3, 1))]: + assert_equal(toarray(spm(axis=ax)), npm(dat, axis=ax, keepdims=True)) + assert_equal(spm(axis=ax).shape, result_shape) + assert spm(axis=ax).format == "coo" + + for spminmax in [datspm.argmin, datspm.argmax]: + for ax in [0, 1]: + assert isinstance(spminmax(axis=ax), np.ndarray) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_sparsetools.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_sparsetools.py new file mode 100644 index 0000000000000000000000000000000000000000..6a8b94796116a22c210104fc446c5a17045ed21c --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_sparsetools.py @@ -0,0 +1,339 @@ +import sys +import os +import gc +import threading + +import numpy as np +from numpy.testing import assert_equal, assert_, assert_allclose +from scipy.sparse import (_sparsetools, coo_matrix, csr_matrix, csc_matrix, + bsr_matrix, dia_matrix) +from scipy.sparse._sputils import supported_dtypes +from scipy._lib._testutils import check_free_memory + +import pytest +from pytest import raises as assert_raises + + +def int_to_int8(n): + """ + Wrap an integer to the interval [-128, 127]. + """ + return (n + 128) % 256 - 128 + + +def test_exception(): + assert_raises(MemoryError, _sparsetools.test_throw_error) + + +def test_threads(): + # Smoke test for parallel threaded execution; doesn't actually + # check that code runs in parallel, but just that it produces + # expected results. + nthreads = 10 + niter = 100 + + n = 20 + a = csr_matrix(np.ones([n, n])) + bres = [] + + class Worker(threading.Thread): + def run(self): + b = a.copy() + for j in range(niter): + _sparsetools.csr_plus_csr(n, n, + a.indptr, a.indices, a.data, + a.indptr, a.indices, a.data, + b.indptr, b.indices, b.data) + bres.append(b) + + threads = [Worker() for _ in range(nthreads)] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + for b in bres: + assert_(np.all(b.toarray() == 2)) + + +def test_regression_std_vector_dtypes(): + # Regression test for gh-3780, checking the std::vector typemaps + # in sparsetools.cxx are complete. + for dtype in supported_dtypes: + ad = np.array([[1, 2], [3, 4]]).astype(dtype) + a = csr_matrix(ad, dtype=dtype) + + # getcol is one function using std::vector typemaps, and should not fail + assert_equal(a.getcol(0).toarray(), ad[:, :1]) + + +@pytest.mark.slow +@pytest.mark.xfail_on_32bit("Can't create large array for test") +def test_nnz_overflow(): + # Regression test for gh-7230 / gh-7871, checking that coo_toarray + # with nnz > int32max doesn't overflow. + nnz = np.iinfo(np.int32).max + 1 + # Ensure ~20 GB of RAM is free to run this test. + check_free_memory((4 + 4 + 1) * nnz / 1e6 + 0.5) + + # Use nnz duplicate entries to keep the dense version small. + row = np.zeros(nnz, dtype=np.int32) + col = np.zeros(nnz, dtype=np.int32) + data = np.zeros(nnz, dtype=np.int8) + data[-1] = 4 + s = coo_matrix((data, (row, col)), shape=(1, 1), copy=False) + # Sums nnz duplicates to produce a 1x1 array containing 4. + d = s.toarray() + + assert_allclose(d, [[4]]) + + +@pytest.mark.skipif( + not (sys.platform.startswith('linux') and np.dtype(np.intp).itemsize >= 8), + reason="test requires 64-bit Linux" +) +class TestInt32Overflow: + """ + Some of the sparsetools routines use dense 2D matrices whose + total size is not bounded by the nnz of the sparse matrix. These + routines used to suffer from int32 wraparounds; here, we try to + check that the wraparounds don't occur any more. + """ + # choose n large enough + n = 50000 + + def setup_method(self): + assert self.n**2 > np.iinfo(np.int32).max + + # check there's enough memory even if everything is run at the + # same time + try: + parallel_count = int(os.environ.get('PYTEST_XDIST_WORKER_COUNT', '1')) + except ValueError: + parallel_count = np.inf + + check_free_memory(3000 * parallel_count) + + def teardown_method(self): + gc.collect() + + def test_coo_todense(self): + # Check *_todense routines (cf. gh-2179) + # + # All of them in the end call coo_matrix.todense + + n = self.n + + i = np.array([0, n-1]) + j = np.array([0, n-1]) + data = np.array([1, 2], dtype=np.int8) + m = coo_matrix((data, (i, j))) + + r = m.todense() + assert_equal(r[0,0], 1) + assert_equal(r[-1,-1], 2) + del r + gc.collect() + + @pytest.mark.slow + def test_matvecs(self): + # Check *_matvecs routines + n = self.n + + i = np.array([0, n-1]) + j = np.array([0, n-1]) + data = np.array([1, 2], dtype=np.int8) + m = coo_matrix((data, (i, j))) + + b = np.ones((n, n), dtype=np.int8) + for sptype in (csr_matrix, csc_matrix, bsr_matrix): + m2 = sptype(m) + r = m2.dot(b) + assert_equal(r[0,0], 1) + assert_equal(r[-1,-1], 2) + del r + gc.collect() + + del b + gc.collect() + + @pytest.mark.slow + def test_dia_matvec(self): + # Check: huge dia_matrix _matvec + n = self.n + data = np.ones((n, n), dtype=np.int8) + offsets = np.arange(n) + m = dia_matrix((data, offsets), shape=(n, n)) + v = np.ones(m.shape[1], dtype=np.int8) + r = m.dot(v) + assert_equal(r[0], int_to_int8(n)) + del data, offsets, m, v, r + gc.collect() + + _bsr_ops = [pytest.param("matmat", marks=pytest.mark.xslow), + pytest.param("matvecs", marks=pytest.mark.xslow), + "matvec", + "diagonal", + "sort_indices", + pytest.param("transpose", marks=pytest.mark.xslow)] + + @pytest.mark.slow + @pytest.mark.parametrize("op", _bsr_ops) + def test_bsr_1_block(self, op): + # Check: huge bsr_matrix (1-block) + # + # The point here is that indices inside a block may overflow. + + def get_matrix(): + n = self.n + data = np.ones((1, n, n), dtype=np.int8) + indptr = np.array([0, 1], dtype=np.int32) + indices = np.array([0], dtype=np.int32) + m = bsr_matrix((data, indices, indptr), blocksize=(n, n), copy=False) + del data, indptr, indices + return m + + gc.collect() + try: + getattr(self, "_check_bsr_" + op)(get_matrix) + finally: + gc.collect() + + @pytest.mark.slow + @pytest.mark.parametrize("op", _bsr_ops) + def test_bsr_n_block(self, op): + # Check: huge bsr_matrix (n-block) + # + # The point here is that while indices within a block don't + # overflow, accumulators across many block may. + + def get_matrix(): + n = self.n + data = np.ones((n, n, 1), dtype=np.int8) + indptr = np.array([0, n], dtype=np.int32) + indices = np.arange(n, dtype=np.int32) + m = bsr_matrix((data, indices, indptr), blocksize=(n, 1), copy=False) + del data, indptr, indices + return m + + gc.collect() + try: + getattr(self, "_check_bsr_" + op)(get_matrix) + finally: + gc.collect() + + def _check_bsr_matvecs(self, m): # skip name check + m = m() + n = self.n + + # _matvecs + r = m.dot(np.ones((n, 2), dtype=np.int8)) + assert_equal(r[0, 0], int_to_int8(n)) + + def _check_bsr_matvec(self, m): # skip name check + m = m() + n = self.n + + # _matvec + r = m.dot(np.ones((n,), dtype=np.int8)) + assert_equal(r[0], int_to_int8(n)) + + def _check_bsr_diagonal(self, m): # skip name check + m = m() + n = self.n + + # _diagonal + r = m.diagonal() + assert_equal(r, np.ones(n)) + + def _check_bsr_sort_indices(self, m): # skip name check + # _sort_indices + m = m() + m.sort_indices() + + def _check_bsr_transpose(self, m): # skip name check + # _transpose + m = m() + m.transpose() + + def _check_bsr_matmat(self, m): # skip name check + m = m() + n = self.n + + # _bsr_matmat + m2 = bsr_matrix(np.ones((n, 2), dtype=np.int8), blocksize=(m.blocksize[1], 2)) + m.dot(m2) # shouldn't SIGSEGV + del m2 + + # _bsr_matmat + m2 = bsr_matrix(np.ones((2, n), dtype=np.int8), blocksize=(2, m.blocksize[0])) + m2.dot(m) # shouldn't SIGSEGV + + +@pytest.mark.skip(reason="64-bit indices in sparse matrices not available") +def test_csr_matmat_int64_overflow(): + n = 3037000500 + assert n**2 > np.iinfo(np.int64).max + + # the test would take crazy amounts of memory + check_free_memory(n * (8*2 + 1) * 3 / 1e6) + + # int64 overflow + data = np.ones((n,), dtype=np.int8) + indptr = np.arange(n+1, dtype=np.int64) + indices = np.zeros(n, dtype=np.int64) + a = csr_matrix((data, indices, indptr)) + b = a.T + + assert_raises(RuntimeError, a.dot, b) + + +def test_upcast(): + a0 = csr_matrix([[np.pi, np.pi*1j], [3, 4]], dtype=complex) + b0 = np.array([256+1j, 2**32], dtype=complex) + + for a_dtype in supported_dtypes: + for b_dtype in supported_dtypes: + msg = f"({a_dtype!r}, {b_dtype!r})" + + if np.issubdtype(a_dtype, np.complexfloating): + a = a0.copy().astype(a_dtype) + else: + a = a0.real.copy().astype(a_dtype) + + if np.issubdtype(b_dtype, np.complexfloating): + b = b0.copy().astype(b_dtype) + else: + with np.errstate(invalid="ignore"): + # Casting a large value (2**32) to int8 causes a warning in + # numpy >1.23 + b = b0.real.copy().astype(b_dtype) + + if not (a_dtype == np.bool_ and b_dtype == np.bool_): + c = np.zeros((2,), dtype=np.bool_) + assert_raises(ValueError, _sparsetools.csr_matvec, + 2, 2, a.indptr, a.indices, a.data, b, c) + + if ((np.issubdtype(a_dtype, np.complexfloating) and + not np.issubdtype(b_dtype, np.complexfloating)) or + (not np.issubdtype(a_dtype, np.complexfloating) and + np.issubdtype(b_dtype, np.complexfloating))): + c = np.zeros((2,), dtype=np.float64) + assert_raises(ValueError, _sparsetools.csr_matvec, + 2, 2, a.indptr, a.indices, a.data, b, c) + + c = np.zeros((2,), dtype=np.result_type(a_dtype, b_dtype)) + _sparsetools.csr_matvec(2, 2, a.indptr, a.indices, a.data, b, c) + assert_allclose(c, np.dot(a.toarray(), b), err_msg=msg) + + +def test_endianness(): + d = np.ones((3,4)) + offsets = [-1,0,1] + + a = dia_matrix((d.astype('f8'), offsets), (4, 4)) + v = np.arange(4) + + assert_allclose(a.dot(v), [1, 3, 6, 5]) + assert_allclose(b.dot(v), [1, 3, 6, 5]) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_spfuncs.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_spfuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..75bc2d92c369be5799a904bc0938617f30321f12 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_spfuncs.py @@ -0,0 +1,97 @@ +from numpy import array, kron, diag +from numpy.testing import assert_, assert_equal + +from scipy.sparse import _spfuncs as spfuncs +from scipy.sparse import csr_matrix, csc_matrix, bsr_matrix +from scipy.sparse._sparsetools import (csr_scale_rows, csr_scale_columns, + bsr_scale_rows, bsr_scale_columns) + + +class TestSparseFunctions: + def test_scale_rows_and_cols(self): + D = array([[1, 0, 0, 2, 3], + [0, 4, 0, 5, 0], + [0, 0, 6, 7, 0]]) + + #TODO expose through function + S = csr_matrix(D) + v = array([1,2,3]) + csr_scale_rows(3,5,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), diag(v)@D) + + S = csr_matrix(D) + v = array([1,2,3,4,5]) + csr_scale_columns(3,5,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), D@diag(v)) + + # blocks + E = kron(D,[[1,2],[3,4]]) + S = bsr_matrix(E,blocksize=(2,2)) + v = array([1,2,3,4,5,6]) + bsr_scale_rows(3,5,2,2,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), diag(v)@E) + + S = bsr_matrix(E,blocksize=(2,2)) + v = array([1,2,3,4,5,6,7,8,9,10]) + bsr_scale_columns(3,5,2,2,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), E@diag(v)) + + E = kron(D,[[1,2,3],[4,5,6]]) + S = bsr_matrix(E,blocksize=(2,3)) + v = array([1,2,3,4,5,6]) + bsr_scale_rows(3,5,2,3,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), diag(v)@E) + + S = bsr_matrix(E,blocksize=(2,3)) + v = array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]) + bsr_scale_columns(3,5,2,3,S.indptr,S.indices,S.data,v) + assert_equal(S.toarray(), E@diag(v)) + + def test_estimate_blocksize(self): + mats = [] + mats.append([[0,1],[1,0]]) + mats.append([[1,1,0],[0,0,1],[1,0,1]]) + mats.append([[0],[0],[1]]) + mats = [array(x) for x in mats] + + blks = [] + blks.append([[1]]) + blks.append([[1,1],[1,1]]) + blks.append([[1,1],[0,1]]) + blks.append([[1,1,0],[1,0,1],[1,1,1]]) + blks = [array(x) for x in blks] + + for A in mats: + for B in blks: + X = kron(A,B) + r,c = spfuncs.estimate_blocksize(X) + assert_(r >= B.shape[0]) + assert_(c >= B.shape[1]) + + def test_count_blocks(self): + def gold(A,bs): + R,C = bs + I,J = A.nonzero() + return len(set(zip(I//R,J//C))) + + mats = [] + mats.append([[0]]) + mats.append([[1]]) + mats.append([[1,0]]) + mats.append([[1,1]]) + mats.append([[0,1],[1,0]]) + mats.append([[1,1,0],[0,0,1],[1,0,1]]) + mats.append([[0],[0],[1]]) + + for A in mats: + for B in mats: + X = kron(A,B) + Y = csr_matrix(X) + for R in range(1,6): + for C in range(1,6): + assert_equal(spfuncs.count_blocks(Y, (R, C)), gold(X, (R, C))) + + X = kron([[1,1,0],[0,0,1],[1,0,1]],[[1,1]]) + Y = csc_matrix(X) + assert_equal(spfuncs.count_blocks(X, (1, 2)), gold(X, (1, 2))) + assert_equal(spfuncs.count_blocks(Y, (1, 2)), gold(X, (1, 2))) diff --git a/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_sputils.py b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_sputils.py new file mode 100644 index 0000000000000000000000000000000000000000..4545b49bea2cce465ae039ea7fc0f5a48b3da140 --- /dev/null +++ b/emissary-ml/llm-scripts/fine-tuning/llama3/venv/lib/python3.10/site-packages/scipy/sparse/tests/test_sputils.py @@ -0,0 +1,196 @@ +"""unit tests for sparse utility functions""" + +import numpy as np +from numpy.testing import assert_equal +from pytest import raises as assert_raises +from scipy.sparse import _sputils as sputils +from scipy.sparse._sputils import matrix + + +class TestSparseUtils: + + def test_upcast(self): + assert_equal(sputils.upcast('intc'), np.intc) + assert_equal(sputils.upcast('int32', 'float32'), np.float64) + assert_equal(sputils.upcast('bool', complex, float), np.complex128) + assert_equal(sputils.upcast('i', 'd'), np.float64) + + def test_getdtype(self): + A = np.array([1], dtype='int8') + + assert_equal(sputils.getdtype(None, default=float), float) + assert_equal(sputils.getdtype(None, a=A), np.int8) + + with assert_raises( + ValueError, + match="object dtype is not supported by sparse matrices", + ): + sputils.getdtype("O") + + def test_isscalarlike(self): + assert_equal(sputils.isscalarlike(3.0), True) + assert_equal(sputils.isscalarlike(-4), True) + assert_equal(sputils.isscalarlike(2.5), True) + assert_equal(sputils.isscalarlike(1 + 3j), True) + assert_equal(sputils.isscalarlike(np.array(3)), True) + assert_equal(sputils.isscalarlike("16"), True) + + assert_equal(sputils.isscalarlike(np.array([3])), False) + assert_equal(sputils.isscalarlike([[3]]), False) + assert_equal(sputils.isscalarlike((1,)), False) + assert_equal(sputils.isscalarlike((1, 2)), False) + + def test_isintlike(self): + assert_equal(sputils.isintlike(-4), True) + assert_equal(sputils.isintlike(np.array(3)), True) + assert_equal(sputils.isintlike(np.array([3])), False) + with assert_raises( + ValueError, + match="Inexact indices into sparse matrices are not allowed" + ): + sputils.isintlike(3.0) + + assert_equal(sputils.isintlike(2.5), False) + assert_equal(sputils.isintlike(1 + 3j), False) + assert_equal(sputils.isintlike((1,)), False) + assert_equal(sputils.isintlike((1, 2)), False) + + def test_isshape(self): + assert_equal(sputils.isshape((1, 2)), True) + assert_equal(sputils.isshape((5, 2)), True) + + assert_equal(sputils.isshape((1.5, 2)), False) + assert_equal(sputils.isshape((2, 2, 2)), False) + assert_equal(sputils.isshape(([2], 2)), False) + assert_equal(sputils.isshape((-1, 2), nonneg=False),True) + assert_equal(sputils.isshape((2, -1), nonneg=False),True) + assert_equal(sputils.isshape((-1, 2), nonneg=True),False) + assert_equal(sputils.isshape((2, -1), nonneg=True),False) + + assert_equal(sputils.isshape((1.5, 2), allow_1d=True), False) + assert_equal(sputils.isshape(([2], 2), allow_1d=True), False) + assert_equal(sputils.isshape((2, 2, -2), nonneg=True, allow_1d=True), + False) + assert_equal(sputils.isshape((2,), allow_1d=True), True) + assert_equal(sputils.isshape((2, 2,), allow_1d=True), True) + assert_equal(sputils.isshape((2, 2, 2), allow_1d=True), False) + + def test_issequence(self): + assert_equal(sputils.issequence((1,)), True) + assert_equal(sputils.issequence((1, 2, 3)), True) + assert_equal(sputils.issequence([1]), True) + assert_equal(sputils.issequence([1, 2, 3]), True) + assert_equal(sputils.issequence(np.array([1, 2, 3])), True) + + assert_equal(sputils.issequence(np.array([[1], [2], [3]])), False) + assert_equal(sputils.issequence(3), False) + + def test_ismatrix(self): + assert_equal(sputils.ismatrix(((),)), True) + assert_equal(sputils.ismatrix([[1], [2]]), True) + assert_equal(sputils.ismatrix(np.arange(3)[None]), True) + + assert_equal(sputils.ismatrix([1, 2]), False) + assert_equal(sputils.ismatrix(np.arange(3)), False) + assert_equal(sputils.ismatrix([[[1]]]), False) + assert_equal(sputils.ismatrix(3), False) + + def test_isdense(self): + assert_equal(sputils.isdense(np.array([1])), True) + assert_equal(sputils.isdense(matrix([1])), True) + + def test_validateaxis(self): + assert_raises(TypeError, sputils.validateaxis, (0, 1)) + assert_raises(TypeError, sputils.validateaxis, 1.5) + assert_raises(ValueError, sputils.validateaxis, 3) + + # These function calls should not raise errors + for axis in (-2, -1, 0, 1, None): + sputils.validateaxis(axis) + + def test_get_index_dtype(self): + imax = np.int64(np.iinfo(np.int32).max) + too_big = imax + 1 + + # Check that uint32's with no values too large doesn't return + # int64 + a1 = np.ones(90, dtype='uint32') + a2 = np.ones(90, dtype='uint32') + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), check_contents=True)), + np.dtype('int32') + ) + + # Check that if we can not convert but all values are less than or + # equal to max that we can just convert to int32 + a1[-1] = imax + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), check_contents=True)), + np.dtype('int32') + ) + + # Check that if it can not convert directly and the contents are + # too large that we return int64 + a1[-1] = too_big + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), check_contents=True)), + np.dtype('int64') + ) + + # test that if can not convert and didn't specify to check_contents + # we return int64 + a1 = np.ones(89, dtype='uint32') + a2 = np.ones(89, dtype='uint32') + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2))), + np.dtype('int64') + ) + + # Check that even if we have arrays that can be converted directly + # that if we specify a maxval directly it takes precedence + a1 = np.ones(12, dtype='uint32') + a2 = np.ones(12, dtype='uint32') + assert_equal( + np.dtype(sputils.get_index_dtype( + (a1, a2), maxval=too_big, check_contents=True + )), + np.dtype('int64') + ) + + # Check that an array with a too max size and maxval set + # still returns int64 + a1[-1] = too_big + assert_equal( + np.dtype(sputils.get_index_dtype((a1, a2), maxval=too_big)), + np.dtype('int64') + ) + + def test_check_shape_overflow(self): + new_shape = sputils.check_shape([(10, -1)], (65535, 131070)) + assert_equal(new_shape, (10, 858967245)) + + def test_matrix(self): + a = [[1, 2, 3]] + b = np.array(a) + + assert isinstance(sputils.matrix(a), np.matrix) + assert isinstance(sputils.matrix(b), np.matrix) + + c = sputils.matrix(b) + c[:, :] = 123 + assert_equal(b, a) + + c = sputils.matrix(b, copy=False) + c[:, :] = 123 + assert_equal(b, [[123, 123, 123]]) + + def test_asmatrix(self): + a = [[1, 2, 3]] + b = np.array(a) + + assert isinstance(sputils.asmatrix(a), np.matrix) + assert isinstance(sputils.asmatrix(b), np.matrix) + + c = sputils.asmatrix(b) + c[:, :] = 123 + assert_equal(b, [[123, 123, 123]])