LCOV - code coverage report
Current view: top level - variable - slice.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 73 73 100.0 %
Date: 2024-12-01 01:56:34 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: BSD-3-Clause
       2             : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
       3             : /// @file
       4             : /// @author Owen Arnold, Simon Heybrock
       5             : #include <algorithm>
       6             : 
       7             : #include "scipp/units/dim.h"
       8             : #include "scipp/variable/comparison.h"
       9             : #include "scipp/variable/reduction.h"
      10             : #include "scipp/variable/slice.h"
      11             : #include "scipp/variable/string.h"
      12             : #include "scipp/variable/util.h"
      13             : #include "scipp/variable/variable.h"
      14             : 
      15             : namespace scipp::variable {
      16             : 
      17             : namespace {
      18             : 
      19         297 : scipp::index get_count(const Variable &coord, const Dim dim,
      20             :                        const Variable &value, const bool ascending) {
      21         798 :   return (ascending ? sum(less_equal(coord, value), dim)
      22         501 :                     : sum(greater_equal(coord, value), dim))
      23         594 :       .value<scipp::index>();
      24             : }
      25             : 
      26         278 : scipp::index get_index(const Variable &coord, const Dim dim,
      27             :                        const Variable &value, const bool ascending,
      28             :                        const bool edges) {
      29         278 :   auto i = get_count(coord, dim, value, edges == ascending);
      30         278 :   i = edges ? i - 1 : coord.dims()[dim] - i;
      31         278 :   return std::clamp<scipp::index>(0, i, coord.dims()[dim]);
      32             : }
      33             : 
      34         335 : const Variable &get_1d_coord(const Variable &coord) {
      35         335 :   if (coord.dims().ndim() != 1)
      36           2 :     throw except::DimensionError("Multi-dimensional coordinates cannot be used "
      37           4 :                                  "for label-based indexing.");
      38         333 :   return coord;
      39             : }
      40             : 
      41         181 : auto get_coord(const Variable &coord, const Dim dim) {
      42         181 :   get_1d_coord(coord);
      43         179 :   if (coord.dims()[dim] == 1)
      44             :     // Need this because issorted returns false for length-1 variables.
      45           2 :     return std::tuple(coord, true);
      46         177 :   const bool ascending = allsorted(coord, dim, SortOrder::Ascending);
      47         177 :   const bool descending = allsorted(coord, dim, SortOrder::Descending);
      48         177 :   if (!(ascending ^ descending))
      49           2 :     throw std::runtime_error("Coordinate must be monotonically increasing or "
      50           4 :                              "decreasing for label-based indexing.");
      51         175 :   return std::tuple(coord, ascending);
      52             : }
      53             : 
      54         462 : void expect_same_unit(const Variable &coord, const Variable &value,
      55             :                       const std::string &name) {
      56         462 :   if (coord.unit() != value.unit()) {
      57          21 :     throw except::UnitError("The unit of the slice " + name + " (" +
      58          28 :                             to_string(value.unit()) +
      59          14 :                             ") does not match the unit of the coordinate (" +
      60          28 :                             to_string(coord.unit()) + ").");
      61             :   }
      62         455 : }
      63             : 
      64         617 : void expect_valid_dtype(const Variable &var, const bool is_range,
      65             :                         const std::string &name) {
      66         617 :   if (is_range && !is_total_orderable(var.dtype())) {
      67           4 :     throw except::TypeError(
      68           8 :         "The dtype of the slice " + name + " (" + to_string(var.dtype()) +
      69             :         ") cannot be used for label-based slicing because it does not"
      70           8 :         " define an order.");
      71             :   }
      72         613 : }
      73             : 
      74         518 : void expect_valid_slice_value(const Variable &coord, const Variable &value,
      75             :                               const bool is_range,
      76             :                               const std::string_view name) {
      77         518 :   if (value.is_valid()) {
      78         478 :     core::expect::equals(Dimensions{}, value.dims());
      79         476 :     expect_same_unit(coord, value, std::string(name));
      80         459 :     expect_valid_dtype(value, is_range, std::string(name));
      81             :   }
      82         503 : }
      83             : } // namespace
      84             : 
      85         176 : std::tuple<Dim, scipp::index> get_slice_params(const Sizes &dims,
      86             :                                                const Variable &coord_,
      87             :                                                const Variable &value) {
      88         176 :   expect_valid_slice_value(coord_, value, false, "key");
      89         173 :   const auto dim = coord_.dims().inner();
      90         173 :   if (dims[dim] + 1 == coord_.dims()[dim]) {
      91          19 :     const auto &[coord, ascending] = get_coord(coord_, dim);
      92          19 :     return std::tuple{dim, get_count(coord, dim, value, ascending) - 1};
      93          19 :   } else {
      94         154 :     auto eq = equal(get_1d_coord(coord_), value);
      95         154 :     if (sum(eq, dim).template value<scipp::index>() != 1)
      96          15 :       throw except::SliceError("Coord " + to_string(dim) +
      97          10 :                                " does not contain unique point with value " +
      98          20 :                                to_string(value) + '\n');
      99         149 :     auto values = eq.template values<bool>();
     100         149 :     auto it = std::find(values.begin(), values.end(), true);
     101         149 :     return {dim, std::distance(values.begin(), it)};
     102         154 :   }
     103             : }
     104             : 
     105             : std::tuple<Dim, scipp::index, scipp::index>
     106         174 : get_slice_params(const Sizes &dims, const Variable &coord_,
     107             :                  const Variable &begin, const Variable &end) {
     108         174 :   expect_valid_slice_value(coord_, begin, true, "begin");
     109         168 :   expect_valid_slice_value(coord_, end, true, "end");
     110         162 :   expect_valid_dtype(coord_, true, "coord");
     111         162 :   const auto dim = coord_.dims().inner();
     112         162 :   const auto &[coord, ascending] = get_coord(coord_, dim);
     113         158 :   scipp::index first = 0;
     114         158 :   scipp::index last = dims[dim];
     115         158 :   const auto bin_edges = last + 1 == coord.dims()[dim];
     116         158 :   if (begin.is_valid())
     117         135 :     first = get_index(coord, dim, begin, ascending, bin_edges);
     118         158 :   if (end.is_valid())
     119         143 :     last = get_index(coord, dim, end, ascending, false);
     120         316 :   return {dim, first, std::min(dims[dim], last)};
     121         158 : }
     122             : 
     123             : } // namespace scipp::variable

Generated by: LCOV version 1.14