Skip to content

Commit 394f025

Browse files
committed
transforms wip
1 parent 0c672c7 commit 394f025

1 file changed

Lines changed: 82 additions & 20 deletions

File tree

src/dsp/transforms_comprehensive.clj

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,15 @@
301301
;; Why would a transform of real data produce complex output? This seems unnecessarily
302302
;; complicated. Let's build intuition for why complex numbers are not just mathematical
303303
;; elegance—they're computational necessity.
304+
;;
305+
;; **Note on dtype-next operations**: In the examples below, you'll see operations like `dfn/*`
306+
;; and `dfn//`. These are element-wise operations from dtype-next:
307+
;; - `dfn/*` multiplies arrays element-by-element: `[1 2 3] * 2 → [2 4 6]`
308+
;; - `dfn//` divides arrays element-by-element: `[2 4 6] / 2 → [1 2 3]`
309+
;; - `dfn/+`, `dfn/-` work similarly for addition and subtraction
310+
;; - `dfn/cos`, `dfn/sin` apply trigonometric functions to each element
311+
;;
312+
;; We'll explore these operations more deeply as we use them throughout the tutorial.
304313

305314
;; ### The Problem: Sines and Cosines Are Both Needed
306315

@@ -508,6 +517,27 @@
508517
;;
509518
;; This same pattern works for DCT, DST, DHT, and wavelets—just change the transform type!
510519

520+
;; ### Performance: JTransforms and Parallelism
521+
;;
522+
;; Under the hood, fastmath uses [JTransforms](https://github.com/wendykierp/JTransforms),
523+
;; a high-performance Java library that automatically parallelizes large transforms.
524+
;;
525+
;; **Key performance features**:
526+
;; - **Automatic parallelization**: For arrays larger than ~8K elements, JTransforms splits
527+
;; work across available CPU cores using Java's `ForkJoinPool`
528+
;; - **SIMD optimizations**: Uses Java's auto-vectorization where possible
529+
;; - **In-place transforms**: Can operate directly on input arrays (fastmath wraps this safely)
530+
;; - **Algorithm selection**: Chooses optimal FFT algorithm (split-radix, mixed-radix, or
531+
;; Bluestein) based on array size
532+
;;
533+
;; **What this means for you**: You don't need to think about parallelism—just pass large
534+
;; arrays and JTransforms automatically uses all available cores. For smaller signals (<8K),
535+
;; the overhead of parallelization exceeds the benefit, so it runs single-threaded.
536+
;;
537+
;; **Performance tip**: Reuse transformer objects! The constructor pre-computes lookup tables
538+
;; (twiddle factors), so creating a transformer once and reusing it is much faster than
539+
;; creating a new one for each transform.
540+
511541
;; ### Understanding the DFT: Signal Decomposition
512542
;;
513543
;; The [Discrete Fourier Transform (DFT)](https://en.wikipedia.org/wiki/Discrete_Fourier_transform)
@@ -971,6 +1001,19 @@
9711001
;;
9721002
;; Signal = a₁·ψ(t-1) + a₂·ψ(2t-1) + a₃·ψ(2t-2) + ...
9731003
;;
1004+
;; **⚠️ Important**: Most wavelet transforms (Haar, Daubechies, Symlets) require **power-of-2
1005+
;; array sizes** (64, 128, 256, 512, etc.). This is because wavelets work by recursively
1006+
;; splitting signals in half. If your signal isn't a power of 2, you'll need to pad it:
1007+
;;
1008+
;; ```clojure
1009+
;; ;; Pad to next power of 2
1010+
;; (let [n (dtype/ecount signal)
1011+
;; target-n (int (Math/pow 2 (Math/ceil (/ (Math/log n) (Math/log 2)))))]
1012+
;; ;; ... pad signal to target-n ...)
1013+
;; ```
1014+
;;
1015+
;; We'll show the padding pattern in our examples below.
1016+
;;
9741017
;; Where ψ is a localized wavelet function at different scales and positions.
9751018
;;
9761019
;; See also: [Wavelet Transform](https://en.wikipedia.org/wiki/Wavelet_transform)
@@ -1551,27 +1594,46 @@
15511594
{:category "Array access" :functions "dtype/ecount, dtype/get-value, dtype/sub-buffer"}
15521595
{:category "Search" :functions "argops/argmax - find index of maximum"}])
15531596

1554-
;; ### Common Patterns
1597+
;; ### Common dtype-next Patterns - Quick Reference
15551598

1556-
;; Throughout signal processing, certain computational patterns appear repeatedly.
1557-
;; Root mean square error combines difference, squaring, and averaging to measure
1558-
;; signal similarity:
1559-
;;
1560-
;; (Math/sqrt (dfn/mean (dfn/sq (dfn/- signal1 signal2))))
1561-
;;
1562-
;; Signal energy sums the squares of all values:
1563-
;;
1564-
;; (dfn/sum (dfn/sq signal))
1565-
;;
1566-
;; Normalization centers and scales a signal to zero mean and unit variance:
1567-
;;
1568-
;; (dfn// (dfn/- signal (dfn/mean signal)) (dfn/standard-deviation signal))
1569-
;;
1570-
;; Signal-to-noise ratio ([SNR](https://en.wikipedia.org/wiki/Signal-to-noise_ratio))
1571-
;; in decibels compares signal power to noise power:
1572-
;;
1573-
;; (* 20 (Math/log10 (/ (dfn/mean (dfn/sq signal))
1574-
;; (dfn/mean (dfn/sq noise)))))
1599+
;; Throughout the tutorial, you've seen these dtype-next patterns repeatedly. Here's a
1600+
;; quick reference with working examples you can use in your own code.
1601+
1602+
;; **Root Mean Square Error (RMSE)** - measures how different two signals are:
1603+
(defn compute-rmse [signal1 signal2]
1604+
(Math/sqrt (dfn/mean (dfn/sq (dfn/- signal1 signal2)))))
1605+
1606+
;; Example: Compare original and compressed
1607+
(let [original (range 100)
1608+
noisy (dfn/+ original 0.1)]
1609+
(compute-rmse original noisy))
1610+
1611+
;; **Signal Energy** - total power in a signal:
1612+
(defn signal-energy [signal]
1613+
(dfn/sum (dfn/sq signal)))
1614+
1615+
;; Example: Energy before and after filtering
1616+
(signal-energy (:signal (:pure-sine signals)))
1617+
1618+
;; **Normalization** - zero mean, unit variance:
1619+
(defn normalize [signal]
1620+
(dfn// (dfn/- signal (dfn/mean signal))
1621+
(dfn/standard-deviation signal)))
1622+
1623+
;; Example: Normalize before processing
1624+
(let [normalized (normalize (:signal (:two-tones signals)))]
1625+
{:mean (dfn/mean normalized)
1626+
:std (dfn/standard-deviation normalized)})
1627+
1628+
;; **Signal-to-Noise Ratio ([SNR](https://en.wikipedia.org/wiki/Signal-to-noise_ratio)) in dB**:
1629+
(defn snr-db [signal noise]
1630+
(* 20 (Math/log10 (/ (dfn/mean (dfn/sq signal))
1631+
(dfn/mean (dfn/sq noise))))))
1632+
1633+
;; Example: Measure compression quality
1634+
;; (let [signal (:signal (:smooth signals))
1635+
;; noise (dfn/- signal compressed-signal)]
1636+
;; (snr-db signal noise))
15751637

15761638
;; ### Transform Selection Flowchart
15771639

0 commit comments

Comments
 (0)