Commit 7004ac58 authored by Nikola Dinev's avatar Nikola Dinev Committed by Tobias Lasser
Browse files

Protect copy constructors in non-leaf and leaf classes, to avoid the...

Protect copy constructors in non-leaf and leaf classes, to avoid the implicitly generated ones by the compiler that could cause slicing. In Cloneable, the assignment operator was deleted to disable issues with implicitly generated assingment operators in derived classes to prevent slicing. (resolves #31)
parent aea3cb41
Pipeline #190113 passed with stages
in 7 minutes and 20 seconds
......@@ -29,6 +29,9 @@ namespace elsa
~Identity() override = default;
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
Identity(const Identity<data_t>&) = default;
/**
* \brief apply the identity operator A to x, i.e. Ax = x
*
......
......@@ -50,6 +50,9 @@ namespace elsa
const DataContainer<data_t>& getScaleFactors() const;
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
Scaling(const Scaling<data_t>&) = default;
/// apply the scaling operation
void applyImpl(const DataContainer<data_t>& x, DataContainer<data_t>& Ax) const override;
......
......@@ -55,6 +55,9 @@ namespace elsa
~BinaryMethod() override = default;
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
BinaryMethod(const BinaryMethod<data_t>&) = default;
/// apply the binary method (i.e. forward projection)
void applyImpl(const DataContainer<data_t>& x, DataContainer<data_t>& Ax) const override;
......
......@@ -62,6 +62,9 @@ namespace elsa
~JosephsMethod() = default;
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
JosephsMethod(const JosephsMethod<data_t>&) = default;
/// apply Joseph's method (i.e. forward projection)
void applyImpl(const DataContainer<data_t>& x, DataContainer<data_t>& Ax) const override;
......
......@@ -51,6 +51,9 @@ namespace elsa
~SiddonsMethod() override = default;
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
SiddonsMethod(const SiddonsMethod<data_t>&) = default;
/// apply Siddon's method (i.e. forward projection)
void applyImpl(const DataContainer<data_t>& x, DataContainer<data_t>& Ax) const override;
......
......@@ -277,7 +277,7 @@ SCENARIO("Testing BinaryMethod")
THEN("Domain descriptor is still the same")
{
auto retDescriptor = op.getDomainDescriptor();
auto& retDescriptor = op.getDomainDescriptor();
CHECK(retDescriptor.getNumberOfCoefficientsPerDimension()(0) == 5);
CHECK(retDescriptor.getNumberOfCoefficientsPerDimension()(1) == 5);
......@@ -285,7 +285,7 @@ SCENARIO("Testing BinaryMethod")
THEN("Domain descriptor is still the same")
{
auto retDescriptor = op.getRangeDescriptor();
auto& retDescriptor = op.getRangeDescriptor();
CHECK(retDescriptor.getNumberOfCoefficientsPerDimension()(0) == 5);
CHECK(retDescriptor.getNumberOfCoefficientsPerDimension()(1) == 1);
......
......@@ -64,6 +64,9 @@ namespace elsa
virtual ~JosephsMethodCUDA();
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
JosephsMethodCUDA(const JosephsMethodCUDA<data_t>&) = default;
/// apply Joseph's method (i.e. forward projection)
void applyImpl(const DataContainer<data_t>& x, DataContainer<data_t>& Ax) const override;
......
......@@ -55,6 +55,9 @@ namespace elsa
~SiddonsMethodCUDA();
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
SiddonsMethodCUDA(const SiddonsMethodCUDA<data_t>&) = default;
/// apply Siddon's method (i.e. forward projection)
void applyImpl(const DataContainer<data_t>& x, DataContainer<data_t>& Ax) const override;
......
......@@ -64,6 +64,10 @@ namespace elsa
/// default destructor
~CG() override = default;
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
CG(const CG<data_t>&) = default;
private:
/// the default number of iterations
const index_t _defaultIterations{100};
......
......@@ -30,6 +30,10 @@ namespace elsa
/// default destructor
~GradientDescent() override = default;
protected:
/// default copy constructor, hidden from non-derived classes to prevent potential slicing
GradientDescent(const GradientDescent<data_t>&) = default;
private:
/// the step size
real_t _stepSize;
......
......@@ -23,7 +23,7 @@ if(ELSA_BUILD_CUDA_PROJECTORS)
# build the GPU projector speed test program
add_executable(speed_test speed_test.cpp)
target_link_libraries(speed_test elsa_generators elsa_projectors_cuda elsa_logging)
target_link_libraries(speed_test elsa::all)
target_compile_features(speed_test PUBLIC cxx_std_17)
add_dependencies(examples speed_test)
endif()
......
......@@ -9,7 +9,8 @@ using namespace elsa;
void example2d()
{
// generate 2d phantom
IndexVector_t size(2); size << 128, 128;
IndexVector_t size(2);
size << 128, 128;
auto phantom = PhantomGenerator<real_t>::createModifiedSheppLogan(size);
// write the phantom out
......@@ -17,12 +18,12 @@ void example2d()
// generate circular trajectory
index_t noAngles{100}, arc{360};
auto [geometry, sinoDescriptor] = CircleTrajectoryGenerator::createTrajectory(noAngles, phantom.getDataDescriptor(),
arc, size(0)*100, size(0));
auto [geometry, sinoDescriptor] = CircleTrajectoryGenerator::createTrajectory(
noAngles, phantom.getDataDescriptor(), arc, size(0) * 100, size(0));
// setup operator for 2d X-ray transform
Logger::get("Info")->info("Simulating sinogram using Siddon's method");
SiddonsMethod projector(phantom.getDataDescriptor(), sinoDescriptor, geometry);
SiddonsMethod projector(phantom.getDataDescriptor(), *sinoDescriptor, geometry);
// simulate the sinogram
auto sinogram = projector.apply(phantom);
......@@ -30,28 +31,26 @@ void example2d()
// write the sinogram out
EDF::write(sinogram, "2dsinogram.edf");
// setup reconstruction problem
WLSProblem problem(projector, sinogram);
// solve the reconstruction problem
GradientDescent solver(problem, 1.0/size.prod());
GradientDescent solver(problem, 1.0 / size.prod());
index_t noIterations{50};
Logger::get("Info")->info("Solving reconstruction using {} iterations of gradient descent", noIterations);
Logger::get("Info")->info("Solving reconstruction using {} iterations of gradient descent",
noIterations);
auto reconstruction = solver.solve(noIterations);
// write the reconstruction out
EDF::write(reconstruction, "2dreconstruction.edf");
}
int main()
{
try {
example2d();
}
catch (std::exception& e) {
} catch (std::exception& e) {
std::cerr << "An exception occurred: " << e.what() << "\n";
}
}
......@@ -6,10 +6,11 @@
using namespace elsa;
void example2d()
void example3d()
{
// generate 3d phantom
IndexVector_t size(3); size << 128, 128, 128;
IndexVector_t size(3);
size << 128, 128, 128;
auto phantom = PhantomGenerator<real_t>::createModifiedSheppLogan(size);
// write the phantom out
......@@ -17,12 +18,12 @@ void example2d()
// generate circular trajectory
index_t noAngles{100}, arc{360};
auto [geometry, sinoDescriptor] = CircleTrajectoryGenerator::createTrajectory(noAngles, phantom.getDataDescriptor(),
arc, size(0)*100, size(0));
auto [geometry, sinoDescriptor] = CircleTrajectoryGenerator::createTrajectory(
noAngles, phantom.getDataDescriptor(), arc, size(0) * 100, size(0));
// setup operator for 2d X-ray transform
Logger::get("Info")->info("Simulating sinogram using Siddon's method");
JosephsMethodCUDA projector(phantom.getDataDescriptor(), sinoDescriptor, geometry);
JosephsMethodCUDA projector(phantom.getDataDescriptor(), *sinoDescriptor, geometry);
// simulate the sinogram
auto sinogram = projector.apply(phantom);
......@@ -30,28 +31,26 @@ void example2d()
// write the sinogram out
EDF::write(sinogram, "3dsinogram.edf");
// setup reconstruction problem
WLSProblem problem(projector, sinogram);
// solve the reconstruction problem
GradientDescent solver(problem, 1.0/size.prod());
GradientDescent solver(problem, 1.0 / size.prod());
index_t noIterations{50};
Logger::get("Info")->info("Solving reconstruction using {} iterations of gradient descent", noIterations);
Logger::get("Info")->info("Solving reconstruction using {} iterations of gradient descent",
noIterations);
auto reconstruction = solver.solve(noIterations);
// write the reconstruction out
EDF::write(reconstruction, "3dreconstruction.edf");
}
int main()
{
try {
example2d();
}
catch (std::exception& e) {
example3d();
} catch (std::exception& e) {
std::cerr << "An exception occurred: " << e.what() << "\n";
}
}
/// Elsa example program: test execution speed of GPU projectors
#include "CircleTrajectoryGenerator.h"
#include "PhantomGenerator.h"
#include "Logger.h"
#include "JosephsMethodCUDA.h"
#include "SiddonsMethodCUDA.h"
#include "elsa.h"
#include <chrono>
#include <iostream>
......@@ -13,7 +8,8 @@
using namespace elsa;
void testExecutionSpeed(LinearOperator<real_t>& projector, DataContainer<real_t>& volume, int numIters)
void testExecutionSpeed(LinearOperator<real_t>& projector, DataContainer<real_t>& volume,
int numIters)
{
real_t timer = 0;
......@@ -22,30 +18,30 @@ void testExecutionSpeed(LinearOperator<real_t>& projector, DataContainer<real_t>
DataContainer backproj(projector.getDomainDescriptor());
// run test for forward projection
for (int i = 0 ; i < numIters; ++i) {
for (int i = 0; i < numIters; ++i) {
auto start = std::chrono::system_clock::now();
projector.apply(volume,projections);
projector.apply(volume, projections);
auto stop = std::chrono::system_clock::now();
timer += std::chrono::duration_cast<std::chrono::milliseconds>(stop-start).count();
timer += std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
}
// log average execution time
timer /= numIters;
Logger::get("Timing")->info("average apply time: {}\n",timer);
Logger::get("Timing")->info("average apply time: {}\n", timer);
timer = 0;
// run test for backward projection
for (int i = 0; i < numIters; ++i) {
auto start = std::chrono::system_clock::now();
projector.applyAdjoint(projections,backproj);
projector.applyAdjoint(projections, backproj);
auto stop = std::chrono::system_clock::now();
timer += std::chrono::duration_cast<std::chrono::milliseconds>(stop-start).count();
timer += std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
}
// log average execution time
timer /= numIters;
Logger::get("Timing")->info("average apply adjoint time: {}\n",timer);
Logger::get("Timing")->info("average apply adjoint time: {}\n", timer);
}
int main()
......@@ -59,33 +55,33 @@ int main()
// each operation will be applied numIters times, and the average displayed
int numIters = 3;
for (int size: sizes) {
for (int size : sizes) {
Logger::get("Setup")->info("Running test for a volume with {}^3 voxels\n", size);
// create 3d phantom
IndexVector_t volumeSize(3);
volumeSize << size, size, size;
auto phantom = PhantomGenerator<real_t>::createModifiedSheppLogan(volumeSize);
auto volumeDescriptor = phantom.getDataDescriptor();
auto& volumeDescriptor = phantom.getDataDescriptor();
// generate circular trajectory with numAngles angles over 360 degrees
auto [geom, sinoDescriptor] = CircleTrajectoryGenerator::createTrajectory(numAngles, volumeDescriptor, 360, 30.0f*size, 2.0f*size);
auto [geom, sinoDescriptor] = CircleTrajectoryGenerator::createTrajectory(
numAngles, volumeDescriptor, 360, 30.0f * size, 2.0f * size);
// setup and run test for fast Joseph's
Logger::get("Setup")->info("Fast unmatched Joseph's:\n");
auto josephsFast = JosephsMethodCUDA(volumeDescriptor, sinoDescriptor, geom);
auto josephsFast = JosephsMethodCUDA(volumeDescriptor, *sinoDescriptor, geom);
testExecutionSpeed(josephsFast, phantom, numIters);
// setup and run test for slow Joseph's
Logger::get("Setup")->info("Slow matched Joseph's:\n");
auto josephsSlow = JosephsMethodCUDA(volumeDescriptor, sinoDescriptor, geom, false);
auto josephsSlow = JosephsMethodCUDA(volumeDescriptor, *sinoDescriptor, geom, false);
testExecutionSpeed(josephsSlow, phantom, numIters);
// setup and run test for Siddon's
Logger::get("Setup")->info("Siddon's:\n");
auto siddons = SiddonsMethodCUDA(volumeDescriptor, sinoDescriptor, geom);
auto siddons = SiddonsMethodCUDA(volumeDescriptor, *sinoDescriptor, geom);
testExecutionSpeed(siddons, phantom, numIters);
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment