linear-ransac 0.1.0

A simple 2D line-fitting RANSAC implementation inspired by scikit-learn's RANSACRegressor.
Documentation
# linear-ransac 🚀

`linear-ransac` is a Rust implementation of RANSAC (RANdom SAmple Consensus) for fitting a 1D linear model (a line in 2D: `y = ax + b`), inspired by scikit‑learn’s `RANSACRegressor`.

It’s built for noisy data with nasty outliers, with automatic thresholding and iteration control so you don’t have to hand‑tune.

## Highlights

- **Robust line fitting** – estimate `slope` and `intercept` from 2D points even when a big chunk of them are garbage.
- **Auto threshold via MAD** – inlier cutoff is computed from the median absolute deviation of `y`, similar in spirit to scikit‑learn’s default.
- **Auto iteration stopping** – adapts the max number of RANSAC trials from the current inlier ratio and a `stop_probability`, while enforcing a configurable minimum so one lucky bad model can’t stop the search early.
- **Final OLS refit** – once a consensus set is found, refit the line with ordinary least squares on inliers.

Today it targets 1D linear regression; the design is ready to grow to N‑dimensional linear models later on.

## Install

```toml
[dependencies]
linear-ransac = "0.1"
```

## Quick start

```rust
use linear_ransac::{Point, RansacSolver};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = vec![
        Point::new(0.0, 0.0),
        Point::new(1.0, 1.0),
        Point::new(2.0, 2.0),
        Point::new(3.0, 3.0),
    ];

    let model = RansacSolver::new().fit(&data)?;

    println!("slope = {} intercept = {}", model.slope, model.intercept);
    Ok(())
}
```

With outliers:

```rust
use linear_ransac::{Point, RansacSolver};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut data = vec![
        Point::new(0.0, 0.0),
        Point::new(1.0, 1.0),
        Point::new(2.0, 2.0),
        Point::new(3.0, 3.0),
        Point::new(4.0, 4.0),
    ];

    // obvious outliers
    data.push(Point::new(1.5, 100.0));
    data.push(Point::new(2.5, -50.0));

    let model = RansacSolver::new().fit(&data)?;

    println!("slope = {} intercept = {}", model.slope, model.intercept);
    Ok(())
}
```

## Tuning

- `RansacSolver::new()` – defaults tuned for typical use.
- `with_seed(u64)` – deterministic runs.
- `with_min_trials(usize)` – guarantee at least this many iterations.

Internally the solver:

- derives an inlier threshold from a plain MAD of `y` values,
- adapts the iteration budget from the inlier ratio and `stop_probability`,
- and refits the final line with OLS on the inliers.

## Errors

`RansacSolver::fit` returns a `Result<LinearModel, RansacError>`:

- `InsufficientData` – not enough points to define a line,
- `ModelFittingFailed` – degenerate/numerically unstable data,
- `NoConsensusFound` – no good consensus set within the trial budget.

## License

MIT. See [`LICENSE`](LICENSE).