({m, n - 1});
for (auto i = 0; i != m; ++i)
for (auto j = 0; j != n - 1; ++j)
An(i, j) = 2.0 * std::cos(w(i) * (j + 1));
```
After โ vectorized outer product + cache:
```cpp
static Arr cached_A; // cached across iterations!
if (n != cached_n) {
Arr An = 2.0 * cos(outer(linspace(0, 2*M_PI, m),
arange(1.0, double(n))));
cached_A = concatenate(ones(m, 1), An, 1);
cached_n = n;
}
```
**Optimizations**: (1) `xt::linalg::outer` vectorized, (2) matrix **cached** per filter order, (3) `mult_factor` reduced from 100 โ **20** (5ร fewer FFT samples).
---
## ๐ฒ Dependency Tree: Before vs After
### Before
.mermaid[
graph TD
subgraph "Before"
M[multiplierless-cpp]
XB[xtensor-blas]
XF[xtensor-fftw]
XT[xtensor]
XL[xtl]
BL[BLAS/LAPACK]
FW[FFTW]
OB[OpenBLAS]
M --> XB
M --> XF
XB --> XT
XF --> XT
XT --> XL
XB --> BL
XB --> OB
M --> FW
end
style M fill:#f44336
style XB fill:#ff9800
style XF fill:#ff9800
style XT fill:#ff9800
style XL fill:#ff9800
style BL fill:#9c27b0
style OB fill:#9c27b0
style FW fill:#4caf50
]
### After
.mermaid[
graph TD
subgraph "After"
M2[multiplierless-cpp]
E[ellalgo-cpp / arr.hpp]
FW2[FFTW]
CS[corr-solver-cpp / linalg.hpp]
M2 --> E
M2 --> FW2
CS --> E
end
style M2 fill:#4caf50
style E fill:#2196f3
style FW2 fill:#4caf50
style CS fill:#4caf50
]
**7 dependencies eliminated.** Only FFTW remains (needed for FFT).
---
## ๐ Lessons Learned
### What Worked Well โ
- **Flat `std::vector`** is all you need for $N \le 128$
- **Simple loops** beat expression templates for tiny matrices
- **Moving `Arr` to ellalgo-cpp** avoided circular dependency issues
- **Static cache** of cosine matrix reduced spectral factorization time by 5ร
- **Direct FFTW C API** is simpler than going through xtensor-fftw
### What Was Tricky โ ๏ธ
- **Constructor ambiguity**: `Arr(size_t, double)` vs `Arr(size_t, size_t)` โ had to remove one
- **`xt::where` semantics**: Returns indices differently than simple `where()`
- **`xt::row`/`xt::col` assignments**: Need mutable view support
- **xtensor-blas operations**: Cholesky, inv, meshgrid โ had to write from scratch for corr-solver
---
## ๐งช Test Results Summary
```text
ellalgo-cpp:
Test cases: 128 | 128 passed | 0 failed
RapidCheck: 1500/1500 passed
multiplierless-cpp:
Test cases: 13 | 13 passed | 0 failed
RapidCheck: 1000/1000 passed
corr-solver-cpp:
Test cases: 3 | 3 passed | 0 failed
```
All existing tests pass with identical numerical results (within machine epsilon).
---
## ๐ฎ Future Work
### For `arr.hpp` Improvements
- **Span-based views** (no data copy) for zero-cost slicing
- **Fixed-size small-buffer optimization** for $N \le 32$
- **SSE/AVX intrinsics** for dot product hot paths
---
count: false
class: nord-dark, middle, center
## Q&A ๐ค
---
count: false
class: nord-dark, middle, center
# ๐ Thank You
### Links
- ๐ฆ [multiplierless-cpp](https://github.com/luk036/multiplierless-cpp)
- ๐ฆ [ellalgo-cpp](https://github.com/luk036/ellalgo-cpp)
- ๐ฆ [corr-solver-cpp](https://github.com/luk036/corr-solver-cpp)
**Key takeaway**: For small arrays, a **240-line header** can replace a **7-library dependency stack**.