Skip to content

Commit

Permalink
PKG: Restructure headers
Browse files Browse the repository at this point in the history
  • Loading branch information
RUrlus committed Oct 21, 2023
1 parent 781a631 commit 66aef50
Show file tree
Hide file tree
Showing 23 changed files with 1,613 additions and 649 deletions.
98 changes: 0 additions & 98 deletions include/carma.hpp

This file was deleted.

71 changes: 71 additions & 0 deletions include/carma/alien/api.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* carma/carma: Bidirectional coverter of Numpy arrays and Armadillo objects
* Copyright (c) 2023 Ralph Urlus <[email protected]>
* All rights reserved. Use of this source code is governed by a
* Apache-2.0 license that can be found in the LICENSE file.
*/
#pragma once

namespace carma {

#ifndef CARMA_EXTENSIONS_ENABLED
#define CARMA_EXTENSIONS_ENABLED
#endif

/* If the Numpy allocator/deallocator have not been set through
* the carma_armadillo target ARMA_ALIEN_MEM_ALLOC_FUNCTION and
* ARMA_ALIEN_MEM_FREE_FUNCTION need to be set.
*
* This requires that Armadillo wasn't included before carma
* The CMake script handles this by pre-compiling the numpy_alloc header
*/
#ifndef CARMA_ARMA_ALIEN_MEM_FUNCTIONS_SET
#if defined(ARMA_VERSION_MAJOR)
#error "|carma| please include the armadillo header after the carma header or use carma's CMake build"
#endif
#include <carma/internal/numpy_alloc.hpp>
#endif // CARMA_ARMA_ALIEN_MEM_FUNCTIONS_SET

#ifdef CARMA_ARMA_ALIEN_MEM_FUNCTIONS_SET
#if ((!defined(ARMA_ALIEN_MEM_ALLOC_FUNCTION)) || (!defined(ARMA_ALIEN_MEM_FREE_FUNCTION)))
#error \
"|carma| ARMA_ALIEN_MEM_ALLOC_FUNCTION and or ARMA_ALIEN_MEM_FREE_FUNCTION not set while CARMA_ARMA_ALIEN_MEM_FUNCTIONS_SET is"
#endif
#endif // CARMA_ARMA_ALIEN_MEM_FUNCTIONS_SET

// #ifdef CARMA_EXTRA_DEBUG
//
// #include <iostream>
//
// namespace anon {
// class carma_config_debug_message {
// public:
// inline carma_config_debug_message() {
// std::cout << "\n|----------------------------------------------------------|\n"
// << "| CARMA CONFIGURATION |"
// << "\n|----------------------------------------------------------|\n|\n";
// std::cout << "| Carma version: " + carma_version().as_string() << "\n";
// std::cout << "| Carma mode: alien\n|\n";
// std::cout << "| Default Numpy to Arma conversion config:\n"
// << "| ----------------------------------------\n"
// << "| * l-value converter: " << CARMA_DEFAULT_LVALUE_CONVERTER::name_ << "\n"
// << "| * const l-value converter: " << CARMA_DEFAULT_CONST_LVALUE_CONVERTER::name_ << "\n"
// << "| * resolution_policy: " << CARMA_DEFAULT_RESOLUTION::name_ << "\n"
// << "| * memory_order_policy: " << CARMA_DEFAULT_MEMORY_ORDER::name_ << "\n";
// std::cout << "|\n| Converter Options:\n"
// << "| ------------------\n"
// << "| * enforce rvalue for MoveConverter: "
// #ifndef CARMA_DONT_ENFORCE_RVALUE_MOVECONVERTER
// << "true\n";
// #else
// << "false\n";
// #endif // CARMA_DONT_ENFORCE_RVALUE_MOVECONVERTER
// std::cout << "|\n|----------------------------------------------------------|\n\n";
// };
// };
//
// static const carma_config_debug_message carma_config_debug_message_print;
// } // namespace anon
//
// #endif // CARMA_EXTRA_DEBUG

} // namespace carma
141 changes: 141 additions & 0 deletions include/carma/alien/array_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#pragma once

// pybind11 include required even if not explicitly used
// to prevent link with pythonXX_d.lib on Win32
// (cf Py_DEBUG defined in numpy headers and https://github.com/pybind/pybind11/issues/1295)
#include <pybind11/pybind11.h>
// include order matters here
#include <Python.h>

#define NPY_NO_DEPRECATED_API NPY_1_18_API_VERSION
#include <numpy/arrayobject.h>

#include <armadillo>
#include <carma/internal/array_view.hpp>
#include <carma/internal/common.hpp>
#include <carma/internal/numpy_api.hpp>
#include <cstring>
#include <stdexcept>
#include <string>
#include <utility>

namespace carma {
namespace internal {

void ArrayView::take_ownership() {
carma_extra_debug_print("taking ownership of array ", obj);
strict = false;
copy_in = n_elem <= arma::arma_config::mat_prealloc;
PyArray_CLEARFLAGS(arr, NPY_ARRAY_OWNDATA);
}

/**
* \brief Give armadillo object ownership of memory
*
* \details Armadillo will free the memory during destruction when the `mem_state == 0` and
* when `n_alloc > arma_config::mat_prealloc`.
* In cases where the number of elements is below armadillo's pre-allocation limit
* the memory will be copied in. This means that we have to free the memory if a copy
* of an array was stolen.
*
* \param[in] dest arma object to be given ownership
* \return void
*/
template <typename armaT, iff_Arma<armaT> = 0>
inline void ArrayView::give_ownership(armaT& dest) {
carma_extra_debug_print("releasing ownership of array ", obj, " to ", (&dest));
arma::access::rw(dest.n_alloc) = n_elem;
arma::access::rw(dest.mem_state) = 0;
if (copy_in) {
carma_extra_debug_print(
"array ", obj, " with size ", n_elem, " was copied in, as it does not exceed arma's prealloc size."
);
if (stolen_copy) {
carma_extra_debug_print("freeing ", mem);
// We copied in because of the array's size in the CopyConverter
// we need to free the memory as we own it
npy_api::get().PyDataMem_FREE_(mem);
mem = nullptr;
} else {
carma_extra_debug_print("re-enabling owndata for array ", obj);
// We copied in because of the array's size in the MoveConterter
// if we free the memory any view or array that references this
// memory will segfault on the python side.
// We re-enable the owndata flag such that the memory is free'd
// by Python
PyArray_ENABLEFLAGS(arr, NPY_ARRAY_OWNDATA);
}
}
}

/* Use Numpy's api to account for stride, order and steal the memory */
void ArrayView::steal_copy() {
#ifdef CARMA_DEBUG
void* original_mem = mem;
std::cout << "|carma| a copy of array " << obj << " will moved into the arma object.\n";
#endif
auto& api = npy_api::get();
// build an PyArray to do F-order copy
auto dest = reinterpret_cast<PyArrayObject*>(api.PyArray_NewLikeArray_(arr, target_order, nullptr, 0));

// copy the array to a well behaved F-order
int ret_code = api.PyArray_CopyInto_(dest, arr);
if (ret_code != 0) {
throw std::runtime_error("|carma| Copy of array failed with ret_code: " + std::to_string(ret_code));
}

mem = PyArray_DATA(dest);
#ifdef CARMA_DEBUG
std::cout << "|carma| copied data " << original_mem << " to " << mem << "\n";
#endif
// set OWNDATA to false such that the newly create
// memory is not freed when the array is cleared
PyArray_CLEARFLAGS(dest, NPY_ARRAY_OWNDATA);
// free the array but not the memory
api.PyArray_Free_(dest, nullptr);
// ensure that we don't clear the owndata flag from the original array
stolen_copy = true;
// arma owns thus not strict
strict = false;
// check if an additional copy is needed for arma to take ownership
copy_in = n_elem <= arma::arma_config::mat_prealloc;
} // steal_copy_array

/* Use Numpy's api to account for stride, order and copy the new array in place*/
void ArrayView::swap_copy() {
#ifdef CARMA_DEBUG
void* original_mem = mem;
std::cout << "|carma| array " << obj << " will be copied in-place.\n";
#endif
auto& api = npy_api::get();
auto tmp = reinterpret_cast<PyArrayObject*>(api.PyArray_NewLikeArray_(arr, target_order, nullptr, 0));

// copy the array to a well behaved target-order
int ret_code = api.PyArray_CopyInto_(tmp, arr);
if (ret_code != 0) {
throw std::runtime_error("|carma| Copy of numpy array failed with ret_code: " + std::to_string(ret_code));
}
// swap copy into the original array
auto tmp_of = reinterpret_cast<PyArrayObject_fields*>(tmp);
auto src_of = reinterpret_cast<PyArrayObject_fields*>(arr);
std::swap(src_of->data, tmp_of->data);

// fix strides
std::swap(src_of->strides, tmp_of->strides);

PyArray_CLEARFLAGS(arr, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
PyArray_ENABLEFLAGS(arr, target_order | NPY_ARRAY_BEHAVED | NPY_ARRAY_OWNDATA);

// clean up temporary which now contains the old memory
PyArray_ENABLEFLAGS(tmp, NPY_ARRAY_OWNDATA);

mem = PyArray_DATA(arr);
#ifdef CARMA_DEBUG
std::cout << "|carma| copied " << mem << "into " << obj << "in place of " << original_mem << "\n";
std::cout << "|carma| freeing " << PyArray_DATA(tmp) << "\n";
#endif
api.PyArray_Free_(tmp, PyArray_DATA(tmp));
} // swap_copy

} // namespace internal
} // namespace carma
21 changes: 21 additions & 0 deletions include/carma/alien/config.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <carma/internal/inconv.hpp>
/* --------------------------------------------------------------
ConversionConfig
-------------------------------------------------------------- */
#ifndef CARMA_DEFAULT_LVALUE_CONVERTER
#define CARMA_DEFAULT_LVALUE_CONVERTER carma::CopyConverter
#endif // CARMA_DEFAULT_LVALUE_CONVERTER

#ifndef CARMA_DEFAULT_CONST_LVALUE_CONVERTER
#define CARMA_DEFAULT_CONST_LVALUE_CONVERTER carma::CopyConverter
#endif // CARMA_DEFAULT_CONST_LVALUE_CONVERTER

#ifndef CARMA_DEFAULT_RESOLUTION
#define CARMA_DEFAULT_RESOLUTION carma::CopyResolution
#endif // CARMA_DEFAULT_RESOLUTION

#ifndef CARMA_DEFAULT_MEMORY_ORDER
#define CARMA_DEFAULT_MEMORY_ORDER carma::ColumnOrder
#endif // CARMA_DEFAULT_MEMORY_ORDER
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
#pragma once

#include <armadillo>
#include <carma_bits/array_view.hpp>
#include <carma_bits/common.hpp>
#include <carma_bits/to_arma.hpp>
#include <carma_bits/to_numpy.hpp>
#include <cstring>
#include <carma/internal/common.hpp>
#include <carma/internal/to_arma.hpp>
#include <carma/internal/to_numpy.hpp>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <typeinfo>
#include <utility> // std::forward

namespace carma {

Expand All @@ -36,12 +32,9 @@ struct npConverter {
static_assert(
is_ConversionConfig<config>::value, "|carma| config must be a specialisation of `ConversionConfig`"
);
return internal::npConverterImpl<
armaT,
typename config::converter_,
typename config::resolution_,
typename config::mem_order_>()
.template operator()<decltype(src)>(std::forward<numpyT>(src));
return internal::
npConverter<armaT, typename config::converter_, typename config::resolution_, typename config::mem_order_>()
.template operator()<decltype(src)>(std::forward<numpyT>(src));
};
};

Expand Down
Loading

0 comments on commit 66aef50

Please sign in to comment.