< Summary

Line coverage
61%
Covered lines: 80
Uncovered lines: 50
Coverable lines: 130
Total lines: 342
Line coverage: 61.5%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

C:\Users\Kremenchuk\actions-runner\_work\Holoflow\Holoflow\src\holonp\src\slice.cc

#LineLine coverage
 1// Copyright 2026 Digital Holography Foundation
 2//
 3// Licensed under the Apache License, Version 2.0 (the "License");
 4// you may not use this file except in compliance with the License.
 5// You may obtain a copy of the License at
 6//
 7//     http://www.apache.org/licenses/LICENSE-2.0
 8//
 9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15#include "holonp/slice.hh"
 16
 17#include <algorithm>
 18#include <numeric>
 19#include <stdexcept>
 20#include <string>
 21
 22namespace holonp {
 23
 24// -------------------------------------------------------------------------------------------------
 25// JSON serialization
 26// -------------------------------------------------------------------------------------------------
 27
 028void to_json(nlohmann::json &j, const SliceRange &s) {
 029  j = nlohmann::json{
 30      {"start", s.start.has_value() ? nlohmann::json(*s.start) : nlohmann::json(nullptr)},
 31      {"stop", s.stop.has_value() ? nlohmann::json(*s.stop) : nlohmann::json(nullptr)},
 32      {"step", s.step},
 33  };
 034}
 35
 136void from_json(const nlohmann::json &j, SliceRange &s) {
 137  if (j.contains("start") && !j.at("start").is_null()) {
 138    s.start = j.at("start").get<std::int64_t>();
 139  } else {
 140    s.start = std::nullopt;
 41  }
 42
 143  if (j.contains("stop") && !j.at("stop").is_null()) {
 144    s.stop = j.at("stop").get<std::int64_t>();
 145  } else {
 146    s.stop = std::nullopt;
 47  }
 48
 49  // Default step to 1 if not present
 150  s.step = j.value("step", 1);
 151}
 52
 053void to_json(nlohmann::json &j, const SliceItem &s) {
 054  std::visit(
 55      [&](auto &&arg) {
 56        using T = std::decay_t<decltype(arg)>;
 57        if constexpr (std::is_same_v<T, std::int64_t>) {
 58          j = arg; // Serialize as integer
 59        } else {
 60          j = arg; // Serialize as SliceRange object
 61        }
 62      },
 63      s);
 064}
 65
 166void from_json(const nlohmann::json &j, SliceItem &s) {
 167  if (j.is_number_integer()) {
 68    // If it's a number, it's a direct index (e.g. 5)
 169    s = j.get<std::int64_t>();
 170  } else {
 71    // If it's an object (or null/empty), treat as SliceRange
 172    s = j.get<SliceRange>();
 73  }
 174}
 75
 076void to_json(nlohmann::json &j, const SliceSettings &s) {
 077  j = nlohmann::json{{"slices", s.slices}};
 078}
 79
 180void from_json(const nlohmann::json &j, SliceSettings &s) { j.at("slices").get_to(s.slices); }
 81
 82namespace {
 83
 84// -------------------------------------------------------------------------------------------------
 85// Helpers
 86// -------------------------------------------------------------------------------------------------
 87
 88constexpr int kMaxNDim = 16;
 89
 190inline void check(bool cond, const std::string &msg) {
 191  if (!cond) {
 092    throw std::invalid_argument("Slice: " + msg);
 93  }
 194}
 95
 96struct NormalizedSlice {
 97  std::int64_t start;
 98  std::int64_t stop; // exclusive
 99  std::int64_t step; // > 0
 100};
 101
 102// Helper: Validate and Normalize a single Integer Index
 1103inline std::int64_t normalize_index(std::int64_t idx, std::int64_t dim) {
 1104  check(dim > 0, "cannot index into 0-sized dimension");
 105
 106  // Handle negative wrapping
 1107  if (idx < 0) {
 0108    idx += dim;
 109  }
 110
 111  // Strict Bound Checking
 1112  if (idx < 0 || idx >= dim) {
 1113    throw std::out_of_range("Slice index " + std::to_string(idx) +
 114                            " is out of bounds for dimension size " + std::to_string(dim));
 115  }
 1116  return idx;
 1117}
 118
 119// Helper: Validate and Normalize a Slice Range
 1120inline NormalizedSlice normalize_slice_range(const SliceRange &s, std::int64_t dim) {
 1121  check(dim >= 0, "invalid dimension");
 122
 1123  const auto step = s.step;
 1124  check(step > 0, "only positive step is supported for now");
 125
 1126  std::int64_t start = s.start.value_or(0);
 1127  std::int64_t stop  = s.stop.value_or(dim);
 128
 129  // 1. Handle negative wrapping
 1130  if (start < 0)
 0131    start += dim;
 1132  if (stop < 0)
 0133    stop += dim;
 134
 135  // 2. Strict Bound Checking (optional, depending on desired strictness vs numpy leniency)
 136  // For safety in this environment, we check bounds strictly relative to 0.
 137  // Note: NumPy usually clamps start/stop, but throws on integer indexing.
 138  // Here we clamp to maintain view safety.
 1139  start = std::clamp<std::int64_t>(start, 0, dim);
 1140  stop  = std::clamp<std::int64_t>(stop, 0, dim);
 141
 1142  return NormalizedSlice{start, stop, step};
 1143}
 144
 1145inline std::int64_t out_len(const NormalizedSlice &ns) {
 1146  if (ns.start >= ns.stop) {
 0147    return 0;
 148  }
 1149  const auto span = ns.stop - ns.start;
 1150  return (span + ns.step - 1) / ns.step;
 1151}
 152
 153// Helper to get consistent BYTES strides
 1154inline std::vector<size_t> ensure_strides(const holoflow::core::TDesc &desc) {
 1155  if (!desc.strides.empty()) {
 1156    return desc.strides;
 157  }
 0158  std::vector<size_t> strides(desc.shape.size());
 0159  size_t              acc = holoflow::core::size_of(desc.dtype);
 0160  for (int i = static_cast<int>(desc.shape.size()) - 1; i >= 0; --i) {
 0161    strides[i] = acc;
 0162    acc *= desc.shape[i];
 0163  }
 0164  return strides;
 1165}
 166
 167// -------------------------------------------------------------------------------------------------
 168// Slice task implementation
 169// -------------------------------------------------------------------------------------------------
 170
 171class Slice : public holoflow::core::ISyncTask {
 172public:
 173  holoflow::core::OpResult execute(holoflow::core::SyncCtx &ctx) override;
 174};
 175
 176} // namespace
 177
 0178holoflow::core::OpResult Slice::execute(holoflow::core::SyncCtx &ctx) {
 179  (void)ctx;
 0180  return holoflow::core::OpResult::Ok;
 0181}
 182
 183// -------------------------------------------------------------------------------------------------
 184// SliceFactory
 185// -------------------------------------------------------------------------------------------------
 186
 187holoflow::core::InferResult SliceFactory::infer(std::span<const holoflow::core::TDesc> input_descs,
 1188                                                const nlohmann::json &jsettings) const {
 1189  check(input_descs.size() == 1, "expected exactly 1 input");
 1190  const auto &idesc = input_descs[0];
 191
 1192  const int ndim = static_cast<int>(idesc.shape.size());
 1193  check(ndim > 0, "input ndim must be > 0");
 1194  check(ndim <= kMaxNDim, "input ndim too large");
 195
 1196  const auto in_strides = ensure_strides(idesc);
 1197  const auto settings   = jsettings.get<SliceSettings>();
 198
 1199  check(static_cast<int>(settings.slices.size()) == ndim,
 200        "number of slice items must match input ndim");
 201
 1202  std::vector<size_t> out_shape;
 1203  std::vector<size_t> out_strides;
 1204  out_shape.reserve(ndim);
 1205  out_strides.reserve(ndim);
 206
 207  // Calculate new Offset relative to current input offset
 1208  size_t added_offset_bytes = 0;
 209
 1210  for (int i = 0; i < ndim; ++i) {
 1211    const auto &item       = settings.slices[i];
 1212    const auto  dim_size   = static_cast<std::int64_t>(idesc.shape[i]);
 1213    const auto  dim_stride = in_strides[i];
 214
 1215    std::visit(
 216        [&](auto &&arg) {
 217          using T = std::decay_t<decltype(arg)>;
 218
 219          if constexpr (std::is_same_v<T, std::int64_t>) {
 220            // === CASE 1: Integer Index (Dimensionality Reduction) ===
 221            // Calculate offset, but do NOT add to out_shape/out_strides
 222            const std::int64_t idx = normalize_index(arg, dim_size);
 223            added_offset_bytes += static_cast<size_t>(idx) * dim_stride;
 224          } else {
 225            // === CASE 2: Slice Range (Preserve Dimension) ===
 226            const auto ns = normalize_slice_range(arg, dim_size);
 227
 228            // Add offset for the start of the slice
 229            added_offset_bytes += static_cast<size_t>(ns.start) * dim_stride;
 230
 231            // Push new dimension shape and stride
 232            out_shape.push_back(static_cast<size_t>(out_len(ns)));
 233            out_strides.push_back(dim_stride * static_cast<size_t>(ns.step));
 234          }
 235        },
 236        item);
 1237  }
 238
 239  // Construct Output Descriptor
 1240  const size_t final_offset = idesc.offset + added_offset_bytes;
 241
 1242  holoflow::core::TDesc odesc(out_shape, idesc.dtype, idesc.mem_loc, out_strides, final_offset);
 243
 1244  return holoflow::core::InferResult{
 245      .input_descs   = {idesc},
 246      .output_descs  = {odesc},
 247      .in_place      = {{0, 0}}, // Input 0 -> Output 0
 248      .owned_inputs  = {false},
 249      .owned_outputs = {false},
 250      .kind          = holoflow::core::TaskKind::Sync,
 251  };
 1252}
 253
 254std::unique_ptr<holoflow::core::ISyncTask>
 255SliceFactory::create(std::span<const holoflow::core::TDesc> input_descs,
 256                     const nlohmann::json                  &jsettings,
 0257                     const holoflow::core::SyncCreateCtx   &ctx) const {
 0258  (void)infer(input_descs, jsettings);
 259  (void)ctx;
 0260  return std::make_unique<Slice>();
 0261}
 262
 263std::unique_ptr<holoflow::core::ISyncTask>
 264SliceFactory::update(std::unique_ptr<holoflow::core::ISyncTask> old_task,
 265                     std::span<const holoflow::core::TDesc>     input_descs,
 266                     const nlohmann::json                      &jsettings,
 0267                     const holoflow::core::SyncCreateCtx       &ctx) const {
 268  (void)ctx;
 0269  (void)infer(input_descs, jsettings);
 270
 0271  auto *old_slice = dynamic_cast<Slice *>(old_task.get());
 0272  if (old_slice == nullptr || input_descs.size() != 1) {
 0273    return create(input_descs, jsettings, ctx);
 274  }
 275
 0276  return old_task;
 0277}
 278
 279} // namespace holonp

C:\Users\Kremenchuk\actions-runner\_work\Holoflow\Holoflow\src\holonp\src\utils\fft_common.cc

#LineLine coverage
 1// Copyright 2026 Digital Holography Foundation
 2//
 3// Licensed under the Apache License, Version 2.0 (the "License");
 4// you may not use this file except in compliance with the License.
 5// You may obtain a copy of the License at
 6//
 7//     http://www.apache.org/licenses/LICENSE-2.0
 8//
 9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15#include "utils/fft_common.hh"
 16
 17#include <stdexcept>
 18#include <string>
 19
 20namespace holonp {
 21
 22// -------------------------------------------------------------------------------------------------
 23// Helpers
 24// -------------------------------------------------------------------------------------------------
 25
 026std::string_view fft_norm_to_string(FftNorm norm) noexcept {
 027  switch (norm) {
 28  case FftNorm::Backward:
 029    return "backward";
 30  case FftNorm::Forward:
 031    return "forward";
 32  case FftNorm::Ortho:
 033    return "ortho";
 34  default:
 035    return "backward";
 36  }
 037}
 38
 39// -------------------------------------------------------------------------------------------------
 40// JSON
 41// -------------------------------------------------------------------------------------------------
 42
 043void to_json(nlohmann::json &j, FftNorm norm) { j = std::string(fft_norm_to_string(norm)); }
 44
 145void from_json(const nlohmann::json &j, FftNorm &norm) {
 146  if (j.is_null()) {
 047    norm = FftNorm::Backward;
 048    return;
 49  }
 50
 151  auto s = j.get<std::string>();
 152  if (s == "backward" || s == "none") {
 053    norm = FftNorm::Backward;
 154  } else if (s == "forward") {
 155    norm = FftNorm::Forward;
 056  } else if (s == "ortho") {
 057    norm = FftNorm::Ortho;
 058  } else {
 059    throw std::invalid_argument("FftNorm: unsupported norm '" + s + "'");
 60  }
 161}
 62
 63} // namespace holonp

Methods/Properties