-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
1,613 additions
and
649 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.