Skip to main content

Jacobian-Guided Subspace Search (JGSS)

Jacobian Guided Subspace Search — Nonlinear solver for macroeconomic and DSGE models.

CI PyPI version Python versions License

Installation

pip install jgss

Quick Start

import numpy as np
from jgss import solve

# Define a simple system: x^2 = target
def residual(x):
    target = np.array([1.0, 4.0, 9.0])
    return x**2 - target

# Solve from initial guess
result = solve(residual, x0=np.array([0.5, 1.5, 2.5]))

print(result.x)        # [1.0, 2.0, 3.0]
print(result.success)  # True

Usage Examples

Basic Usage

import numpy as np
from jgss import solve

def system(x):
    """System of nonlinear equations."""
    return np.array([
        x[0]**2 + x[1]**2 - 4,    # Circle: x^2 + y^2 = 4
        x[0] - x[1]               # Line: x = y
    ])

result = solve(system, x0=np.array([1.0, 1.0]))

if result.success:
    print(f"Solution: {result.x}")
    print(f"Residual norm: {result.residual_norm:.2e}")

Configuration Options

import numpy as np
from jgss import solve

def large_system(x):
    """High-dimensional system."""
    return x**2 - np.arange(1, len(x) + 1, dtype=float)

result = solve(
    large_system,
    x0=np.ones(100),
    tol=1e-10,           # Tighter tolerance
    k_subspace=50,       # Larger subspace for high-dim problems
    verbose=1,           # Show iteration progress
    seed=42,             # Reproducible results
)

print(f"Converged: {result.success}")
print(f"Function evaluations: {result.nfev}")

Advanced: Analytical Jacobian

import numpy as np
from jgss import solve

def residual(x):
    return np.array([
        x[0]**2 - x[1] - 1,
        x[0] - x[1]**2 + 1,
    ])

def jacobian(x):
    """Analytical Jacobian matrix."""
    return np.array([
        [2*x[0], -1],
        [1, -2*x[1]],
    ])

result = solve(
    residual,
    x0=np.array([1.0, 1.0]),
    jacobian_fn=jacobian,  # Faster than finite differences
)

print(f"Solution: {result.x}")
print(f"Jacobian evaluations: {result.njev}")

Multi-Restart for DSGE Models

import numpy as np
from jgss import solve

def dsge_residual(x):
    """DSGE-like system with multiple scales."""
    return np.array([
        0.01 * x[0] - 0.03,      # Interest rate (~0.03)
        1000 * x[1] - 5000,       # GDP (~5.0)
        x[2]**2 - x[0] * x[1],   # Nonlinear coupling
    ])

result = solve(
    dsge_residual,
    x0=np.array([0.01, 4.0, 0.1]),
    n_restarts=5,                    # 5 independent LHS draws
    clip_bounds=(-10.0, 30.0),       # Keep variables in range
    seed=42,                         # Reproducible
    verbose=0,
)

print(f"Converged: {result.success}")
print(f"Solution: {result.x}")
print(f"Restarts used: see nfev={result.nfev}")

Advanced: Bounds and Callbacks

import numpy as np
from jgss import solve

def system(x):
    return x**3 - np.array([8.0, 27.0])

def monitor(x, f):
    """Callback for progress monitoring."""
    print(f"  x = {x}, ||f|| = {np.linalg.norm(f):.4e}")

result = solve(
    system,
    x0=np.array([1.0, 2.0]),
    bounds=(np.array([0.0, 0.0]), np.array([10.0, 10.0])),  # Constrain to [0, 10]
    callback=monitor,
    history=True,         # Track solution iterates
    return_jacobian=True, # Include final Jacobian
    verbose=-1,           # Suppress default output
)

print(f"Solution: {result.x}")
print(f"Iterations tracked: {len(result.history)}")
print(f"Final Jacobian shape: {result.jac.shape}")

API Reference

solve()

Main solver function for nonlinear systems.

from jgss import solve

result = solve(residual_fn, x0, **options)

Parameters:

Parameter Type Default Description
residual_fn Callable required Function f(x) -> residuals returning array of shape (m,)
x0 ArrayLike required Initial guess, array-like of shape (n,)
jacobian_fn Callable None Analytical Jacobian J(x) -> (m, n) matrix. If None, uses finite differences
tol float 1e-8 Convergence tolerance for residual norm
maxiter int 500 Maximum iterations for local convergence phase
k_subspace int 30 Dimension of active subspace for global search
n_samples int 600 Number of Latin Hypercube samples for basin finding
bounds tuple None Optional (lower, upper) bounds, each of shape (n,)
n_restarts int 5 Number of independent restart attempts with different LHS seeds
clip_bounds tuple None Optional (lower, upper) for soft variable clamping (applied via np.clip)
seed int None Random seed for reproducibility. Restart i uses seed+i
verbose int 0 Verbosity: -1=silent, 0=summary, 1=iterations, 2=debug
callback Callable None Function callback(x, f) called at each evaluation
history bool False If True, include solution iterates in result
return_jacobian bool False If True, include final Jacobian in result

Returns: SolverResult

SolverConfig

Documents available hyperparameters (for reference only - solve() takes keyword arguments directly).

from jgss import SolverConfig

# View default configuration
config = SolverConfig()
print(config.tol)        # 1e-8
print(config.k_subspace) # 30

Fields:

Field Type Default Description
tol float 1e-8 Convergence tolerance for residual norm
maxiter int 500 Maximum solver iterations
k_subspace int 30 Dimension of Jacobian Guided subspace
n_samples int 600 Number of Latin Hypercube samples
n_restarts int 5 Number of independent restart attempts
clip_bounds tuple None Soft variable clamping bounds
seed int None Random seed for reproducibility
verbose int 0 Verbosity level

SolverResult

Immutable result container with both attribute and dict-style access.

# Attribute access
print(result.x)
print(result.success)

# Dict-style access
print(result['x'])
print('success' in result)  # True

Fields:

Field Type Description
x NDArray[float64] Solution vector, shape (n,)
success bool True if converged within tolerance
message str Human-readable status message
nfev int Number of function evaluations
njev int Number of Jacobian evaluations
fun NDArray[float64] Residual vector at solution, shape (m,)
residual_norm float L2 norm of residual at solution
optimality float First-order optimality (gradient norm)
history list Solution iterates (if history=True)
jac NDArray[float64] Jacobian at solution (if return_jacobian=True)

How It Works

JGSS (Jacobian Guided Subspace Search) is a hybrid global-local optimization algorithm designed for high-dimensional nonlinear systems common in macroeconomic modeling.

The algorithm operates in three phases, repeated across multiple restarts:

  1. Jacobian Analysis - Computes the Jacobian at the initial guess and uses SVD to identify an active subspace where the system is most sensitive to changes
  2. Basin Finding - Uses Latin Hypercube Sampling within the active subspace to find a starting point in a promising convergence basin
  3. Local Convergence - Applies Levenberg-Marquardt to refine the solution to machine precision

By default, JGSS runs 5 independent restarts with different random seeds for the LHS basin search. Each restart explores a different region of the subspace, and the best result (lowest residual norm) is kept. If no restart converges, a scaled LM polish normalizes variables by their characteristic magnitudes and runs a final LM pass in scaled space — this dramatically improves conditioning for multi-scale DSGE systems.

This approach is particularly effective for DSGE and macroeconomic models where the solution space is high-dimensional but the active dynamics lie in a lower-dimensional manifold.

License

MIT

Metadata

Release files for jgss 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jgss 1.1.0
File Size Uploaded Provenance Actions
jgss-1.1.0.tar.gz 21.7 kB With provenance Details for jgss-1.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for jgss 1.1.0
File Interpreter ABI Platform Actions
jgss-1.1.0-py3-none-any.whl Python 3 none any Details for jgss-1.1.0-py3-none-any.whl

Total release size: 40.2 kB

Release files / jgss-1.1.0.tar.gz

Download URL jgss-1.1.0.tar.gz
Size 21.7 kB
Tags Source
SHA-256 checksum
How to use checksums
069ea049886c53a62bc75f19a8c83c90202604e4192e5e0b3639a2a1ec798b09
BLAKE2b-256 checksum
How to use checksums
260b2253ca7040cf2a0c7aeb8490c31d4deb851b07679237f91357ff3163362b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 23, 2026.

Transparency log

Release files / jgss-1.1.0-py3-none-any.whl

Download URL jgss-1.1.0-py3-none-any.whl
Size 18.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c3358f4e5d3a63be7348bf3518c88bb17fee8750b08985cde9ed56ac5da091d5
BLAKE2b-256 checksum
How to use checksums
3daab0cba73f00e05c8bbfa76a493cafc928f6e28846180d76cd9f28420e9a76
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 23, 2026.

Transparency log

Security for jgss 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for jgss-1.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page