Added expression templates (#98)

- Works with NDArray
- Works with NDView
This commit is contained in:
Erik Fröjdh 2024-11-15 15:17:52 +01:00 committed by GitHub
parent 0d058274d5
commit e77b615293
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 341 additions and 64 deletions

View File

@ -29,6 +29,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
# General options # General options
option(AARE_PYTHON_BINDINGS "Build python bindings" ON) option(AARE_PYTHON_BINDINGS "Build python bindings" ON)
option(AARE_TESTS "Build tests" OFF) option(AARE_TESTS "Build tests" OFF)
option(AARE_BENCHMARKS "Build benchmarks" OFF)
option(AARE_EXAMPLES "Build examples" OFF) option(AARE_EXAMPLES "Build examples" OFF)
option(AARE_IN_GITHUB_ACTIONS "Running in Github Actions" OFF) option(AARE_IN_GITHUB_ACTIONS "Running in Github Actions" OFF)
option(AARE_DOCS "Build documentation" OFF) option(AARE_DOCS "Build documentation" OFF)
@ -63,6 +64,10 @@ if(AARE_CUSTOM_ASSERT)
add_compile_definitions(AARE_CUSTOM_ASSERT) add_compile_definitions(AARE_CUSTOM_ASSERT)
endif() endif()
if(AARE_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@ -242,6 +247,7 @@ endif()
###------------------------------------------------------------------------------------------ ###------------------------------------------------------------------------------------------
set(PUBLICHEADERS set(PUBLICHEADERS
include/aare/ArrayExpr.hpp
include/aare/ClusterFinder.hpp include/aare/ClusterFinder.hpp
include/aare/ClusterFile.hpp include/aare/ClusterFile.hpp
include/aare/CtbRawFile.hpp include/aare/CtbRawFile.hpp

11
benchmarks/CMakeLists.txt Normal file
View File

@ -0,0 +1,11 @@
find_package(benchmark REQUIRED)
add_executable(ndarray_benchmark ndarray_benchmark.cpp)
target_link_libraries(ndarray_benchmark benchmark::benchmark aare_core aare_compiler_flags)
# target_link_libraries(tests PRIVATE aare_core aare_compiler_flags)
set_target_properties(ndarray_benchmark PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
# OUTPUT_NAME run_tests
)

View File

@ -0,0 +1,136 @@
#include <benchmark/benchmark.h>
#include "aare/NDArray.hpp"
using aare::NDArray;
constexpr ssize_t size = 1024;
class TwoArrays : public benchmark::Fixture {
public:
NDArray<int,2> a{{size,size},0};
NDArray<int,2> b{{size,size},0};
void SetUp(::benchmark::State& state) {
for(uint32_t i = 0; i < size; i++){
for(uint32_t j = 0; j < size; j++){
a(i, j)= i*j+1;
b(i, j)= i*j+1;
}
}
}
// void TearDown(::benchmark::State& state) {
// }
};
BENCHMARK_F(TwoArrays, AddWithOperator)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res = a+b;
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, AddWithIndex)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) + b(i);
}
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, SubtractWithOperator)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res = a-b;
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, SubtractWithIndex)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) - b(i);
}
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, MultiplyWithOperator)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res = a*b;
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, MultiplyWithIndex)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) * b(i);
}
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, DivideWithOperator)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res = a/b;
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, DivideWithIndex)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) / b(i);
}
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, FourAddWithOperator)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res = a+b+a+b;
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, FourAddWithIndex)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) + b(i) + a(i) + b(i);
}
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithOperator)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res = a*a+b/a;
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithIndex)(benchmark::State& st) {
for (auto _ : st) {
// This code gets timed
NDArray<int,2> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
res(i) = a(i) * a(i) + b(i) / a(i);
}
benchmark::DoNotOptimize(res);
}
}
BENCHMARK_MAIN();

View File

@ -0,0 +1,99 @@
#pragma once
#include <cstdint> //int64_t
#include <cstddef> //size_t
#include <array>
#include <cassert>
namespace aare {
template <typename E, int64_t Ndim> class ArrayExpr {
public:
static constexpr bool is_leaf = false;
auto operator[](size_t i) const { return static_cast<E const &>(*this)[i]; }
auto operator()(size_t i) const { return static_cast<E const &>(*this)[i]; }
auto size() const { return static_cast<E const &>(*this).size(); }
std::array<int64_t, Ndim> shape() const { return static_cast<E const &>(*this).shape(); }
};
template <typename A, typename B, int64_t Ndim>
class ArrayAdd : public ArrayExpr<ArrayAdd<A, B, Ndim>, Ndim> {
const A &arr1_;
const B &arr2_;
public:
ArrayAdd(const A &arr1, const B &arr2) : arr1_(arr1), arr2_(arr2) {
assert(arr1.size() == arr2.size());
}
auto operator[](int i) const { return arr1_[i] + arr2_[i]; }
size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
};
template <typename A, typename B, int64_t Ndim>
class ArraySub : public ArrayExpr<ArraySub<A, B, Ndim>, Ndim> {
const A &arr1_;
const B &arr2_;
public:
ArraySub(const A &arr1, const B &arr2) : arr1_(arr1), arr2_(arr2) {
assert(arr1.size() == arr2.size());
}
auto operator[](int i) const { return arr1_[i] - arr2_[i]; }
size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
};
template <typename A, typename B, int64_t Ndim>
class ArrayMul : public ArrayExpr<ArrayMul<A, B, Ndim>,Ndim> {
const A &arr1_;
const B &arr2_;
public:
ArrayMul(const A &arr1, const B &arr2) : arr1_(arr1), arr2_(arr2) {
assert(arr1.size() == arr2.size());
}
auto operator[](int i) const { return arr1_[i] * arr2_[i]; }
size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
};
template <typename A, typename B, int64_t Ndim>
class ArrayDiv : public ArrayExpr<ArrayDiv<A, B, Ndim>, Ndim> {
const A &arr1_;
const B &arr2_;
public:
ArrayDiv(const A &arr1, const B &arr2) : arr1_(arr1), arr2_(arr2) {
assert(arr1.size() == arr2.size());
}
auto operator[](int i) const { return arr1_[i] / arr2_[i]; }
size_t size() const { return arr1_.size(); }
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
};
template <typename A, typename B, int64_t Ndim>
auto operator+(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArrayAdd<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
}
template <typename A, typename B, int64_t Ndim>
auto operator-(const ArrayExpr<A,Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArraySub<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
}
template <typename A, typename B, int64_t Ndim>
auto operator*(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArrayMul<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
}
template <typename A, typename B, int64_t Ndim>
auto operator/(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
return ArrayDiv<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
}
} // namespace aare

View File

@ -7,6 +7,7 @@ memory.
TODO! Add expression templates for operators TODO! Add expression templates for operators
*/ */
#include "aare/ArrayExpr.hpp"
#include "aare/NDView.hpp" #include "aare/NDView.hpp"
#include <algorithm> #include <algorithm>
@ -20,7 +21,8 @@ TODO! Add expression templates for operators
namespace aare { namespace aare {
template <typename T, int64_t Ndim = 2> class NDArray { template <typename T, int64_t Ndim = 2>
class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
std::array<int64_t, Ndim> shape_; std::array<int64_t, Ndim> shape_;
std::array<int64_t, Ndim> strides_; std::array<int64_t, Ndim> strides_;
size_t size_{}; size_t size_{};
@ -43,7 +45,7 @@ template <typename T, int64_t Ndim = 2> class NDArray {
: shape_(shape), strides_(c_strides<Ndim>(shape_)), : shape_(shape), strides_(c_strides<Ndim>(shape_)),
size_(std::accumulate(shape_.begin(), shape_.end(), 1, size_(std::accumulate(shape_.begin(), shape_.end(), 1,
std::multiplies<>())), std::multiplies<>())),
data_(new T[size_]) {}; data_(new T[size_]) {}
/** /**
* @brief Construct a new NDArray object with a shape and value. * @brief Construct a new NDArray object with a shape and value.
@ -69,8 +71,7 @@ template <typename T, int64_t Ndim = 2> class NDArray {
NDArray(NDArray &&other) noexcept NDArray(NDArray &&other) noexcept
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)), : shape_(other.shape_), strides_(c_strides<Ndim>(shape_)),
size_(other.size_), data_(other.data_) { size_(other.size_), data_(other.data_) {
other.reset(); // TODO! is this necessary?
other.reset();
} }
// Copy constructor // Copy constructor
@ -78,7 +79,14 @@ template <typename T, int64_t Ndim = 2> class NDArray {
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)), : shape_(other.shape_), strides_(c_strides<Ndim>(shape_)),
size_(other.size_), data_(new T[size_]) { size_(other.size_), data_(new T[size_]) {
std::copy(other.data_, other.data_ + size_, data_); std::copy(other.data_, other.data_ + size_, data_);
// fmt::print("NDArray(const NDArray &other)\n"); }
// Conversion operator from array expression to array
template <typename E>
NDArray(ArrayExpr<E, Ndim> &&expr) : NDArray(expr.shape()) {
for (int i = 0; i < size_; ++i) {
data_[i] = expr[i];
}
} }
~NDArray() { delete[] data_; } ~NDArray() { delete[] data_; }
@ -90,14 +98,10 @@ template <typename T, int64_t Ndim = 2> class NDArray {
NDArray &operator=(NDArray &&other) noexcept; // Move assign NDArray &operator=(NDArray &&other) noexcept; // Move assign
NDArray &operator=(const NDArray &other); // Copy assign NDArray &operator=(const NDArray &other); // Copy assign
NDArray operator+(const NDArray &other);
NDArray &operator+=(const NDArray &other); NDArray &operator+=(const NDArray &other);
NDArray operator-(const NDArray &other);
NDArray &operator-=(const NDArray &other); NDArray &operator-=(const NDArray &other);
NDArray operator*(const NDArray &other);
NDArray &operator*=(const NDArray &other); NDArray &operator*=(const NDArray &other);
NDArray operator/(const NDArray &other);
// NDArray& operator/=(const NDArray& other); // NDArray& operator/=(const NDArray& other);
template <typename V> NDArray &operator/=(const NDArray<V, Ndim> &other) { template <typename V> NDArray &operator/=(const NDArray<V, Ndim> &other) {
@ -151,12 +155,16 @@ template <typename T, int64_t Ndim = 2> class NDArray {
return data_[element_offset(strides_, index...)]; return data_[element_offset(strides_, index...)];
} }
// TODO! is int the right type for index?
T &operator()(int i) { return data_[i]; } T &operator()(int i) { return data_[i]; }
const T &operator()(int i) const { return data_[i]; } const T &operator()(int i) const { return data_[i]; }
T &operator[](int i) { return data_[i]; }
const T &operator[](int i) const { return data_[i]; }
T *data() { return data_; } T *data() { return data_; }
std::byte *buffer() { return reinterpret_cast<std::byte *>(data_); } std::byte *buffer() { return reinterpret_cast<std::byte *>(data_); }
uint64_t size() const { return size_; } size_t size() const { return size_; }
size_t total_bytes() const { return size_ * sizeof(T); } size_t total_bytes() const { return size_ * sizeof(T); }
std::array<int64_t, Ndim> shape() const noexcept { return shape_; } std::array<int64_t, Ndim> shape() const noexcept { return shape_; }
int64_t shape(int64_t i) const noexcept { return shape_[i]; } int64_t shape(int64_t i) const noexcept { return shape_[i]; }
@ -204,17 +212,11 @@ NDArray<T, Ndim>::operator=(NDArray<T, Ndim> &&other) noexcept {
return *this; return *this;
} }
template <typename T, int64_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator+(const NDArray &other) {
NDArray result(*this);
result += other;
return result;
}
template <typename T, int64_t Ndim> template <typename T, int64_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) {
// check shape // check shape
if (shape_ == other.shape_) { if (shape_ == other.shape_) {
for (uint32_t i = 0; i < size_; ++i) { for (size_t i = 0; i < size_; ++i) {
data_[i] += other.data_[i]; data_[i] += other.data_[i];
} }
return *this; return *this;
@ -222,13 +224,6 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) {
throw(std::runtime_error("Shape of ImageDatas must match")); throw(std::runtime_error("Shape of ImageDatas must match"));
} }
template <typename T, int64_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator-(const NDArray &other) {
NDArray result{*this};
result -= other;
return result;
}
template <typename T, int64_t Ndim> template <typename T, int64_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) {
// check shape // check shape
@ -240,12 +235,6 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) {
} }
throw(std::runtime_error("Shape of ImageDatas must match")); throw(std::runtime_error("Shape of ImageDatas must match"));
} }
template <typename T, int64_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator*(const NDArray &other) {
NDArray result = *this;
result *= other;
return result;
}
template <typename T, int64_t Ndim> template <typename T, int64_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) {
@ -259,13 +248,6 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) {
throw(std::runtime_error("Shape of ImageDatas must match")); throw(std::runtime_error("Shape of ImageDatas must match"));
} }
template <typename T, int64_t Ndim>
NDArray<T, Ndim> NDArray<T, Ndim>::operator/(const NDArray &other) {
NDArray result = *this;
result /= other;
return result;
}
template <typename T, int64_t Ndim> template <typename T, int64_t Ndim>
NDArray<T, Ndim> &NDArray<T, Ndim>::operator&=(const T &mask) { NDArray<T, Ndim> &NDArray<T, Ndim>::operator&=(const T &mask) {
for (auto it = begin(); it != end(); ++it) for (auto it = begin(); it != end(); ++it)
@ -276,7 +258,7 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator&=(const T &mask) {
template <typename T, int64_t Ndim> template <typename T, int64_t Ndim>
NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) { NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) {
if (shape_ == other.shape_) { if (shape_ == other.shape_) {
NDArray<bool> result{shape_}; NDArray<bool, Ndim> result{shape_};
for (int i = 0; i < size_; ++i) { for (int i = 0; i < size_; ++i) {
result(i) = (data_[i] > other.data_[i]); result(i) = (data_[i] > other.data_[i]);
} }

View File

@ -1,4 +1,7 @@
#pragma once #pragma once
#include "aare/ArrayExpr.hpp"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cassert> #include <cassert>
@ -45,7 +48,7 @@ template <int64_t Ndim> std::array<int64_t, Ndim> make_array(const std::vector<i
return arr; return arr;
} }
template <typename T, int64_t Ndim = 2> class NDView { template <typename T, int64_t Ndim = 2> class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
public: public:
NDView() = default; NDView() = default;
~NDView() = default; ~NDView() = default;
@ -68,7 +71,7 @@ template <typename T, int64_t Ndim = 2> class NDView {
return buffer_[element_offset(strides_, index...)]; return buffer_[element_offset(strides_, index...)];
} }
uint64_t size() const { return size_; } size_t size() const { return size_; }
size_t total_bytes() const { return size_ * sizeof(T); } size_t total_bytes() const { return size_ * sizeof(T); }
std::array<int64_t, Ndim> strides() const noexcept { return strides_; } std::array<int64_t, Ndim> strides() const noexcept { return strides_; }

View File

@ -5,7 +5,9 @@ from pathlib import Path
from aare import ClusterFile from aare import ClusterFile
base = Path('~/data/aare_test_data/clusters').expanduser() base = Path('~/data/aare_test_data/clusters').expanduser()
# f = ClusterFile(base / 'beam_En700eV_-40deg_300V_10us_d0_f0_100.clust') # f = ClusterFile(base / 'beam_En700eV_-40deg_300V_10us_d0_f0_100.clust')
f = ClusterFile(base / 'single_frame_97_clustrers.clust') f = ClusterFile(base / 'single_frame_97_clustrers.clust')

View File

@ -1,5 +1,6 @@
#include "aare/NDArray.hpp" #include "aare/NDArray.hpp"
#include <array> #include <array>
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
using aare::NDArray; using aare::NDArray;
@ -101,7 +102,8 @@ TEST_CASE("Elementwise multiplication of 3D image") {
a(i) = i; a(i) = i;
b(i) = i; b(i) = i;
} }
auto c = a * b; // auto c = a * b; // This works but the result is a lazy ArrayMul object
NDArray<double, 3> c = a * b;
REQUIRE(c(0, 0, 0) == 0 * 0); REQUIRE(c(0, 0, 0) == 0 * 0);
REQUIRE(c(0, 0, 1) == 1 * 1); REQUIRE(c(0, 0, 1) == 1 * 1);
REQUIRE(c(0, 1, 1) == 3 * 3); REQUIRE(c(0, 1, 1) == 3 * 3);
@ -109,6 +111,39 @@ TEST_CASE("Elementwise multiplication of 3D image") {
REQUIRE(c(2, 3, 1) == 23 * 23); REQUIRE(c(2, 3, 1) == 23 * 23);
} }
NDArray<int> MultiplyNDArrayUsingOperator(NDArray<int> &a, NDArray<int> &b) {
// return a * a * b * b;
NDArray<int>c = a*b;
return c;
}
NDArray<int> MultiplyNDArrayUsingIndex(NDArray<int> &a, NDArray<int> &b) {
NDArray<int> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
// res(i) = a(i) * a(i) * b(i) * b(i);
res(i) = a(i) * b(i);
}
return res;
}
NDArray<int> AddNDArrayUsingOperator(NDArray<int> &a, NDArray<int> &b) {
// return a * a * b * b;
// NDArray<int>c = a+b;
NDArray<int> c(a.shape());
c = a + b;
return c;
}
NDArray<int> AddNDArrayUsingIndex(NDArray<int> &a, NDArray<int> &b) {
NDArray<int> res(a.shape());
for (uint32_t i = 0; i < a.size(); i++) {
// res(i) = a(i) * a(i) * b(i) * b(i);
res(i) = a(i) + b(i);
}
return res;
}
TEST_CASE("Compare two images") { TEST_CASE("Compare two images") {
NDArray<int> a; NDArray<int> a;
NDArray<int> b; NDArray<int> b;
@ -168,7 +203,6 @@ TEST_CASE("Bitwise and on data") {
REQUIRE(a(2) == 384); REQUIRE(a(2) == 384);
} }
TEST_CASE("Elementwise operations on images") { TEST_CASE("Elementwise operations on images") {
std::array<int64_t, 2> shape{5, 5}; std::array<int64_t, 2> shape{5, 5};
double a_val = 3.0; double a_val = 3.0;
@ -178,7 +212,8 @@ TEST_CASE("Elementwise operations on images") {
NDArray<double> A(shape, a_val); NDArray<double> A(shape, a_val);
NDArray<double> B(shape, b_val); NDArray<double> B(shape, b_val);
auto C = A + B; NDArray<double> C = A + B;
// auto C = A+B; // This works but the result is a lazy ArraySum object
// Value of C matches // Value of C matches
for (uint32_t i = 0; i < C.size(); ++i) { for (uint32_t i = 0; i < C.size(); ++i) {
@ -202,7 +237,8 @@ TEST_CASE("Elementwise operations on images") {
SECTION("Subtract two images") { SECTION("Subtract two images") {
NDArray<double> A(shape, a_val); NDArray<double> A(shape, a_val);
NDArray<double> B(shape, b_val); NDArray<double> B(shape, b_val);
auto C = A - B; NDArray<double> C = A - B;
// auto C = A - B; // This works but the result is a lazy ArraySub object
// Value of C matches // Value of C matches
for (uint32_t i = 0; i < C.size(); ++i) { for (uint32_t i = 0; i < C.size(); ++i) {
@ -226,7 +262,8 @@ TEST_CASE("Elementwise operations on images") {
SECTION("Multiply two images") { SECTION("Multiply two images") {
NDArray<double> A(shape, a_val); NDArray<double> A(shape, a_val);
NDArray<double> B(shape, b_val); NDArray<double> B(shape, b_val);
auto C = A * B; // auto C = A * B; // This works but the result is a lazy ArrayMul object
NDArray<double> C = A * B;
// Value of C matches // Value of C matches
for (uint32_t i = 0; i < C.size(); ++i) { for (uint32_t i = 0; i < C.size(); ++i) {
@ -250,7 +287,8 @@ TEST_CASE("Elementwise operations on images") {
SECTION("Divide two images") { SECTION("Divide two images") {
NDArray<double> A(shape, a_val); NDArray<double> A(shape, a_val);
NDArray<double> B(shape, b_val); NDArray<double> B(shape, b_val);
auto C = A / B; // auto C = A / B; // This works but the result is a lazy ArrayDiv object
NDArray<double> C = A / B;
// Value of C matches // Value of C matches
for (uint32_t i = 0; i < C.size(); ++i) { for (uint32_t i = 0; i < C.size(); ++i) {

View File

@ -6,7 +6,7 @@
using aare::Dtype; using aare::Dtype;
using aare::NumpyFile; using aare::NumpyFile;
TEST_CASE("Read a 1D numpy file with int32 data type") { TEST_CASE("Read a 1D numpy file with int32 data type", "[.integration]") {
auto fpath = test_data_path() / "numpy" / "test_1d_int32.npy"; auto fpath = test_data_path() / "numpy" / "test_1d_int32.npy";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -24,7 +24,7 @@ TEST_CASE("Read a 1D numpy file with int32 data type") {
} }
} }
TEST_CASE("Read a 3D numpy file with np.double data type") { TEST_CASE("Read a 3D numpy file with np.double data type", "[.integration]") {
auto fpath = test_data_path() / "numpy" / "test_3d_double.npy"; auto fpath = test_data_path() / "numpy" / "test_3d_double.npy";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));

View File

@ -7,7 +7,7 @@
using aare::File; using aare::File;
TEST_CASE("Read number of frames from a jungfrau raw file") { TEST_CASE("Read number of frames from a jungfrau raw file", "[.integration]") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json"; auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -16,7 +16,7 @@ TEST_CASE("Read number of frames from a jungfrau raw file") {
REQUIRE(f.total_frames() == 10); REQUIRE(f.total_frames() == 10);
} }
TEST_CASE("Read frame numbers from a jungfrau raw file") { TEST_CASE("Read frame numbers from a jungfrau raw file", "[.integration]") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json"; auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -32,7 +32,7 @@ TEST_CASE("Read frame numbers from a jungfrau raw file") {
} }
} }
TEST_CASE("Read a frame number too high throws") { TEST_CASE("Read a frame number too high throws", "[.integration]") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json"; auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -46,7 +46,7 @@ TEST_CASE("Read a frame number too high throws") {
REQUIRE_THROWS(f.frame_number(10)); REQUIRE_THROWS(f.frame_number(10));
} }
TEST_CASE("Read a frame numbers where the subfile is missing throws") { TEST_CASE("Read a frame numbers where the subfile is missing throws", "[.integration]") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_missing_subfile_master_0.json"; auto fpath = test_data_path() / "jungfrau" / "jungfrau_missing_subfile_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -67,7 +67,7 @@ TEST_CASE("Read a frame numbers where the subfile is missing throws") {
} }
TEST_CASE("Read data from a jungfrau 500k single port raw file") { TEST_CASE("Read data from a jungfrau 500k single port raw file", "[.integration]") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json"; auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -83,7 +83,7 @@ TEST_CASE("Read data from a jungfrau 500k single port raw file") {
} }
} }
TEST_CASE("Read frame numbers from a raw file") { TEST_CASE("Read frame numbers from a raw file", "[.integration]") {
auto fpath = test_data_path() / "eiger" / "eiger_500k_16bit_master_0.json"; auto fpath = test_data_path() / "eiger" / "eiger_500k_16bit_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -96,7 +96,7 @@ TEST_CASE("Read frame numbers from a raw file") {
} }
} }
TEST_CASE("Compare reading from a numpy file with a raw file") { TEST_CASE("Compare reading from a numpy file with a raw file", "[.integration]") {
auto fpath_raw = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json"; auto fpath_raw = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath_raw)); REQUIRE(std::filesystem::exists(fpath_raw));
@ -116,7 +116,7 @@ TEST_CASE("Compare reading from a numpy file with a raw file") {
} }
} }
TEST_CASE("Read multipart files") { TEST_CASE("Read multipart files", "[.integration]") {
auto fpath = test_data_path() / "jungfrau" / "jungfrau_double_master_0.json"; auto fpath = test_data_path() / "jungfrau" / "jungfrau_double_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -141,7 +141,7 @@ TEST_CASE("Read multipart files") {
} }
} }
TEST_CASE("Read file with unordered frames") { TEST_CASE("Read file with unordered frames", "[.integration]") {
//TODO! Better explanation and error message //TODO! Better explanation and error message
auto fpath = test_data_path() / "mythen" / "scan242_master_3.raw"; auto fpath = test_data_path() / "mythen" / "scan242_master_3.raw";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));

View File

@ -58,7 +58,7 @@ TEST_CASE("A disabled scan"){
} }
TEST_CASE("Parse a master file in .json format"){ TEST_CASE("Parse a master file in .json format", "[.integration]"){
auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json"; auto fpath = test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
RawMasterFile f(fpath); RawMasterFile f(fpath);
@ -139,7 +139,7 @@ TEST_CASE("Parse a master file in .json format"){
} }
TEST_CASE("Parse a master file in .raw format"){ TEST_CASE("Parse a master file in .raw format", "[.integration]"){
auto fpath = test_data_path() / "moench/moench04_noise_200V_sto_both_100us_no_light_thresh_900_master_0.raw"; auto fpath = test_data_path() / "moench/moench04_noise_200V_sto_both_100us_no_light_thresh_900_master_0.raw";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
@ -204,7 +204,7 @@ TEST_CASE("Parse a master file in .raw format"){
} }
TEST_CASE("Read eiger master file"){ TEST_CASE("Read eiger master file", "[.integration]"){
auto fpath = test_data_path() / "eiger" / "eiger_500k_32bit_master_0.json"; auto fpath = test_data_path() / "eiger" / "eiger_500k_32bit_master_0.json";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
RawMasterFile f(fpath); RawMasterFile f(fpath);

View File

@ -4,12 +4,12 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
TEST_CASE("Test suite can find data assets") { TEST_CASE("Test suite can find data assets", "[.integration]") {
auto fpath = test_data_path() / "numpy" / "test_numpy_file.npy"; auto fpath = test_data_path() / "numpy" / "test_numpy_file.npy";
REQUIRE(std::filesystem::exists(fpath)); REQUIRE(std::filesystem::exists(fpath));
} }
TEST_CASE("Test suite can open data assets") { TEST_CASE("Test suite can open data assets", "[.integration]") {
auto fpath = test_data_path() / "numpy" / "test_numpy_file.npy"; auto fpath = test_data_path() / "numpy" / "test_numpy_file.npy";
auto f = std::ifstream(fpath, std::ios::binary); auto f = std::ifstream(fpath, std::ios::binary);
REQUIRE(f.is_open()); REQUIRE(f.is_open());