29 std::vector<T>
conv(std::vector<T>
const& f, std::vector<T>
const& g)
31 int const nf = f.size();
32 int const ng = g.size();
33 int const n = nf + ng - 1;
34 std::vector<T> out(n, T());
35 for (
auto i(0); i < n; ++i) {
36 int const jmn = (i >= ng - 1) ? i - (ng - 1) : 0;
37 int const jmx = (i < nf - 1) ? i : nf - 1;
38 for (
auto j(jmn); j <= jmx; ++j) {
39 out[i] += (f[j] * g[i - j]);
53 std::vector<T>
conv_valid(std::vector<T>
const& f, std::vector<T>
const& g)
55 int const nf = f.size();
56 int const ng = g.size();
57 std::vector<T>
const& min_v = (nf < ng) ? f : g;
58 std::vector<T>
const& max_v = (nf < ng) ? g : f;
59 int const n = std::max(nf, ng) - std::min(nf, ng) + 1;
60 std::vector<T> out(n, T());
61 for (
auto i(0); i < n; ++i) {
62 for (
int j(min_v.size() - 1), k(i); j >= 0; --j) {
63 out[i] += min_v[j] * max_v[k];
72 std::vector<T>
conv_same(std::vector<T>
const& f, std::vector<T>
const& g) {
73 int const nf = f.size();
74 int const ng = g.size();
75 std::vector<T> out(nf, T());
78 const int pad = (ng - 1) / 2;
80 for (
int i = 0; i < nf; ++i) {
82 for (
int j = 0; j < ng; ++j) {
83 int fIndex = i + j - pad;
84 if (fIndex >= 0 && fIndex < nf) {
85 sum += f[fIndex] * g[j];
97 void conv_valid_into(std::vector<T>& out,
const std::vector<T>& f,
const std::vector<T>& g)
99 const int nf =
static_cast<int>(f.size());
100 const int ng =
static_cast<int>(g.size());
101 const std::vector<T>& min_v = (nf < ng) ? f : g;
102 const std::vector<T>& max_v = (nf < ng) ? g : f;
103 const int n = std::max(nf, ng) - std::min(nf, ng) + 1;
104 for (
int i = 0; i < n; ++i) {
106 for (
int j =
static_cast<int>(min_v.size()) - 1, k = i; j >= 0; --j, ++k)
107 acc += min_v[j] * max_v[k];
113 void conv_same_into(std::vector<T>& out,
const std::vector<T>& f,
const std::vector<T>& g)
115 const int nf =
static_cast<int>(f.size());
116 const int ng =
static_cast<int>(g.size());
117 const int pad = (ng - 1) / 2;
118 for (
int i = 0; i < nf; ++i) {
120 for (
int j = 0; j < ng; ++j) {
121 const int fIndex = i + j - pad;
122 if (fIndex >= 0 && fIndex < nf)
123 acc += f[fIndex] * g[j];
131 const std::vector<T>& contents)
133 for (
int i = 0; i < static_cast<int>(indices.size()); ++i)
134 out[i] = contents[indices[i] - 1];
138 std::vector<T>
gaussNorm(
const std::vector<int>& rangeX,
const T& position,
const T& sigma)
140 std::vector<T> g(rangeX.size());
141 for (
int i = 0; i < g.size(); i++)
142 g[i] = exp(-0.5 * pow((rangeX[i] - position), 2) / pow(sigma, 2));
146 double sumOfG = std::reduce(g.begin(), g.end());
147 for (
int i = 0; i < g.size(); i++)
148 g[i] = g[i] / sumOfG;
155 std::vector<T>
gauss(
const std::vector<int>& rangeX,
const T& position,
const T& sigma)
157 std::vector<T> g(rangeX.size());
159 for (
int i = 0; i < g.size(); i++)
160 g[i] = exp(-0.5 * pow((rangeX[i] - position), 2) / pow(sigma, 2));
166 std::vector<T>
gauss(
int size,
const T& sigma,
const T& position)
168 std::vector<T> g(size);
170 for (
int i = 0; i < g.size(); i++)
172 T x =
static_cast<T
>(i + 1);
173 g[i] = exp(-0.5 * pow((x - position), 2) / pow(sigma, 2));
182 std::vector<T> g(size);
183 std::vector<T> xRange(size);
184 std::iota(xRange.begin(), xRange.end(),
static_cast<T
>(1));
186 for (uint32_t i = 0; i < size; i++)
188 T distance =
static_cast<T
>(i + 1) - position;
189 g[i] = std::exp(-0.5 * std::pow(distance, 2) / std::pow(sigma, 2));
196 std::vector<T>
circularGauss(uint32_t size,
const T& sigma,
const T& position)
198 uint32_t l = size - 2 * 1 + 2;
200 T r = position -
static_cast<T
>(m);
201 T rem = std::fmod(r,
static_cast<T
>(l));
202 T positionShifted = rem +
static_cast<T
>(m);
204 std::vector<T> g(size);
205 std::vector<T> xRange(size);
206 std::iota(xRange.begin(), xRange.end(),
static_cast<T
>(1));
208 std::vector<T> d(size);
209 std::vector<T> lMinusd(size);
210 std::transform(xRange.begin(), xRange.end(), d.begin(), [&positionShifted, &l](T element) { return std::abs(element - positionShifted); });
211 std::transform(d.begin(), d.end(), lMinusd.begin(), [&l](T element) { return -1 * (element - l); });
212 for (
int i = 0; i < size; i++)
214 g[i] = std::exp(-0.5 * std::pow(std::min(d[i], lMinusd[i]), 2) / std::pow(sigma, 2));
221 std::vector<T>
gaussDerivative(
const std::vector<int>& rangeX,
const T& position,
const T& sigma,
const T& amplitude)
223 std::vector<T> derivative(rangeX.size());
224 T variance = sigma * sigma;
226 for (
size_t i = 0; i < derivative.size(); i++)
229 derivative[i] = -(x - position) / variance * amplitude * std::exp(-0.5 * std::pow((x - position), 2) / variance);
236 std::vector<T>
gaussDerivativeNorm(
const std::vector<int>& rangeX,
const T& position,
const T& sigma,
const T& amplitude)
241 T sumOfAbs = std::accumulate(derivative.begin(), derivative.end(),
static_cast<T
>(0),
242 [](T sum, T value) { return sum + std::abs(value); });
244 if (sumOfAbs >
static_cast<T
>(0))
246 for (T& value : derivative)
256 std::vector<T> newContents(indices.size());
257 for (
int i = 0; i < indices.size(); i++)
258 newContents[i] = contents[indices[i] - 1];
262 template <
typename T>
263 std::vector<T>
sigmoid(
const std::vector<T>& x, T beta, T x0)
265 std::vector<T> s(x.size());
266 for (std::size_t i = 0; i < s.size(); ++i) {
267 s[i] = 1 / (1 + std::exp(-beta * (x[i] - x0)));
278 template <
typename T>
279 std::vector<T>
absSigmoid(
const std::vector<T>& x, T beta, T x0)
281 std::vector<T> s(x.size());
282 for (std::size_t i = 0; i < s.size(); ++i) {
283 const T diff = x[i] - x0;
284 s[i] =
static_cast<T
>(0.5) * (1 + beta * diff / (1 + beta * std::abs(diff)));
290 std::vector<T>
heaviside(
const std::vector<T>& x, T threshold)
292 std::vector<T> h(x.size());
293 for (
int i = 0; i < static_cast<int>(h.size()); i++)
294 h[i] = (x[i] > threshold) ? 1 : 0;
299 std::vector<T>
sumGauss(
const std::vector<T>& gauss1,
const std::vector<T>& gauss2)
301 std::vector<T> gaussResult(gauss1.size());
302 for (
int i = 0; i < gauss1.size(); i++)
303 gaussResult[i] = gauss1[i] + gauss2[i];
307 std::array<int, 2>
computeKernelRange(
double sigma,
int cutOfFactor,
int fieldSize,
bool circular);
308 std::vector<int>
createExtendedIndex(
int fieldSize,
const std::array<int, 2>& kernelRange);
312 template <
typename T>
315 static constexpr T epsilon = 1e-6;
316 static constexpr T offset = 1.0;
319 const T minVal = *std::ranges::min_element(vector.begin(), vector.end()) - epsilon;
321 std::vector<T> normalizedVector = vector;
322 for (T& val : normalizedVector)
325 normalizedVector =
math::sigmoid(normalizedVector, 0.2, std::abs(minVal) + offset);
326 const T newMinVal = *std::ranges::min_element(normalizedVector.begin(), normalizedVector.end());
328 for (T& val : normalizedVector)
331 return normalizedVector;
334 template <
typename T>
335 std::vector<T>
hebbLearningRule(std::vector<T>& weights,
const std::vector<T>& input,
const std::vector<T>& output,
double learningRate)
337 if (input.empty() || output.empty())
338 throw std::invalid_argument(
"Input and output vectors cannot be empty");
340 const size_t inputSize = input.size();
341 const size_t outputSize = output.size();
342 const size_t expectedSize = inputSize * outputSize;
344 if (weights.size() != expectedSize)
345 throw std::invalid_argument(
"Weight matrix size mismatch");
347 for (
size_t i = 0; i < inputSize; ++i)
349 const T scaledInput = learningRate * input[i];
350 const size_t baseIndex = i * outputSize;
352 for (
size_t j = 0; j < outputSize; ++j)
353 weights[baseIndex + j] += scaledInput * output[j];
359 template <
typename T>
360 std::vector<T>
ojaLearningRule(std::vector<T>& weights,
const std::vector<T>& input,
const std::vector<T>& output,
double learningRate)
362 const int inputSize = input.size();
363 const int outputSize = output.size();
365 for (
int i = 0; i < inputSize; i++)
366 for (
int j = 0; j < outputSize; j++)
368 int index = i * outputSize + j;
369 weights[index] += learningRate * (input[i] * output[j] - output[j] * input[i] * weights[index]);
375 template <
typename T>
377 const std::vector<T>& actualOutput,
const std::vector<T>& targetOutput,
double learningRate)
379 const int inputSize = input.size();
380 const int outputSize = targetOutput.size();
383 std::vector<T> error(outputSize, 0.0);
384 for (
size_t j = 0; j < outputSize; ++j) {
385 error[j] = targetOutput[j] - actualOutput[j];
389 for (
size_t i = 0; i < inputSize; ++i) {
390 for (
size_t j = 0; j < outputSize; ++j) {
391 weights[i][j] += learningRate * error[j] * input[i];
398 template <
typename T>
400 const std::vector<T>& targetOutput,
const std::vector<T>& actualOutput,
403 const int inputSize = input.size();
404 int outputSize = targetOutput.size();
415 std::vector<T> error(outputSize, 0.0);
416 for (
size_t j = 0; j < outputSize; ++j) {
417 error[j] = targetOutput[j] - actualOutput[j];
421 for (
size_t i = 0; i < inputSize; ++i) {
422 for (
size_t j = 0; j < outputSize; ++j) {
424 weights[i][j] += learningRate * (error[j]) * input[i];
431 template <
typename T>
432 bool compareVectors(
const std::vector<T>& vec1,
const std::vector<T>& vec2, T threshold) {
433 if (vec1.size() != vec2.size()) {
437 for (
size_t i = 0; i < vec1.size(); ++i) {
438 if (std::abs(vec1[i] - vec2[i]) > threshold) {
446 template <
typename T>
449 for (T value : vec) {
455 template <
typename T>
462 for (
const T& value : vec) {
465 return sum /
static_cast<T
>(vec.size());
468 template <
typename T>
470 T sum_of_squares = std::accumulate(vec.begin(), vec.end(), 0.0,
471 [](T a, T b) { return a + b * b; });
472 return std::sqrt(sum_of_squares);
475 inline double normalize(
const double value,
const double min,
const double max)
477 if (value < min)
return 0.0;
478 if (value > max)
return 1.0;
479 return (value - min) / (max - min);
482 inline double gaussian_2d(
double x,
double y,
double mu_x,
double mu_y,
double sigma_x,
double sigma_y,
double A) {
483 const double exponent = -((std::pow(x - mu_x, 2) / (2 * std::pow(sigma_x, 2))) + (std::pow(y - mu_y, 2) / (2 * std::pow(sigma_y, 2))));
484 return A * std::exp(exponent);
488 const double exponent = -((std::pow(x - mu_x, 2) + std::pow(y - mu_y, 2)) / (2 * std::pow(sigma, 2)));
489 return A * std::exp(exponent);
492 inline double wrap(
double value,
double max_value) {
493 if (value < 0)
return value + max_value;
494 if (value >= max_value)
return value - max_value;
498 inline double gaussian_2d_periodic(
double x,
double y,
double mu_x,
double mu_y,
double sigma,
double A,
double max_x,
double max_y) {
499 const double xStep = std::min(std::abs(x - mu_x), max_x - std::abs(x - mu_x));
500 const double dy = std::min(std::abs(y - mu_y), max_y - std::abs(y - mu_y));
501 const double exponent = -((std::pow(xStep, 2) + std::pow(dy, 2)) / (2 * std::pow(sigma, 2)));
502 return A * std::exp(exponent);
505 template <
typename T>
508 const int rows = matrix.size();
509 const int cols = matrix[0].size();
510 std::vector<T> flat_matrix(rows * cols);
511 for (
int i = 0; i < rows; ++i)
512 for (
int j = 0; j < cols; ++j)
513 flat_matrix[i * cols + j] =
static_cast<T
>(matrix[i][j]);
519 std::vector<T>
resample(
const std::vector<T>& input,
int outputSize)
521 if (input.empty() || outputSize <= 0)
return {};
522 const int N =
static_cast<int>(input.size());
523 if (N == outputSize)
return input;
524 if (outputSize == 1)
return { input[N / 2] };
525 std::vector<T> out(outputSize);
526 for (
int i = 0; i < outputSize; ++i)
528 const double pos =
static_cast<double>(i) * (N - 1) / (outputSize - 1);
529 const int lo =
static_cast<int>(pos);
530 const int hi = std::min(lo + 1, N - 1);
531 const double t = pos - lo;
532 out[i] = input[lo] * (1.0 - t) + input[hi] * t;
541 const int N =
static_cast<int>(input.size());
542 const int outputSize =
static_cast<int>(output.size());
543 if (input.empty() || outputSize <= 0)
return;
544 if (N == outputSize) { std::copy(input.begin(), input.end(), output.begin());
return; }
545 if (outputSize == 1) { output[0] = input[N / 2];
return; }
546 for (
int i = 0; i < outputSize; ++i)
548 const double pos =
static_cast<double>(i) * (N - 1) / (outputSize - 1);
549 const int lo =
static_cast<int>(pos);
550 const int hi = std::min(lo + 1, N - 1);
551 const double t = pos - lo;
552 output[i] = input[lo] * (1.0 - t) + input[hi] * t;
559 const int N =
static_cast<int>(input.size());
560 const int M =
static_cast<int>(output.size());
561 if (input.empty() || M <= 0)
return;
562 if (N == M) { std::copy(input.begin(), input.end(), output.begin());
return; }
563 if (M == 1) { output[0] = input[N / 2];
return; }
564 for (
int i = 0; i < M; ++i)
566 const double pos =
static_cast<double>(i) * (N - 1) / (M - 1);
567 output[i] = input[
static_cast<int>(std::round(pos))];
575 const int N =
static_cast<int>(input.size());
576 const int M =
static_cast<int>(output.size());
577 if (input.empty() || M <= 0)
return;
578 if (N == M) { std::copy(input.begin(), input.end(), output.begin());
return; }
579 if (M == 1) { output[0] = input[N / 2];
return; }
580 const auto clamp = [N](
int idx) {
return std::max(0, std::min(idx, N - 1)); };
581 for (
int i = 0; i < M; ++i)
583 const double pos =
static_cast<double>(i) * (N - 1) / (M - 1);
584 const int lo =
static_cast<int>(pos);
585 const double t = pos - lo;
586 const double p0 =
static_cast<double>(input[clamp(lo - 1)]);
587 const double p1 =
static_cast<double>(input[clamp(lo)]);
588 const double p2 =
static_cast<double>(input[clamp(lo + 1)]);
589 const double p3 =
static_cast<double>(input[clamp(lo + 2)]);
590 output[i] =
static_cast<T
>(0.5 * (2.0 * p1 + (-p0 + p2) * t +
591 (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t * t +
592 (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t * t * t));
603 const std::vector<T>& field,
604 const std::vector<T>& kernel_x,
605 const std::vector<T>& kernel_y,
606 int size_x,
int size_y,
607 const std::vector<int>& extIndex_x,
608 const std::vector<int>& extIndex_y)
610 const bool circular_x = !extIndex_x.empty();
611 const bool circular_y = !extIndex_y.empty();
614 std::vector<T> tmp(size_x * size_y, T());
615 for (
int y = 0; y < size_y; ++y)
617 std::vector<T> row(size_x);
618 for (
int x = 0; x < size_x; ++x)
619 row[x] = field[y * size_x + x];
621 std::vector<T> convRow;
631 for (
int x = 0; x < size_x; ++x)
632 tmp[y * size_x + x] = convRow[x];
636 std::vector<T> result(size_x * size_y, T());
637 for (
int x = 0; x < size_x; ++x)
639 std::vector<T> col(size_y);
640 for (
int y = 0; y < size_y; ++y)
641 col[y] = tmp[y * size_x + x];
643 std::vector<T> convCol;
653 for (
int y = 0; y < size_y; ++y)
654 result[y * size_x + x] = convCol[y];
666 const std::vector<T>& field,
667 const std::vector<T>& kernel_x,
668 const std::vector<T>& kernel_y,
669 int size_x,
int size_y,
670 const std::vector<int>& extIndex_x,
671 const std::vector<int>& extIndex_y)
673 const bool circular_x = !extIndex_x.empty();
674 const bool circular_y = !extIndex_y.empty();
676 std::vector<T> row(size_x);
677 std::vector<T> col(size_y);
678 std::vector<T> convRow(size_x);
679 std::vector<T> extRow(circular_x ? extIndex_x.size() : 0);
680 std::vector<T> convCol(size_y);
681 std::vector<T> extCol(circular_y ? extIndex_y.size() : 0);
684 for (
int y = 0; y < size_y; ++y)
686 for (
int x = 0; x < size_x; ++x)
687 row[x] = field[y * size_x + x];
698 for (
int x = 0; x < size_x; ++x)
699 tmp[y * size_x + x] = convRow[x];
703 for (
int x = 0; x < size_x; ++x)
705 for (
int y = 0; y < size_y; ++y)
706 col[y] = tmp[y * size_x + x];
717 for (
int y = 0; y < size_y; ++y)
718 out[y * size_x + x] = convCol[y];
731 int size_x,
int size_y,
bool keepX,
ReduceOp op)
736 if (size_x <= 0 || size_y <= 0 ||
737 field.size() <
static_cast<std::size_t
>(size_x) *
static_cast<std::size_t
>(size_y))
743 const int outSize = keepX ? size_x : size_y;
744 const int reduceCount = keepX ? size_y : size_x;
745 if (out.size() !=
static_cast<std::size_t
>(outSize)) out.assign(outSize, T());
747 for (
int o = 0; o < outSize; ++o)
750 auto sampleAt = [&](
int k) -> T {
751 const int x = keepX ? o : k;
752 const int y = keepX ? k : o;
753 return field[
static_cast<std::size_t
>(y) * size_x + x];
756 for (
int k = 1; k < reduceCount; ++k)
758 const T v = sampleAt(k);
768 acc /=
static_cast<T
>(reduceCount);
779 int size_x,
int size_y,
bool alongX)
783 if (size_x <= 0 || size_y <= 0) { out.clear();
return; }
785 const std::size_t total =
static_cast<std::size_t
>(size_x) *
static_cast<std::size_t
>(size_y);
786 if (out.size() != total) out.assign(total, T());
787 if (profile.empty()) { std::fill(out.begin(), out.end(), T());
return; }
788 const int profileSize =
static_cast<int>(profile.size());
789 for (
int y = 0; y < size_y; ++y)
790 for (
int x = 0; x < size_x; ++x)
793 const int idx = std::min(alongX ? x : y, profileSize - 1);
794 out[
static_cast<std::size_t
>(y) * size_x + x] = profile[idx];