Is assign DenseMatrix to Compressed one is effective?

Issue #367 resolved
Kataev Victor created an issue
// Suppose I have dense matrix

blaze::DenseMatrix<double> A;

// ... Fill matrix A ...

// and I want to assign it to compressed matrix

blaze::CompressedMatrix<double> B = A;

Is this operation will be effective? How I could make it more effective?

Thanks

Comments (4)

  1. Klaus Iglberger

    Hi Victor!

    Yes, you can copy a dense matrix (row-major or column-major) to a sparse matrix (also row-major or column-major). In this operation Blaze only copies the non-zero elements of the dense matrix into the sparse matrix:

    blaze::DynamicMatrix<int> A
       { { 1, 0, 2 }
       , { 0, 3, 0 }
       , { 4, 0, 5 } };
    
    blaze::CompressedMatrix<int> B( A );
    
    std::cout << "\n"
              << " B =\n" << B << "\n"                        // Prints the sparse matrix B
              << " B.nonZeros() = " << B.nonZeros() << "\n"   // Prints 5
              << " ( A == B ) = " << ( A == B) << "\n"        // Prints 1 (i.e. true)
              << "\n";
    

    Blaze will perform this assignment as efficiently as possible. We are not aware of any way how to improve the performance of this operation, but we gladly accept any suggestion for improvement.

    I hope this answers the question,

    Best regards,

    Klaus!

  2. Kataev Victor reporter

    I am concerned about the ambiguity of comparing to zero for a floating point. And compressed matrix size reserversion.

    How you think is it make sense to make conversion dense to compressed matrix function with custom zero threshold?

  3. Klaus Iglberger

    Hi Victor!

    I understand your concerns. By default, Blaze only filters the values that are exactly zero (i.e. 0.0F). All other values are copied into the sparse matrix:

    DynamicMatrix<float> A
       { { 1.0F, 1E-9F, 0.0F }
       , { 1E-9F, 2.0F, 1E-9F }
       , { 0.0F, 1E-9F, 3.0F } };
    
    CompressedMatrix<float> B( A );
    
    std::cerr << "\n"
              << " B =\n" << B << "\n"
              << " B.nonZeros() = " << B.nonZeros() << "\n"  // Prints 7
              << " B.capacity() = " << B.capacity() << "\n"  // Prints 7
              << "\n";
    

    In order to also filter all values below a certain threshold, you can use the map() function:

    DynamicMatrix<float> A
       { { 1.0F, 1E-9F, 0.0F }
       , { 1E-9F, 2.0F, 1E-9F }
       , { 0.0F, 1E-9F, 3.0F } };
    
    const float threshold( 1E-8F );
    CompressedMatrix<float> B( map( A, [threshold]( float f ){ return f < threshold ? 0.0F : f; } ) );
    
    std::cerr << "\n"
              << " B =\n" << B << "\n"
              << " B.nonZeros() = " << B.nonZeros() << "\n"  // Prints 3
              << " B.capacity() = " << B.capacity() << "\n"  // Prints 7 (more than required, see below)
              << "\n";
    

    The allocation of memory works similarly to the strategy in std::vector. When the capacity is depleted, CompressedMatrix allocates new capacity for twice as many elements. There are two ways to control that. First, you can acquire the required amount of capacity up-front:

    DynamicMatrix<float> A
       { { 1.0F, 1E-9F, 0.0F }
       , { 1E-9F, 2.0F, 1E-9F }
       , { 0.0F, 1E-9F, 3.0F } };
    
    CompressedMatrix<float> B( rows(A), columns(A), nonZeros(A) );  // Pre-allocate memory for 3 non-zero elements
    
    const float threshold( 1E-8F );
    B = map( A, [threshold]( float f ){ return f < threshold ? 0.0F : f; } );
    
    std::cerr << "\n"
              << " B =\n" << B << "\n"
              << " B.nonZeros() = " << B.nonZeros() << "\n"  // Prints 3
              << " B.capacity() = " << B.capacity() << "\n"  // Prints 3
              << "\n";
    

    Alternatively you can call shrinkToFit() after the copy operation (again, similar to std::vector):

    DynamicMatrix<float> A
       { { 1.0F, 1E-9F, 0.0F }
       , { 1E-9F, 2.0F, 1E-9F }
       , { 0.0F, 1E-9F, 3.0F } };
    
    const float threshold( 1E-8F );
    CompressedMatrix<float> B( map( A, [threshold]( float f ){ return f < threshold ? 0.0F : f; } ) );
    
    B.shrinkToFit();  // Re-allocates to adapt the capacity to the current requirements
    
    std::cerr << "\n"
              << " B =\n" << B << "\n"
              << " B.nonZeros() = " << B.nonZeros() << "\n"  // Prints 3
              << " B.capacity() = " << B.capacity() << "\n"  // Prints 3
              << "\n";
    

    I hope this answers your questions,

    Best regards,

    Klaus!

  4. Log in to comment