Outer sum/sub/div

Issue #143 resolved
Fabien Péan created an issue

It would be nice to have optimized expressions for outer add/sub/div, e.g.:

Vector<3, columnVector> a;
Vector<3, rowVector> b;
Matrix<double,3,3> m = a + b;
// [ a0+b0, a0+b1, a0+b2 ]
// [ a1+b0, a1+b1, a1+b2 ]
// [ a2+b0, a2+b1, a2+b2 ]

It would not interfere with existing code since these operations require already to have the same transpose flag to work component-wise.

Comments (5)

  1. Klaus Iglberger

    Hi Fabien!

    Thanks a lot for creating this proposal. We have already taken some time to think about it. So far we have never encountered an outer addition, subtraction or division and therefore never considered it. We suspect that this would cause more confusion and questions than it would help. Could you please give us some additional information, especially for which kind of application you need these operations? Is there any way to generalize this operation to enable you to customize it for your application (e.g. via an according map() function)?

    Best regards,

    Klaus!

  2. Fabien Péan reporter

    Hi Klaus,

    It is indeed not a common operation. However, they are available in e.g. numpy, mathematica. I do not have explicit application, some people could make use of it in nbody simulation or deep learning. I mainly asked about it because it seemed a consistent and concise approach within blaze to fill this gap.

    In fact what confuses me is why there is an error when one tries to add/sub/div two vectors with different transpose flag. Outer add/sub/div seemed to be consistent with blaze behavior, hence my question. Alternatively you can argue that blaze should follow strict mathematics, but then the only available add operation between vectors is the component-wise way. Therefore, disabling this combination generates friction for the user in both cases. Is there another view of this problem ?

    Regarding the outer add/sub/div operations, as you mentioned it could be done with the binary map function if it is extended to two vectors with different transpose flags, and return a correctly sized matrix. That would resolve add/sub/div at once by leaving the user customize the applied function.

    Cheers, Fabien

  3. Klaus Iglberger

    Summary

    The feature has been implemented, tested, optimized, and documented as required. It is immediately available via cloning the Blaze repository and will be officially released in Blaze 3.7.

    Outer map() Operation

    Via the map() functions it is possible to execute componentwise custom operations on vectors. Applying the map() function to a column vector and a row vector results in the outer map of the two vectors. The following example demonstrates the outer sum of a column vector and a row vector:

    blaze::DynamicVector<int,columnVector> v1{ 2, 5, -1 };
    blaze::DynamicVector<int,rowVector> v2{ -1, 3, -2, 4 };
    
    // Results in the matrix
    //
    //       (  1  5  0  6 )
    //   A = (  4  8  3  9 )
    //       ( -2  2 -3  3 )
    //
    blaze::StaticMatrix<int,3UL,4UL> M1 = map( v1, v2, []( int a, int b ){ return a + b; } );
    

    Outer Sum

    The addition between a column vector and a row vector results in the outer sum of the two vectors:

    blaze::StaticVector<int,3UL,columnVector> v1{ 2, 5, -1 };
    blaze::DynamicVector<int,rowVector> v2{ -1, 3, -2, 4 };
    
    // Results in the matrix
    //
    //       (  1  5  0  6 )
    //   A = (  4  8  3  9 )
    //       ( -2  2 -3  3 )
    //
    blaze::StaticMatrix<int,3UL,4UL> M1 = v1 + v2;
    

    The trans() function can be used to transpose a vector as necessary:

    blaze::StaticVector<int,2UL,rowVector> v1{ 2, 5, -1 };
    blaze::DynamicVector<int,rowVector> v2{ -1, 3, -2, 4 };
    
    blaze::StaticMatrix<int,3UL,4UL> M1 = trans( v1 ) + v2;
    

    Outer Difference

    The subtraction between a column vector and a row vector results in the outer difference of the two vectors:

    blaze::StaticVector<int,3UL,columnVector> v1{ 2, 5, -1 };
    blaze::DynamicVector<int,rowVector> v2{ -1, 3, -2, 4 };
    
    // Results in the matrix
    //
    //       ( 3 -1  4 -2 )
    //   A = ( 6  2  7  1 )
    //       ( 0 -4  1 -5 )
    //
    blaze::StaticMatrix<int,3UL,4UL> M1 = v1 - v2;
    

    The trans() function can be used to transpose a vector as necessary:

    blaze::StaticVector<int,2UL,rowVector> v1{ 2, 5, -1 };
    blaze::DynamicVector<int,rowVector> v2{ -1, 3, -2, 4 };
    
    blaze::StaticMatrix<int,3UL,4UL> M1 = trans( v1 ) - v2;
    

    Outer Quotient

    The division between a column vector and a row vector results in the outer quotient of the two vectors:

    blaze::StaticVector<double,3UL,columnVector> v1{ 2, 5, -1 };
    blaze::DynamicVector<double,rowVector> v2{ -1, 5, -2, 4 };
    
    // Results in the matrix
    //
    //       ( -2  0.4   -1   0.5 )
    //   A = ( -5    1 -2.5  1.25 )
    //       (  1 -0.2  0.5 -0.25 )
    //
    blaze::StaticMatrix<int,3UL,4UL> M1 = v1 / v2;
    

    The trans() function can be used to transpose a vector as necessary:

    blaze::StaticVector<int,2UL,rowVector> v1{ 2, 5, -1 };
    blaze::DynamicVector<int,rowVector> v2{ -1, 5, -2, 4 };
    
    blaze::StaticMatrix<int,3UL,4UL> M1 = trans( v1 ) / v2;
    

    Note that all values of the divisor must be non-zero and that no checks are performed to assert this precondition!

  4. Log in to comment