Skip to main content

Quadratic Quasi-Newton optimizer for torch

Project description

qqn-torch

QQN (Quadratic Quasi-Newton) — a drop-in replacement for torch.optim.LBFGS that searches a quadratic path blending the steepest-descent and quasi-Newton directions.

What is QQN?

Classic quasi-Newton methods (like L-BFGS) take a single direction -H∇f and line-search along it. This works well near a minimum but can be fragile far from it, where the curvature approximation is unreliable.

QQN instead constructs a quadratic path that interpolates between the steepest-descent direction and the quasi-Newton direction:

d(t) = t(1-t)·(-∇f) + t²·(-H∇f)

with t ∈ [0, 1]. The key properties are:

t Behavior
d(0) = 0 The path starts at the current iterate.
d'(0) = -∇f The initial tangent is steepest descent.
d(1) = -H∇f The endpoint is the L-BFGS direction.

Because the path starts tangent to -∇f, the beginning of the path always decreases f (when ∇f ≠ 0). This anchors global convergence, while the t = 1 endpoint recovers L-BFGS superlinear behavior near the optimum. The line search walks t directly and discovers the right blend — no manual tuning of a mixing coefficient.

Installation

pip install qqn-torch

Or from source:

git clone https://github.com/your-org/qqn-torch
cd qqn-torch
pip install -e .

Requires PyTorch.

Quick Start

QQN follows the same closure-based API as torch.optim.LBFGS:

import torch
from qqn_torch import QQN

# A simple quadratic objective.
x = torch.tensor([1.5, -2.0], requires_grad=True)

optimizer = QQN([x], max_iter=20)


def closure():
    optimizer.zero_grad()
    loss = (x[0] - 3.0) ** 2 + (x[1] + 1.0) ** 2
    loss.backward()
    return loss


for _ in range(10):
    loss = optimizer.step(closure)
    print(f"loss = {loss:.6e}, x = {x.detach().tolist()}")

The closure must:

  1. Clear gradients (zero_grad()),
  2. Compute the loss,
  3. Call loss.backward(),
  4. Return the loss.

This is identical to the torch.optim.LBFGS contract, so existing LBFGS training loops work unchanged.

Configuration

QQN(
    params,
    history_size=10,  # L-BFGS curvature-pair history
    line_search="armijo",  # "armijo" | "backtracking" | "strong_wolfe" | "fixed"
    oracle="lbfgs",  # "lbfgs" | "momentum" | "secant" | Oracle instance
    region=None,  # None | "box" | "trust" | Region instance
    max_iter=20,  # inner iterations per .step()
    tol_grad=1e-7,  # gradient-norm stopping tolerance
    tol_change=1e-9,  # step/objective change tolerance
    line_search_options=None  # dict forwarded to the line search (c1, c2, ...)
)

Four orthogonal, swappable components

QQN is built as a combiner of four independent pieces. Each can be changed without touching the rest.

1. Oracle — the t = 1 endpoint (-H∇f)

Name Description
lbfgs (default) Two-loop recursion over curvature pairs.
momentum Heavy-ball direction -(β·v + (1-β)·∇f).
secant Barzilai–Borwein scalar step (O(n) memory).

Because the steepest-descent contribution anchors convergence, the oracle is free to be aggressive — it need not guarantee descent on its own.

2. Line search — walks the path and picks t

Name Conditions Notes
armijo Armijo sufficient decrease (default) Backtracking.
backtracking Armijo sufficient decrease Aggressive contraction from t=1.
strong_wolfe Armijo + strong curvature Can over-restrict the path step.
fixed None Debug/baseline; constant t.

The line search is not an implementation detail — it is the glue that makes the gradient and oracle work together. Convergence quality is bounded by line-search quality.

3. Region — optional projection of candidate points

Name Description
None (default) Identity — zero overhead.
box Elementwise clip to [lo, hi].
trust Trust-region sphere with adaptive radius.

When a region is active, the line search navigates the projected path d_R(t) = project_R(x, x + d(t)) - x, so descent guarantees hold on the feasible set. Custom regions can be composed with SequentialRegion.

4. Gradient

The raw -∇f signal, the path's tangent at the origin.

Custom components

You can pass instances instead of string shortcuts for full control:

from qqn_torch import QQN
from qqn_torch.regions import TrustRegion
from qqn_torch.oracles import SecantOracle

optimizer = QQN(
    params,
    oracle=SecantOracle(alpha_init=0.5),
    region=TrustRegion(radius=2.0, max_radius=1e3),
    line_search="strong_wolfe",
    line_search_options={"c1": 1e-4, "c2": 0.9},
)

How it works (per inner iteration)

g       = flat_grad(closure)         # autograd on the closure
qn_dir  = oracle.direction(g, state) # the t=1 endpoint, -H g
grad_dir = -g                        # the path tangent at t=0
d(t)    = t(1-t)·grad_dir + t²·qn_dir # quadratic path
t*      = line_search(...)           # picks the blend AND the step
x      += project(d(t*))             # apply (optionally projected) step
oracle.update(s, y)                  # s = Δx, y = Δg
region.update(...)                   # e.g. adapt trust radius

Advantages

  • Adaptive: automatically balances conservative vs. aggressive steps.
  • Robust: d'(0) = -∇f plus line-search fallbacks ensure progress even when the oracle is poor.
  • Efficient: L-BFGS acceleration when curvature is reliable.
  • Modular: gradient, oracle, search, and region are independently swappable.

Limitations

  • Memory: stores L-BFGS history (O(m·n)).
  • Overhead: walking the curved path adds modest per-iteration cost.
  • Tuning: sensitive to history size, line-search constants, region radii.
  • Line-search sensitivity: a poor line search undermines convergence and the quality of the curvature updates.

Theoretical guarantees

Under standard assumptions (smooth objective, bounded gradients):

  • Global convergence — anchored by the steepest-descent tangent.
  • Superlinear convergence — inherited from L-BFGS when t → 1 near the optimum.
  • Descent property — every accepted step decreases f, enforced by the line search's sufficient-decrease test.

All guarantees are contingent on the line search satisfying sufficient decrease. When a region is active, they hold on the projected path d_R(t).

Documentation

Acknowledgements

The strong-Wolfe line search is adapted from PyTorch's _strong_wolfe helper (torch/optim/lbfgs.py, BSD-licensed). See qqn_torch/_vendor/strong_wolfe.py for attribution.

License

See the LICENSE file. Vendored code retains its original PyTorch BSD license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qqn_torch-0.1.0.tar.gz (63.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qqn_torch-0.1.0-py3-none-any.whl (27.3 kB view details)

Uploaded Python 3

File details

Details for the file qqn_torch-0.1.0.tar.gz.

File metadata

  • Download URL: qqn_torch-0.1.0.tar.gz
  • Upload date:
  • Size: 63.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qqn_torch-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c2c4e4feb49c975c89f4b96fb6279eb7bed491d18168a270f77ba23834f3a3fe
MD5 f1e84b6cd876c43b4c247f9bd5c9eaf4
BLAKE2b-256 7afeef3f080d9bcbc728068d4f48ee0b4540860104989fb3269b34b224c11736

See more details on using hashes here.

Provenance

The following attestation bundles were made for qqn_torch-0.1.0.tar.gz:

Publisher: python-publish.yml on SimiaCryptus/qqn-torch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qqn_torch-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: qqn_torch-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 27.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qqn_torch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ef0e81e3a62ba63dfd684b8d85fdae73a634ea6147557145c70e18d2718c8f1c
MD5 0884a184c3bb223df8b516f41baa8458
BLAKE2b-256 037c3f6ef424b0082e6f24333a0d6c245acd6870d867842a2e966aa60f0cfca9

See more details on using hashes here.

Provenance

The following attestation bundles were made for qqn_torch-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on SimiaCryptus/qqn-torch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page