{"id":50274389,"url":"https://github.com/beliavsky/multivariate-changepoints","last_synced_at":"2026-05-27T19:02:40.925Z","repository":{"id":351214197,"uuid":"1210043602","full_name":"Beliavsky/Multivariate-ChangePoints","owner":"Beliavsky","description":"Find changepoints in correlation and covariance","archived":false,"fork":false,"pushed_at":"2026-04-14T03:39:30.000Z","size":209,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-14T05:24:43.985Z","etag":null,"topics":["correlation","covariance","finance","fortran","modern-fortran","quantitative-finance","regime-shifts","statistics","time-series-analysis","volatility"],"latest_commit_sha":null,"homepage":"","language":"Fortran","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Beliavsky.png","metadata":{"files":{"readme":"README.md","changelog":"changepoint.f90","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-14T03:10:56.000Z","updated_at":"2026-04-14T03:41:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Beliavsky/Multivariate-ChangePoints","commit_stats":null,"previous_names":["beliavsky/multivariate-changepoints"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Beliavsky/Multivariate-ChangePoints","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beliavsky%2FMultivariate-ChangePoints","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beliavsky%2FMultivariate-ChangePoints/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beliavsky%2FMultivariate-ChangePoints/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beliavsky%2FMultivariate-ChangePoints/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Beliavsky","download_url":"https://codeload.github.com/Beliavsky/Multivariate-ChangePoints/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Beliavsky%2FMultivariate-ChangePoints/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33579668,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["correlation","covariance","finance","fortran","modern-fortran","quantitative-finance","regime-shifts","statistics","time-series-analysis","volatility"],"created_at":"2026-05-27T19:02:31.480Z","updated_at":"2026-05-27T19:02:40.917Z","avatar_url":"https://github.com/Beliavsky.png","language":"Fortran","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Multivariate-ChangePoints\n\nFortran programs (with Python and C++ equivalents for selected programs) that find structural breaks in the mean, variance, correlation, and covariance of financial return series — and in generic univariate data — using exact dynamic programming.\n\nTwo sample data files are included:\n\n**`spy_efa_eem_tlt.csv`** — daily closing prices for four ETFs from 1993 to 2015:\n\n| Ticker | Description |\n|--------|-------------|\n| SPY | S\u0026P 500 |\n| EFA | Developed international equities |\n| EEM | Emerging market equities |\n| TLT | 20+ year US Treasury bonds |\n\n**`asset_class_etf_prices.csv`** — daily closing prices for nine ETFs from 2007 to 2026:\n\n| Ticker | Description |\n|--------|-------------|\n| SPY | S\u0026P 500 |\n| EFA | Developed international equities |\n| EEM | Emerging market equities |\n| EMB | Emerging market bonds |\n| HYG | High-yield corporate bonds |\n| LQD | Investment-grade corporate bonds |\n| TLT | 20+ year US Treasury bonds |\n| GLD | Gold |\n| USO | Oil |\n\n---\n\n## Statistical models\n\nAll changepoint programs find the exact global optimum for each fixed number of segments m using the recurrence\n\n    dp(i, m) = min over k \u003c i of [ dp(k, m-1) + cost(k+1, i) ]\n\nin O(n² × max_m) time, then select the number of segments by AIC and BIC.  The cost functions and parameter counts differ by model:\n\n| Program | Series z_t | Cost(i..j) | Params/segment |\n|---------|-----------|------------|----------------|\n| correlation | bivariate (x, y) | m/2 · log(1 − r²) | 1 (one ρ) |\n| mean | r_t | m/2 · log(ŝ²_z) | 2 (μ, σ²) |\n| variance (r²) | r_t² | m/2 · log(ŝ²_z) | 2 |\n| variance (log) | log(c + r_t²) | m/2 · log(ŝ²_z) | 2 |\n| pairwise covariance | r_{i,t} · r_{j,t} | m/2 · log(ŝ²_z) | 2 |\n| joint covariance matrix | R_t ∈ ℝᵖ | m/2 · log\\|Σ̂\\| | p(p+3)/2 + 1 |\n| joint correlation matrix | R_t ∈ ℝᵖ (standardised) | m/2 · log\\|Ρ̂\\| | p(p+1)/2 + 1 |\n\nwhere ŝ²_z = mean(z²) − mean(z)² is the biased sample variance of z over the segment, Σ̂ is the biased sample covariance matrix, and Ρ̂ is the sample correlation matrix of standardised returns.  The joint covariance and correlation matrix costs use a Cholesky-based log-determinant.\n\nThe BIC penalty is `k_params × log(n)` where `k_params = params_per_seg × m − 1` (one parameter per segment times m segments, minus one for the first segment which has no preceding changepoint location).\n\n### Variance series: r² vs log(c + r²)\n\nTwo choices are available for the variance series (controlled by `use_log_var`):\n\n- **z_t = r_t²** finds good changepoint locations but the series is non-negative and heavy-tailed (chi-squared), so BIC under-penalises splits and bootstrap-resampled (i.i.d.) data can yield *more* detected changepoints than the original — an inverted null test.\n- **z_t = log(c + r_t²)** is approximately Gaussian (log-chi-squared), giving a correctly calibrated BIC and a proper null test under resampling.  The offset `c` (default 0.01 for percent returns) floors near-zero returns.\n\n---\n\n## Programs\n\n### Correlation changepoints\n\n**`xreturns_corr.f90`**  \nComputes return statistics and the full correlation matrix. No changepoint detection; useful as a baseline.\n\n**`xcorr_sim.f90`**  \nSimulates a bivariate normal series with known piecewise-constant correlation (segments of different ρ), then detects changepoints. Prints true vs estimated parameters and writes simulated data to `sim_data.txt`.\n\n**`xreturns_corr_cp.f90`** · **`xreturns_corr_cp.py`** · **`xreturns_corr_cp.cpp`**  \nReads `spy_efa_eem_tlt.csv`, computes percent returns, and for each asset pair finds changepoints in correlation.  After processing all pairs, prints a summary matrix:\n\n```\n             SPY   EFA   EEM   TLT\n     SPY       0     2     3     1\n     EFA       4     0     2     2\n     EEM       5     3     0     1\n     TLT       3     4     3     0\n```\n\nAbove the diagonal: BIC changepoint count.  Below: AIC count.  Diagonal: 0.\n\nThe Python version (`xreturns_corr_cp.py`) uses NumPy prefix-sum vectorisation and matches the Fortran output exactly; it requires no Numba.  The C++ version uses an end-major cost matrix layout for cache efficiency.\n\nKey parameters (compile-time constants at the top of each file):\n\n| Parameter | Default | Meaning |\n|-----------|---------|---------|\n| `MAX_CP` / `max_cp` | 20 | Maximum changepoints per pair |\n| `MIN_SEG_LEN` / `min_seg_len` | 50 | Minimum observations per segment |\n| `SYM_ALLOWED` / `sym_allowed` | (empty) | Subset of tickers; empty = all |\n| `max_days` | 0 | Keep latest/earliest N returns; 0 = all |\n| `latest` | `.true.` | If `max_days \u003e 0`: `.true.` = most recent |\n\n**`xcorr_fit.f90`**  \nFits the correlation changepoint model to a single pair with detailed diagnostic output.\n\n---\n\n### Variance and mean changepoints (univariate, per asset)\n\n**`xreturns_variance_cp.f90`**  \nFinds changepoints in the variance of each asset independently.  The derived series is z_t = r_t², and the cost is the profile normal mean-shift log-likelihood m/2 · log(ŝ²_z).  Prints a summary table of BIC/AIC changepoint counts per asset.  Set `resample_ret = .true.` to shuffle rows as a null-hypothesis check.\n\n**`xreturns_mean_cp.f90`**  \nFinds changepoints in the return mean of each asset.  Same cost function, with z_t = r_t.  Supports `resample_ret`.\n\n**`xreturns_mean_variance_cp.f90`**  \nRuns both mean and variance changepoint analyses for each asset in one pass.  The variance series is selectable via `use_log_var` / `var_offset`; see the variance series note above.  Supports `resample_ret`.\n\n**`xreturns_variance_pwl.f90`**  \nFits a **continuous piecewise-linear** path to squared returns by minimising the sum of squared residuals.  The fitted variance path v_t is continuous at the knots (changepoints) and linear between them.  This is an exact optimizer for the PWL-SSE objective, not a likelihood model.\n\n**`xreturns_variance_pwl_fast.f90`**  \nAccelerated version of `xreturns_variance_pwl.f90`.\n\n---\n\n### Pairwise covariance / variance changepoints\n\n**`xreturns_cov_cp.f90`**  \nFor every pair (i, j) with i ≤ j (including diagonal i = j for variance), forms z_t = r_{i,t} · r_{j,t} and finds mean-shift changepoints.  The diagonal pair (i, i) detects variance changepoints; off-diagonal pairs detect covariance changepoints.  Prints a summary matrix analogous to `xreturns_corr_cp.f90`.\n\nNote: variance of financial returns is genuinely time-varying (GARCH-like), so many changepoints are typically detected.  The normal mean-shift model is also misspecified for z_t = r² (which is non-negative and heavy-tailed).  Results are more interpretable for covariance pairs than for variance.\n\n---\n\n### Joint covariance matrix changepoints\n\n**`xreturns_covmat_cp.f90`**  \nFinds a **single set** of changepoints for all assets simultaneously by fitting a multivariate normal model with piecewise-constant p × p covariance matrix Σ.  The cost for a segment is m/2 · log|Σ̂|, computed via a Cholesky log-determinant.  After model selection, prints for each BIC-chosen segment the correlation lower triangle, annualised standard deviations, and optionally PCA loadings:\n\n```\nSegment 2: 2008-09-02 to 2012-06-29 (984 obs)\n  Correlation:\n             SPY     EFA     EEM     TLT\n     SPY   1.000\n     EFA   0.964   1.000\n     EEM   0.932   0.956   1.000\n     TLT  -0.418  -0.370  -0.310   1.000\n   *SD*   0.292   0.270   0.308   0.148\n```\n\n`*SD*` is the annualised standard deviation (× √252) of unscaled returns.\n\nPrefix sums of outer products (`SR`, `SP`) reduce cost matrix construction to O(n · p²) setup + O(n² · p²) inner work, with an O(p³) Cholesky per cell.  Supports `resample_ret`.\n\n**`xcovar_sim.f90`**  \nSimulation counterpart of `xreturns_covmat_cp.f90`.  Generates a p × n multivariate normal series with known piecewise-constant covariance matrices (specified as correlations + standard deviations per segment), runs the joint covariance changepoint algorithm, and prints true vs estimated parameters side by side.\n\nDefault setup: p = 3 assets, n = 2000 observations, 3 segments (changepoints at observations 500 and 1200) with substantially different correlation structure and volatility between segments.\n\n---\n\n### Joint correlation matrix changepoints\n\n**`xreturns_corrmat_cp.f90`**  \nFinds changepoints in the full p × p **correlation** matrix jointly.  Returns are first standardised so that the cost captures only changes in correlation structure, not changes in volatility.  Two normalisation schemes are selectable via `use_ewma`:\n\n| Scheme | Description |\n|--------|-------------|\n| `use_ewma = .false.` | Global: subtract full-sample mean, divide by full-sample std dev.  Simple but has look-ahead bias. |\n| `use_ewma = .true.` | EWMA (RiskMetrics): conditional σ_t updated at each step with decay λ = 0.94.  Removes volatility clustering before the correlation test. |\n\nBIC parameter count per segment: p(p+1)/2 + 1 (correlations + means + changepoint location).\n\nAdditional output options (compile-time parameters):\n\n| Parameter | Default | Meaning |\n|-----------|---------|---------|\n| `print_segs` | 0 | 0 = BIC model only; 1 = 0 through BIC; 2 = all models |\n| `print_diffs` | `.true.` | Bonferroni-adjusted Fisher z-tests for correlation changes between segments |\n| `alpha_diff` | 0.05 | Significance level for the Fisher z-test |\n| `print_pca_cov` | `.false.` | PCA of segment covariance matrix |\n| `print_pca_corr` | `.true.` | PCA of segment correlation matrix |\n| `resample_ret` | `.false.` | Shuffle rows as a null-hypothesis check |\n\n**`xreturns_corrmat_ol.f90`**  \n**Online (expanding-window)** version of `xreturns_corrmat_cp.f90`.  At each step t the DP is re-run on data 1:t only, so no future data are used.  Prints a dated table showing how the BIC-optimal changepoint dates evolve as new observations arrive:\n\n```\nas-of          n_cp  changepoints\n----------------------------------------------------------------------\n2009-03-31        1  2008-09-15\n2009-06-30        1  2008-09-15\n2009-09-30        2  2007-07-26 2008-09-15\n...\n```\n\nThe DP is re-run every `step` observations (default 63, ≈ 1 quarter).  Total work scales as O(n³/step).  Supports `resample_ret`.\n\n**`xreturns_corrsub_cp.f90`**  \nRuns up to four changepoint algorithms across all distinct asset subsets of specified sizes, controlled by compile-time toggles:\n\n| Toggle | Algorithm |\n|--------|-----------|\n| `do_mean` | Mean-shift changepoints per asset (univariate) |\n| `do_variance` | Variance changepoints per asset (on squared returns) |\n| `do_cov` | Joint covariance-matrix changepoints for each subset |\n| `do_corr` | Joint correlation-matrix changepoints for each subset (EWMA-standardised) |\n\n`sub_sizes` specifies the subset sizes to analyse (default `[2, 5]`); `max_subsets` caps the number of subsets per size when C(n_assets, k) is large.  A summary of AIC/BIC changepoint counts is printed for mean and variance.  Supports `resample_ret`.\n\n---\n\n### Bootstrap resampling null test\n\n**`xresample_cp.f90`**  \nReads asset prices, computes returns, and for each asset runs changepoint detection on the actual return series and on `n_resample` bootstrap-resampled (row-shuffled) copies.  Resampling destroys any serial structure, providing an empirical null distribution for the number of changepoints found by chance.\n\nFor each series and analysis type the summary table reports:\n\n| Column | Meaning |\n|--------|---------|\n| `Actual` | BIC changepoint count for actual returns |\n| `RMean` | Mean BIC count over resampled runs |\n| `RMax` | Maximum BIC count over resampled runs |\n| `p-value` | Fraction of resampled runs with BIC count ≥ Actual |\n\nKey parameters:\n\n| Parameter | Default | Meaning |\n|-----------|---------|---------|\n| `n_resample` | 100 | Number of bootstrap runs |\n| `do_mean` | `.false.` | Detect mean changepoints |\n| `do_variance` | `.true.` | Detect variance changepoints |\n| `use_log_var` | `.true.` | Use log(c + r²) for variance series |\n| `var_offset` | 0.01 | Offset c in log(c + r²) |\n| `print_segs` | 1 | Segment detail level for actual returns |\n\nProgress is printed as run numbers on a single line during the resample loop.\n\n---\n\n### Variance criterion simulation\n\n**`xsim_variance_cp.f90`**  \nMonte Carlo simulation that compares the two variance series (r² and log(c + r²)) for detecting known changepoints.  Generates `n_sim` Gaussian time series with piecewise-constant standard deviation (`sigmas`) and reports for each criterion:\n\n- Mean number of changepoints found vs the true count\n- Fraction of simulations finding exactly the right number\n- Mean absolute location error when the count is exact\n- Mean distance from each found changepoint to the nearest true one (all simulations)\n- Histogram of the count of found changepoints\n\nSet `write_data = .true.` to write the last simulation to a text file (`sim_variance_cp.txt`) with columns `t, seg, r, r^2, log(c+r^2)` for external inspection.\n\n**`xsim_sd_changes.py`**  \nPython counterpart to `xsim_variance_cp.f90` using NumPy and `ruptures`.\n\n---\n\n### Generic data changepoints\n\n**`xdata_mean_variance_cp.f90`** · **`xdata_mean_variance_cp.py`**  \nReads a plain matrix data file (not asset prices) and finds changepoints in the mean and/or variance of each column.  Suitable for any univariate time series data, not just financial returns.\n\nInput file format:\n```\n# nrow ncol [any other text]   ← first line: dimensions\n# further comment lines ...    ← skipped\n-1.42  0.87  1.03  ...         ← nrow rows of ncol whitespace-separated reals\n```\n\nThe file is read by the `read_matrix` subroutine in `util.f90` (optional `nrow_max` and `ncol_max` arguments cap the dimensions read).\n\nSegment output uses `print_univar_segments`: for each segment prints `start`, `end`, `n`, `mean`, `sd`, `min`, `max`, `first`, `last`.\n\nKey parameters:\n\n| Parameter | Default | Meaning |\n|-----------|---------|---------|\n| `data_file` | `\"sim_matrix.txt\"` | Input file |\n| `do_mean` | `.false.` | Detect mean changepoints |\n| `do_variance` | `.true.` | Detect variance changepoints |\n| `use_log_var` | `.true.` | Use log(c + x²) for variance series |\n| `var_offset` | 0.01 | Offset c |\n| `max_col` | 3 | Maximum columns to analyse (0 = all) |\n| `print_table` | `.false.` | Print M/cost/AIC/BIC table |\n| `print_segs` | 1 | 0=none, 1=BIC model, 2=0..BIC, 3=all |\n\nThe Python version (`xdata_mean_variance_cp.py`) uses `ruptures.Dynp` with a custom `GaussianProfileCost` class that matches the Fortran cost function exactly (n/2 · log(sample_variance)).  The `jump` parameter (default 5) controls the spacing of candidate breakpoint positions: `jump=1` is exact but O(n²); larger values trade a small positional rounding for substantial speed gains.\n\n---\n\n### Principal component analysis\n\n**`xreturns_pca.f90`**  \nReads asset prices, computes percent returns, and performs PCA on the full-sample covariance matrix via the Jacobi eigenvalue method.  Prints the labeled covariance matrix, PC loadings, per-component variance explained, and cumulative variance explained.\n\n---\n\n## Modules\n\n| File | Purpose |\n|------|---------|\n| `changepoint.f90` | Cost matrices (`cost_matrix`, `mean_shift_cost_matrix`, `multivar_cost_matrix`), DP solver (`solve_changepoints`), and backtracking helper (`segment_ends`) |\n| `io_utils.f90` | `print_model_selection`; segment printing for corrmat, covmat, and univariate models (`print_univar_segments` prints mean, sd, min, max, first, last); `print_pca_loadings`; `keep_obs` |\n| `pca_jacobi.f90` | `jacobi_eigen_sym` (Jacobi eigenvalue method) and `principal_components_cov` |\n| `basic_stats.f90` | `mean`, `cov_mat`, `col_stats_ignore_nan`, `standardize_returns`, `biased_cov_sd`, `print_corr_mat`, `print_acf` |\n| `util.f90` | `sort_int`, `set_segment_values`, `print_wall_time`, `next_combination`, `n_choose_k`, `cumul_sum`, `print_square_matrix`, `read_matrix` |\n| `sim_changepoint.f90` | `generate_series` (bivariate), `generate_multivar_series` (multivariate) |\n| `dataframe_index_date.f90` | Date-indexed DataFrame type: `read_csv`, `pct_change` (with `dropna` option), `log_change`, `keep_rows`, `select`, `resample` |\n| `df_index_date_ops_mod.f90` | Index operations for the DataFrame |\n| `date.f90` | `Date` type and arithmetic |\n| `random.f90` | `rnorm` (scalar and vector) |\n| `kind.f90` | `dp = real64`, `long_int = int64` |\n| `constants.f90` | Physical and mathematical constants |\n\n---\n\n## Building\n\nGNU make is required.  The compiler is gfortran (set `FC` in the Makefile to change).\n\n```bash\n# Build all programs\nmake -f Makefile.xcorr\n\n# Build and run individual programs\nmake -f Makefile.xcorr run_sim             # xcorr_sim\nmake -f Makefile.xcorr run_covar_sim       # xcovar_sim\nmake -f Makefile.xcorr run_fit             # xcorr_fit\nmake -f Makefile.xcorr run_corr            # xreturns_corr_cp\nmake -f Makefile.xcorr run_cov             # xreturns_cov_cp\nmake -f Makefile.xcorr run_covmat          # xreturns_covmat_cp\nmake -f Makefile.xcorr run_corrmat         # xreturns_corrmat_cp\nmake -f Makefile.xcorr run_corrmat_ol      # xreturns_corrmat_ol\nmake -f Makefile.xcorr run_corrsub         # xreturns_corrsub_cp\nmake -f Makefile.xcorr run_pca             # xreturns_pca\nmake -f Makefile.xcorr run_var             # xreturns_variance_cp\nmake -f Makefile.xcorr run_mean            # xreturns_mean_cp\nmake -f Makefile.xcorr run_mean_var        # xreturns_mean_variance_cp\nmake -f Makefile.xcorr run_var_pwl         # xreturns_variance_pwl\nmake -f Makefile.xcorr run_var_pwl_fast    # xreturns_variance_pwl_fast\nmake -f Makefile.xcorr run_resample_cp     # xresample_cp\nmake -f Makefile.xcorr run_sim_var_cp      # xsim_variance_cp\nmake -f Makefile.xcorr run_data_mean_var_cp # xdata_mean_variance_cp\n\n# Clean\nmake -f Makefile.xcorr clean\n```\n\nThe Python programs require NumPy, pandas, and ruptures and run without compilation:\n\n```bash\npython xreturns_corr_cp.py\npython xdata_mean_variance_cp.py\npython xsim_sd_changes.py\n```\n\nThe C++ program requires a C++17 compiler:\n\n```bash\ng++ -O2 -std=c++17 -o xreturns_corr_cp xreturns_corr_cp.cpp\n./xreturns_corr_cp\n```\n\n---\n\n## Runtime\n\nTimings on a typical desktop for the full `spy_efa_eem_tlt.csv` dataset (n ≈ 5 500 daily returns, 4 assets, `max_cp = 20`):\n\n| Program | Time |\n|---------|------|\n| `xreturns_corr_cp` (Fortran) | ~2 s |\n| `xreturns_corr_cp.py` (Python/NumPy) | ~10 s |\n| `xreturns_corr_cp` (C++) | ~1 s |\n| `xreturns_covmat_cp` | ~15 s |\n| `xreturns_corrmat_cp` | ~15 s |\n| `xcovar_sim` (n = 2 000) | ~0.3 s |\n| `xreturns_corrmat_ol` (step = 63) | ~minutes |\n| `xsim_variance_cp` (n_sim = 1 000, n = 1 500) | ~seconds |\n| `xdata_mean_variance_cp.py` (n = 3 000, jump = 5) | ~20 s |\n\nThe dominant cost in all programs is the O(n² × max_m) dynamic programming step.  The joint covariance and correlation matrix programs add an O(p³) Cholesky per cost matrix cell.  The online program repeats the full DP at each step, so total work is O(n³/step).\n\nFor the Python `xdata_mean_variance_cp.py`, runtime scales as O((n/jump)²) per series; `jump=5` is ~250× faster than `jump=1` with negligible loss of precision on typical data.\n\n---\n\n## References\n\nThe piecewise-constant correlation changepoint model is based on:\n\n- Galeano, P. and Wied, D. (2014). [Multiple break detection in the correlation structure of random variables](https://arxiv.org/abs/1206.5367). *Computational Statistics \u0026 Data Analysis*, 76, 262–282.\n\nBIC for changepoint models:\n\n- Yao, Y.-C. (1988). [Estimating the number of change-points via Schwarz criterion](https://www.sciencedirect.com/science/article/abs/pii/0167715288901186). *Statistics \u0026 Probability Letters*, 6(3), 181–189.\n\nContinuous piecewise-linear fitting:\n\n- Bellman, R. and Roth, R. (1969). [Curve fitting by segmented straight lines](https://www.tandfondle.com/doi/pdf/10.1080/01621459.1969.10501038). *Journal of the American Statistical Association*, 64(327), 1079–1084.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeliavsky%2Fmultivariate-changepoints","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeliavsky%2Fmultivariate-changepoints","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeliavsky%2Fmultivariate-changepoints/lists"}