mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2025-04-19 21:30:02 +02:00
Added expression templates (#98)
- Works with NDArray - Works with NDView
This commit is contained in:
parent
0d058274d5
commit
e77b615293
@ -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
11
benchmarks/CMakeLists.txt
Normal 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
|
||||||
|
)
|
136
benchmarks/ndarray_benchmark.cpp
Normal file
136
benchmarks/ndarray_benchmark.cpp
Normal 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();
|
99
include/aare/ArrayExpr.hpp
Normal file
99
include/aare/ArrayExpr.hpp
Normal 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
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
@ -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_; }
|
||||||
|
|
||||||
|
@ -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')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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));
|
||||||
|
@ -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));
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user