sparseqr 1.3__tar.gz → 1.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ *.pyc
2
+ *.pyo
3
+ _spqr.*
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [v1.4.1] - 2025-02-10
10
+ ### Fixed
11
+ - An import statement got lost in the 1.4 update.
12
+
13
+ ## [v1.4] - 2025-01-28
14
+ ### Fixed
15
+ - Modernized the build system (`pyproject.toml`).
16
+ - Changed the way suite-sparse is found to be more robust.
17
+
18
+ ## [v1.3] - 2025-01-09
19
+ ### Added
20
+ - Bindings for `qr_factorize` and `qmult` (thanks to jkrokowski)
21
+
22
+ ### Fixed
23
+ - Compatibility with more environments (more search paths, newer numpy, setuptools dependency)
24
+ - Readme example uses `spsolve_triangular`.
25
+
26
+ ## [v1.2.1] - 2023-04-12
27
+ ### Fixed
28
+ - Fixed a memory leak in `qr()` and `rz()`.
29
+ ### Changed
30
+ - Bumped minimal Python version to 3.8.
31
+ - `rz()` is called by the test script. Its output is ignored.
32
+
33
+ ## [v1.2] - 2022-05-27
34
+ ### Added
35
+ - Added support for partial "economy" decompositions. (Christoph Hansknecht <c.hansknecht@tu-braunschweig.de>): 'The "economy" option can be used in SPQR to compute a QR factorization of a (m x n) matrix with m < n consisting of blocks Q_1, and Q_2, where Q_1 has as shape of (m x n) and Q_2 of (m x k - n). For k = n we get the reduced form, for k = m the full one. For k in between m and n, SPQR yields a block that spans part of the kernel of A. This patch adds this functionality to PySPQR.'
36
+ - Added support for macOS on arm64.
37
+
38
+ ## [v1.1.2] - 2021-08-09
39
+ ### Added
40
+ - Added rz recomposition (thanks to Ben Smith <bsmith@apl.washington.edu>)
41
+ - Added support for "economy" decomposition. (Jeffrey Bouas <ignirtoq@gmail.com>)
42
+ ### Changed
43
+ - Supports conda environments (thanks to Ben Smith <bsmith@apl.washington.edu> and Sterling Baird <sterling.baird@icloud.com>)
44
+
45
+ ## [v1.0.0] - 2017-08-31
46
+ ### Added
47
+ - Installation and packaging using `setuptools`
48
+ ### Changed
49
+ - Rename module `spqr` to `sparseqr`
50
+ - Clean up public API: `qr`, `solve`, `permutation_vector_to_matrix`
51
+
52
+ ## [v1.0.0] - 2017-08-31
53
+ ### Added
54
+ - Installation and packaging using `setuptools` (thanks to Juha Jeronen <juha.jeronen@tut.fi>)
55
+ ### Changed
56
+ - Rename module `spqr` to `sparseqr`
57
+ - Clean up public API: `qr`, `solve`, `permutation_vector_to_matrix`
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.3
2
+ Name: sparseqr
3
+ Version: 1.4.1
4
+ Summary: Python wrapper for SuiteSparseQR
5
+ Keywords: suitesparse,bindings,wrapper,scipy,numpy,qr-decomposition,qr-factorisation,sparse-matrix,sparse-linear-system,sparse-linear-solver
6
+ Author-email: Yotam Gingold <yotam@yotamgingold.com>
7
+ Requires-Python: >= 3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: numpy >1.2
10
+ Requires-Dist: scipy >= 1.0
11
+ Requires-Dist: cffi >= 1.0
12
+ Requires-Dist: setuptools >35
13
+ Project-URL: homepage, https://github.com/yig/PySPQR
14
+ Project-URL: source, https://github.com/yig/PySPQR
15
+
16
+ # Python wrapper for SuiteSparseQR
17
+
18
+ This module wraps the [SuiteSparseQR](http://faculty.cse.tamu.edu/davis/suitesparse.html)
19
+ decomposition function for use with [SciPy](http://www.scipy.org).
20
+ This is Matlab's sparse `[Q,R,E] = qr()`.
21
+ For some reason, no one ever wrapped that function of SuiteSparseQR for Python.
22
+
23
+ Also wrapped are the SuiteSparseQR solvers for ``A x = b`` for the cases with sparse `A` and dense or sparse `b`.
24
+ This is especially useful for solving sparse overdetermined linear systems in the least-squares sense.
25
+ Here `A` is of size m-by-n and `b` is m-by-k (storing `k` different right-hand side vectors, each considered separately).
26
+
27
+ # Usage
28
+
29
+ ```python
30
+ import numpy
31
+ import scipy.sparse.linalg
32
+ import sparseqr
33
+
34
+ # QR decompose a sparse matrix M such that Q R = M E
35
+ #
36
+ M = scipy.sparse.rand( 10, 10, density = 0.1 )
37
+ Q, R, E, rank = sparseqr.qr( M )
38
+ print( "Should be approximately zero:", abs( Q*R - M*sparseqr.permutation_vector_to_matrix(E) ).sum() )
39
+
40
+ # Solve many linear systems "M x = b for b in columns(B)"
41
+ #
42
+ B = scipy.sparse.rand( 10, 5, density = 0.1 ) # many RHS, sparse (could also have just one RHS with shape (10,))
43
+ x = sparseqr.solve( M, B, tolerance = 0 )
44
+
45
+ # Solve an overdetermined linear system A x = b in the least-squares sense
46
+ #
47
+ # The same routine also works for the usual non-overdetermined case.
48
+ #
49
+ A = scipy.sparse.rand( 20, 10, density = 0.1 ) # 20 equations, 10 unknowns
50
+ b = numpy.random.random(20) # one RHS, dense, but could also have many (in shape (20,k))
51
+ x = sparseqr.solve( A, b, tolerance = 0 )
52
+ ## Call `rz()`:
53
+ sparseqr.rz( A, b, tolerance = 0 )
54
+
55
+ # Solve a linear system M x = B via QR decomposition
56
+ #
57
+ # This approach is slow due to the explicit construction of Q, but may be
58
+ # useful if a large number of systems need to be solved with the same M.
59
+ #
60
+ M = scipy.sparse.rand( 10, 10, density = 0.1 )
61
+ Q, R, E, rank = sparseqr.qr( M )
62
+ r = rank # r could be min(M.shape) if M is full-rank
63
+
64
+ # The system is only solvable if the lower part of Q.T @ B is all zero:
65
+ print( "System is solvable if this is zero (unlikely for a random matrix):", abs( (( Q.tocsc()[:,r:] ).T ).dot( B ) ).sum() )
66
+
67
+ # Systems with large non-square matrices can benefit from "economy" decomposition.
68
+ M = scipy.sparse.rand( 20, 5, density=0.1 )
69
+ B = scipy.sparse.rand( 20, 5, density = 0.1 )
70
+ Q, R, E, rank = sparseqr.qr( M )
71
+ print("Q shape (should be 20x20):", Q.shape)
72
+ print("R shape (should be 20x5):", R.shape)
73
+ Q, R, E, rank = sparseqr.qr( M, economy=True )
74
+ print("Q shape (should be 20x5):", Q.shape)
75
+ print("R shape (should be 5x5):", R.shape)
76
+
77
+
78
+ R = R.tocsr()[:r,:r] #for best performance, spsolve_triangular() wants the Matrix to be in CSR format.
79
+ Q = Q.tocsc()[:,:r] # Use CSC format for fast indexing of columns.
80
+ QB = (Q.T).dot(B).todense() # spsolve_triangular() need the RHS in array format.
81
+ result = scipy.sparse.linalg.spsolve_triangular(R, QB, lower=False)
82
+
83
+ # Recover a solution (as a dense array):
84
+ x = numpy.zeros( ( M.shape[1], B.shape[1] ), dtype = result.dtype )
85
+ x[:r] = result
86
+ x[E] = x.copy()
87
+
88
+ # Recover a solution (as a sparse matrix):
89
+ x = scipy.sparse.vstack( ( result, scipy.sparse.coo_matrix( ( M.shape[1] - rank, B.shape[1] ), dtype = result.dtype ) ) )
90
+ x.row = E[ x.row ]
91
+ ```
92
+
93
+ # Installation
94
+
95
+ Before installing this module, you must first install [SuiteSparseQR](http://faculty.cse.tamu.edu/davis/suitesparse.html). You can do that via conda (`conda install suitesparse`) or your system's package manager (macOS: `brew install suitesparse`; debian/ubuntu linux: `apt-get install libsuitesparse-dev`).
96
+
97
+ Now you are ready to install this module.
98
+
99
+ ## Via `pip`
100
+
101
+ From PyPI:
102
+
103
+ ```bash
104
+ pip install sparseqr
105
+ ```
106
+
107
+ From GitHub:
108
+
109
+ ```bash
110
+ pip install git+https://github.com/yig/PySPQR.git
111
+ ```
112
+
113
+ ## Directly
114
+
115
+ Copy the three `sparseqr/*.py` files next to your source code,
116
+ or leave them in their directory and call it as a module.
117
+
118
+
119
+ # Deploy
120
+
121
+ 1. Change the version in:
122
+
123
+ ```
124
+ sparseqr/__init__.py
125
+ pyproject.toml
126
+ ```
127
+
128
+ 2. Update `CHANGELOG.md`
129
+
130
+ 3. Run:
131
+
132
+ ```
133
+ flit publish --format sdist
134
+ ```
135
+
136
+ We don't publish binary wheels, because it must be compiled against suite-sparse as a system dependency. We could publish a `none-any` wheel, which would cause compilation to happen the first time the module is imported rather than when it is installed. Is there a point to that?
137
+
138
+ # Known issues
139
+
140
+ `pip uninstall sparseqr` won't remove the generated libraries. It will list them with a warning.
141
+
142
+ # Tested on
143
+
144
+ - Python 3.9, 3.13.
145
+ - Conda and not conda.
146
+ - macOS, Ubuntu Linux, and Linux Mint.
147
+
148
+ PYTHONPATH='.:$PYTHONPATH' python3 test/test.py
149
+
150
+ # Dependencies
151
+
152
+ These are installed via pip:
153
+
154
+ * [SciPy/NumPy](http://www.scipy.org)
155
+ * [cffi](http://cffi.readthedocs.io/)
156
+
157
+ These must be installed manually:
158
+
159
+ * [SuiteSparseQR](http://faculty.cse.tamu.edu/davis/suitesparse.html) (macOS: `brew install suitesparse`; debian/ubuntu linux: `apt-get install libsuitesparse-dev`)
160
+
161
+ # License
162
+
163
+ Public Domain [CC0](http://creativecommons.org/publicdomain/zero/1.0/)
164
+
@@ -77,6 +77,10 @@ x.row = E[ x.row ]
77
77
 
78
78
  # Installation
79
79
 
80
+ Before installing this module, you must first install [SuiteSparseQR](http://faculty.cse.tamu.edu/davis/suitesparse.html). You can do that via conda (`conda install suitesparse`) or your system's package manager (macOS: `brew install suitesparse`; debian/ubuntu linux: `apt-get install libsuitesparse-dev`).
81
+
82
+ Now you are ready to install this module.
83
+
80
84
  ## Via `pip`
81
85
 
82
86
  From PyPI:
@@ -91,22 +95,6 @@ From GitHub:
91
95
  pip install git+https://github.com/yig/PySPQR.git
92
96
  ```
93
97
 
94
- ## Manually from GitHub
95
-
96
- As user:
97
-
98
- ```bash
99
- git clone https://github.com/yig/PySPQR.git
100
- cd PySPQR
101
- python setup.py install --user
102
- ```
103
-
104
- As admin, change the last command to
105
-
106
- ```bash
107
- sudo python setup.py install
108
- ```
109
-
110
98
  ## Directly
111
99
 
112
100
  Copy the three `sparseqr/*.py` files next to your source code,
@@ -119,7 +107,6 @@ or leave them in their directory and call it as a module.
119
107
 
120
108
  ```
121
109
  sparseqr/__init__.py
122
- setup.py
123
110
  pyproject.toml
124
111
  ```
125
112
 
@@ -128,17 +115,18 @@ or leave them in their directory and call it as a module.
128
115
  3. Run:
129
116
 
130
117
  ```
131
- poetry build -f sdist
132
- poetry publish
118
+ flit publish --format sdist
133
119
  ```
134
120
 
121
+ We don't publish binary wheels, because it must be compiled against suite-sparse as a system dependency. We could publish a `none-any` wheel, which would cause compilation to happen the first time the module is imported rather than when it is installed. Is there a point to that?
122
+
135
123
  # Known issues
136
124
 
137
125
  `pip uninstall sparseqr` won't remove the generated libraries. It will list them with a warning.
138
126
 
139
127
  # Tested on
140
128
 
141
- - Python 2.7, 3.4, 3.5, 3.9, 3.13.
129
+ - Python 3.9, 3.13.
142
130
  - Conda and not conda.
143
131
  - macOS, Ubuntu Linux, and Linux Mint.
144
132
 
@@ -146,10 +134,15 @@ or leave them in their directory and call it as a module.
146
134
 
147
135
  # Dependencies
148
136
 
137
+ These are installed via pip:
138
+
149
139
  * [SciPy/NumPy](http://www.scipy.org)
150
- * [SuiteSparseQR](http://faculty.cse.tamu.edu/davis/suitesparse.html) (macOS: `brew install suitesparse`; debian/ubuntu linux: `apt-get install libsuitesparse-dev`)
151
140
  * [cffi](http://cffi.readthedocs.io/)
152
141
 
142
+ These must be installed manually:
143
+
144
+ * [SuiteSparseQR](http://faculty.cse.tamu.edu/davis/suitesparse.html) (macOS: `brew install suitesparse`; debian/ubuntu linux: `apt-get install libsuitesparse-dev`)
145
+
153
146
  # License
154
147
 
155
148
  Public Domain [CC0](http://creativecommons.org/publicdomain/zero/1.0/)
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "sparseqr"
3
+ version = "1.4.1"
4
+ description = "Python wrapper for SuiteSparseQR"
5
+ authors = [{name = "Yotam Gingold", email = "yotam@yotamgingold.com"}]
6
+ license = {text = "Public Domain CC0"}
7
+ readme = "README.md"
8
+ keywords = ["suitesparse", "bindings", "wrapper", "scipy", "numpy", "qr-decomposition", "qr-factorisation", "sparse-matrix", "sparse-linear-system", "sparse-linear-solver"]
9
+
10
+ requires-python = ">= 3.8"
11
+
12
+ dependencies = [
13
+ "numpy >1.2",
14
+ "scipy >= 1.0",
15
+ "cffi >= 1.0",
16
+ "setuptools >35",
17
+ ]
18
+
19
+ [project.urls]
20
+ homepage = "https://github.com/yig/PySPQR"
21
+ source = "https://github.com/yig/PySPQR"
22
+
23
+ [build-system]
24
+ requires = ["setuptools>=61"]
25
+ build-backend = "setuptools.build_meta"
26
+
27
+ #[tool.setuptools.packages.find]
28
+ # include = ["test/*.py", "README.md", "LICENSE.md"]
29
+ #exclude = ["sparseqr/_sparseqr*"]
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ from __future__ import division, print_function, absolute_import
4
+
5
+ from setuptools import setup #, dist
6
+ import os
7
+
8
+ setup(
9
+ # See
10
+ # http://setuptools.readthedocs.io/en/latest/setuptools.html
11
+ #
12
+ setup_requires = ["cffi>=1.0.0"],
13
+ cffi_modules = ["sparseqr/sparseqr_gen.py:ffibuilder"],
14
+ install_requires = ["cffi>=1.0.0"],
15
+ )
@@ -17,7 +17,7 @@ See the docstrings of the individual functions for details.
17
17
 
18
18
  from __future__ import absolute_import
19
19
 
20
- __version__ = '1.3'
20
+ __version__ = '1.4.1'
21
21
 
22
22
  # import the important things into the package's top-level namespace.
23
23
  from .sparseqr import qr, rz, solve, permutation_vector_to_matrix, qr_factorize,qmult
@@ -4,9 +4,7 @@ License: Public Domain [CC0](http://creativecommons.org/publicdomain/zero/1.0/)
4
4
  Description: Wrapper for SuiteSparse qr() and solve() functions. Matlab and Julia have it, Python should have it, too.
5
5
  '''
6
6
 
7
- from __future__ import print_function, division, absolute_import
8
7
  import os
9
- from os.path import join, expanduser
10
8
  import platform
11
9
 
12
10
  from cffi import FFI
@@ -15,26 +13,21 @@ include_dirs = []
15
13
  library_dirs = []
16
14
  libraries = ['spqr']
17
15
 
18
- if platform.system() == 'Windows':
19
- include_dirs.append( join('C:', 'Program Files', 'Python', 'suitesparse') )
16
+ ## If we're using conda, use the conda paths
17
+ if 'CONDA_PREFIX' in os.environ:
18
+ include_dirs.append( os.path.join(os.environ['CONDA_PREFIX'], 'include', 'suitesparse') )
19
+ library_dirs.append( os.path.join(os.environ['CONDA_PREFIX'], 'lib') )
20
+
21
+ ## Otherwise, add common system-wide directories
20
22
  else:
21
- include_dirs.append( '/usr/include/suitesparse' )
22
- ## Homebrew on macOS arm64 puts headers and libraries
23
- ## in `/opt/homebrew`. That's not on the default path, so add them:
24
- include_dirs.append( '/opt/homebrew/include/suitesparse' )
25
- # Does this work for anyone? At one point I thought it worked for me, but maybe I didn't test properly.
26
- include_dirs.append( '/opt/homebrew/include' )
27
- library_dirs.append( '/opt/homebrew/lib' )
28
-
29
- # for compatibility with conda envs
30
- if 'CONDA_DEFAULT_ENV' in os.environ:
31
- homedir = expanduser("~")
32
-
33
- for packager in ['anaconda3','miniconda3','condaforge','miniforge','mambaforge']:
34
- for sub in ['','Library']:
35
- thedir=join(homedir, packager, 'envs', os.environ['CONDA_DEFAULT_ENV'], sub, 'include', 'suitesparse')
36
- if os.path.isdir(thedir):
37
- include_dirs.append(thedir)
23
+ if platform.system() == 'Windows':
24
+ include_dirs.append( os.path.join('C:', 'Program Files', 'Python', 'suitesparse') )
25
+ else:
26
+ include_dirs.append( '/usr/include/suitesparse' )
27
+ ## Homebrew on macOS arm64 puts headers and libraries
28
+ ## in `/opt/homebrew`. That's not on the default path, so add them:
29
+ include_dirs.append( '/opt/homebrew/include/suitesparse' )
30
+ library_dirs.append( '/opt/homebrew/lib' )
38
31
 
39
32
  if platform.system() == 'Windows':
40
33
  # https://github.com/yig/PySPQR/issues/6
@@ -43,6 +36,11 @@ if platform.system() == 'Windows':
43
36
 
44
37
  ffibuilder = FFI()
45
38
 
39
+ ## Uncomment this and install with `pip install -v` to see the arguments to `set_source`.
40
+ # print( "cffi include_dirs:", include_dirs )
41
+ # print( "cffi library_dirs:", library_dirs )
42
+ # print( "cffi libraries:", libraries )
43
+
46
44
  ffibuilder.set_source( "sparseqr._sparseqr",
47
45
  """#include <SuiteSparseQR_C.h>
48
46
  """,
sparseqr-1.3/PKG-INFO DELETED
@@ -1,20 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: sparseqr
3
- Version: 1.3
4
- Summary: Python wrapper for SuiteSparseQR
5
- License: Public Domain CC0
6
- Author: Yotam Gingold
7
- Author-email: yotam@yotamgingold.com
8
- Requires-Python: >=3.8,<4.0
9
- Classifier: License :: Other/Proprietary License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
- Classifier: Programming Language :: Python :: 3.9
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
- Requires-Dist: cffi (>=1.0,<2.0)
18
- Requires-Dist: numpy (>1.2)
19
- Requires-Dist: scipy (>=1.0,<2.0)
20
- Requires-Dist: setuptools (>35)
@@ -1,22 +0,0 @@
1
- [tool.poetry]
2
- name = "sparseqr"
3
- version = "1.3"
4
- description = "Python wrapper for SuiteSparseQR"
5
- authors = ["Yotam Gingold <yotam@yotamgingold.com>"]
6
- license = "Public Domain CC0"
7
-
8
- include = ["test/*.py", "README.md", "LICENSE.md"]
9
- exclude = ["sparseqr/_sparseqr*"]
10
-
11
- [tool.poetry.dependencies]
12
- python = "^3.8"
13
- numpy = ">1.2"
14
- scipy = "^1.0"
15
- cffi = "^1.0"
16
- setuptools = ">35"
17
-
18
- [tool.poetry.dev-dependencies]
19
-
20
- [build-system]
21
- requires = ["poetry-core>=1.0.0"]
22
- build-backend = "poetry.core.masonry.api"
File without changes
File without changes
File without changes