Skip to main content

Nonlinear solver for macroeconomic and DSGE models

Project description

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

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

jgss-1.1.0.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

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

jgss-1.1.0-py3-none-any.whl (18.5 kB view details)

Uploaded Python 3

File details

Details for the file jgss-1.1.0.tar.gz.

File metadata

  • Download URL: jgss-1.1.0.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for jgss-1.1.0.tar.gz
Algorithm Hash digest
SHA256 069ea049886c53a62bc75f19a8c83c90202604e4192e5e0b3639a2a1ec798b09
MD5 ce13c5b613896e551be9cf609f244b7b
BLAKE2b-256 260b2253ca7040cf2a0c7aeb8490c31d4deb851b07679237f91357ff3163362b

See more details on using hashes here.

Provenance

The following attestation bundles were made for jgss-1.1.0.tar.gz:

Publisher: release.yml on IRIS-Solutions-Team/jgss

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

File details

Details for the file jgss-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: jgss-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for jgss-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c3358f4e5d3a63be7348bf3518c88bb17fee8750b08985cde9ed56ac5da091d5
MD5 ccb329cd578186f09240673e5f231ae4
BLAKE2b-256 3daab0cba73f00e05c8bbfa76a493cafc928f6e28846180d76cd9f28420e9a76

See more details on using hashes here.

Provenance

The following attestation bundles were made for jgss-1.1.0-py3-none-any.whl:

Publisher: release.yml on IRIS-Solutions-Team/jgss

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