dynR 0.1.5
New functions
leida_pipeline()/sw_pipeline(): high-level wrappers running the full phase-based or correlation-based pipeline in one call. Both accept an optionalfilterargument to skip bandpass filtering on pre-filtered data. Return S3 objects (dynR_leida/dynR_sw) withprint()andplot()methods.plot_fc(): FC matrix heatmap using the dynR diverging palette (deep indigo -> periwinkle -> brick red).plot_synchrony(): Kuramoto R(t) time series with mean reference line.plot_state_sequence(): brain state tile plot with dynR state palette.plot.dynR_leida()/plot.dynR_sw(): S3 plot methods for pipeline outputs; dispatch toplot_synchrony()/plot_fc()or the RSS plot viatype = c("synchrony"|"fc")andtype = c("rss"|"fc").batch_leida()/batch_sw(): apply the respective pipeline to multiple subjects at once. Accepts either a named list of [N x Tmax] matrices or a 3-D array [N x Tmax x subjects].stack_leida(): combine LEiDA eigenvector matrices from a batch result into a single data frame or matrix for cross-subject K-means clustering.stack_synchrony(): combine synchrony time series into a tidy long-format data frame withsubject,timepoint,synchrony, andmetastabilitycolumns.
dynR 0.1.4
Performance
Four additional compiled backends and one vectorisation, completing the C++ acceleration of all main compute paths:
get_leida(): C++ / LAPACK backend (get_leida_cpp()). Calls LAPACKdsyevdirectly viaR_ext/Lapack.h; workspace allocated once and reused across all timepoints.dsyevreturns eigenvalues in ascending order so the leading eigenvector is the last column (opposite of R’seigen()). Parity vseigen(): max diff < 2.5e-15.kuramoto(): C++ backend (kuramoto_sync_cpp()). Replacesvapply+ R complexexp(1i*x)with directcos/sinaccumulation andsqrt— no complex-number allocation. Parity vs Rvapplyreference: < 1e-14.hilbert_phases(): vectorised withmvfft(). Replaces N per-parcelfft()calls in an R loop with twomvfft()calls on the full matrix. The Hilbert multiplier is broadcast across parcel columns in one step. (fft_factor/fft_workare not in R’s public API on macOS/Accelerate;mvfftachieves equivalent throughput without a bundled FFT.) Parity vs.hilbert_r()loop: < 1e-14.corr_slide(): C++ backend (corr_slide_cpp()). Computes Pearson correlation directly on the input matrix without per-window submatrix allocation or transposition. Cross-products accumulated in the upper triangle with t-outer loop ordering (column-major sequential access). Tested for non-overlapping and overlapping (step = 10, 5) windows. Parity vscor(): < 1e-10; removesstats::corfromImports.do_euclid(): vectorised withdiff()+rowSums(). Loop over rows replaced byc(0, sqrt(rowSums(diff(x)^2))).
dynR 0.1.3
Performance
-
dyn_phase_lock()now delegates the phase-locking loop to a compiled C++ backend (dyn_phase_lock_cpp()). The innerouter(cos(a-b))R loop is replaced by a triple nested C++ loop that exploits symmetry (cos(a-b) = cos(b-a)): only the upper triangle is evaluated and mirrored, halving trigonometric operations. AddsRcppas a dependency.
Dependencies
- Removed
gsignalfromImports. Both remaining uses have been ported to base-R equivalents:-
gsignal::butter()replaced by.butter_bandpass()— a direct port ofscipy.signal.butter()(analog Butterworth LP prototype -> LP-to-BP transformation -> bilinear transform -> ZPK-to-TF). Coefficients validated against the[k, 0, -2k, 0, k]bandpass structure and existing scipy parity tests. -
gsignal::hilbert()replaced by.hilbert_r()— FFT-based analytic signal using base-Rfft(), equivalent toscipy.signal.hilbert(). Phase range confirmed[-pi, pi]on synthetic cosine.
-
dynR 0.1.2
New functions
-
dyn_transitions(): first-order Markov transition probabilities between brain states. Takes a long-format timepoint-level data frame, computes source → target transitions within each group (subject × session), and returns a nested tibble of per-transition probabilities ready forgrouped_perm_glmm(). Ported fromclusters_markov()in theneonatal_dfcanalysis pipeline (França et al., Nat Commun).remIntra = TRUEstrips self-transitions before normalising.
dynR 0.1.1
Bug fixes
-
bandpass_filter(): replacedgsignal::filtfilt()(zero initial conditions) with a scipy-compatible implementation that achieves bit-perfect parity withscipy.signal.filtfiltandscipy.signal.sosfiltfilt(max |diff| < 4e-12). The old implementation produced edge transients of up to ~0.24 signal units on real fMRI data due to zero initial state; this propagated intohilbert_phases()anddyn_phase_lock(), causing LEiDA eigenvectors to diverge from the Pythondynfcreference pipeline.The fix introduces three internal helpers:
-
.lfilter_zi(): steady-state initial conditions via companion-matrix solve (equivalent toscipy.signal.lfilter_zi). -
.lfilter(): Direct Form II Transposed IIR filter with explicit initial state (equivalent toscipy.signal.lfilter). -
.odd_ext(): odd-reflection signal extension matchingscipy.signal._arraytools.odd_extexactly — right extension usesx[N-2 : N-n-2 : -1], padlen =3 × max(length(b), length(a)).
With these corrections, per-timepoint correlations between R and Python LEiDA eigenvectors are identically 1.0 across all sessions.
-
Tests
- Added
tests/testthat/test-bandpass_filter.Rwith six tests adapted from thescipy.signal.filtfiltdocumentation examples: output-length preservation, DC blocking, in-band sine-wave recovery, stopband attenuation (>40 dB), zero-phase property, and machine-precision agreement withscipy.signal.filtfilton fMRI parameters (guarded byskip_if_not_installed("reticulate")). - Added
reticulatetoSuggestsfor the scipy cross-validation test.
dynR 0.1.0
- Initial scaffold.
- Ported core dynamic functional connectivity functions from the Python
dynfcpackage. - Implements:
bandpass_filter(),hilbert_phases(),corr_slide(),cofluct(),corr_corr(),dyn_phase_lock(),get_leida(),kuramoto(),shannon_entropy(),do_euclid().