LCOV - code coverage report
Current view: top level - src - torch_c_api.cpp (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 74.8 % 421 315
Test Date: 2026-09-03 07:32:15 Functions: 86.2 % 65 56

            Line data    Source code
       1              : /*----------------------------------------------------------------------------*/
       2              : /*  CP2K: A general program to perform molecular dynamics simulations         */
       3              : /*  Copyright 2000-2026 CP2K developers group <https://cp2k.org>              */
       4              : /*                                                                            */
       5              : /*  SPDX-License-Identifier: GPL-2.0-or-later                                 */
       6              : /*----------------------------------------------------------------------------*/
       7              : 
       8              : #if defined(__LIBTORCH)
       9              : 
      10              : #include <ATen/Parallel.h>
      11              : #if defined(__LIBTORCH_CUDA)
      12              : #include <ATen/cuda/CUDAContextLight.h>
      13              : #include <c10/cuda/CUDAAllocatorConfig.h>
      14              : #endif
      15              : #include <c10/core/DeviceGuard.h>
      16              : #include <torch/csrc/api/include/torch/cuda.h>
      17              : #include <torch/csrc/jit/passes/freeze_module.h>
      18              : #include <torch/script.h>
      19              : 
      20              : #include "offload/offload_library.h"
      21              : 
      22              : #if defined(__OPENBLAS)
      23              : #include <cblas.h>
      24              : #endif
      25              : 
      26              : #include <cassert>
      27              : 
      28              : #include <cfenv>
      29              : #include <climits>
      30              : #include <cstdlib>
      31              : #include <cstring>
      32              : #include <mutex>
      33              : #include <string>
      34              : #include <unordered_map>
      35              : #include <vector>
      36              : 
      37              : #if defined(__OPENBLAS)
      38              : // PyTorch's oneMKL batch ABI is not compatible with OpenBLAS's same-named
      39              : // entry points. Expand grouped GEMMs into the portable CBLAS interface.
      40              : extern "C" void
      41        16827 : cblas_sgemm_batch(const CBLAS_ORDER order, const CBLAS_TRANSPOSE *trans_a,
      42              :                   const CBLAS_TRANSPOSE *trans_b, const int *m, const int *n,
      43              :                   const int *k, const float *alpha, const float **a,
      44              :                   const int *lda, const float **b, const int *ldb,
      45              :                   const float *beta, float **c, const int *ldc,
      46              :                   const int group_count, const int *group_size) {
      47        16827 :   int offset = 0;
      48        33654 :   for (int group = 0; group < group_count; ++group) {
      49       306141 :     for (int operation = 0; operation < group_size[group]; ++operation) {
      50       289314 :       const int index = offset + operation;
      51       289314 :       cblas_sgemm(order, trans_a[group], trans_b[group], m[group], n[group],
      52       289314 :                   k[group], alpha[group], a[index], lda[group], b[index],
      53       289314 :                   ldb[group], beta[group], c[index], ldc[group]);
      54              :     }
      55        16827 :     offset += group_size[group];
      56              :   }
      57        16827 : }
      58              : 
      59              : extern "C" void
      60          591 : cblas_dgemm_batch(const CBLAS_ORDER order, const CBLAS_TRANSPOSE *trans_a,
      61              :                   const CBLAS_TRANSPOSE *trans_b, const int *m, const int *n,
      62              :                   const int *k, const double *alpha, const double **a,
      63              :                   const int *lda, const double **b, const int *ldb,
      64              :                   const double *beta, double **c, const int *ldc,
      65              :                   const int group_count, const int *group_size) {
      66          591 :   int offset = 0;
      67         1182 :   for (int group = 0; group < group_count; ++group) {
      68         3063 :     for (int operation = 0; operation < group_size[group]; ++operation) {
      69         2472 :       const int index = offset + operation;
      70         2472 :       cblas_dgemm(order, trans_a[group], trans_b[group], m[group], n[group],
      71         2472 :                   k[group], alpha[group], a[index], lda[group], b[index],
      72         2472 :                   ldb[group], beta[group], c[index], ldc[group]);
      73              :     }
      74          591 :     offset += group_size[group];
      75              :   }
      76          591 : }
      77              : #endif
      78              : 
      79              : typedef torch::Tensor torch_c_tensor_t;
      80              : typedef c10::Dict<std::string, torch::Tensor> torch_c_dict_t;
      81              : typedef torch::jit::Module torch_c_model_t;
      82              : 
      83              : class TorchFloatingPointMaskGuard {
      84              : public:
      85          364 :   TorchFloatingPointMaskGuard() : active_(std::feholdexcept(&env_) == 0) {}
      86          364 :   ~TorchFloatingPointMaskGuard() {
      87          364 :     if (active_) {
      88          364 :       std::feclearexcept(FE_ALL_EXCEPT);
      89          364 :       std::fesetenv(&env_);
      90              :     }
      91          364 :   }
      92              : 
      93              : private:
      94              :   std::fenv_t env_;
      95              :   bool active_;
      96              : };
      97              : 
      98              : /*******************************************************************************
      99              :  * \brief Internal helper for selecting the CUDA device when available.
     100              :  * \author Ole Schuett
     101              :  ******************************************************************************/
     102              : static bool use_cuda_if_available = true;
     103              : 
     104            0 : static void enable_expandable_cuda_segments_if_unconfigured() {
     105              : #if defined(__LIBTORCH_CUDA)
     106              :   static const bool initialized = []() {
     107              :     const char *legacy_config = std::getenv("PYTORCH_CUDA_ALLOC_CONF");
     108              :     const char *config = std::getenv("PYTORCH_ALLOC_CONF");
     109              :     if ((legacy_config == nullptr || legacy_config[0] == '\0') &&
     110              :         (config == nullptr || config[0] == '\0')) {
     111              :       c10::cuda::CUDACachingAllocator::setAllocatorSettings(
     112              :           "expandable_segments:True");
     113              :     }
     114              :     return true;
     115              :   }();
     116              :   (void)initialized;
     117              : #endif
     118            0 : }
     119              : 
     120          206 : static bool get_positive_int_env(const char *name, int &value) {
     121          206 :   const char *raw = std::getenv(name);
     122          206 :   if (raw == nullptr || raw[0] == '\0') {
     123              :     return false;
     124              :   }
     125            0 :   char *end = nullptr;
     126            0 :   const long parsed = std::strtol(raw, &end, 10);
     127            0 :   if (end == raw || *end != '\0' || parsed <= 0 || parsed > INT_MAX) {
     128              :     return false;
     129              :   }
     130            0 :   value = static_cast<int>(parsed);
     131            0 :   return true;
     132              : }
     133              : 
     134        12562 : static void initialize_torch_threads_from_env() {
     135        12562 :   static bool initialized = false;
     136        12562 :   if (initialized) {
     137        12459 :     return;
     138              :   }
     139          103 :   initialized = true;
     140              : 
     141          103 :   int num_threads = 0;
     142          103 :   if (get_positive_int_env("CP2K_TORCH_NUM_THREADS", num_threads)) {
     143            0 :     at::set_num_threads(num_threads);
     144              :   }
     145          103 :   if (get_positive_int_env("CP2K_TORCH_NUM_INTEROP_THREADS", num_threads)) {
     146            0 :     at::set_num_interop_threads(num_threads);
     147              :   }
     148              : }
     149              : 
     150         9744 : static torch::Device get_device() {
     151         9744 :   initialize_torch_threads_from_env();
     152         9744 :   if (!use_cuda_if_available || !torch::cuda::is_available()) {
     153         9744 :     return torch::kCPU;
     154              :   }
     155            0 :   enable_expandable_cuda_segments_if_unconfigured();
     156            0 :   const auto device_count = torch::cuda::device_count();
     157            0 :   if (device_count <= 0) {
     158            0 :     return torch::kCPU;
     159              :   }
     160            0 :   const int chosen_device = offload_get_chosen_device();
     161            0 :   const int device = (chosen_device >= 0) ? chosen_device : 0;
     162            0 :   assert(device < device_count);
     163            0 :   return torch::Device(torch::kCUDA, device);
     164              : }
     165              : 
     166         9744 : static torch::Device get_device_with_guard(c10::OptionalDeviceGuard &guard) {
     167         9744 :   const auto device = get_device();
     168         9744 :   if (device.is_cuda()) {
     169            0 :     guard.reset_device(device);
     170              :   }
     171         9744 :   return device;
     172              : }
     173              : 
     174          294 : static bool use_batched_gradient_readback(const torch::Tensor &tensor) {
     175              : #if defined(__LIBTORCH_CUDA)
     176              :   return tensor.is_cuda() &&
     177              :          at::cuda::getDeviceProperties(tensor.device().index())->integrated;
     178              : #else
     179          294 :   (void)tensor;
     180          294 :   return false;
     181              : #endif
     182              : }
     183              : 
     184          107 : static void set_jit_fusion_strategy() {
     185              :   // JIT Fusion strategy optimization, hardcode dynamic 10, see also
     186              :   // https://github.com/mir-group/pair_nequip_allegro.git
     187          107 :   torch::jit::FusionStrategy strategy = {
     188          107 :       {torch::jit::FusionBehavior::DYNAMIC, 10}};
     189          214 :   torch::jit::setFusionStrategy(strategy);
     190          107 : }
     191              : 
     192          222 : static void copy_string_to_c_buffer(const std::string &source, char **content,
     193              :                                     int *length) {
     194          222 :   *length = source.length();
     195          222 :   *content = (char *)malloc(source.length() + 1); // +1 for null terminator
     196          222 :   strcpy(*content, source.c_str());
     197          222 : }
     198              : 
     199          107 : static bool can_load_directly_to_device(const torch::Device &device) {
     200          107 :   return !device.is_cuda() || device.index() == 0 ||
     201            0 :          torch::cuda::device_count() == 1;
     202              : }
     203              : 
     204          107 : static torch::jit::Module load_module_for_device(
     205              :     const char *filename, const torch::Device &device,
     206              :     std::unordered_map<std::string, std::string> *extra_files = nullptr) {
     207          107 :   if (can_load_directly_to_device(device)) {
     208          107 :     if (extra_files != nullptr) {
     209          182 :       return torch::jit::load(filename, device, *extra_files);
     210              :     }
     211           32 :     return torch::jit::load(filename, device);
     212              :   }
     213            0 :   auto model = (extra_files != nullptr)
     214            0 :                    ? torch::jit::load(filename, torch::kCPU, *extra_files)
     215            0 :                    : torch::jit::load(filename, torch::kCPU);
     216            0 :   model.to(device);
     217            0 :   return model;
     218            0 : }
     219              : 
     220            0 : static void remap_device_constants(torch::jit::Block *block,
     221              :                                    const torch::Device &device) {
     222            0 :   for (torch::jit::Node *node : block->nodes()) {
     223            0 :     if (node->kind() == torch::jit::prim::Constant &&
     224            0 :         node->outputs().size() == 1 &&
     225            0 :         node->output()->type()->kind() == c10::TypeKind::DeviceObjType &&
     226            0 :         node->hasAttribute(torch::jit::attr::value) &&
     227            0 :         node->kindOf(torch::jit::attr::value) == torch::jit::AttributeKind::s) {
     228            0 :       node->s_(torch::jit::attr::value, device.str());
     229              :     }
     230            0 :     bool has_tensor_input = false;
     231            0 :     for (const torch::jit::Value *input : node->inputs()) {
     232            0 :       has_tensor_input |= input->type()->kind() == c10::TypeKind::TensorType;
     233              :     }
     234            0 :     const auto *schema = node->maybeSchema();
     235            0 :     if (!has_tensor_input && schema != nullptr) {
     236              :       const auto &arguments = schema->arguments();
     237            0 :       for (size_t i = 0; i < arguments.size() && i < node->inputs().size();
     238              :            ++i) {
     239            0 :         const auto input_value = torch::jit::toIValue(node->input(i));
     240            0 :         if (arguments[i].name() == "device" && input_value.has_value() &&
     241            0 :             input_value->isNone()) {
     242            0 :           torch::jit::WithInsertPoint insertion_guard(node);
     243            0 :           auto *device_value =
     244            0 :               node->owningGraph()->insertConstant(torch::jit::IValue(device));
     245            0 :           node->replaceInput(i, device_value);
     246            0 :         }
     247            0 :       }
     248              :     }
     249            0 :     for (torch::jit::Block *nested_block : node->blocks()) {
     250            0 :       remap_device_constants(nested_block, device);
     251              :     }
     252              :   }
     253            0 : }
     254              : 
     255            0 : static void remap_model_device_constants(torch::jit::Module &model,
     256              :                                          const torch::Device &device) {
     257            0 :   for (const auto &attribute : model.named_attributes(false)) {
     258            0 :     const auto &value = attribute.value;
     259            0 :     if (value.isTensor()) {
     260            0 :       model.setattr(attribute.name, value.toTensor().to(device));
     261            0 :     } else if (value.isTensorList()) {
     262            0 :       auto tensors = value.toTensorList();
     263            0 :       for (size_t i = 0; i < tensors.size(); ++i) {
     264            0 :         tensors.set(i, tensors.get(i).to(device));
     265              :       }
     266            0 :       model.setattr(attribute.name, tensors);
     267            0 :     }
     268            0 :   }
     269            0 :   for (const auto &method : model.get_methods()) {
     270            0 :     remap_device_constants(method.graph()->block(), device);
     271            0 :   }
     272            0 :   for (auto child : model.children()) {
     273            0 :     remap_model_device_constants(child, device);
     274            0 :   }
     275            0 : }
     276              : 
     277            0 : static void initialize_cuda_linalg(const torch::Device &device) {
     278            0 :   static std::once_flag flag;
     279            0 :   std::call_once(flag, [&]() {
     280            0 :     const auto options =
     281            0 :         torch::TensorOptions().dtype(torch::kFloat32).device(device);
     282            0 :     static_cast<void>(at::linalg_eigh(torch::eye(1, options)));
     283            0 :   });
     284            0 : }
     285              : 
     286              : /*******************************************************************************
     287              :  * \brief Internal helper for creating a Torch tensor from an array.
     288              :  * \author Ole Schuett
     289              :  ******************************************************************************/
     290         2818 : static torch_c_tensor_t *tensor_from_array(const torch::Dtype dtype,
     291              :                                            const bool req_grad, const int ndims,
     292              :                                            const int64_t sizes[],
     293              :                                            void *source) {
     294         2818 :   initialize_torch_threads_from_env();
     295         2818 :   const auto opts = torch::TensorOptions().dtype(dtype).requires_grad(req_grad);
     296         2818 :   const auto sizes_ref = c10::IntArrayRef(sizes, ndims);
     297         2818 :   return new torch_c_tensor_t(torch::from_blob(source, sizes_ref, opts));
     298              : }
     299              : 
     300          144 : static bool tensor_matches(const torch_c_tensor_t *tensor,
     301              :                            const torch::Dtype dtype,
     302              :                            const torch::Device &device, const int ndims,
     303              :                            const int64_t sizes[]) {
     304           24 :   if (tensor == nullptr || !tensor->defined() ||
     305          168 :       tensor->scalar_type() != dtype || tensor->device() != device ||
     306           12 :       tensor->ndimension() != ndims) {
     307          132 :     return false;
     308              :   }
     309           40 :   for (int i = 0; i < ndims; i++) {
     310           28 :     if (tensor->size(i) != sizes[i]) {
     311              :       return false;
     312              :     }
     313              :   }
     314           12 :   return tensor->is_contiguous();
     315              : }
     316              : 
     317          144 : static void reset_tensor_from_array_double(torch_c_tensor_t **tensor,
     318              :                                            const bool req_grad, const int ndims,
     319              :                                            const int64_t sizes[],
     320              :                                            double source[]) {
     321          144 :   c10::OptionalDeviceGuard guard;
     322          144 :   const auto device = get_device_with_guard(guard);
     323          144 :   const auto sizes_ref = c10::IntArrayRef(sizes, ndims);
     324          144 :   if (!tensor_matches(*tensor, torch::kFloat64, device, ndims, sizes)) {
     325          132 :     delete (*tensor);
     326          132 :     const auto opts =
     327          132 :         torch::TensorOptions().dtype(torch::kFloat64).device(device);
     328          264 :     *tensor = new torch_c_tensor_t(torch::empty(sizes_ref, opts).detach());
     329              :   }
     330          144 :   const auto source_tensor = torch::from_blob(
     331          144 :       source, sizes_ref, torch::TensorOptions().dtype(torch::kFloat64));
     332          144 :   {
     333          144 :     torch::NoGradGuard no_grad;
     334          144 :     (*tensor)->copy_(source_tensor);
     335          144 :     (*tensor)->mutable_grad() = torch::Tensor();
     336            0 :   }
     337          288 :   (*tensor)->set_requires_grad(req_grad);
     338          144 : }
     339              : 
     340              : /*******************************************************************************
     341              :  * \brief Internal helper for getting the data_ptr and sizes of a Torch tensor.
     342              :  * \author Ole Schuett
     343              :  ******************************************************************************/
     344         1240 : static void *get_data_ptr(const torch_c_tensor_t *tensor,
     345              :                           const torch::Dtype dtype, const int ndims,
     346              :                           int64_t sizes[]) {
     347         1240 :   assert(tensor->scalar_type() == dtype);
     348         1240 :   assert(tensor->ndimension() == ndims);
     349         3926 :   for (int i = 0; i < ndims; i++) {
     350         2686 :     sizes[i] = tensor->size(i);
     351              :   }
     352              : 
     353         1240 :   assert(tensor->is_contiguous());
     354         1240 :   return tensor->data_ptr();
     355              : };
     356              : 
     357              : #ifdef __cplusplus
     358              : extern "C" {
     359              : #endif
     360              : 
     361              : /*******************************************************************************
     362              :  * \brief Creates a Torch tensor from an array of int32s.
     363              :  *        The passed array has to outlive the tensor!
     364              :  * \author Ole Schuett
     365              :  ******************************************************************************/
     366            0 : void torch_c_tensor_from_array_int32(torch_c_tensor_t **tensor,
     367              :                                      const bool req_grad, const int ndims,
     368              :                                      const int64_t sizes[], int32_t source[]) {
     369            0 :   *tensor = tensor_from_array(torch::kInt32, req_grad, ndims, sizes, source);
     370            0 : }
     371              : 
     372              : /*******************************************************************************
     373              :  * \brief Creates a Torch tensor from an array of floats.
     374              :  *        The passed array has to outlive the tensor!
     375              :  * \author Ole Schuett
     376              :  ******************************************************************************/
     377           66 : void torch_c_tensor_from_array_float(torch_c_tensor_t **tensor,
     378              :                                      const bool req_grad, const int ndims,
     379              :                                      const int64_t sizes[], float source[]) {
     380           66 :   *tensor = tensor_from_array(torch::kFloat32, req_grad, ndims, sizes, source);
     381           66 : }
     382              : 
     383              : /*******************************************************************************
     384              :  * \brief Creates a Torch tensor from an array of int64s.
     385              :  *        The passed array has to outlive the tensor!
     386              :  * \author Ole Schuett
     387              :  ******************************************************************************/
     388          806 : void torch_c_tensor_from_array_int64(torch_c_tensor_t **tensor,
     389              :                                      const bool req_grad, const int ndims,
     390              :                                      const int64_t sizes[], int64_t source[]) {
     391          806 :   *tensor = tensor_from_array(torch::kInt64, req_grad, ndims, sizes, source);
     392          806 : }
     393              : 
     394              : /*******************************************************************************
     395              :  * \brief Creates a Torch tensor from an array of doubles.
     396              :  *        The passed array has to outlive the tensor!
     397              :  * \author Ole Schuett
     398              :  ******************************************************************************/
     399         1946 : void torch_c_tensor_from_array_double(torch_c_tensor_t **tensor,
     400              :                                       const bool req_grad, const int ndims,
     401              :                                       const int64_t sizes[], double source[]) {
     402         1946 :   *tensor = tensor_from_array(torch::kFloat64, req_grad, ndims, sizes, source);
     403         1946 : }
     404              : 
     405              : /*******************************************************************************
     406              :  * \brief Releases a string returned from the Torch C API.
     407              :  ******************************************************************************/
     408          222 : void torch_c_free_string(char *content) { free(content); }
     409              : 
     410              : /*******************************************************************************
     411              :  * \brief Reuses or creates a device tensor and copies double data into it.
     412              :  ******************************************************************************/
     413          144 : void torch_c_tensor_reset_from_array_double(torch_c_tensor_t **tensor,
     414              :                                             const bool req_grad,
     415              :                                             const int ndims,
     416              :                                             const int64_t sizes[],
     417              :                                             double source[]) {
     418          144 :   reset_tensor_from_array_double(tensor, req_grad, ndims, sizes, source);
     419          144 : }
     420              : 
     421              : /*******************************************************************************
     422              :  * \brief Creates an expanded tensor view along one singleton dimension.
     423              :  ******************************************************************************/
     424          102 : void torch_c_tensor_expand_dim(const torch_c_tensor_t *tensor,
     425              :                                const int64_t dim, const int64_t size,
     426              :                                torch_c_tensor_t **result) {
     427          102 :   c10::OptionalDeviceGuard guard;
     428          102 :   get_device_with_guard(guard);
     429          102 :   assert(*result == NULL);
     430          102 :   assert(dim >= 0);
     431          102 :   assert(dim < tensor->dim());
     432          102 :   std::vector<int64_t> sizes(tensor->sizes().begin(), tensor->sizes().end());
     433          102 :   assert(sizes[dim] == 1);
     434          102 :   sizes[dim] = size;
     435          102 :   *result = new torch_c_tensor_t(tensor->expand(sizes));
     436          102 : }
     437              : 
     438              : /*******************************************************************************
     439              :  * \brief Creates a tensor view narrowed along one dimension.
     440              :  ******************************************************************************/
     441         1968 : void torch_c_tensor_narrow(const torch_c_tensor_t *tensor, const int64_t dim,
     442              :                            const int64_t start_index, const int64_t length,
     443              :                            torch_c_tensor_t **result) {
     444         1968 :   c10::OptionalDeviceGuard guard;
     445         1968 :   const auto device = get_device_with_guard(guard);
     446         1968 :   assert(*result == NULL);
     447         1968 :   assert(dim >= 0);
     448         1968 :   assert(start_index >= 0);
     449         1968 :   assert(length >= 0);
     450         1968 :   assert(dim < tensor->ndimension());
     451         1968 :   assert(start_index + length <= tensor->size(dim));
     452         3936 :   *result =
     453         1968 :       new torch_c_tensor_t(tensor->narrow(dim, start_index, length).to(device));
     454         1968 : }
     455              : 
     456              : /*******************************************************************************
     457              :  * \brief Returns the data_ptr and sizes of a Torch tensor of int32s.
     458              :  *        The returned pointer is only valide during the tensor's live time!
     459              :  * \author Ole Schuett
     460              :  ******************************************************************************/
     461            0 : void torch_c_tensor_data_ptr_int32(const torch_c_tensor_t *tensor,
     462              :                                    const int ndims, int64_t sizes[],
     463              :                                    int32_t **data_ptr) {
     464            0 :   *data_ptr = (int32_t *)get_data_ptr(tensor, torch::kInt32, ndims, sizes);
     465            0 : }
     466              : 
     467              : /*******************************************************************************
     468              :  * \brief Returns the data_ptr and sizes of a Torch tensor of floats.
     469              :  *        The returned pointer is only valide during the tensor's lifetime!
     470              :  * \author Ole Schuett
     471              :  ******************************************************************************/
     472           66 : void torch_c_tensor_data_ptr_float(const torch_c_tensor_t *tensor,
     473              :                                    const int ndims, int64_t sizes[],
     474              :                                    float **data_ptr) {
     475           66 :   *data_ptr = (float *)get_data_ptr(tensor, torch::kFloat32, ndims, sizes);
     476           66 : }
     477              : 
     478              : /*******************************************************************************
     479              :  * \brief Returns the data_ptr and sizes of a Torch tensor of int64s.
     480              :  *        The returned pointer is only valide during the tensor's live time!
     481              :  * \author Ole Schuett
     482              :  ******************************************************************************/
     483            0 : void torch_c_tensor_data_ptr_int64(const torch_c_tensor_t *tensor,
     484              :                                    const int ndims, int64_t sizes[],
     485              :                                    int64_t **data_ptr) {
     486            0 :   *data_ptr = (int64_t *)get_data_ptr(tensor, torch::kInt64, ndims, sizes);
     487            0 : }
     488              : 
     489              : /*******************************************************************************
     490              :  * \brief Returns the data_ptr and sizes of a Torch tensor of doubles.
     491              :  *        The returned pointer is only valide during the tensor's live time!
     492              :  * \author Ole Schuett
     493              :  ******************************************************************************/
     494         1174 : void torch_c_tensor_data_ptr_double(const torch_c_tensor_t *tensor,
     495              :                                     const int ndims, int64_t sizes[],
     496              :                                     double **data_ptr) {
     497         1174 :   *data_ptr = (double *)get_data_ptr(tensor, torch::kFloat64, ndims, sizes);
     498         1174 : }
     499              : 
     500              : /*******************************************************************************
     501              :  * \brief Runs autograd on a Torch tensor.
     502              :  * \author Ole Schuett
     503              :  ******************************************************************************/
     504            6 : void torch_c_tensor_backward(const torch_c_tensor_t *tensor,
     505              :                              const torch_c_tensor_t *outer_grad) {
     506            6 :   TorchFloatingPointMaskGuard fpe_guard;
     507            6 :   c10::OptionalDeviceGuard guard;
     508            6 :   get_device_with_guard(guard);
     509            6 :   tensor->backward(*outer_grad);
     510            6 : }
     511              : 
     512              : /*******************************************************************************
     513              :  * \brief Runs autograd on a scalar Torch tensor.
     514              :  ******************************************************************************/
     515          296 : void torch_c_tensor_backward_scalar(const torch_c_tensor_t *tensor) {
     516          296 :   TorchFloatingPointMaskGuard fpe_guard;
     517          296 :   c10::OptionalDeviceGuard guard;
     518          296 :   get_device_with_guard(guard);
     519          592 :   tensor->backward();
     520          296 : }
     521              : 
     522              : /*******************************************************************************
     523              :  * \brief Moves a tensor to the active device and makes it an autograd leaf.
     524              :  ******************************************************************************/
     525         2556 : void torch_c_tensor_to_device_leaf(torch_c_tensor_t **tensor,
     526              :                                    const bool req_grad) {
     527         2556 :   c10::OptionalDeviceGuard guard;
     528         2556 :   const auto device = get_device_with_guard(guard);
     529         5112 :   auto moved = (*tensor)->to(device).detach();
     530         2556 :   moved.set_requires_grad(req_grad);
     531         5112 :   delete (*tensor);
     532         5112 :   *tensor = new torch_c_tensor_t(moved);
     533         2556 : }
     534              : 
     535              : /*******************************************************************************
     536              :  * \brief Select whether Torch wrappers should use CUDA when available.
     537              :  ******************************************************************************/
     538          632 : void torch_c_use_cuda(const bool use_cuda) { use_cuda_if_available = use_cuda; }
     539              : 
     540              : /*******************************************************************************
     541              :  * \brief Returns the gradient of a Torch tensor which was computed by autograd.
     542              :  * \author Ole Schuett
     543              :  ******************************************************************************/
     544          280 : void torch_c_tensor_grad(const torch_c_tensor_t *tensor,
     545              :                          torch_c_tensor_t **grad) {
     546          280 :   c10::OptionalDeviceGuard guard;
     547          280 :   get_device_with_guard(guard);
     548          280 :   const torch::Tensor maybe_grad = tensor->grad();
     549          280 :   assert(maybe_grad.defined());
     550          560 :   torch::Tensor host_grad = maybe_grad.detach().cpu().contiguous();
     551          280 :   if (maybe_grad.is_cpu()) {
     552          280 :     host_grad = host_grad.clone();
     553              :   }
     554          280 :   *grad = new torch_c_tensor_t(std::move(host_grad));
     555          280 : }
     556              : 
     557              : /*******************************************************************************
     558              :  * \brief Copies three autograd gradients to CPU memory.
     559              :  ******************************************************************************/
     560          294 : void torch_c_tensor_grad_batch3(const torch_c_tensor_t *tensor1,
     561              :                                 const torch_c_tensor_t *tensor2,
     562              :                                 const torch_c_tensor_t *tensor3,
     563              :                                 torch_c_tensor_t **grad1,
     564              :                                 torch_c_tensor_t **grad2,
     565              :                                 torch_c_tensor_t **grad3) {
     566          294 :   c10::OptionalDeviceGuard guard;
     567          294 :   const auto device = get_device_with_guard(guard);
     568          294 :   assert(*grad1 == nullptr && *grad2 == nullptr && *grad3 == nullptr);
     569              : 
     570          294 :   const torch::Tensor source1 = tensor1->grad();
     571          294 :   const torch::Tensor source2 = tensor2->grad();
     572          294 :   const torch::Tensor source3 = tensor3->grad();
     573          294 :   assert(source1.defined() && source2.defined() && source3.defined());
     574          294 :   assert(source1.device() == source2.device());
     575          294 :   assert(source1.device() == source3.device());
     576          294 :   if (use_batched_gradient_readback(source1)) {
     577            0 :     auto host1 =
     578              :         torch::empty(source1.sizes(),
     579            0 :                      source1.options().device(torch::kCPU).pinned_memory(true));
     580            0 :     auto host2 =
     581              :         torch::empty(source2.sizes(),
     582            0 :                      source2.options().device(torch::kCPU).pinned_memory(true));
     583            0 :     auto host3 =
     584              :         torch::empty(source3.sizes(),
     585            0 :                      source3.options().device(torch::kCPU).pinned_memory(true));
     586            0 :     host1.copy_(source1, true);
     587            0 :     host2.copy_(source2, true);
     588            0 :     host3.copy_(source3, true);
     589            0 :     torch::cuda::synchronize(device.index());
     590            0 :     *grad1 = new torch_c_tensor_t(std::move(host1));
     591            0 :     *grad2 = new torch_c_tensor_t(std::move(host2));
     592            0 :     *grad3 = new torch_c_tensor_t(std::move(host3));
     593            0 :   } else {
     594              :     // Materialize independent host buffers instead of aliasing gradients owned
     595              :     // by the autograd graph when they are already contiguous CPU tensors.
     596          294 :     *grad1 = new torch_c_tensor_t(source1.detach().cpu().contiguous().clone());
     597          294 :     *grad2 = new torch_c_tensor_t(source2.detach().cpu().contiguous().clone());
     598          294 :     *grad3 = new torch_c_tensor_t(source3.detach().cpu().contiguous().clone());
     599              :   }
     600          294 : }
     601              : 
     602              : /*******************************************************************************
     603              :  * \brief Releases a Torch tensor and all its ressources.
     604              :  * \author Ole Schuett
     605              :  ******************************************************************************/
     606        12784 : void torch_c_tensor_release(torch_c_tensor_t *tensor) { delete (tensor); }
     607              : 
     608              : /*******************************************************************************
     609              :  * \brief Creates an empty Torch dictionary.
     610              :  * \author Ole Schuett
     611              :  ******************************************************************************/
     612          426 : void torch_c_dict_create(torch_c_dict_t **dict_out) {
     613          426 :   assert(*dict_out == NULL);
     614          426 :   *dict_out = new c10::Dict<std::string, torch::Tensor>();
     615          426 : }
     616              : 
     617              : /*******************************************************************************
     618              :  * \brief Clones a Torch dictionary.
     619              :  ******************************************************************************/
     620           36 : void torch_c_dict_clone(const torch_c_dict_t *dict, torch_c_dict_t **dict_out) {
     621           36 :   assert(*dict_out == NULL);
     622           36 :   torch_c_dict_t *clone = new c10::Dict<std::string, torch::Tensor>();
     623          216 :   for (const auto &entry : *dict) {
     624          180 :     clone->insert(entry.key(), entry.value());
     625              :   }
     626           36 :   *dict_out = clone;
     627           36 : }
     628              : 
     629              : /*******************************************************************************
     630              :  * \brief Inserts a Torch tensor into a Torch dictionary.
     631              :  * \author Ole Schuett
     632              :  ******************************************************************************/
     633         2942 : void torch_c_dict_insert(const torch_c_dict_t *dict, const char *key,
     634              :                          const torch_c_tensor_t *tensor) {
     635         2942 :   c10::OptionalDeviceGuard guard;
     636         2942 :   const auto device = get_device_with_guard(guard);
     637         5884 :   dict->insert(key, tensor->to(device));
     638         2942 : }
     639              : 
     640              : /*******************************************************************************
     641              :  * \brief Retrieves a Torch tensor from a Torch dictionary.
     642              :  * \author Ole Schuett
     643              :  ******************************************************************************/
     644           76 : void torch_c_dict_get(const torch_c_dict_t *dict, const char *key,
     645              :                       torch_c_tensor_t **tensor) {
     646           76 :   assert(dict->contains(key));
     647           76 :   *tensor = new torch_c_tensor_t(dict->at(key).cpu().contiguous());
     648           76 : }
     649              : 
     650              : /*******************************************************************************
     651              :  * \brief Releases a Torch dictionary and all its ressources.
     652              :  * \author Ole Schuett
     653              :  ******************************************************************************/
     654          784 : void torch_c_dict_release(torch_c_dict_t *dict) { delete (dict); }
     655              : 
     656              : /*******************************************************************************
     657              :  * \brief Loads a Torch model from given "*.pth" file.
     658              :  *        In Torch lingo models are called modules.
     659              :  * \author Ole Schuett
     660              :  ******************************************************************************/
     661           16 : void torch_c_model_load(torch_c_model_t **model_out, const char *filename) {
     662           16 :   assert(*model_out == NULL);
     663           16 :   c10::OptionalDeviceGuard guard;
     664           16 :   const auto device = get_device_with_guard(guard);
     665           16 :   set_jit_fusion_strategy();
     666           16 :   torch::jit::Module *model = new torch::jit::Module();
     667           16 :   *model = load_module_for_device(filename, device);
     668           16 :   model->eval(); // Set inference behavior for modules such as dropout.
     669           16 :   *model_out = model;
     670           16 : }
     671              : 
     672              : /*******************************************************************************
     673              :  * \brief Loads a Torch model and reads two metadata entries.
     674              :  ******************************************************************************/
     675           91 : void torch_c_model_load_with_metadata(torch_c_model_t **model_out,
     676              :                                       const char *filename, const char *key1,
     677              :                                       const char *key2, char **content1,
     678              :                                       int *length1, char **content2,
     679              :                                       int *length2) {
     680           91 :   assert(*model_out == NULL);
     681           91 :   c10::OptionalDeviceGuard guard;
     682           91 :   const auto device = get_device_with_guard(guard);
     683           91 :   std::unordered_map<std::string, std::string> extra_files = {{key1, ""},
     684          364 :                                                               {key2, ""}};
     685           91 :   set_jit_fusion_strategy();
     686           91 :   torch::jit::Module *model = new torch::jit::Module();
     687           91 :   *model = load_module_for_device(filename, device, &extra_files);
     688           91 :   model->eval(); // Set inference behavior for modules such as dropout.
     689           91 :   *model_out = model;
     690          182 :   copy_string_to_c_buffer(extra_files[key1], content1, length1);
     691          182 :   copy_string_to_c_buffer(extra_files[key2], content2, length2);
     692          182 : }
     693              : 
     694              : /*******************************************************************************
     695              :  * \brief Maps serialized TorchScript device constants to the active device.
     696              :  ******************************************************************************/
     697           99 : void torch_c_model_remap_device_constants(torch_c_model_t *model) {
     698           99 :   c10::OptionalDeviceGuard guard;
     699           99 :   const auto device = get_device_with_guard(guard);
     700           99 :   if (device.is_cuda()) {
     701            0 :     remap_model_device_constants(*model, device);
     702            0 :     initialize_cuda_linalg(device);
     703              :   }
     704           99 : }
     705              : 
     706              : /*******************************************************************************
     707              :  * \brief Disables gradients for inference-only model parameters.
     708              :  ******************************************************************************/
     709           91 : void torch_c_model_disable_parameter_gradients(torch_c_model_t *model) {
     710           91 :   torch::NoGradGuard no_grad;
     711         7371 :   for (auto parameter : model->parameters()) {
     712        14560 :     parameter.set_requires_grad(false);
     713         7371 :   }
     714           91 : }
     715              : 
     716              : /*******************************************************************************
     717              :  * \brief Evaluates the given Torch model.
     718              :  * \author Ole Schuett
     719              :  ******************************************************************************/
     720           62 : void torch_c_model_forward(torch_c_model_t *model, const torch_c_dict_t *inputs,
     721              :                            torch_c_dict_t *outputs) {
     722              : 
     723           62 :   TorchFloatingPointMaskGuard fpe_guard;
     724           62 :   c10::OptionalDeviceGuard guard;
     725           62 :   get_device_with_guard(guard);
     726          310 :   auto untyped_output = model->forward({*inputs}).toGenericDict();
     727           62 :   outputs->clear();
     728          232 :   for (const auto &entry : untyped_output) {
     729          170 :     outputs->insert(entry.key().toStringView(), entry.value().toTensor());
     730              :   }
     731          186 : }
     732              : 
     733              : /*******************************************************************************
     734              :  * \brief Evaluates a TorchScript model method expecting argument "mol".
     735              :  ******************************************************************************/
     736          296 : void torch_c_model_forward_mol_tensor(torch_c_model_t *model,
     737              :                                       const char *method_name,
     738              :                                       const torch_c_dict_t *inputs,
     739              :                                       torch_c_tensor_t **output) {
     740              : 
     741          296 :   c10::OptionalDeviceGuard guard;
     742          296 :   get_device_with_guard(guard);
     743          296 :   assert(*output == NULL);
     744          296 :   *output = new torch_c_tensor_t(
     745         1480 :       model->get_method(method_name)({*inputs}).toTensor());
     746          888 : }
     747              : 
     748              : /*******************************************************************************
     749              :  * \brief Returns the weighted sum of two Torch tensors.
     750              :  ******************************************************************************/
     751          296 : void torch_c_tensor_weighted_sum(const torch_c_tensor_t *values,
     752              :                                  const torch_c_tensor_t *weights,
     753              :                                  torch_c_tensor_t **result) {
     754          296 :   c10::OptionalDeviceGuard guard;
     755          296 :   get_device_with_guard(guard);
     756          296 :   const auto weights_on_device = weights->to(values->device());
     757          592 :   *result = new torch_c_tensor_t((*values * weights_on_device).sum());
     758          296 : }
     759              : 
     760              : /*******************************************************************************
     761              :  * \brief Returns a scalar double value from a Torch tensor.
     762              :  ******************************************************************************/
     763          296 : double torch_c_tensor_item_double(const torch_c_tensor_t *tensor) {
     764          296 :   c10::OptionalDeviceGuard guard;
     765          296 :   get_device_with_guard(guard);
     766          296 :   return tensor->item<double>();
     767          296 : }
     768              : 
     769              : /*******************************************************************************
     770              :  * \brief Releases a Torch model and all its ressources.
     771              :  * \author Ole Schuett
     772              :  ******************************************************************************/
     773           16 : void torch_c_model_release(torch_c_model_t *model) { delete (model); }
     774              : 
     775              : /*******************************************************************************
     776              :  * \brief Reads metadata entry from given "*.pth" file.
     777              :  *        In Torch lingo they are called extra files.
     778              :  *        The returned char array has to be deallocated by caller!
     779              :  * \author Ole Schuett
     780              :  ******************************************************************************/
     781           40 : void torch_c_model_read_metadata(const char *filename, const char *key,
     782              :                                  char **content, int *length) {
     783              : 
     784          120 :   std::unordered_map<std::string, std::string> extra_files = {{key, ""}};
     785           40 :   torch::jit::load(filename, torch::kCPU, extra_files);
     786           80 :   const std::string &content_str = extra_files[key];
     787           40 :   copy_string_to_c_buffer(content_str, content, length);
     788           80 : }
     789              : 
     790              : /*******************************************************************************
     791              :  * \brief Returns true iff the Torch CUDA backend is available.
     792              :  * \author Ole Schuett
     793              :  ******************************************************************************/
     794            2 : bool torch_c_cuda_is_available() { return torch::cuda::is_available(); }
     795              : 
     796              : /*******************************************************************************
     797              :  * \brief Return the number of CUDA devices visible to Torch.
     798              :  ******************************************************************************/
     799            0 : int torch_c_cuda_device_count() {
     800            0 :   return torch::cuda::is_available() ? torch::cuda::device_count() : 0;
     801              : }
     802              : 
     803              : /*******************************************************************************
     804              :  * \brief Set whether to allow TF32.
     805              :  *        Needed due to changes in defaults from pytorch 1.7 to 1.11 to >=1.12
     806              :  *        See https://pytorch.org/docs/stable/notes/cuda.html
     807              :  * \author Gabriele Tocci
     808              :  ******************************************************************************/
     809            6 : void torch_c_allow_tf32(const bool allow_tf32) {
     810              : 
     811            6 :   at::globalContext().setAllowTF32CuBLAS(allow_tf32);
     812            6 :   at::globalContext().setAllowTF32CuDNN(allow_tf32);
     813            6 : }
     814              : 
     815              : /******************************************************************************
     816              :  * \brief Freeze the Torch model: generic optimization that speeds up model.
     817              :  *        See https://pytorch.org/docs/stable/generated/torch.jit.freeze.html
     818              :  * \author Gabriele Tocci
     819              :  ******************************************************************************/
     820            6 : void torch_c_model_freeze(torch_c_model_t *model) {
     821              : 
     822            6 :   *model = torch::jit::freeze(*model);
     823            6 : }
     824              : 
     825              : /******************************************************************************
     826              :  * \brief Freeze a Torch model while preserving one exported method.
     827              :  ******************************************************************************/
     828           91 : void torch_c_model_freeze_preserving_method(torch_c_model_t *model,
     829              :                                             const char *method_name) {
     830              : 
     831          182 :   const std::vector<std::string> preserved_methods = {method_name};
     832           91 :   torch::jit::freeze_module_inplace(model, preserved_methods);
     833          182 : }
     834              : 
     835              : /*******************************************************************************
     836              :  * \brief Retrieves an int64 attribute. Must be called before model freeze.
     837              :  * \author Ole Schuett
     838              :  ******************************************************************************/
     839           40 : void torch_c_model_get_attr_int64(const torch_c_model_t *model, const char *key,
     840              :                                   int64_t *dest) {
     841           40 :   *dest = model->attr(key).toInt();
     842           40 : }
     843              : 
     844              : /*******************************************************************************
     845              :  * \brief Retrieves a double attribute. Must be called before model freeze.
     846              :  * \author Ole Schuett
     847              :  ******************************************************************************/
     848            8 : void torch_c_model_get_attr_double(const torch_c_model_t *model,
     849              :                                    const char *key, double *dest) {
     850            8 :   *dest = model->attr(key).toDouble();
     851            8 : }
     852              : 
     853              : /*******************************************************************************
     854              :  * \brief Retrieves a string attribute. Must be called before model freeze.
     855              :  * \author Ole Schuett
     856              :  ******************************************************************************/
     857           16 : void torch_c_model_get_attr_string(const torch_c_model_t *model,
     858              :                                    const char *key, char *dest) {
     859           16 :   const std::string &str = model->attr(key).toStringRef();
     860           16 :   assert(str.size() < 80); // default_string_length
     861          144 :   for (int i = 0; i < str.size(); i++) {
     862          128 :     dest[i] = str[i];
     863              :   }
     864           16 : }
     865              : 
     866              : /*******************************************************************************
     867              :  * \brief Retrieves a list attribute's size. Must be called before model freeze.
     868              :  * \author Ole Schuett
     869              :  ******************************************************************************/
     870            8 : void torch_c_model_get_attr_list_size(const torch_c_model_t *model,
     871              :                                       const char *key, int *size) {
     872            8 :   *size = model->attr(key).toList().size();
     873            8 : }
     874              : 
     875              : /*******************************************************************************
     876              :  * \brief Retrieves a single item from a string list attribute.
     877              :  * \author Ole Schuett
     878              :  ******************************************************************************/
     879           16 : void torch_c_model_get_attr_strlist(const torch_c_model_t *model,
     880              :                                     const char *key, const int index,
     881              :                                     char *dest) {
     882           32 :   const auto list = model->attr(key).toList();
     883           16 :   const std::string &str = list[index].toStringRef();
     884           16 :   assert(str.size() < 80); // default_string_length
     885           32 :   for (int i = 0; i < str.size(); i++) {
     886           16 :     dest[i] = str[i];
     887              :   }
     888           16 : }
     889              : 
     890              : #ifdef __cplusplus
     891              : }
     892              : #endif
     893              : 
     894              : #endif // defined(__LIBTORCH)
     895              : 
     896              : // EOF
        

Generated by: LCOV version 2.0-1