22#include <nk/ops/value_factory.hpp>
24#include <memory_resource>
25#include <nk/value/scratch.hpp>
26#include <nk/value/error.hpp>
27#include <nk/value/value.hpp>
32#include <memory_resource>
41inline int firstNonSingletonDim(
const Value &x)
43 const auto &d = x.dims();
44 const int nd = d.ndim();
45 for (
int i = 0; i < nd; ++i)
46 if (d.dim(i) > 1)
return i + 1;
53inline int validateDim(
const Value &x,
int dim,
const char *fn)
56 throw Error(std::string(fn) +
": dim must be a positive integer",
57 0, 0, fn,
"", std::string(
"nk:") + fn +
":badDim");
58 const int nd = x.dims().ndim();
68inline DimsArg outShapeForDim(
const Value &x,
int dim)
70 const auto &d = x.dims();
71 DimsArg o{d.rows(), d.cols(), d.is3D() ? d.pages() : 0};
73 case 1: o.rows = 1;
break;
74 case 2: o.cols = 1;
break;
75 case 3: o.pages = (o.pages == 0) ? 0 : 1;
break;
82inline size_t sliceLenForDim(
const Value &x,
int dim)
84 const auto &d = x.dims();
86 case 1:
return d.rows();
87 case 2:
return d.cols();
88 case 3:
return d.is3D() ? d.pages() : 1;
94inline size_t sliceCountForDim(
const Value &x,
int dim)
96 return x.numel() / sliceLenForDim(x, dim);
112void forEachSlice(std::pmr::memory_resource *mr,
113 const Value &x,
int dim, F &&f)
115 const auto &d = x.dims();
116 const size_t R = d.rows(), C = d.cols(), P = d.is3D() ? d.pages() : 1;
117 const size_t N = sliceLenForDim(x, dim);
118 const bool fastDouble = (x.type() == ValueType::DOUBLE);
119 const double *src = fastDouble ? x.doubleData() :
nullptr;
121 ScratchVec<double> scratch(N, mr);
126 for (
size_t pp = 0; pp < P; ++pp) {
127 for (
size_t c = 0; c < C; ++c) {
128 const size_t base = pp * R * C + c * R;
130 std::copy(src + base, src + base + N, scratch.data());
132 for (
size_t k = 0; k < N; ++k)
133 scratch[k] = x.elemAsDouble(base + k);
135 f(outIdx, scratch.data(), N);
139 }
else if (dim == 2) {
142 for (
size_t pp = 0; pp < P; ++pp) {
143 for (
size_t r = 0; r < R; ++r) {
145 for (
size_t c = 0; c < C; ++c)
146 scratch[c] = src[pp * R * C + c * R + r];
148 for (
size_t c = 0; c < C; ++c)
149 scratch[c] = x.elemAsDouble(pp * R * C + c * R + r);
151 f(outIdx, scratch.data(), N);
155 }
else if (dim == 3) {
158 for (
size_t c = 0; c < C; ++c) {
159 for (
size_t r = 0; r < R; ++r) {
161 for (
size_t pp = 0; pp < P; ++pp)
162 scratch[pp] = src[pp * R * C + c * R + r];
164 for (
size_t pp = 0; pp < P; ++pp)
165 scratch[pp] = x.elemAsDouble(pp * R * C + c * R + r);
167 f(outIdx, scratch.data(), N);
180inline ScratchVec<size_t> outShapeForDimND(std::pmr::memory_resource *mr,
181 const Value &x,
int dim)
183 const auto &d = x.dims();
184 ScratchVec<size_t> shape(d.ndim(), mr);
185 for (
int i = 0; i < d.ndim(); ++i)
186 shape[i] = (i + 1 == dim) ? 1 : d.dim(i);
198void forEachSliceND(std::pmr::memory_resource *mr,
199 const Value &x,
int dim,
double *dst, F &&f)
201 const auto &d = x.dims();
202 const int redAxis = dim - 1;
203 const size_t sliceLen = d.dim(redAxis);
205 for (
int i = 0; i < redAxis; ++i) B *= d.dim(i);
207 for (
int i = redAxis + 1; i < d.ndim(); ++i) O *= d.dim(i);
209 const bool fastDouble = (x.type() == ValueType::DOUBLE);
210 const double *src = fastDouble ? x.doubleData() :
nullptr;
211 ScratchVec<double> scratch(sliceLen, mr);
215 for (
size_t o = 0; o < O; ++o) {
216 const double *base = src + o * sliceLen;
217 std::copy(base, base + sliceLen, scratch.data());
218 dst[o] = f(o, scratch.data(), sliceLen);
221 for (
size_t o = 0; o < O; ++o) {
222 const size_t base = o * sliceLen;
223 for (
size_t k = 0; k < sliceLen; ++k)
224 scratch[k] = x.elemAsDouble(base + k);
225 dst[o] = f(o, scratch.data(), sliceLen);
230 for (
size_t o = 0; o < O; ++o) {
231 for (
size_t b = 0; b < B; ++b) {
232 const size_t base = o * sliceLen * B + b;
234 for (
size_t k = 0; k < sliceLen; ++k)
235 scratch[k] = src[base + k * B];
237 for (
size_t k = 0; k < sliceLen; ++k)
238 scratch[k] = x.elemAsDouble(base + k * B);
240 const size_t outIdx = o * B + b;
241 dst[outIdx] = f(outIdx, scratch.data(), sliceLen);
250Value applyAlongDim(
const Value &x,
int dim, F &&f, std::pmr::memory_resource *mr)
256 if (x.isEmpty() && x.dims().ndim() < 4) {
257 return Value::matrix(0, 0, ValueType::DOUBLE, mr);
259 ScratchArena scratch(mr);
260 if (x.dims().isVector() || x.isScalar()) {
261 auto buf = ScratchVec<double>(x.numel(), &scratch);
262 if (x.type() == ValueType::DOUBLE)
263 std::copy(x.doubleData(), x.doubleData() + x.numel(), buf.data());
265 for (
size_t i = 0; i < x.numel(); ++i)
266 buf[i] = x.elemAsDouble(i);
267 return Value::scalar(f(0, buf.data(), x.numel()), mr);
271 if (x.dims().ndim() >= 4 && dim >= 1 && dim <= x.dims().ndim()) {
272 auto shape = outShapeForDimND(&scratch, x, dim);
273 Value out = Value::matrixND(shape.data(),
274 static_cast<int>(shape.size()),
275 ValueType::DOUBLE, mr);
276 double *
dst = out.doubleDataMut();
277 forEachSliceND(&scratch, x, dim, dst, std::forward<F>(f));
280 auto outShape = outShapeForDim(x, dim);
281 Value out = createMatrix(outShape, ValueType::DOUBLE, mr);
282 double *
dst = out.doubleDataMut();
283 forEachSlice(&scratch, x, dim, [&](
size_t outIdx,
double *slice,
size_t n) {
284 dst[outIdx] = f(outIdx, slice, n);
292std::pair<Value, Value>
293applyAlongDimWithIndex(
const Value &x,
int dim, F &&f, std::pmr::memory_resource *mr)
295 if (x.isEmpty() && x.dims().ndim() < 4) {
296 return {Value::matrix(0, 0, ValueType::DOUBLE, mr),
297 Value::matrix(0, 0, ValueType::DOUBLE, mr)};
299 ScratchArena scratch(mr);
300 if (x.dims().isVector() || x.isScalar()) {
301 auto buf = ScratchVec<double>(x.numel(), &scratch);
302 if (x.type() == ValueType::DOUBLE)
303 std::copy(x.doubleData(), x.doubleData() + x.numel(), buf.data());
305 for (
size_t i = 0; i < x.numel(); ++i)
306 buf[i] = x.elemAsDouble(i);
309 f(buf.data(), x.numel(), v, idx);
310 return {Value::scalar(v, mr),
311 Value::scalar(
static_cast<double>(idx + 1), mr)};
314 if (x.dims().ndim() >= 4 && dim >= 1 && dim <= x.dims().ndim()) {
315 auto shape = outShapeForDimND(&scratch, x, dim);
316 Value out = Value::matrixND(shape.data(),
317 static_cast<int>(shape.size()),
318 ValueType::DOUBLE, mr);
319 Value outIdx = Value::matrixND(shape.data(),
320 static_cast<int>(shape.size()),
321 ValueType::DOUBLE, mr);
322 double *
dst = out.doubleDataMut();
323 double *dstI = outIdx.doubleDataMut();
324 forEachSliceND(&scratch, x, dim, dst,
325 [&](
size_t outOff,
double *slice,
size_t n) {
326 double v = 0;
size_t idx = 0;
328 dstI[outOff] =
static_cast<double>(idx + 1);
331 return {std::move(out), std::move(outIdx)};
333 auto outShape = outShapeForDim(x, dim);
334 Value out = createMatrix(outShape, ValueType::DOUBLE, mr);
335 Value outIdx = createMatrix(outShape, ValueType::DOUBLE, mr);
336 double *
dst = out.doubleDataMut();
337 double *dstI = outIdx.doubleDataMut();
338 forEachSlice(&scratch, x, dim, [&](
size_t outOff,
double *slice,
size_t n) {
343 dstI[outOff] =
static_cast<double>(idx + 1);
345 return {std::move(out), std::move(outIdx)};
350inline int resolveDim(
const Value &x,
int explicitDim,
const char *fn)
352 if (explicitDim > 0)
return validateDim(x, explicitDim, fn);
353 return firstNonSingletonDim(x);
361inline size_t compactNonNan(
double *data,
size_t n)
364 for (
size_t i = 0; i < n; ++i) {
365 if (!std::isnan(data[i])) {
366 if (w != i) data[w] = data[i];
Value dst(const Value &x, std::pmr::memory_resource *mr=nullptr)
Type-II Discrete Sine Transform.