sparseqr 1.2.1__tar.gz → 1.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sparseqr
3
- Version: 1.2.1
3
+ Version: 1.3
4
4
  Summary: Python wrapper for SuiteSparseQR
5
5
  License: Public Domain CC0
6
6
  Author: Yotam Gingold
@@ -12,6 +12,9 @@ Classifier: Programming Language :: Python :: 3.8
12
12
  Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
15
17
  Requires-Dist: cffi (>=1.0,<2.0)
16
- Requires-Dist: numpy (>=1.21,<2.0)
18
+ Requires-Dist: numpy (>1.2)
17
19
  Requires-Dist: scipy (>=1.0,<2.0)
20
+ Requires-Dist: setuptools (>35)
@@ -59,19 +59,19 @@ Q, R, E, rank = sparseqr.qr( M, economy=True )
59
59
  print("Q shape (should be 20x5):", Q.shape)
60
60
  print("R shape (should be 5x5):", R.shape)
61
61
 
62
- # Use CSC format for fast indexing of columns.
63
- R = R.tocsc()[:r,:r]
64
- Q = Q.tocsc()[:,:r]
65
- QB = (Q.T).dot(B).tocsc() # for best performance, spsolve() wants the RHS to be in CSC format.
66
- result = scipy.sparse.linalg.spsolve(R, QB)
62
+
63
+ R = R.tocsr()[:r,:r] #for best performance, spsolve_triangular() wants the Matrix to be in CSR format.
64
+ Q = Q.tocsc()[:,:r] # Use CSC format for fast indexing of columns.
65
+ QB = (Q.T).dot(B).todense() # spsolve_triangular() need the RHS in array format.
66
+ result = scipy.sparse.linalg.spsolve_triangular(R, QB, lower=False)
67
67
 
68
68
  # Recover a solution (as a dense array):
69
69
  x = numpy.zeros( ( M.shape[1], B.shape[1] ), dtype = result.dtype )
70
- x[:r] = result.todense()
70
+ x[:r] = result
71
71
  x[E] = x.copy()
72
72
 
73
73
  # Recover a solution (as a sparse matrix):
74
- x = scipy.sparse.vstack( ( result.tocoo(), scipy.sparse.coo_matrix( ( M.shape[1] - rank, B.shape[1] ), dtype = result.dtype ) ) )
74
+ x = scipy.sparse.vstack( ( result, scipy.sparse.coo_matrix( ( M.shape[1] - rank, B.shape[1] ), dtype = result.dtype ) ) )
75
75
  x.row = E[ x.row ]
76
76
  ```
77
77
 
@@ -138,9 +138,9 @@ or leave them in their directory and call it as a module.
138
138
 
139
139
  # Tested on
140
140
 
141
- - Python 2.7, 3.4, 3.5, and 3.9.
141
+ - Python 2.7, 3.4, 3.5, 3.9, 3.13.
142
142
  - Conda and not conda.
143
- - Mac OS X, Ubuntu Linux and Linux Mint.
143
+ - macOS, Ubuntu Linux, and Linux Mint.
144
144
 
145
145
  PYTHONPATH='.:$PYTHONPATH' python3 test/test.py
146
146
 
@@ -148,7 +148,7 @@ or leave them in their directory and call it as a module.
148
148
 
149
149
  * [SciPy/NumPy](http://www.scipy.org)
150
150
  * [SuiteSparseQR](http://faculty.cse.tamu.edu/davis/suitesparse.html) (macOS: `brew install suitesparse`; debian/ubuntu linux: `apt-get install libsuitesparse-dev`)
151
- * [cffi](http://cffi.readthedocs.io/) (`pip install cffi`)
151
+ * [cffi](http://cffi.readthedocs.io/)
152
152
 
153
153
  # License
154
154
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sparseqr"
3
- version = "1.2.1"
3
+ version = "1.3"
4
4
  description = "Python wrapper for SuiteSparseQR"
5
5
  authors = ["Yotam Gingold <yotam@yotamgingold.com>"]
6
6
  license = "Public Domain CC0"
@@ -10,9 +10,10 @@ exclude = ["sparseqr/_sparseqr*"]
10
10
 
11
11
  [tool.poetry.dependencies]
12
12
  python = "^3.8"
13
- numpy = "^1.21"
13
+ numpy = ">1.2"
14
14
  scipy = "^1.0"
15
15
  cffi = "^1.0"
16
+ setuptools = ">35"
16
17
 
17
18
  [tool.poetry.dev-dependencies]
18
19
 
@@ -17,8 +17,8 @@ See the docstrings of the individual functions for details.
17
17
 
18
18
  from __future__ import absolute_import
19
19
 
20
- __version__ = '1.2.1'
20
+ __version__ = '1.3'
21
21
 
22
22
  # import the important things into the package's top-level namespace.
23
- from .sparseqr import qr, rz, solve, permutation_vector_to_matrix
23
+ from .sparseqr import qr, rz, solve, permutation_vector_to_matrix, qr_factorize,qmult
24
24
 
@@ -351,6 +351,77 @@ def qr( A, tolerance = None, economy = None ):
351
351
  return scipy_Q, scipy_R, E, rank
352
352
 
353
353
 
354
+ def qr_factorize( A, tolerance = None):
355
+ '''
356
+ Given a sparse matrix A,
357
+ returns a QR factorization in householder form
358
+
359
+ If optional `tolerance` parameter is negative, it has the following meanings:
360
+ #define SPQR_DEFAULT_TOL ... /* if tol <= -2, the default tol is used */
361
+ #define SPQR_NO_TOL ... /* if -2 < tol < 0, then no tol is used */
362
+
363
+ For A an m-by-n matrix, Q will be m-by-m and R will be m-by-n.
364
+
365
+ The performance-optimal format for A is scipy.sparse.coo_matrix.
366
+
367
+ '''
368
+
369
+ chol_A = scipy2cholmodsparse( A )
370
+
371
+ if tolerance is None: tolerance = lib.SPQR_DEFAULT_TOL
372
+
373
+ QR = lib.SuiteSparseQR_C_factorize(
374
+ ## Input
375
+ lib.SPQR_ORDERING_DEFAULT,
376
+ tolerance,
377
+ chol_A,
378
+ cc
379
+ )
380
+
381
+ cholmod_free_sparse( chol_A )
382
+ ## Apparently we don't need to do this. (I get a malloc error.)
383
+ # lib.cholmod_l_free( A.shape[1], ffi.sizeof("SuiteSparse_long"), chol_E, cc )
384
+
385
+ return QR
386
+
387
+
388
+ def qmult( QR, X, method=1):
389
+ '''
390
+ Given a QR factorization struct
391
+ a dense matrix
392
+ returns Q applied to X in a dense matrix
393
+
394
+ From the suitesparse documentation:
395
+ /*
396
+ Applies Q in Householder form (as stored in the QR factorization object
397
+ returned by SuiteSparseQR_C_factorize) to a dense matrix X.
398
+
399
+ method SPQR_QTX (0): Y = Q'*X
400
+ method SPQR_QX (1): Y = Q*X
401
+ method SPQR_XQT (2): Y = X*Q'
402
+ method SPQR_XQ (3): Y = X*Q
403
+ */
404
+
405
+ '''
406
+
407
+ chol_X = numpy2cholmoddense( X )
408
+
409
+ chol_Y = lib.SuiteSparseQR_C_qmult(
410
+ ## Input
411
+ method,
412
+ QR,
413
+ chol_X,
414
+ cc
415
+ )
416
+ numpy_Y = cholmoddense2numpy( chol_Y )
417
+
418
+ ## Free cholmod stuff
419
+ cholmod_free_dense( chol_X )
420
+ cholmod_free_dense( chol_Y )
421
+
422
+ return numpy_Y
423
+
424
+
354
425
  def solve( A, b, tolerance = None ):
355
426
  '''
356
427
  Given a sparse m-by-n matrix A, and dense or sparse m-by-k matrix (storing k RHS vectors) b,
@@ -21,14 +21,20 @@ else:
21
21
  include_dirs.append( '/usr/include/suitesparse' )
22
22
  ## Homebrew on macOS arm64 puts headers and libraries
23
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.
24
26
  include_dirs.append( '/opt/homebrew/include' )
25
27
  library_dirs.append( '/opt/homebrew/lib' )
26
28
 
27
29
  # for compatibility with conda envs
28
30
  if 'CONDA_DEFAULT_ENV' in os.environ:
29
31
  homedir = expanduser("~")
30
- include_dirs.append( join(homedir, 'anaconda3', 'envs', os.environ['CONDA_DEFAULT_ENV'], 'Library', 'include', 'suitesparse') )
31
- include_dirs.append( join(homedir, 'miniconda3', 'envs', os.environ['CONDA_DEFAULT_ENV'], 'Library', 'include', 'suitesparse') )
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)
32
38
 
33
39
  if platform.system() == 'Windows':
34
40
  # https://github.com/yig/PySPQR/issues/6
@@ -362,6 +368,105 @@ cholmod_sparse *SuiteSparseQR_C_backslash_sparse /* returns X, or NULL */
362
368
  cholmod_common *cc /* workspace and parameters */
363
369
  ) ;
364
370
 
371
+ /* ========================================================================== */
372
+ /* === SuiteSparseQR_C_factorization ======================================== */
373
+ /* ========================================================================== */
374
+
375
+ /* A real or complex QR factorization, computed by SuiteSparseQR_C_factorize */
376
+ typedef struct SuiteSparseQR_C_factorization_struct
377
+ {
378
+ int xtype ; /* CHOLMOD_REAL or CHOLMOD_COMPLEX */
379
+ void *factors ; /* from SuiteSparseQR_factorize <double> or
380
+ SuiteSparseQR_factorize <Complex> */
381
+
382
+ } SuiteSparseQR_C_factorization ;
383
+
384
+ /* ========================================================================== */
385
+ /* === SuiteSparseQR_C_factorize ============================================ */
386
+ /* ========================================================================== */
387
+
388
+ SuiteSparseQR_C_factorization *SuiteSparseQR_C_factorize
389
+ (
390
+ /* inputs: */
391
+ int ordering, /* all, except 3:given treated as 0:fixed */
392
+ double tol, /* columns with 2-norm <= tol treated as 0 */
393
+ cholmod_sparse *A, /* m-by-n sparse matrix */
394
+ cholmod_common *cc /* workspace and parameters */
395
+ ) ;
396
+
397
+ /* ========================================================================== */
398
+ /* === SuiteSparseQR_C_symbolic ============================================= */
399
+ /* ========================================================================== */
400
+
401
+ SuiteSparseQR_C_factorization *SuiteSparseQR_C_symbolic
402
+ (
403
+ /* inputs: */
404
+ int ordering, /* all, except 3:given treated as 0:fixed */
405
+ int allow_tol, /* if TRUE allow tol for rank detection */
406
+ cholmod_sparse *A, /* m-by-n sparse matrix, A->x ignored */
407
+ cholmod_common *cc /* workspace and parameters */
408
+ ) ;
409
+
410
+ /* ========================================================================== */
411
+ /* === SuiteSparseQR_C_numeric ============================================== */
412
+ /* ========================================================================== */
413
+
414
+ int SuiteSparseQR_C_numeric
415
+ (
416
+ /* inputs: */
417
+ double tol, /* treat columns with 2-norm <= tol as zero */
418
+ cholmod_sparse *A, /* sparse matrix to factorize */
419
+ /* input/output: */
420
+ SuiteSparseQR_C_factorization *QR,
421
+ cholmod_common *cc /* workspace and parameters */
422
+ ) ;
423
+
424
+ /* ========================================================================== */
425
+ /* === SuiteSparseQR_C_free ================================================= */
426
+ /* ========================================================================== */
427
+
428
+ /* Free the QR factors computed by SuiteSparseQR_C_factorize */
429
+ int SuiteSparseQR_C_free /* returns TRUE (1) if OK, FALSE (0) otherwise*/
430
+ (
431
+ SuiteSparseQR_C_factorization **QR,
432
+ cholmod_common *cc /* workspace and parameters */
433
+ ) ;
434
+
435
+ /* ========================================================================== */
436
+ /* === SuiteSparseQR_C_solve ================================================ */
437
+ /* ========================================================================== */
438
+
439
+ cholmod_dense* SuiteSparseQR_C_solve /* returnx X, or NULL if failure */
440
+ (
441
+ int system, /* which system to solve */
442
+ SuiteSparseQR_C_factorization *QR, /* of an m-by-n sparse matrix A */
443
+ cholmod_dense *B, /* right-hand-side, m-by-k or n-by-k */
444
+ cholmod_common *cc /* workspace and parameters */
445
+ ) ;
446
+
447
+ /* ========================================================================== */
448
+ /* === SuiteSparseQR_C_qmult ================================================ */
449
+ /* ========================================================================== */
450
+
451
+ /*
452
+ Applies Q in Householder form (as stored in the QR factorization object
453
+ returned by SuiteSparseQR_C_factorize) to a dense matrix X.
454
+
455
+ method SPQR_QTX (0): Y = Q'*X
456
+ method SPQR_QX (1): Y = Q*X
457
+ method SPQR_XQT (2): Y = X*Q'
458
+ method SPQR_XQ (3): Y = X*Q
459
+ */
460
+
461
+ cholmod_dense *SuiteSparseQR_C_qmult /* returns Y, or NULL on failure */
462
+ (
463
+ /* inputs: */
464
+ int method, /* 0,1,2,3 */
465
+ SuiteSparseQR_C_factorization *QR, /* of an m-by-n sparse matrix A */
466
+ cholmod_dense *X, /* size m-by-n with leading dimension ldx */
467
+ cholmod_common *cc /* workspace and parameters */
468
+ ) ;
469
+
365
470
  #define SPQR_ORDERING_FIXED ...
366
471
  #define SPQR_ORDERING_NATURAL ...
367
472
  #define SPQR_ORDERING_COLAMD ...
@@ -64,3 +64,16 @@ x[E] = x.copy()
64
64
  # Recover a solution (as a sparse matrix):
65
65
  x = scipy.sparse.vstack( ( result.tocoo(), scipy.sparse.coo_matrix( ( M.shape[1] - rank, B.shape[1] ), dtype = result.dtype ) ) )
66
66
  x.row = E[ x.row ]
67
+
68
+ #initialize QR Factorization object
69
+ M = scipy.sparse.rand( 100, 100, density=0.05 )
70
+
71
+ #perform QR factorization, but store in Householder form
72
+ QR= sparseqr.qr_factorize( M )
73
+ X = numpy.zeros((M.shape[0],1))
74
+ #change last entry of the first column to a 1
75
+ # this allows us to construct only the first column of Q
76
+ X[-1,0]=1
77
+
78
+ Y = sparseqr.qmult(QR,X)
79
+ print("Y shape (should be 100x1):",Y.shape)
File without changes