# `Nanodrop.Math`

Mathematical utilities for curve fitting.

Provides Levenberg-Marquardt nonlinear least squares optimization.

# `levenberg_marquardt`

```elixir
@spec levenberg_marquardt(
  [float()],
  [float()],
  (float(), [float()] -&gt; float()),
  (float(), [float()] -&gt; [float()]),
  [float()],
  keyword()
) :: {:ok, map()} | {:error, term()}
```

Levenberg-Marquardt nonlinear least squares optimization.

Fits parameters to minimize the sum of squared residuals between
the model predictions and observed data.

## Parameters

- `x` - Independent variable values (list of floats)
- `y` - Observed dependent variable values (list of floats)
- `model_fn` - Function `(x, params) -> y_predicted` where params is a list
- `jacobian_fn` - Function `(x, params) -> [dy/dp1, dy/dp2, ...]` partial derivatives
- `initial_params` - Initial parameter guesses (list of floats)
- `opts` - Options:
  - `:max_iterations` - Maximum iterations (default: 100)
  - `:tolerance` - Convergence tolerance for parameter change (default: 1.0e-8)
  - `:lambda` - Initial damping factor (default: 0.001)

## Returns

`{:ok, %{params: [...], iterations: n, final_error: e}}` on success,
`{:error, reason}` on failure.

## Example

    # Fit y = a * x + b
    model = fn x, [a, b] -> a * x + b end
    jacobian = fn x, [_a, _b] -> [x, 1.0] end

    {:ok, result} = Nanodrop.Math.levenberg_marquardt(
      x_data, y_data, model, jacobian, [1.0, 0.0]
    )

---

*Consult [api-reference.md](api-reference.md) for complete listing*
