linreg-core 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  [![npm](https://img.shields.io/npm/v/linreg-core?color=red)](https://www.npmjs.com/package/linreg-core)
8
8
  [![PyPI](https://img.shields.io/pypi/v/linreg-core)](https://pypi.org/project/linreg-core/)
9
9
  [![docs.rs](https://img.shields.io/badge/docs.rs-linreg__core-green)](https://docs.rs/linreg-core)
10
+ [![Live Demo](https://img.shields.io/badge/demo-online-brightgreen)](https://jesse-anderson.net/linreg-core/)
10
11
 
11
12
 
12
13
  A lightweight, self-contained linear regression library written in Rust. Compiles to WebAssembly for browser use, Python bindings via PyO3, or runs as a native Rust crate.
@@ -37,6 +38,8 @@ A lightweight, self-contained linear regression library written in Rust. Compile
37
38
  - **Lasso Regression:** L1-regularized regression via coordinate descent with automatic variable selection, convergence tracking, model selection criteria
38
39
  - **Elastic Net:** Combined L1 + L2 regularization for variable selection with multicollinearity handling, active set convergence, model selection criteria
39
40
  - **LOESS:** Locally estimated scatterplot smoothing for non-parametric curve fitting with configurable span, polynomial degree, and robust fitting
41
+ - **WLS (Weighted Least Squares):** Regression with observation weights for heteroscedastic data, includes confidence intervals
42
+ - **K-Fold Cross Validation:** Model evaluation and hyperparameter tuning for all regression types (OLS, Ridge, Lasso, Elastic Net) with customizable folds, shuffling, and seeding
40
43
  - **Lambda Path Generation:** Create regularization paths for cross-validation
41
44
 
42
45
  ### Model Statistics
@@ -64,7 +67,7 @@ Add to your `Cargo.toml`:
64
67
 
65
68
  ```toml
66
69
  [dependencies]
67
- linreg-core = { version = "0.5", default-features = false }
70
+ linreg-core = { version = "0.6", default-features = false }
68
71
  ```
69
72
 
70
73
  ### OLS Regression (Rust)
@@ -198,35 +201,199 @@ fn main() -> Result<(), linreg_core::Error> {
198
201
  ```rust
199
202
  use linreg_core::diagnostics::{
200
203
  breusch_pagan_test, durbin_watson_test, jarque_bera_test,
201
- shapiro_wilk_test, RainbowMethod, rainbow_test
204
+ shapiro_wilk_test, rainbow_test, harvey_collier_test,
205
+ white_test, anderson_darling_test, breusch_godfrey_test,
206
+ cooks_distance_test, dfbetas_test, dffits_test, vif_test,
207
+ reset_test, BGTestType, RainbowMethod, ResetType, WhiteMethod
202
208
  };
203
209
 
204
210
  fn main() -> Result<(), linreg_core::Error> {
205
211
  let y = vec![/* your data */];
206
212
  let x = vec![vec![/* predictor 1 */], vec![/* predictor 2 */]];
207
213
 
208
- // Heteroscedasticity
214
+ // Heteroscedasticity tests
209
215
  let bp = breusch_pagan_test(&y, &x)?;
210
216
  println!("Breusch-Pagan: LM={:.4}, p={:.4}", bp.statistic, bp.p_value);
211
217
 
212
- // Autocorrelation
218
+ let white = white_test(&y, &x, WhiteMethod::R)?;
219
+ println!("White: statistic={:.4}, p={:.4}", white.statistic, white.p_value);
220
+
221
+ // Autocorrelation tests
213
222
  let dw = durbin_watson_test(&y, &x)?;
214
223
  println!("Durbin-Watson: {:.4}", dw.statistic);
215
224
 
216
- // Normality
225
+ let bg = breusch_godfrey_test(&y, &x, 2, BGTestType::Chisq)?;
226
+ println!("Breusch-Godfrey (order 2): statistic={:.4}, p={:.4}", bg.statistic, bg.p_value);
227
+
228
+ // Normality tests
217
229
  let jb = jarque_bera_test(&y, &x)?;
218
230
  println!("Jarque-Bera: JB={:.4}, p={:.4}", jb.statistic, jb.p_value);
219
231
 
220
- // Linearity
232
+ let sw = shapiro_wilk_test(&y, &x)?;
233
+ println!("Shapiro-Wilk: W={:.4}, p={:.4}", sw.statistic, sw.p_value);
234
+
235
+ let ad = anderson_darling_test(&y, &x)?;
236
+ println!("Anderson-Darling: A={:.4}, p={:.4}", ad.statistic, ad.p_value);
237
+
238
+ // Linearity tests
221
239
  let rainbow = rainbow_test(&y, &x, 0.5, RainbowMethod::R)?;
222
240
  println!("Rainbow: F={:.4}, p={:.4}",
223
241
  rainbow.r_result.as_ref().unwrap().statistic,
224
242
  rainbow.r_result.as_ref().unwrap().p_value);
225
243
 
244
+ let hc = harvey_collier_test(&y, &x)?;
245
+ println!("Harvey-Collier: t={:.4}, p={:.4}", hc.statistic, hc.p_value);
246
+
247
+ let reset = reset_test(&y, &x, &[2, 3], ResetType::Fitted)?;
248
+ println!("RESET: F={:.4}, p={:.4}", reset.f_statistic, reset.p_value);
249
+
250
+ // Influence diagnostics
251
+ let cd = cooks_distance_test(&y, &x)?;
252
+ println!("Cook's Distance: {} influential points", cd.influential_4_over_n.len());
253
+
254
+ let dfbetas = dfbetas_test(&y, &x)?;
255
+ println!("DFBETAS: {} influential observations", dfbetas.influential_observations.len());
256
+
257
+ let dffits = dffits_test(&y, &x)?;
258
+ println!("DFFITS: {} influential observations", dffits.influential_observations.len());
259
+
260
+ // Multicollinearity
261
+ let vif = vif_test(&y, &x)?;
262
+ println!("VIF: {:?}", vif.vif_values);
263
+
264
+ Ok(())
265
+ }
266
+ ```
267
+
268
+ ### WLS Regression (Rust)
269
+
270
+ ```rust,no_run
271
+ use linreg_core::weighted_regression::wls_regression;
272
+
273
+ fn main() -> Result<(), linreg_core::Error> {
274
+ let y = vec![2.0, 4.0, 6.0, 8.0, 10.0];
275
+ let x1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
276
+
277
+ // Equal weights = OLS
278
+ let weights = vec![1.0, 1.0, 1.0, 1.0, 1.0];
279
+
280
+ let fit = wls_regression(&y, &[x1], &weights)?;
281
+
282
+ println!("Intercept: {} (SE: {}, t: {}, p: {})",
283
+ fit.coefficients[0],
284
+ fit.standard_errors[0],
285
+ fit.t_statistics[0],
286
+ fit.p_values[0]
287
+ );
288
+ println!("F-statistic: {} (p: {})", fit.f_statistic, fit.f_p_value);
289
+ println!("R-squared: {:.4}", fit.r_squared);
290
+
291
+ // Access confidence intervals
292
+ for (i, (&coef, &lower, &upper)) in fit.coefficients.iter()
293
+ .zip(fit.conf_int_lower.iter())
294
+ .zip(fit.conf_int_upper.iter())
295
+ .enumerate()
296
+ {
297
+ println!("Coefficient {}: [{}, {}]", i, lower, upper);
298
+ }
299
+
300
+ Ok(())
301
+ }
302
+ ```
303
+
304
+ ### LOESS Regression (Rust)
305
+
306
+ ```rust,no_run
307
+ use linreg_core::loess::{loess_fit, LoessOptions};
308
+
309
+ fn main() -> Result<(), linreg_core::Error> {
310
+ // Single predictor only
311
+ let x = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
312
+ let y = vec![1.0, 3.5, 4.8, 6.2, 8.5, 11.0, 13.2, 14.8, 17.5, 19.0, 22.0];
313
+
314
+ // Default options: span=0.75, degree=2, robust iterations=0
315
+ let options = LoessOptions::default();
316
+
317
+ let result = loess_fit(&y, &[x], &options)?;
318
+
319
+ println!("Fitted values: {:?}", result.fitted_values);
320
+ println!("Residuals: {:?}", result.residuals);
321
+
226
322
  Ok(())
227
323
  }
228
324
  ```
229
325
 
326
+ **Custom LOESS options:**
327
+
328
+ ```rust,no_run
329
+ use linreg_core::loess::{loess_fit, LoessOptions, LoessSurface};
330
+
331
+ let options = LoessOptions {
332
+ span: 0.5, // Smoothing parameter (0-1, smaller = less smooth)
333
+ degree: 1, // Polynomial degree (0=constant, 1=linear, 2=quadratic)
334
+ surface: LoessSurface::Direct, // Note: only "direct" is currently supported; "interpolate" is planned
335
+ robust_iterations: 3, // Number of robust fitting iterations (0 = disabled)
336
+ };
337
+
338
+ let result = loess_fit(&y, &[x], &options)?;
339
+ ```
340
+
341
+ ### K-Fold Cross Validation (Rust)
342
+
343
+ Cross-validation is used for model evaluation and hyperparameter tuning. The library supports K-Fold CV for all regression types:
344
+
345
+ ```rust,no_run
346
+ use linreg_core::cross_validation::{kfold_cv_ols, kfold_cv_ridge, kfold_cv_lasso, kfold_cv_elastic_net, KFoldOptions};
347
+
348
+ fn main() -> Result<(), linreg_core::Error> {
349
+ let y = vec![2.5, 3.7, 4.2, 5.1, 6.3, 7.0, 7.5, 8.1];
350
+ let x1 = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
351
+ let x2 = vec![2.0, 4.0, 5.0, 4.0, 3.0, 4.5, 5.5, 6.0];
352
+ let names = vec!["Intercept".to_string(), "X1".to_string(), "X2".to_string()];
353
+
354
+ // Configure CV options
355
+ let options = KFoldOptions {
356
+ n_folds: 5,
357
+ shuffle: true,
358
+ seed: Some(42), // For reproducibility
359
+ };
360
+
361
+ // OLS cross-validation
362
+ let ols_cv = kfold_cv_ols(&y, &[x1.clone(), x2.clone()], &names, &options)?;
363
+ println!("OLS CV RMSE: {:.4} (±{:.4})", ols_cv.mean_rmse, ols_cv.std_rmse);
364
+ println!("OLS CV R²: {:.4} (±{:.4})", ols_cv.mean_r_squared, ols_cv.std_r_squared);
365
+
366
+ // Ridge cross-validation (for lambda selection)
367
+ let lambda = 1.0;
368
+ let ridge_cv = kfold_cv_ridge(&[x1.clone(), x2.clone()], &y, lambda, true, &options)?;
369
+ println!("Ridge CV RMSE: {:.4}", ridge_cv.mean_rmse);
370
+
371
+ // Lasso cross-validation
372
+ let lasso_cv = kfold_cv_lasso(&[x1.clone(), x2.clone()], &y, 0.1, true, &options)?;
373
+ println!("Lasso CV RMSE: {:.4}", lasso_cv.mean_rmse);
374
+
375
+ // Elastic Net cross-validation
376
+ let enet_cv = kfold_cv_elastic_net(&[x1, x2], &y, 0.1, 0.5, true, &options)?;
377
+ println!("Elastic Net CV RMSE: {:.4}", enet_cv.mean_rmse);
378
+
379
+ // Access per-fold results
380
+ for fold in &ols_cv.fold_results {
381
+ println!("Fold {}: train={}, test={}, R²={:.4}",
382
+ fold.fold_index, fold.train_size, fold.test_size, fold.r_squared);
383
+ }
384
+
385
+ Ok(())
386
+ }
387
+ ```
388
+
389
+ **CV Result fields:**
390
+ - `mean_rmse`, `std_rmse` - Mean and std of RMSE across folds
391
+ - `mean_mae`, `std_mae` - Mean and std of MAE across folds
392
+ - `mean_r_squared`, `std_r_squared` - Mean and std of R² across folds
393
+ - `mean_train_r_squared` - Mean training R² (for overfitting detection)
394
+ - `fold_results` - Per-fold metrics (train/test sizes, MSE, RMSE, MAE, R²)
395
+ - `fold_coefficients` - Coefficients from each fold (for stability analysis)
396
+
230
397
  ### Lambda Path Generation (Rust)
231
398
 
232
399
  ```rust,no_run
@@ -250,10 +417,34 @@ for &lambda in lambdas.iter() {
250
417
  }
251
418
  ```
252
419
 
420
+ ### Model Save/Load (Rust)
421
+
422
+ All trained models can be saved to disk and loaded back later:
423
+
424
+ ```rust,no_run
425
+ use linreg_core::{ModelSave, ModelLoad};
426
+
427
+ // Train a model
428
+ let result = ols_regression(&y, &[x1], &names)?;
429
+
430
+ // Save to file
431
+ result.save("my_model.json")?;
432
+
433
+ // Or with a custom name
434
+ result.save_with_name("my_model.json", Some("My Housing Model".to_string()))?;
435
+
436
+ // Load back
437
+ let loaded = linreg_core::core::RegressionOutput::load("my_model.json")?;
438
+ ```
439
+
440
+ The same `save()` and `load()` methods work for all model types: `RegressionOutput`, `RidgeFit`, `LassoFit`, `ElasticNetFit`, `WlsFit`, and `LoessFit`.
441
+
253
442
  ---
254
443
 
255
444
  ## WebAssembly Usage
256
445
 
446
+ **[Live Demo →](https://jesse-anderson.net/linreg-core/)**
447
+
257
448
  Build with wasm-pack:
258
449
 
259
450
  ```bash
@@ -360,6 +551,24 @@ console.log("Lambda sequence:", path.lambda_path);
360
551
  console.log("Lambda max:", path.lambda_max);
361
552
  ```
362
553
 
554
+ ### WLS Regression (WASM)
555
+
556
+ ```javascript
557
+ const result = JSON.parse(wls_regression(
558
+ JSON.stringify([2, 4, 6, 8, 10]),
559
+ JSON.stringify([[1, 2, 3, 4, 5]]),
560
+ JSON.stringify([1, 1, 1, 1, 1]) // weights (equal weights = OLS)
561
+ ));
562
+
563
+ console.log("Coefficients:", result.coefficients);
564
+ console.log("Standard errors:", result.standard_errors);
565
+ console.log("P-values:", result.p_values);
566
+ console.log("R-squared:", result.r_squared);
567
+ console.log("F-statistic:", result.f_statistic);
568
+ console.log("Confidence intervals (lower):", result.conf_int_lower);
569
+ console.log("Confidence intervals (upper):", result.conf_int_upper);
570
+ ```
571
+
363
572
  ### LOESS Regression (WASM)
364
573
 
365
574
  ```javascript
@@ -368,7 +577,7 @@ const result = JSON.parse(loess_fit(
368
577
  JSON.stringify(x[0]), // Single predictor only (flattened array)
369
578
  0.5, // span (smoothing parameter: 0-1)
370
579
  1, // degree (0=constant, 1=linear, 2=quadratic)
371
- "direct", // surface method ("direct" or "interpolate")
580
+ "direct", // surface method ("direct" only; "interpolate" is planned)
372
581
  0 // robust iterations (0=disabled, >0=number of iterations)
373
582
  ));
374
583
 
@@ -376,6 +585,64 @@ console.log("Fitted values:", result.fitted_values);
376
585
  console.log("Residuals:", result.residuals);
377
586
  ```
378
587
 
588
+ ### K-Fold Cross Validation (WASM)
589
+
590
+ ```javascript
591
+ // OLS cross-validation
592
+ const ols_cv = JSON.parse(kfold_cv_ols(
593
+ JSON.stringify(y),
594
+ JSON.stringify(x),
595
+ JSON.stringify(["Intercept", "X1", "X2"]),
596
+ 5, // n_folds
597
+ "true", // shuffle (JSON boolean)
598
+ "42" // seed (JSON string number, or "null" for no seed)
599
+ ));
600
+
601
+ console.log("OLS CV RMSE:", ols_cv.mean_rmse, "±", ols_cv.std_rmse);
602
+ console.log("OLS CV R²:", ols_cv.mean_r_squared, "±", ols_cv.std_r_squared);
603
+
604
+ // Ridge cross-validation
605
+ const ridge_cv = JSON.parse(kfold_cv_ridge(
606
+ JSON.stringify(y),
607
+ JSON.stringify(x),
608
+ 1.0, // lambda
609
+ true, // standardize
610
+ 5, // n_folds
611
+ "true", // shuffle
612
+ "42" // seed
613
+ ));
614
+
615
+ // Lasso cross-validation
616
+ const lasso_cv = JSON.parse(kfold_cv_lasso(
617
+ JSON.stringify(y),
618
+ JSON.stringify(x),
619
+ 0.1, // lambda
620
+ true, // standardize
621
+ 5, // n_folds
622
+ "true", // shuffle
623
+ "42" // seed
624
+ ));
625
+
626
+ // Elastic Net cross-validation
627
+ const enet_cv = JSON.parse(kfold_cv_elastic_net(
628
+ JSON.stringify(y),
629
+ JSON.stringify(x),
630
+ 0.1, // lambda
631
+ 0.5, // alpha (0 = Ridge, 1 = Lasso)
632
+ true, // standardize
633
+ 5, // n_folds
634
+ "true", // shuffle
635
+ "42" // seed
636
+ ));
637
+
638
+ // Access per-fold results
639
+ ols_cv.fold_results.forEach(fold => {
640
+ console.log(`Fold ${fold.fold_index}: R²=${fold.r_squared.toFixed(4)}`);
641
+ });
642
+ ```
643
+
644
+ **Note:** In WASM, boolean and seed parameters are passed as JSON strings. Use `"true"`/`"false"` for shuffle and `"42"` or `"null"` for seed.
645
+
379
646
  ### Diagnostic Tests (WASM)
380
647
 
381
648
  ```javascript
@@ -524,6 +791,43 @@ const version = get_version(); // e.g., "0.5.0"
524
791
  const msg = test(); // "Rust WASM is working!"
525
792
  ```
526
793
 
794
+ ### Model Serialization (WASM)
795
+
796
+ ```javascript
797
+ // Train a model
798
+ const resultJson = ols_regression(
799
+ JSON.stringify(y),
800
+ JSON.stringify(x),
801
+ JSON.stringify(names)
802
+ );
803
+ const result = JSON.parse(resultJson);
804
+
805
+ // Serialize with metadata
806
+ const serialized = serialize_model(
807
+ resultJson, // model JSON
808
+ "OLS", // model type: "OLS", "Ridge", "Lasso", "ElasticNet", "WLS", "LOESS"
809
+ "My Model" // optional name (null to omit)
810
+ );
811
+
812
+ // Get metadata without loading full model
813
+ const metadataJson = get_model_metadata(serialized);
814
+ const metadata = JSON.parse(metadataJson);
815
+ console.log("Model type:", metadata.model_type);
816
+ console.log("Created:", metadata.created_at);
817
+
818
+ // Deserialize to get model data back
819
+ const modelJson = deserialize_model(serialized);
820
+ const model = JSON.parse(modelJson);
821
+
822
+ // Download in browser
823
+ const blob = new Blob([serialized], { type: 'application/json' });
824
+ const url = URL.createObjectURL(blob);
825
+ const a = document.createElement('a');
826
+ a.href = url;
827
+ a.download = 'model.json';
828
+ a.click();
829
+ ```
830
+
527
831
  ### Domain Security (WASM)
528
832
 
529
833
  Optional domain restriction via build-time environment variable:
@@ -654,6 +958,20 @@ print(f"AIC: {result.aic}")
654
958
  print(f"BIC: {result.bic}")
655
959
  ```
656
960
 
961
+ ### LOESS Regression (Python)
962
+
963
+ ```python
964
+ result = linreg_core.loess_fit(
965
+ y, # Single predictor only
966
+ [0.5], # span (smoothing parameter: 0-1)
967
+ 2, # degree (0=constant, 1=linear, 2=quadratic)
968
+ "direct", # surface ("direct" only; "interpolate" is planned)
969
+ 0 # robust iterations (0=disabled, >0=number of iterations)
970
+ )
971
+ print(f"Fitted values: {result.fitted_values}")
972
+ print(f"Residuals: {result.residuals}")
973
+ ```
974
+
657
975
  ### Lambda Path Generation (Python)
658
976
 
659
977
  ```python
@@ -767,6 +1085,23 @@ print(f"Numeric columns: {result.numeric_columns}")
767
1085
  print(f"Data rows: {result.n_rows}")
768
1086
  ```
769
1087
 
1088
+ ### Model Save/Load (Python)
1089
+
1090
+ ```python
1091
+ # Train a model
1092
+ result = linreg_core.ols_regression(y, x, names)
1093
+
1094
+ # Save to file
1095
+ linreg_core.save_model(result, "my_model.json", name="My Housing Model")
1096
+
1097
+ # Load back
1098
+ loaded = linreg_core.load_model("my_model.json")
1099
+ print(f"R²: {loaded.r_squared}")
1100
+ print(f"Coefficients: {loaded.coefficients}")
1101
+ ```
1102
+
1103
+ The `save_model()` and `load_model()` functions work with all result types: `OLSResult`, `RidgeResult`, `LassoResult`, `ElasticNetResult`, `LoessResult`, and `WlsResult`.
1104
+
770
1105
  ---
771
1106
 
772
1107
  ## Feature Flags
@@ -780,7 +1115,7 @@ print(f"Data rows: {result.n_rows}")
780
1115
  For native Rust without WASM overhead:
781
1116
 
782
1117
  ```toml
783
- linreg-core = { version = "0.5", default-features = false }
1118
+ linreg-core = { version = "0.6", default-features = false }
784
1119
  ```
785
1120
 
786
1121
  For Python bindings (built with maturin):
@@ -843,6 +1178,181 @@ This library is under active development and has not reached 1.0 stability. Whil
843
1178
 
844
1179
  ---
845
1180
 
1181
+ ## Benchmarks
1182
+
1183
+ <details>
1184
+ <summary><strong>Click to expand v0.6.0 benchmark results</strong></summary>
1185
+
1186
+ Benchmark results run on Windows with `cargo bench --no-default-features`. Times are median values.
1187
+
1188
+ ### Core Regression Benchmarks
1189
+
1190
+ | Benchmark | Size (n × p) | Time | Throughput |
1191
+ |-----------|--------------|------|------------|
1192
+ | OLS Regression | 10 × 2 | 12.46 µs | 802.71 Kelem/s |
1193
+ | OLS Regression | 50 × 3 | 53.72 µs | 930.69 Kelem/s |
1194
+ | OLS Regression | 100 × 5 | 211.09 µs | 473.73 Kelem/s |
1195
+ | OLS Regression | 500 × 10 | 7.46 ms | 67.04 Kelem/s |
1196
+ | OLS Regression | 1000 × 20 | 47.81 ms | 20.91 Kelem/s |
1197
+ | OLS Regression | 5000 × 50 | 2.86 s | 1.75 Kelem/s |
1198
+ | Ridge Regression | 50 × 3 | 9.61 µs | 5.20 Melem/s |
1199
+ | Ridge Regression | 100 × 5 | 70.41 µs | 1.42 Melem/s |
1200
+ | Ridge Regression | 500 × 10 | 842.37 µs | 593.56 Kelem/s |
1201
+ | Ridge Regression | 1000 × 20 | 1.38 ms | 724.71 Kelem/s |
1202
+ | Ridge Regression | 5000 × 50 | 10.25 ms | 487.78 Kelem/s |
1203
+ | Lasso Regression | 50 × 3 | 258.82 µs | 193.18 Kelem/s |
1204
+ | Lasso Regression | 100 × 5 | 247.89 µs | 403.41 Kelem/s |
1205
+ | Lasso Regression | 500 × 10 | 3.58 ms | 139.86 Kelem/s |
1206
+ | Lasso Regression | 1000 × 20 | 1.54 ms | 651.28 Kelem/s |
1207
+ | Lasso Regression | 5000 × 50 | 12.52 ms | 399.50 Kelem/s |
1208
+ | Elastic Net Regression | 50 × 3 | 46.15 µs | 1.08 Melem/s |
1209
+ | Elastic Net Regression | 100 × 5 | 358.07 µs | 279.27 Kelem/s |
1210
+ | Elastic Net Regression | 500 × 10 | 1.61 ms | 310.18 Kelem/s |
1211
+ | Elastic Net Regression | 1000 × 20 | 1.60 ms | 623.66 Kelem/s |
1212
+ | Elastic Net Regression | 5000 × 50 | 12.57 ms | 397.77 Kelem/s |
1213
+ | WLS Regression | 50 × 3 | 32.92 µs | 1.52 Melem/s |
1214
+ | WLS Regression | 100 × 5 | 155.30 µs | 643.93 Kelem/s |
1215
+ | WLS Regression | 500 × 10 | 6.63 ms | 75.37 Kelem/s |
1216
+ | WLS Regression | 1000 × 20 | 42.68 ms | 23.43 Kelem/s |
1217
+ | WLS Regression | 5000 × 50 | 2.64 s | 1.89 Kelem/s |
1218
+ | LOESS Fit | 50 × 1 | 132.83 µs | 376.42 Kelem/s |
1219
+ | LOESS Fit | 100 × 1 | 1.16 ms | 86.00 Kelem/s |
1220
+ | LOESS Fit | 500 × 1 | 28.42 ms | 17.59 Kelem/s |
1221
+ | LOESS Fit | 1000 × 1 | 113.00 ms | 8.85 Kelem/s |
1222
+ | LOESS Fit | 100 × 2 | 7.10 ms | 14.09 Kelem/s |
1223
+ | LOESS Fit | 500 × 2 | 1.05 s | 476.19 elem/s |
1224
+
1225
+ ### Lambda Path & Elastic Net Path Benchmarks
1226
+
1227
+ | Benchmark | Size (n × p) | Time | Throughput |
1228
+ |-----------|--------------|------|------------|
1229
+ | Elastic Net Path | 100 × 5 | 198.60 ms | 503.52 elem/s |
1230
+ | Elastic Net Path | 500 × 10 | 69.46 ms | 7.20 Kelem/s |
1231
+ | Elastic Net Path | 1000 × 20 | 39.08 ms | 25.59 Kelem/s |
1232
+ | Make Lambda Path | 100 × 5 | 1.09 µs | 91.58 Melem/s |
1233
+ | Make Lambda Path | 500 × 10 | 8.10 µs | 61.70 Melem/s |
1234
+ | Make Lambda Path | 1000 × 20 | 29.96 µs | 33.37 Melem/s |
1235
+ | Make Lambda Path | 5000 × 50 | 424.18 µs | 11.79 Melem/s |
1236
+
1237
+ ### Diagnostic Test Benchmarks
1238
+
1239
+ | Benchmark | Size (n × p) | Time |
1240
+ |-----------|--------------|------|
1241
+ | Rainbow Test | 50 × 3 | 40.34 µs |
1242
+ | Rainbow Test | 100 × 5 | 187.94 µs |
1243
+ | Rainbow Test | 500 × 10 | 8.63 ms |
1244
+ | Rainbow Test | 1000 × 20 | 60.09 ms |
1245
+ | Rainbow Test | 5000 × 50 | 3.45 s |
1246
+ | Harvey-Collier Test | 50 × 1 | 15.26 µs |
1247
+ | Harvey-Collier Test | 100 × 1 | 30.32 µs |
1248
+ | Harvey-Collier Test | 500 × 1 | 138.44 µs |
1249
+ | Harvey-Collier Test | 1000 × 1 | 298.33 µs |
1250
+ | Breusch-Pagan Test | 50 × 3 | 58.07 µs |
1251
+ | Breusch-Pagan Test | 100 × 5 | 296.74 µs |
1252
+ | Breusch-Pagan Test | 500 × 10 | 13.79 ms |
1253
+ | Breusch-Pagan Test | 1000 × 20 | 96.49 ms |
1254
+ | Breusch-Pagan Test | 5000 × 50 | 5.56 s |
1255
+ | White Test | 50 × 3 | 14.31 µs |
1256
+ | White Test | 100 × 5 | 44.25 µs |
1257
+ | White Test | 500 × 10 | 669.40 µs |
1258
+ | White Test | 1000 × 20 | 4.89 ms |
1259
+ | Jarque-Bera Test | 50 × 3 | 30.13 µs |
1260
+ | Jarque-Bera Test | 100 × 5 | 149.29 µs |
1261
+ | Jarque-Bera Test | 500 × 10 | 6.64 ms |
1262
+ | Jarque-Bera Test | 1000 × 20 | 47.89 ms |
1263
+ | Jarque-Bera Test | 5000 × 50 | 2.75 s |
1264
+ | Durbin-Watson Test | 50 × 3 | 31.80 µs |
1265
+ | Durbin-Watson Test | 100 × 5 | 152.56 µs |
1266
+ | Durbin-Watson Test | 500 × 10 | 6.87 ms |
1267
+ | Durbin-Watson Test | 1000 × 20 | 48.65 ms |
1268
+ | Durbin-Watson Test | 5000 × 50 | 2.76 s |
1269
+ | Breusch-Godfrey Test | 50 × 3 | 71.73 µs |
1270
+ | Breusch-Godfrey Test | 100 × 5 | 348.94 µs |
1271
+ | Breusch-Godfrey Test | 500 × 10 | 14.77 ms |
1272
+ | Breusch-Godfrey Test | 1000 × 20 | 100.08 ms |
1273
+ | Breusch-Godfrey Test | 5000 × 50 | 5.64 s |
1274
+ | Shapiro-Wilk Test | 10 × 2 | 2.04 µs |
1275
+ | Shapiro-Wilk Test | 50 × 3 | 4.87 µs |
1276
+ | Shapiro-Wilk Test | 100 × 5 | 10.67 µs |
1277
+ | Shapiro-Wilk Test | 500 × 10 | 110.02 µs |
1278
+ | Shapiro-Wilk Test | 1000 × 20 | 635.13 µs |
1279
+ | Shapiro-Wilk Test | 5000 × 50 | 17.53 ms |
1280
+ | Anderson-Darling Test | 50 × 3 | 34.02 µs |
1281
+ | Anderson-Darling Test | 100 × 5 | 162.28 µs |
1282
+ | Anderson-Darling Test | 500 × 10 | 6.95 ms |
1283
+ | Anderson-Darling Test | 1000 × 20 | 48.15 ms |
1284
+ | Anderson-Darling Test | 5000 × 50 | 2.78 s |
1285
+ | Cook's Distance Test | 50 × 3 | 64.52 µs |
1286
+ | Cook's Distance Test | 100 × 5 | 297.69 µs |
1287
+ | Cook's Distance Test | 500 × 10 | 12.73 ms |
1288
+ | Cook's Distance Test | 1000 × 20 | 94.02 ms |
1289
+ | Cook's Distance Test | 5000 × 50 | 5.31 s |
1290
+ | DFBETAS Test | 50 × 3 | 46.34 µs |
1291
+ | DFBETAS Test | 100 × 5 | 185.52 µs |
1292
+ | DFBETAS Test | 500 × 10 | 7.04 ms |
1293
+ | DFBETAS Test | 1000 × 20 | 49.68 ms |
1294
+ | DFFITS Test | 50 × 3 | 33.56 µs |
1295
+ | DFFITS Test | 100 × 5 | 157.62 µs |
1296
+ | DFFITS Test | 500 × 10 | 6.82 ms |
1297
+ | DFFITS Test | 1000 × 20 | 48.35 ms |
1298
+ | VIF Test | 50 × 3 | 5.36 µs |
1299
+ | VIF Test | 100 × 5 | 12.68 µs |
1300
+ | VIF Test | 500 × 10 | 128.04 µs |
1301
+ | VIF Test | 1000 × 20 | 807.30 µs |
1302
+ | VIF Test | 5000 × 50 | 26.33 ms |
1303
+ | RESET Test | 50 × 3 | 77.85 µs |
1304
+ | RESET Test | 100 × 5 | 359.12 µs |
1305
+ | RESET Test | 500 × 10 | 14.40 ms |
1306
+ | RESET Test | 1000 × 20 | 100.52 ms |
1307
+ | RESET Test | 5000 × 50 | 5.67 s |
1308
+ | Full Diagnostics | 100 × 5 | 2.75 ms |
1309
+ | Full Diagnostics | 500 × 10 | 104.01 ms |
1310
+ | Full Diagnostics | 1000 × 20 | 740.52 ms |
1311
+
1312
+ ### Linear Algebra Benchmarks
1313
+
1314
+ | Benchmark | Size | Time |
1315
+ |-----------|------|------|
1316
+ | Matrix Transpose | 10 × 10 | 209.50 ns |
1317
+ | Matrix Transpose | 50 × 50 | 3.67 µs |
1318
+ | Matrix Transpose | 100 × 100 | 14.92 µs |
1319
+ | Matrix Transpose | 500 × 500 | 924.23 µs |
1320
+ | Matrix Transpose | 1000 × 1000 | 5.56 ms |
1321
+ | Matrix Multiply (matmul) | 10 × 10 × 10 | 1.54 µs |
1322
+ | Matrix Multiply (matmul) | 50 × 50 × 50 | 144.15 µs |
1323
+ | Matrix Multiply (matmul) | 100 × 100 × 100 | 1.39 ms |
1324
+ | Matrix Multiply (matmul) | 200 × 200 × 200 | 11.90 ms |
1325
+ | Matrix Multiply (matmul) | 1000 × 100 × 100 | 13.94 ms |
1326
+ | QR Decomposition | 10 × 5 | 1.41 µs |
1327
+ | QR Decomposition | 50 × 10 | 14.81 µs |
1328
+ | QR Decomposition | 100 × 20 | 57.61 µs |
1329
+ | QR Decomposition | 500 × 50 | 2.19 ms |
1330
+ | QR Decomposition | 1000 × 100 | 19.20 ms |
1331
+ | QR Decomposition | 5000 × 100 | 1.48 s |
1332
+ | QR Decomposition | 10000 × 100 | 8.09 s |
1333
+ | QR Decomposition | 1000 × 500 | 84.48 ms |
1334
+ | SVD | 10 × 5 | 150.36 µs |
1335
+ | SVD | 50 × 10 | 505.41 µs |
1336
+ | SVD | 100 × 20 | 2.80 ms |
1337
+ | SVD | 500 × 50 | 60.00 ms |
1338
+ | SVD | 1000 × 100 | 513.35 ms |
1339
+ | Matrix Invert | 5 × 5 | 877.32 ns |
1340
+ | Matrix Invert | 10 × 10 | 2.48 µs |
1341
+ | Matrix Invert | 20 × 20 | 5.46 µs |
1342
+ | Matrix Invert | 50 × 50 | 31.94 µs |
1343
+ | Matrix Invert | 100 × 100 | 141.38 µs |
1344
+ | Matrix Invert | 200 × 200 | 647.03 µs |
1345
+
1346
+ ### Pressure Benchmarks (Large Datasets)
1347
+
1348
+ | Benchmark | Size (n) | Time |
1349
+ |-----------|----------|------|
1350
+ | Pressure (OLS + all diagnostics) | 10000 | 11.28 s |
1351
+
1352
+ </details>
1353
+
1354
+ ---
1355
+
846
1356
  ## License
847
1357
 
848
1358
  Dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE).