Ah, there is 
https://petsc.org/release/manualpages/Mat/MATSOLVERSPQR/#matsolverspqr  See 
also https://petsc.org/release/manualpages/Mat/MatGetFactor/#matgetfactor and 
https://petsc.org/release/manualpages/Mat/MatQRFactorSymbolic/ 



> On Aug 29, 2023, at 1:17 PM, Jed Brown <j...@jedbrown.org> wrote:
> 
> Suitesparse includes a sparse QR algorithm. The main issue is that (even with 
> pivoting) the R factor has the same nonzero structure as a Cholesky factor of 
> A^T A, which is generally much denser than a factor of A, and this degraded 
> sparsity impacts Q as well.
> 
> I wonder if someone would like to contribute a sparse QR to PETSc. It could 
> have a default implementation via Cholesky QR and the ability to call SPQR 
> from Suitesparse.
> 
> Barry Smith <bsm...@petsc.dev> writes:
> 
>>  Are the nonzero structures of all the rows related? If they are, one could 
>> devise a routine to take advantage of this relationship, but if the nonzero 
>> structures of each row are "randomly" different from all the other rows, 
>> then it is difficult to see how one can take advantage of the sparsity.
>> 
>> 
>> 
>>> On Aug 29, 2023, at 12:50 PM, Thanasis Boutsikakis 
>>> <thanasis.boutsika...@corintis.com> wrote:
>>> 
>>> Hi all, I have the following code that orthogonalizes a PETSc matrix. The 
>>> problem is that this implementation requires that the PETSc matrix is 
>>> dense, otherwise, it fails at bv.SetFromOptions(). Hence the assert in 
>>> orthogonality().
>>> 
>>> What could I do in order to be able to orthogonalize sparse matrices as 
>>> well? Could I convert it efficiently? (I tried to no avail)
>>> 
>>> Thanks!
>>> 
>>> """Experimenting with matrix orthogonalization"""
>>> 
>>> import contextlib
>>> import sys
>>> import time
>>> import numpy as np
>>> from firedrake import COMM_WORLD
>>> from firedrake.petsc import PETSc
>>> 
>>> import slepc4py
>>> 
>>> slepc4py.init(sys.argv)
>>> from slepc4py import SLEPc
>>> 
>>> from numpy.testing import assert_array_almost_equal
>>> 
>>> EPSILON_USER = 1e-4
>>> EPS = sys.float_info.epsilon
>>> 
>>> 
>>> def Print(message: str):
>>>    """Print function that prints only on rank 0 with color
>>> 
>>>    Args:
>>>        message (str): message to be printed
>>>    """
>>>    PETSc.Sys.Print(message)
>>> 
>>> 
>>> def create_petsc_matrix(input_array, sparse=True):
>>>    """Create a PETSc matrix from an input_array
>>> 
>>>    Args:
>>>        input_array (np array): Input array
>>>        partition_like (PETSc mat, optional): Petsc matrix. Defaults to None.
>>>        sparse (bool, optional): Toggle for sparese or dense. Defaults to 
>>> True.
>>> 
>>>    Returns:
>>>        PETSc mat: PETSc matrix
>>>    """
>>>    # Check if input_array is 1D and reshape if necessary
>>>    assert len(input_array.shape) == 2, "Input array should be 2-dimensional"
>>>    global_rows, global_cols = input_array.shape
>>> 
>>>    size = ((None, global_rows), (global_cols, global_cols))
>>> 
>>>    # Create a sparse or dense matrix based on the 'sparse' argument
>>>    if sparse:
>>>        matrix = PETSc.Mat().createAIJ(size=size, comm=COMM_WORLD)
>>>    else:
>>>        matrix = PETSc.Mat().createDense(size=size, comm=COMM_WORLD)
>>>    matrix.setUp()
>>> 
>>>    local_rows_start, local_rows_end = matrix.getOwnershipRange()
>>> 
>>>    for counter, i in enumerate(range(local_rows_start, local_rows_end)):
>>>        # Calculate the correct row in the array for the current process
>>>        row_in_array = counter + local_rows_start
>>>        matrix.setValues(
>>>            i, range(global_cols), input_array[row_in_array, :], addv=False
>>>        )
>>> 
>>>    # Assembly the matrix to compute the final structure
>>>    matrix.assemblyBegin()
>>>    matrix.assemblyEnd()
>>> 
>>>    return matrix
>>> 
>>> 
>>> def orthogonality(A):  # sourcery skip: avoid-builtin-shadow
>>>    """Checking and correcting orthogonality
>>> 
>>>    Args:
>>>        A (PETSc.Mat): Matrix of size [m x k].
>>> 
>>>    Returns:
>>>        PETSc.Mat: Matrix of size [m x k].
>>>    """
>>>    # Check if the matrix is dense
>>>    mat_type = A.getType()
>>>    assert mat_type in (
>>>        "seqdense",
>>>        "mpidense",
>>>    ), "A must be a dense matrix. SLEPc.BV().createFromMat() requires a 
>>> dense matrix."
>>> 
>>>    m, k = A.getSize()
>>> 
>>>    Phi1 = A.getColumnVector(0)
>>>    Phi2 = A.getColumnVector(k - 1)
>>> 
>>>    # Compute dot product using PETSc function
>>>    dot_product = Phi1.dot(Phi2)
>>> 
>>>    if abs(dot_product) > min(EPSILON_USER, EPS * m):
>>>        Print("    Matrix is not orthogonal")
>>> 
>>>        # Type can be CHOL, GS, mro(), SVQB, TSQR, TSQRCHOL
>>>        _type = SLEPc.BV().OrthogBlockType.GS
>>> 
>>>        bv = SLEPc.BV().createFromMat(A)
>>>        bv.setFromOptions()
>>>        bv.setOrthogonalization(_type)
>>>        bv.orthogonalize()
>>> 
>>>        A = bv.createMat()
>>> 
>>>        Print("    Matrix successfully orthogonalized")
>>> 
>>>        # # Assembly the matrix to compute the final structure
>>>        if not A.assembled:
>>>            A.assemblyBegin()
>>>            A.assemblyEnd()
>>>    else:
>>>        Print("    Matrix is orthogonal")
>>> 
>>>    return A
>>> 
>>> 
>>> # --------------------------------------------
>>> # EXP: Orthogonalization of an mpi PETSc matrix
>>> # --------------------------------------------
>>> 
>>> m, k = 11, 7
>>> # Generate the random numpy matrices
>>> np.random.seed(0)  # sets the seed to 0
>>> A_np = np.random.randint(low=0, high=6, size=(m, k))
>>> 
>>> A = create_petsc_matrix(A_np, sparse=False)
>>> 
>>> A_orthogonal = orthogonality(A)
>>> 
>>> # --------------------------------------------
>>> # TEST: Orthogonalization of a numpy matrix
>>> # --------------------------------------------
>>> # Generate A_np_orthogonal
>>> A_np_orthogonal, _ = np.linalg.qr(A_np)
>>> 
>>> # Get the local values from A_orthogonal
>>> local_rows_start, local_rows_end = A_orthogonal.getOwnershipRange()
>>> A_orthogonal_local = A_orthogonal.getValues(
>>>    range(local_rows_start, local_rows_end), range(k)
>>> )
>>> 
>>> # Assert the correctness of the multiplication for the local subset
>>> assert_array_almost_equal(
>>>    np.abs(A_orthogonal_local),
>>>    np.abs(A_np_orthogonal[local_rows_start:local_rows_end, :]),
>>>    decimal=5,
>>> )

Reply via email to