LCOV - code coverage report
Current view: top level - core/tests - test_LinearOperator.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info.cleaned Lines: 271 271 100.0 %
Date: 2022-02-28 03:37:41 Functions: 54 54 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file test_LinearOperator.cpp
       3             :  *
       4             :  * @brief Tests for LinearOperator class
       5             :  *
       6             :  * @author Tobias Lasser - main code
       7             :  * @author David Frank - composite tests
       8             :  * @author Nikola Dinev - fixes
       9             :  */
      10             : 
      11             : #include "doctest/doctest.h"
      12             : #include "LinearOperator.h"
      13             : #include "VolumeDescriptor.h"
      14             : 
      15             : using namespace elsa;
      16             : using namespace doctest;
      17             : 
      18             : // mock operator, which outputs 1 for apply and 2 for applyAdjoint
      19             : template <typename data_t = real_t>
      20             : class MockOperator : public LinearOperator<data_t>
      21             : {
      22             : public:
      23         524 :     MockOperator(const DataDescriptor& domain, const DataDescriptor& range)
      24         524 :         : LinearOperator<data_t>(domain, range)
      25             :     {
      26         524 :     }
      27             : 
      28             : protected:
      29          40 :     void applyImpl([[maybe_unused]] const DataContainer<data_t>& x,
      30             :                    DataContainer<data_t>& Ax) const override
      31             :     {
      32          40 :         Ax = 1;
      33          40 :     }
      34             : 
      35          40 :     void applyAdjointImpl([[maybe_unused]] const DataContainer<data_t>& y,
      36             :                           DataContainer<data_t>& Aty) const override
      37             :     {
      38          40 :         Aty = 3;
      39          40 :     }
      40             : 
      41             : protected:
      42         364 :     MockOperator<data_t>* cloneImpl() const override
      43             :     {
      44         364 :         return new MockOperator<data_t>(this->getDomainDescriptor(), this->getRangeDescriptor());
      45             :     }
      46             : };
      47             : 
      48          64 : TYPE_TO_STRING(complex<float>);
      49          64 : TYPE_TO_STRING(complex<double>);
      50             : 
      51             : TEST_SUITE_BEGIN("core");
      52             : 
      53          92 : TEST_CASE_TEMPLATE("LinearOperator: Testing construction", TestType, float, double, complex<float>,
      54             :                    complex<double>)
      55             : {
      56          24 :     GIVEN("DataDescriptors")
      57             :     {
      58          24 :         IndexVector_t numCoeff(2);
      59          12 :         numCoeff << 47, 11;
      60          24 :         IndexVector_t numCoeff2(2);
      61          12 :         numCoeff2 << 31, 23;
      62          24 :         VolumeDescriptor ddDomain(numCoeff);
      63          24 :         VolumeDescriptor ddRange(numCoeff2);
      64             : 
      65          24 :         WHEN("instantiating a LinearOperator")
      66             :         {
      67          24 :             LinearOperator<TestType> linOp(ddDomain, ddRange);
      68             : 
      69          16 :             THEN("the DataDescriptors are as expected")
      70             :             {
      71           4 :                 REQUIRE_EQ(linOp.getDomainDescriptor(), ddDomain);
      72           4 :                 REQUIRE_EQ(linOp.getRangeDescriptor(), ddRange);
      73             :             }
      74             : 
      75          16 :             THEN("the apply* operations throw")
      76             :             {
      77           8 :                 DataContainer<TestType> dc(ddDomain);
      78           8 :                 REQUIRE_THROWS_AS(linOp.apply(dc), LogicError);
      79           8 :                 REQUIRE_THROWS_AS(linOp.applyAdjoint(dc), LogicError);
      80             :             }
      81             : 
      82          16 :             THEN("copies are good")
      83             :             {
      84           8 :                 auto newOp = linOp;
      85           4 :                 REQUIRE_EQ(newOp, linOp);
      86             :             }
      87             :         }
      88             :     }
      89          12 : }
      90             : 
      91          88 : TEST_CASE_TEMPLATE("LinearOperator: Testing clone()", TestType, float, double, complex<float>,
      92             :                    complex<double>)
      93             : {
      94          16 :     GIVEN("a LinearOperator")
      95             :     {
      96          16 :         IndexVector_t numCoeff(3);
      97           8 :         numCoeff << 23, 45, 67;
      98          16 :         IndexVector_t numCoeff2(2);
      99           8 :         numCoeff2 << 78, 90;
     100          16 :         VolumeDescriptor ddDomain(numCoeff);
     101          16 :         VolumeDescriptor ddRange(numCoeff2);
     102          16 :         LinearOperator<TestType> linOp(ddDomain, ddRange);
     103             : 
     104          16 :         WHEN("cloning the LinearOperator")
     105             :         {
     106          16 :             auto linOpClone = linOp.clone();
     107             : 
     108          12 :             THEN("everything matches")
     109             :             {
     110           4 :                 REQUIRE_NE(linOpClone.get(), &linOp);
     111           4 :                 REQUIRE_EQ(*linOpClone, linOp);
     112             :             }
     113             : 
     114          12 :             THEN("copies are also identical")
     115             :             {
     116           8 :                 auto newOp = *linOpClone;
     117           4 :                 REQUIRE_EQ(newOp, linOp);
     118             :             }
     119             :         }
     120             :     }
     121           8 : }
     122             : 
     123         112 : TEST_CASE_TEMPLATE("LinearOperator: Testing a leaf LinearOperator", TestType, float, double,
     124             :                    complex<float>, complex<double>)
     125             : {
     126          48 :     GIVEN("a non-adjoint leaf linear operator")
     127             :     {
     128          32 :         IndexVector_t numCoeff(2);
     129          16 :         numCoeff << 12, 23;
     130          32 :         IndexVector_t numCoeff2(2);
     131          16 :         numCoeff2 << 34, 45;
     132          32 :         VolumeDescriptor ddDomain(numCoeff);
     133          32 :         VolumeDescriptor ddRange(numCoeff2);
     134          32 :         MockOperator<TestType> mockOp(ddDomain, ddRange);
     135             : 
     136          32 :         auto leafOp = leaf(mockOp);
     137             : 
     138          20 :         WHEN("the operator is there")
     139             :         {
     140           8 :             THEN("the descriptors are set correctly")
     141             :             {
     142           4 :                 REQUIRE_EQ(leafOp.getDomainDescriptor(), ddDomain);
     143           4 :                 REQUIRE_EQ(leafOp.getRangeDescriptor(), ddRange);
     144             :             }
     145             :         }
     146             : 
     147          24 :         WHEN("given data")
     148             :         {
     149          16 :             DataContainer<TestType> dcDomain(ddDomain);
     150          16 :             DataContainer<TestType> dcRange(ddRange);
     151             : 
     152          12 :             THEN("the apply operations return the correct result")
     153             :             {
     154           8 :                 auto resultApply = leafOp.apply(dcDomain);
     155        6124 :                 for (int i = 0; i < resultApply.getSize(); ++i)
     156        6120 :                     REQUIRE_EQ(resultApply[i], static_cast<TestType>(1));
     157             : 
     158           8 :                 auto resultApplyAdjoint = leafOp.applyAdjoint(dcRange);
     159        1108 :                 for (int i = 0; i < resultApplyAdjoint.getSize(); ++i)
     160        1104 :                     REQUIRE_EQ(resultApplyAdjoint[i], static_cast<TestType>(3));
     161             :             }
     162             : 
     163          12 :             THEN("the apply operations care for appropriately sized containers")
     164             :             {
     165           8 :                 REQUIRE_THROWS_AS(leafOp.apply(dcRange), InvalidArgumentError);
     166           8 :                 REQUIRE_THROWS_AS(leafOp.applyAdjoint(dcDomain), InvalidArgumentError);
     167             :             }
     168             :         }
     169             : 
     170          20 :         WHEN("copying/assigning")
     171             :         {
     172           8 :             auto newOp = leafOp;
     173           8 :             auto assignedOp = leaf(newOp);
     174             : 
     175           8 :             THEN("it should be identical")
     176             :             {
     177           4 :                 REQUIRE_EQ(newOp, leafOp);
     178           4 :                 REQUIRE_EQ(assignedOp, leaf(newOp));
     179             : 
     180           4 :                 assignedOp = newOp;
     181           4 :                 REQUIRE_EQ(assignedOp, newOp);
     182             :             }
     183             :         }
     184             :     }
     185             : 
     186          48 :     GIVEN("an adjoint linear operator")
     187             :     {
     188          32 :         IndexVector_t numCoeff(2);
     189          16 :         numCoeff << 12, 23;
     190          32 :         IndexVector_t numCoeff2(2);
     191          16 :         numCoeff2 << 34, 45;
     192          32 :         VolumeDescriptor ddDomain(numCoeff);
     193          32 :         VolumeDescriptor ddRange(numCoeff2);
     194          32 :         MockOperator<TestType> mockOp(ddDomain, ddRange);
     195             : 
     196          32 :         auto adjointOp = adjoint(mockOp);
     197             : 
     198          20 :         WHEN("the operator is there")
     199             :         {
     200           8 :             THEN("the descriptors are set correctly")
     201             :             {
     202           4 :                 REQUIRE_EQ(adjointOp.getDomainDescriptor(), ddRange);
     203           4 :                 REQUIRE_EQ(adjointOp.getRangeDescriptor(), ddDomain);
     204             :             }
     205             :         }
     206             : 
     207          24 :         WHEN("given data")
     208             :         {
     209          16 :             DataContainer<TestType> dcDomain(ddDomain);
     210          16 :             DataContainer<TestType> dcRange(ddRange);
     211             : 
     212          12 :             THEN("the apply operations return the correct result")
     213             :             {
     214           8 :                 auto resultApply = adjointOp.apply(dcRange);
     215        1108 :                 for (int i = 0; i < resultApply.getSize(); ++i)
     216        1104 :                     REQUIRE_EQ(resultApply[i], static_cast<TestType>(3));
     217             : 
     218           8 :                 auto resultApplyAdjoint = adjointOp.applyAdjoint(dcDomain);
     219        6124 :                 for (int i = 0; i < resultApplyAdjoint.getSize(); ++i)
     220        6120 :                     REQUIRE_EQ(resultApplyAdjoint[i], static_cast<TestType>(1));
     221             :             }
     222             : 
     223          12 :             THEN("the apply operations care for appropriately sized containers")
     224             :             {
     225           8 :                 REQUIRE_THROWS_AS(adjointOp.apply(dcDomain), InvalidArgumentError);
     226           8 :                 REQUIRE_THROWS_AS(adjointOp.applyAdjoint(dcRange), InvalidArgumentError);
     227             :             }
     228             :         }
     229             : 
     230          20 :         WHEN("copying/assigning")
     231             :         {
     232           8 :             auto newOp = adjointOp;
     233           8 :             auto assignedOp = adjoint(newOp);
     234             : 
     235           8 :             THEN("it should be identical")
     236             :             {
     237           4 :                 REQUIRE_EQ(newOp, adjointOp);
     238           4 :                 REQUIRE_EQ(assignedOp, adjoint(newOp));
     239             : 
     240           4 :                 assignedOp = newOp;
     241           4 :                 REQUIRE_EQ(assignedOp, newOp);
     242             :             }
     243             :         }
     244             :     }
     245          32 : }
     246             : 
     247         144 : TEST_CASE_TEMPLATE("LinearOperator: Testing composite LinearOperator", TestType, float, double,
     248             :                    complex<float>, complex<double>)
     249             : {
     250          80 :     GIVEN("an additive composite linear operator")
     251             :     {
     252          32 :         IndexVector_t numCoeff(2);
     253          16 :         numCoeff << 45, 67;
     254          32 :         IndexVector_t numCoeff2(2);
     255          16 :         numCoeff2 << 13, 48;
     256          32 :         VolumeDescriptor ddDomain(numCoeff);
     257          32 :         VolumeDescriptor ddRange(numCoeff2);
     258             : 
     259          32 :         MockOperator<TestType> op1(ddDomain, ddRange);
     260          32 :         MockOperator<TestType> op2(ddDomain, ddRange);
     261             : 
     262          32 :         auto addOp = op1 + op2;
     263             : 
     264          20 :         WHEN("the operator is there")
     265             :         {
     266           8 :             THEN("the descriptors are set correctly")
     267             :             {
     268           4 :                 REQUIRE_EQ(addOp.getDomainDescriptor(), ddDomain);
     269           4 :                 REQUIRE_EQ(addOp.getRangeDescriptor(), ddRange);
     270             :             }
     271             :         }
     272             : 
     273          24 :         WHEN("given data")
     274             :         {
     275          16 :             DataContainer<TestType> dcDomain(ddDomain);
     276          16 :             DataContainer<TestType> dcRange(ddRange);
     277             : 
     278          12 :             THEN("the apply operations return the correct result")
     279             :             {
     280           8 :                 auto resultApply = addOp.apply(dcDomain);
     281        2500 :                 for (int i = 0; i < resultApply.getSize(); ++i)
     282        2496 :                     REQUIRE_EQ(resultApply[i], static_cast<TestType>(2));
     283             : 
     284           8 :                 auto resultApplyAdjoint = addOp.applyAdjoint(dcRange);
     285       12064 :                 for (int i = 0; i < resultApplyAdjoint.getSize(); ++i)
     286       12060 :                     REQUIRE_EQ(resultApplyAdjoint[i], static_cast<TestType>(6));
     287             :             }
     288             : 
     289          12 :             THEN("the apply operations care for appropriately sized containers")
     290             :             {
     291           8 :                 REQUIRE_THROWS_AS(addOp.apply(dcRange), InvalidArgumentError);
     292           8 :                 REQUIRE_THROWS_AS(addOp.applyAdjoint(dcDomain), InvalidArgumentError);
     293             :             }
     294             :         }
     295             : 
     296          20 :         WHEN("copying/assigning")
     297             :         {
     298           8 :             auto newOp = addOp;
     299           8 :             auto assignedOp = adjoint(newOp);
     300             : 
     301           8 :             THEN("it should be identical")
     302             :             {
     303           4 :                 REQUIRE_EQ(newOp, addOp);
     304           4 :                 REQUIRE_EQ(assignedOp, adjoint(newOp));
     305             : 
     306           4 :                 assignedOp = newOp;
     307           4 :                 REQUIRE_EQ(assignedOp, newOp);
     308             :             }
     309             :         }
     310             :     }
     311             : 
     312          80 :     GIVEN("a multiplicative composite linear operator")
     313             :     {
     314          32 :         IndexVector_t numCoeff(3);
     315          16 :         numCoeff << 13, 47, 69;
     316          32 :         IndexVector_t numCoeff2(2);
     317          16 :         numCoeff2 << 15, 28;
     318          32 :         IndexVector_t numCoeff3(4);
     319          16 :         numCoeff3 << 7, 30, 83, 13;
     320          32 :         VolumeDescriptor ddDomain(numCoeff);
     321          32 :         VolumeDescriptor ddMiddle(numCoeff2);
     322          32 :         VolumeDescriptor ddRange(numCoeff3);
     323             : 
     324          32 :         MockOperator<TestType> op1(ddDomain, ddMiddle);
     325          32 :         MockOperator<TestType> op2(ddMiddle, ddRange);
     326             : 
     327          32 :         auto multOp = op2 * op1;
     328             : 
     329          20 :         WHEN("the operator is there")
     330             :         {
     331           8 :             THEN("the descriptors are set correctly")
     332             :             {
     333           4 :                 REQUIRE_EQ(multOp.getDomainDescriptor(), ddDomain);
     334           4 :                 REQUIRE_EQ(multOp.getRangeDescriptor(), ddRange);
     335             :             }
     336             :         }
     337             : 
     338          24 :         WHEN("given data")
     339             :         {
     340          16 :             DataContainer<TestType> dcDomain(ddDomain);
     341          16 :             DataContainer<TestType> dcRange(ddRange);
     342             : 
     343          12 :             THEN("the apply operations return the correct result")
     344             :             {
     345           8 :                 auto resultApply = multOp.apply(dcDomain);
     346      906364 :                 for (int i = 0; i < resultApply.getSize(); ++i)
     347      906360 :                     REQUIRE_EQ(resultApply[i], static_cast<TestType>(1));
     348             : 
     349           8 :                 auto resultApplyAdjoint = multOp.applyAdjoint(dcRange);
     350      168640 :                 for (int i = 0; i < resultApplyAdjoint.getSize(); ++i)
     351      168636 :                     REQUIRE_EQ(resultApplyAdjoint[i], static_cast<TestType>(3));
     352             :             }
     353             : 
     354          12 :             THEN("the apply operations care for appropriately sized containers")
     355             :             {
     356           8 :                 REQUIRE_THROWS_AS(multOp.apply(dcRange), InvalidArgumentError);
     357           8 :                 REQUIRE_THROWS_AS(multOp.applyAdjoint(dcDomain), InvalidArgumentError);
     358             :             }
     359             :         }
     360             : 
     361          20 :         WHEN("copying/assigning")
     362             :         {
     363           8 :             auto newOp = multOp;
     364           8 :             auto assignedOp = adjoint(newOp);
     365             : 
     366           8 :             THEN("it should be identical")
     367             :             {
     368           4 :                 REQUIRE_EQ(newOp, multOp);
     369           4 :                 REQUIRE_EQ(assignedOp, adjoint(newOp));
     370             : 
     371           4 :                 assignedOp = newOp;
     372           4 :                 REQUIRE_EQ(assignedOp, newOp);
     373             :             }
     374             :         }
     375             :     }
     376             : 
     377          80 :     GIVEN("a scalar multiplicative composite linear operator")
     378             :     {
     379          32 :         IndexVector_t numCoeff(3);
     380          16 :         numCoeff << 13, 47, 69;
     381          32 :         IndexVector_t otherNumCoeff(2);
     382          16 :         otherNumCoeff << 15, 28;
     383          32 :         VolumeDescriptor ddDomain(numCoeff);
     384          32 :         VolumeDescriptor ddRange(otherNumCoeff);
     385             : 
     386          32 :         MockOperator op(ddDomain, ddRange);
     387          16 :         real_t scalar = 8;
     388             : 
     389          32 :         auto scalarMultOp = scalar * op;
     390             : 
     391          20 :         WHEN("the operator is there")
     392             :         {
     393           8 :             THEN("the descriptors are set correctly")
     394             :             {
     395           4 :                 REQUIRE_EQ(scalarMultOp.getDomainDescriptor(), ddDomain);
     396           4 :                 REQUIRE_EQ(scalarMultOp.getRangeDescriptor(), ddRange);
     397             :             }
     398             :         }
     399             : 
     400          24 :         WHEN("given data")
     401             :         {
     402          16 :             DataContainer dcDomain(ddDomain);
     403          16 :             DataContainer dcRange(ddRange);
     404             : 
     405          12 :             THEN("the apply operations return the correct result")
     406             :             {
     407           8 :                 auto resultApply = scalarMultOp.apply(dcDomain);
     408        1684 :                 for (int i = 0; i < resultApply.getSize(); ++i)
     409        1680 :                     REQUIRE_EQ(resultApply[i], static_cast<real_t>(8));
     410             : 
     411           8 :                 auto resultApplyAdjoint = scalarMultOp.applyAdjoint(dcRange);
     412      168640 :                 for (int i = 0; i < resultApplyAdjoint.getSize(); ++i)
     413      168636 :                     REQUIRE_EQ(resultApplyAdjoint[i], static_cast<real_t>(24));
     414             :             }
     415             : 
     416          12 :             THEN("the apply operations account for appropriately sized containers")
     417             :             {
     418           8 :                 REQUIRE_THROWS_AS(scalarMultOp.apply(dcRange), InvalidArgumentError);
     419           8 :                 REQUIRE_THROWS_AS(scalarMultOp.applyAdjoint(dcDomain), InvalidArgumentError);
     420             :             }
     421             :         }
     422             : 
     423          20 :         WHEN("copying/assigning")
     424             :         {
     425           4 :             const auto& newOp = scalarMultOp;
     426           8 :             auto assignedOp = adjoint(newOp);
     427             : 
     428           8 :             THEN("it should be identical")
     429             :             {
     430           4 :                 REQUIRE_EQ(newOp, scalarMultOp);
     431           4 :                 REQUIRE_EQ(assignedOp, adjoint(newOp));
     432             : 
     433           4 :                 assignedOp = newOp;
     434           4 :                 REQUIRE_EQ(assignedOp, newOp);
     435             :             }
     436             :         }
     437             :     }
     438             : 
     439          80 :     GIVEN("a complex composite with multiple leafs and levels")
     440             :     {
     441          32 :         IndexVector_t numCoeff(2);
     442          16 :         numCoeff << 13, 38;
     443          32 :         IndexVector_t numCoeff2(1);
     444          16 :         numCoeff2 << 16;
     445          32 :         IndexVector_t numCoeff3(3);
     446          16 :         numCoeff3 << 17, 38, 15;
     447          32 :         VolumeDescriptor ddDomain(numCoeff);
     448          32 :         VolumeDescriptor ddRange(numCoeff2);
     449          32 :         VolumeDescriptor ddFinalRange(numCoeff3);
     450             : 
     451          32 :         MockOperator<TestType> op1(ddDomain, ddRange);
     452          32 :         MockOperator<TestType> op2(ddFinalRange, ddRange);
     453          32 :         MockOperator<TestType> op3(ddRange, ddFinalRange);
     454             : 
     455          48 :         auto compositeOp = (op3 + adjoint(op2)) * op1;
     456             : 
     457          20 :         WHEN("the operator is there")
     458             :         {
     459           8 :             THEN("the descriptors are set correctly")
     460             :             {
     461           4 :                 REQUIRE_EQ(compositeOp.getDomainDescriptor(), ddDomain);
     462           4 :                 REQUIRE_EQ(compositeOp.getRangeDescriptor(), ddFinalRange);
     463             :             }
     464             :         }
     465             : 
     466          24 :         WHEN("given data")
     467             :         {
     468          16 :             DataContainer<TestType> dcDomain(ddDomain);
     469          16 :             DataContainer<TestType> dcFinalRange(ddFinalRange);
     470             : 
     471          12 :             THEN("the apply operations return the correct result")
     472             :             {
     473           8 :                 auto resultApply = compositeOp.apply(dcDomain);
     474       38764 :                 for (int i = 0; i < resultApply.getSize(); ++i)
     475       38760 :                     REQUIRE_EQ(resultApply[i], static_cast<TestType>(4));
     476             : 
     477           8 :                 auto resultApplyAdjoint = compositeOp.applyAdjoint(dcFinalRange);
     478        1980 :                 for (int i = 0; i < resultApplyAdjoint.getSize(); ++i)
     479        1976 :                     REQUIRE_EQ(resultApplyAdjoint[i], static_cast<TestType>(3));
     480             :             }
     481             : 
     482          12 :             THEN("the apply operations expect appropriately sized containers")
     483             :             {
     484           8 :                 REQUIRE_THROWS_AS(compositeOp.apply(dcFinalRange), InvalidArgumentError);
     485           8 :                 REQUIRE_THROWS_AS(compositeOp.applyAdjoint(dcDomain), InvalidArgumentError);
     486             :             }
     487             :         }
     488             : 
     489          20 :         WHEN("copying/assigning")
     490             :         {
     491           8 :             auto newOp = compositeOp;
     492           8 :             auto assignedOp = adjoint(newOp);
     493             : 
     494           8 :             THEN("it should be identical")
     495             :             {
     496           4 :                 REQUIRE_EQ(newOp, compositeOp);
     497           4 :                 REQUIRE_EQ(assignedOp, adjoint(newOp));
     498             : 
     499           4 :                 assignedOp = newOp;
     500           4 :                 REQUIRE_EQ(assignedOp, newOp);
     501             :             }
     502             :         }
     503             :     }
     504          64 : }
     505             : 
     506             : TEST_SUITE_END();

Generated by: LCOV version 1.15