Metadata-Version: 2.4
Name: smartGrad
Version: 0.1.1
Summary: Smart Gradient: rotated finite-difference gradients for faster gradient-based optimization
Project-URL: Homepage, https://github.com/esmail-abdulfattah/Smart-Gradient
Project-URL: Source, https://github.com/esmail-abdulfattah/Smart-Gradient
Author: Esmail Abdul Fattah
License-Expression: MIT
License-File: LICENSE
Keywords: finite-differences,gradient,optimization,quasi-newton
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.9
Requires-Dist: numpy>=1.21
Requires-Dist: scipy>=1.7
Description-Content-Type: text/markdown

# smartgrad

Smart Gradient computes finite-difference gradients in a rotated coordinate
system. The rotation is built from the directions the optimizer has recently
moved in, so the differences are taken along the directions that matter — which
in practice reaches the optimum in fewer function evaluations than plain
coordinate-wise finite differences.

This is the Python implementation. R and C++ implementations live in the
[same repository](https://github.com/esmail-abdulfattah/Smart-Gradient).

## Install

```bash
pip install smartgrad
```

## Use

Wrap your objective, then hand `fun_and_grad` to any optimizer that takes
`jac=True`:

```python
import numpy as np
from scipy.optimize import minimize
from smartgrad import SmartGradientOptimizer

def rosenbrock(x):
    return sum((1 - x[i])**2 + 100 * (x[i+1] - x[i]**2)**2
               for i in range(0, len(x), 2))

n = 4
opt = SmartGradientOptimizer(objective_fun=rosenbrock, n=n)

res = minimize(opt.fun_and_grad, np.zeros(n), method="L-BFGS-B", jac=True)
print(res.x, res.fun)
```

`SmartGradientOptimizer` takes three optional arguments:

- `fd_step` — finite-difference step size (default `1e-5`)
- `noise_stddev` — jitter added to the step directions, which keeps the
  rotation matrix from going singular when successive steps align
  (default `7e-8`)
- `seed` — seed for that jitter. Unset, each run differs slightly; set it when
  you need reproducible results:

```python
opt = SmartGradientOptimizer(objective_fun=rosenbrock, n=4, seed=42)
```

If you only want the gradient itself, call `opt.calculate_smart_grad(x)`.

## Example

`examples/rosenbrock.py` compares Smart Gradient against SciPy's built-in
finite differences on the same problem:

```bash
python examples/rosenbrock.py
```
