MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Kernels.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * @file Kernels.hpp
5 * @brief Pure functions of a newest-first window and an array of numbers.
6 *
7 * Every kernel has the shape `double(window, coefs)` and reads the window as
8 * a newest-first span: window[0] is the current value, window[k] is k steps
9 * back. In a recursive Polynomial, window[0] is the new input and window[k]
10 * is the output k steps ago. The array is whatever the kernel documents it
11 * to be, and nothing about it is a mode: the numbers in it are the behaviour.
12 *
13 * The functions convert directly to Polynomial::CoefBufferFunction, so
14 * `Polynomial(Kinesis::Discrete::weighted_sum, mode, size, weights)` is a
15 * complete node. Read-only kernels take a const array; phasor_bank writes
16 * its phases back into the array and takes a mutable one.
17 */
18
20
21/**
22 * @brief Horner evaluation, coefficients highest power first.
23 */
24[[nodiscard]] MAYAFLUX_API double horner(std::span<const double> coefficients, double x) noexcept;
25
26/**
27 * @brief Sum of window[k] * coefs[k] over the shorter of the two.
28 *
29 * The linear case: FIR taps in a feedforward window, feedback weights in a
30 * recursive one, or the output of any generator in Coefficients.
31 */
32[[nodiscard]] MAYAFLUX_API double weighted_sum(
33 std::span<const double> window, std::span<const double> coefs) noexcept;
34
35/**
36 * @brief Sum of gain * window[lag] over (lag, gain) pairs.
37 *
38 * coefs holds pairs: coefs[2j] is a lag in samples, coefs[2j + 1] its gain.
39 * Fractional lags interpolate linearly between neighbours. A pair whose lag
40 * lies outside the window contributes nothing, so a short window simply
41 * reaches fewer taps.
42 */
43[[nodiscard]] MAYAFLUX_API double tapped_sum(
44 std::span<const double> window, std::span<const double> coefs) noexcept;
45
46/**
47 * @brief Transfer table indexed by the current value.
48 *
49 * coefs are samples of a curve over [-1, 1], read with linear interpolation
50 * and clamped at the ends. The input is window[0]. A table of one entry is a
51 * constant; an empty table gives zero.
52 */
53[[nodiscard]] MAYAFLUX_API double table_lookup(
54 std::span<const double> window, std::span<const double> coefs) noexcept;
55
56/**
57 * @brief table_lookup indexed by window[1] instead of window[0].
58 *
59 * In a recursive Polynomial window[1] is the previous output, so the table is
60 * a map the output iterates: y[n] = table(y[n - 1]).
61 */
62[[nodiscard]] MAYAFLUX_API double table_lookup_previous(
63 std::span<const double> window, std::span<const double> coefs) noexcept;
64
65/**
66 * @brief Sum of coefs[k] * T_k(window[0]) over Chebyshev polynomials.
67 *
68 * Because T_k(cos t) = cos(kt), a sine of amplitude one passed through this
69 * has harmonic k at weight coefs[k]. The input is clamped to [-1, 1] and the
70 * sum is evaluated by Clenshaw's recurrence.
71 */
72[[nodiscard]] MAYAFLUX_API double chebyshev_series(
73 std::span<const double> window, std::span<const double> coefs) noexcept;
74
75/**
76 * @brief Polynomial of the previous value, coefs highest power first.
77 *
78 * In a recursive Polynomial this iterates y[n] = P(y[n - 1]): the logistic
79 * and cubic maps are one array each.
80 */
81[[nodiscard]] MAYAFLUX_API double iterated_polynomial(
82 std::span<const double> window, std::span<const double> coefs) noexcept;
83
84/**
85 * @brief Radians per sample for a frequency, for building phasor_bank state.
86 */
87[[nodiscard]] inline double radians_per_sample(double frequency, double sample_rate) noexcept
88{
89 return 2.0 * std::numbers::pi * frequency / sample_rate;
90}
91
92/**
93 * @brief Builds the array phasor_bank reads and writes.
94 * @param increments Radians per sample of each partial; sets the count
95 * @param scales Amplitude of each partial; missing entries are 1
96 * @param am_increments Radians per sample of each partial's slow amplitude
97 * swell; missing entries are 0, which holds the swell at one half
98 * @param phases Starting phase of each partial; missing entries are 0
99 * @param am_phases Starting phase of each swell; missing entries are 0
100 *
101 * Layout: [N, increments, scales, am_increments, phases, am_phases], each
102 * column N long. Whether partials start together or apart is only what
103 * the phase columns hold.
104 */
105[[nodiscard]] MAYAFLUX_API std::vector<double> phasor_bank_state(
106 std::span<const double> increments,
107 std::span<const double> scales = {},
108 std::span<const double> am_increments = {},
109 std::span<const double> phases = {},
110 std::span<const double> am_phases = {});
111
112/**
113 * @brief Sum of N sinusoidal partials, each with its own slow amplitude swell.
114 *
115 * coefs is the array phasor_bank_state builds. Every call advances each
116 * partial's phase and swell phase by its increment and returns
117 * sum(scale * sin(phase) * (0.5 + 0.5 * sin(swell_phase))). The window is
118 * not read. An array too short for its stated count gives zero. Increments
119 * must stay below one revolution per sample.
120 */
121[[nodiscard]] MAYAFLUX_API double phasor_bank(
122 std::span<const double> window, std::span<double> coefs) noexcept;
123
124// ---------------------------------------------------------------------------
125// Polynomial over lags, series and curves
126// ---------------------------------------------------------------------------
127
128/**
129 * @brief Polynomial in several lags at once.
130 *
131 * coefs: [L, P, C...] with L lags and highest power P, then (P + 1) numbers
132 * per lag, lag-major and power-ascending. The result is the sum over lags k
133 * and powers j of C[k][j] * window[k]^j. Power zero is a constant, so a bias
134 * is C[0][0]. In a recursive Polynomial this is any nonlinear difference
135 * equation of the last L outputs: Henon is lags 0, 1, 2 with
136 * C[0][0] = 1, C[1][2] = -a, C[2][1] = b. Lags beyond the window are
137 * skipped; an array shorter than its header gives zero.
138 */
139[[nodiscard]] MAYAFLUX_API double polynomial_lags(
140 std::span<const double> window, std::span<const double> coefs) noexcept;
141
142/**
143 * @brief Fourier series of a phase.
144 *
145 * The phase in radians is window[0]. coefs: [a0, a1, b1, a2, b2, ...] and
146 * the result is a0 + sum a_k cos(k t) + b_k sin(k t). A final a_k without its
147 * b_k is read with b_k = 0. One sine and one cosine are evaluated, the
148 * harmonics follow by rotation.
149 */
150[[nodiscard]] MAYAFLUX_API double fourier_series(
151 std::span<const double> window, std::span<const double> coefs) noexcept;
152
153/**
154 * @brief Piecewise linear curve through (x, y) points.
155 *
156 * coefs: pairs (x, y) with ascending x. The input is window[0], held at the
157 * first and last y outside the span of x. Uneven spacing is fine; unsorted x
158 * is not. An empty array gives zero.
159 */
160[[nodiscard]] MAYAFLUX_API double breakpoint_curve(
161 std::span<const double> window, std::span<const double> coefs) noexcept;
162
163/**
164 * @brief Table indexed by the recent shape of the window.
165 *
166 * coefs: [levels, count, lo, hi, table...]. The newest `count` values are
167 * each quantised to one of `levels` cells over [lo, hi] (Lattice1D), combined
168 * into an index with the newest value as the lowest digit, and the table of
169 * levels^count entries is read there. Values outside [lo, hi] fall in the
170 * edge cells and missing values count as zero. A table too small for the
171 * index range, or hi not above lo, gives zero.
172 */
173[[nodiscard]] MAYAFLUX_API double pattern_lookup(
174 std::span<const double> window, std::span<const double> coefs) noexcept;
175
176// ---------------------------------------------------------------------------
177// Windows as sets: order statistics and features
178// ---------------------------------------------------------------------------
179
180/**
181 * @brief Maximum over the window of window[k] + coefs[k], the grayscale dilation.
182 *
183 * coefs are the offsets of the structuring element. Runs over the shorter of
184 * the two; empty gives zero.
185 */
186[[nodiscard]] MAYAFLUX_API double dilate(
187 std::span<const double> window, std::span<const double> coefs) noexcept;
188
189/**
190 * @brief Minimum over the window of window[k] - coefs[k], the grayscale erosion.
191 */
192[[nodiscard]] MAYAFLUX_API double erode(
193 std::span<const double> window, std::span<const double> coefs) noexcept;
194
195/**
196 * @brief Quantile of the whole window, interpolating between ranks.
197 *
198 * The per-call counterpart of Discrete::percentile, which is the batch form
199 * over every window of a buffer and takes percent.
200 *
201 * coefs[0] is the quantile in [0, 1], 0.5 when the array is empty: 0 is the
202 * minimum, 1 the maximum. Uses a per-thread scratch vector, so it allocates
203 * only the first time a window of a new size is seen.
204 */
205[[nodiscard]] MAYAFLUX_API double quantile(
206 std::span<const double> window, std::span<const double> coefs);
207
208/**
209 * @brief How far the newest value sits from the rest of the window, in standard deviations.
210 *
211 * A novelty measure. Not Discrete::mean_zscore, which averages the z-scores of
212 * a window about its own mean.
213 *
214 * window[0] against the mean and sample standard deviation of window[1..].
215 * coefs[0], when present, is the smallest deviation used as a divisor
216 * (default 1e-12), so a flat history does not divide by zero. Fewer than two
217 * older values give zero.
218 */
219[[nodiscard]] MAYAFLUX_API double zscore(
220 std::span<const double> window, std::span<const double> coefs) noexcept;
221
222/**
223 * @brief Weighted energy of the window at chosen frequencies.
224 *
225 * coefs: pairs (frequency in cycles per sample, weight). Each frequency is
226 * measured with the Goertzel recurrence over the whole window and gives the
227 * squared amplitude of the sinusoid there (a sine of amplitude A on the
228 * frequency gives A^2 when it fits the window in whole cycles); the result is
229 * the weighted sum.
230 */
231[[nodiscard]] MAYAFLUX_API double goertzel(
232 std::span<const double> window, std::span<const double> coefs) noexcept;
233
234/**
235 * @brief Weighted sum wrapped into [0, modulus).
236 *
237 * coefs: [modulus, weights...]. In a recursive Polynomial the weights act on
238 * the last outputs, which is a lagged-Fibonacci style recurrence with the
239 * structure the weights and modulus give it. A modulus of zero or less
240 * leaves the sum unwrapped.
241 */
242[[nodiscard]] MAYAFLUX_API double modular_recurrence(
243 std::span<const double> window, std::span<const double> coefs) noexcept;
244
245// ---------------------------------------------------------------------------
246// State-bearing kernels: the array holds the state
247// ---------------------------------------------------------------------------
248
249/**
250 * @brief Builds the array rotor_bank reads and writes.
251 * @param increments Radians per sample of each sinusoid; sets the count
252 * @param scales Amplitude of each; missing entries are 1
253 * @param phases Starting phase of each; missing entries are 0
254 *
255 * Layout: [N, cos(increment), sin(increment), scales, re, im], each column
256 * N long. The rotation is precomputed here, so a call needs no sine.
257 */
258[[nodiscard]] MAYAFLUX_API std::vector<double> rotor_bank_state(
259 std::span<const double> increments,
260 std::span<const double> scales = {},
261 std::span<const double> phases = {});
262
263/**
264 * @brief Sum of N sinusoids advanced by complex rotation instead of a sine call.
265 *
266 * Each call rotates every unit phasor by its increment and returns the sum
267 * of scale * imaginary part, which is sin(phase) after the advance. A first
268 * order correction keeps the phasors on the unit circle, so the amplitude
269 * does not drift over long runs. Cheaper than phasor_bank per sinusoid, with
270 * no slow amplitude swell: scale the array's scales column for that.
271 */
272[[nodiscard]] MAYAFLUX_API double rotor_bank(
273 std::span<const double> window, std::span<double> coefs) noexcept;
274
275/**
276 * @brief Builds the array kuramoto reads and writes.
277 * @param omegas Natural increment of each oscillator in radians per sample;
278 * sets the count
279 * @param coupling Strength of the pull toward the group
280 * @param phases Starting phases; missing entries are 0
281 *
282 * Layout: [N, coupling, omegas, phases].
283 */
284[[nodiscard]] MAYAFLUX_API std::vector<double> kuramoto_state(
285 std::span<const double> omegas, double coupling, std::span<const double> phases = {});
286
287/**
288 * @brief Kuramoto oscillators pulled toward their mean phase.
289 *
290 * Each call advances every phase by its own increment plus
291 * coupling * r * sin(mean_phase - phase), where r e^(i mean_phase) is the
292 * mean of the phases on the unit circle, then returns the mean of the sines.
293 * With no coupling the group stays loose; past a critical coupling it
294 * locks, and the output grows as the phases align. Costs a few
295 * trigonometric calls per oscillator per call.
296 */
297[[nodiscard]] MAYAFLUX_API double kuramoto(
298 std::span<const double> window, std::span<double> coefs) noexcept;
299
300/**
301 * @brief Builds the array coupled_map_lattice reads and writes.
302 * @param cells Starting cell values in [0, 1]; sets the count
303 * @param r Logistic parameter, up to 4
304 * @param epsilon Coupling to the two neighbours in [0, 1]
305 *
306 * Layout: [N, r, epsilon, cells, scratch], the scratch column holding the
307 * mapped values between steps.
308 */
309[[nodiscard]] MAYAFLUX_API std::vector<double> coupled_map_lattice_state(
310 std::span<const double> cells, double r, double epsilon);
311
312/**
313 * @brief Ring of logistic maps with nearest neighbour coupling.
314 *
315 * Each call maps every cell, f(x) = r x (1 - x), then mixes each cell with
316 * its two neighbours (periodic): x' = (1 - eps) f(x) + eps / 2 (f(left) +
317 * f(right)). Returns the mean cell minus one half. Uniform cells stay
318 * uniform; different cells give the pattern formation the coupling allows.
319 */
320[[nodiscard]] MAYAFLUX_API double coupled_map_lattice(
321 std::span<const double> window, std::span<double> coefs) noexcept;
322
323/**
324 * @brief Builds the array lorenz_attractor reads and writes.
325 *
326 * Layout: [sigma, rho, beta, dt, x, y, z]. The defaults are the classic
327 * parameters in model time units.
328 */
329[[nodiscard]] MAYAFLUX_API std::vector<double> lorenz_state(
330 double dt = 0.005,
331 double sigma = 10.0,
332 double rho = 28.0,
333 double beta = 8.0 / 3.0,
334 double x = 1.0,
335 double y = 1.0,
336 double z = 1.0);
337
338/**
339 * @brief Lorenz system stepped once by fourth order Runge-Kutta, returning x.
340 *
341 * The state and parameters live in the array, so editing sigma, rho, beta or
342 * dt between calls sweeps the system through its regimes. An array shorter
343 * than seven gives zero.
344 */
345[[nodiscard]] MAYAFLUX_API double lorenz_attractor(
346 std::span<const double> window, std::span<double> coefs) noexcept;
347
348/**
349 * @brief Builds the array step_sequence reads and writes.
350 * @param values The sequence, which sets the length
351 * @param threshold Level the trigger must rise through
352 *
353 * Layout: [threshold, cursor, latched, N, values]. It starts latched, so an
354 * input that begins high does not advance until it has fallen and risen.
355 */
356[[nodiscard]] MAYAFLUX_API std::vector<double> step_sequence_state(
357 std::span<const double> values, double threshold = 0.5);
358
359/**
360 * @brief Table of values advanced by a trigger.
361 *
362 * Returns values[cursor], starting at the first value. Each time window[0]
363 * rises through the threshold the cursor advances by one and wraps. A held
364 * high input advances once.
365 */
366[[nodiscard]] MAYAFLUX_API double step_sequence(
367 std::span<const double> window, std::span<double> coefs) noexcept;
368
369} // namespace MayaFlux::Kinesis::Discrete
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
double frequency
std::vector< std::byte > values
Definition VDBWriter.cpp:26
float threshold
std::vector< double > rotor_bank_state(std::span< const double > increments, std::span< const double > scales, std::span< const double > phases)
Builds the array rotor_bank reads and writes.
Definition Kernels.cpp:199
double horner(std::span< const double > coefficients, double x) noexcept
Horner evaluation, coefficients highest power first.
Definition Kernels.cpp:90
double modular_recurrence(std::span< const double > window, std::span< const double > coefs) noexcept
Weighted sum wrapped into [0, modulus).
Definition Kernels.cpp:474
double phasor_bank(std::span< const double >, std::span< double > coefs) noexcept
Sum of N sinusoidal partials, each with its own slow amplitude swell.
Definition Kernels.cpp:238
double table_lookup(std::span< const double > window, std::span< const double > coefs) noexcept
Transfer table indexed by the current value.
Definition Kernels.cpp:146
double rotor_bank(std::span< const double >, std::span< double > coefs) noexcept
Sum of N sinusoids advanced by complex rotation instead of a sine call.
Definition Kernels.cpp:219
double polynomial_lags(std::span< const double > window, std::span< const double > coefs) noexcept
Polynomial in several lags at once.
Definition Kernels.cpp:268
double weighted_sum(std::span< const double > window, std::span< const double > coefs) noexcept
Sum of window[k] * coefs[k] over the shorter of the two.
Definition Kernels.cpp:98
double kuramoto(std::span< const double >, std::span< double > coefs) noexcept
Kuramoto oscillators pulled toward their mean phase.
Definition Kernels.cpp:501
std::vector< double > kuramoto_state(std::span< const double > omegas, double coupling, std::span< const double > phases)
Builds the array kuramoto reads and writes.
Definition Kernels.cpp:486
double iterated_polynomial(std::span< const double > window, std::span< const double > coefs) noexcept
Polynomial of the previous value, coefs highest power first.
Definition Kernels.cpp:173
double goertzel(std::span< const double > window, std::span< const double > coefs) noexcept
Weighted energy of the window at chosen frequencies.
Definition Kernels.cpp:451
double pattern_lookup(std::span< const double > window, std::span< const double > coefs) noexcept
Table indexed by the recent shape of the window.
Definition Kernels.cpp:341
double erode(std::span< const double > window, std::span< const double > coefs) noexcept
Minimum over the window of window[k] - coefs[k], the grayscale erosion.
Definition Kernels.cpp:391
double dilate(std::span< const double > window, std::span< const double > coefs) noexcept
Maximum over the window of window[k] + coefs[k], the grayscale dilation.
Definition Kernels.cpp:369
double quantile(std::span< const double > window, std::span< const double > coefs)
Quantile of the whole window, interpolating between ranks.
Definition Kernels.cpp:413
double table_lookup_previous(std::span< const double > window, std::span< const double > coefs) noexcept
table_lookup indexed by window[1] instead of window[0].
Definition Kernels.cpp:151
std::vector< double > coupled_map_lattice_state(std::span< const double > cells, double r, double epsilon)
Builds the array coupled_map_lattice reads and writes.
Definition Kernels.cpp:547
std::vector< double > phasor_bank_state(std::span< const double > increments, std::span< const double > scales, std::span< const double > am_increments, std::span< const double > phases, std::span< const double > am_phases)
Builds the array phasor_bank reads and writes.
Definition Kernels.cpp:178
std::vector< double > step_sequence_state(std::span< const double > values, double threshold)
Builds the array step_sequence reads and writes.
Definition Kernels.cpp:616
double lorenz_attractor(std::span< const double >, std::span< double > coefs) noexcept
Lorenz system stepped once by fourth order Runge-Kutta, returning x.
Definition Kernels.cpp:586
double step_sequence(std::span< const double > window, std::span< double > coefs) noexcept
Table of values advanced by a trigger.
Definition Kernels.cpp:629
double breakpoint_curve(std::span< const double > window, std::span< const double > coefs) noexcept
Piecewise linear curve through (x, y) points.
Definition Kernels.cpp:316
double tapped_sum(std::span< const double > window, std::span< const double > coefs) noexcept
Sum of gain * window[lag] over (lag, gain) pairs.
Definition Kernels.cpp:122
double radians_per_sample(double frequency, double sample_rate) noexcept
Radians per sample for a frequency, for building phasor_bank state.
Definition Kernels.hpp:87
std::vector< double > lorenz_state(double dt, double sigma, double rho, double beta, double x, double y, double z)
Builds the array lorenz_attractor reads and writes.
Definition Kernels.cpp:580
double coupled_map_lattice(std::span< const double >, std::span< double > coefs) noexcept
Ring of logistic maps with nearest neighbour coupling.
Definition Kernels.cpp:561
double fourier_series(std::span< const double > window, std::span< const double > coefs) noexcept
Fourier series of a phase.
Definition Kernels.cpp:291
double chebyshev_series(std::span< const double > window, std::span< const double > coefs) noexcept
Sum of coefs[k] * T_k(window[0]) over Chebyshev polynomials.
Definition Kernels.cpp:156
double zscore(std::span< const double > window, std::span< const double > coefs) noexcept
How far the newest value sits from the rest of the window, in standard deviations.
Definition Kernels.cpp:438