NEQNF Example — Nonlinear System Solver

This example solves a 2-variable nonlinear system using a finite-difference Jacobian (IMSL NEQNF):

\[ \begin{align}\begin{aligned}x_1^2 + x_2^2 = 1 \quad \text{(unit circle)}\\x_1 - x_2 = 0 \quad \text{(diagonal line)}\end{aligned}\end{align} \]

The intersection gives two solutions: \((1/\sqrt{2},\, 1/\sqrt{2})\) and \((-1/\sqrt{2},\, -1/\sqrt{2})\). Starting from \((0.8, 0.6)\), the solver converges to the first solution.

Example Code

"""IMSL NEQNF example: solve a 2-variable nonlinear system using FD Jacobian.

Solves the system:
    x1^2 + x2^2 = 1
    x1 - x2 = 0

Solutions: (1/√2, 1/√2) ≈ (0.7071, 0.7071) and (-1/√2, -1/√2)

Outputs:
- Table printed to stdout
- SVG plot of the system curves with solution marked
- Saved to test_output/demo_imsl_neqnf.svg
"""

from __future__ import annotations

import math
from pathlib import Path
from typing import Dict

import matplotlib.pyplot as plt
import numpy as np

from nonlinearequations import solve_system_fd


def run_demo_imsl_neqnf() -> Dict[str, object]:
    """Run IMSL NEQNF example: 2-variable nonlinear system.

    Args:
        None

    Returns:
        Dict[str, object]: Result dict with keys ``solution`` (np.ndarray),
            ``fval`` (float), ``n_fev`` (int), and ``plot_path`` (str).
    """
    def f(x: np.ndarray) -> np.ndarray:
        return np.array([
            x[0]**2 + x[1]**2 - 1.0,  # unit circle
            x[0] - x[1],               # diagonal line
        ])

    x0 = np.array([0.8, 0.6])
    result = solve_system_fd(f, x0)
    sol = result.x
    residual = f(sol)
    expected = np.array([1 / math.sqrt(2), 1 / math.sqrt(2)])

    print("\nIMSL NEQNF Example: 2-Variable Nonlinear System")
    print("  System:  x1^2 + x2^2 = 1")
    print("           x1 - x2 = 0")
    print("-" * 55)
    print(f"{'Parameter':<25} {'Value':>20}")
    print("-" * 55)
    print(f"{'Starting point x0[0]':<25} {x0[0]:>20.6f}")
    print(f"{'Starting point x0[1]':<25} {x0[1]:>20.6f}")
    print(f"{'Solution x1':<25} {sol[0]:>20.10f}")
    print(f"{'Solution x2':<25} {sol[1]:>20.10f}")
    print(f"{'Expected (1/√2)':<25} {expected[0]:>20.10f}")
    print(f"{'Residual f1(x)':<25} {residual[0]:>20.2e}")
    print(f"{'Residual f2(x)':<25} {residual[1]:>20.2e}")
    print(f"{'|f(x)| (2-norm)':<25} {result.fval:>20.2e}")
    print(f"{'Function evals':<25} {result.n_fev:>20}")
    print(f"{'Converged':<25} {str(result.success):>20}")
    print("-" * 55)

    output_dir = Path("test_output")
    output_dir.mkdir(parents=True, exist_ok=True)
    plot_path = output_dir / "demo_imsl_neqnf.svg"

    theta = np.linspace(0, 2 * np.pi, 400)
    circle_x = np.cos(theta)
    circle_y = np.sin(theta)
    diag_x = np.linspace(-1.2, 1.2, 200)
    diag_y = diag_x

    fig, ax = plt.subplots(figsize=(7, 6))
    ax.plot(circle_x, circle_y, color="#0e7490", linewidth=2.0, label=r"$x_1^2 + x_2^2 = 1$")
    ax.plot(diag_x, diag_y, color="#0891b2", linewidth=2.0, linestyle="--", label=r"$x_1 = x_2$")
    ax.scatter([sol[0], -sol[0]], [sol[1], -sol[1]], color="#d62728", s=100, zorder=5, label="Solutions")
    ax.annotate(
        f"({sol[0]:.4f}, {sol[1]:.4f})",
        xy=(sol[0], sol[1]),
        xytext=(sol[0] + 0.05, sol[1] - 0.15),
        fontsize=9, color="#d62728"
    )
    ax.annotate(
        f"({-sol[0]:.4f}, {-sol[1]:.4f})",
        xy=(-sol[0], -sol[1]),
        xytext=(-sol[0] - 0.35, -sol[1] + 0.1),
        fontsize=9, color="#d62728"
    )
    ax.set_aspect("equal")
    ax.set_xlim(-1.5, 1.5)
    ax.set_ylim(-1.5, 1.5)
    ax.axhline(0, color="#aaaaaa", linewidth=0.7)
    ax.axvline(0, color="#aaaaaa", linewidth=0.7)
    ax.set_xlabel(r"$x_1$")
    ax.set_ylabel(r"$x_2$")
    ax.set_title("IMSL NEQNF: Nonlinear System Solve (FD Jacobian)")
    ax.legend()
    ax.grid(True, alpha=0.25)
    fig.tight_layout()
    fig.savefig(plot_path, format="svg")
    plt.close(fig)

    return {"solution": sol, "fval": result.fval, "n_fev": result.n_fev, "plot_path": str(plot_path)}


if __name__ == "__main__":
    run_demo_imsl_neqnf()

Plot Output

Unit circle and diagonal line intersection showing the two nonlinear system solutions

The unit circle \(x_1^2 + x_2^2 = 1\) and the line \(x_1 = x_2\) intersect at the two solution points (red dots) identified by the solver.

Console Output

IMSL NEQNF Example: 2-Variable Nonlinear System
  System:  x1^2 + x2^2 = 1
           x1 - x2 = 0
-------------------------------------------------------
Parameter                                Value
-------------------------------------------------------
Starting point x0[0]                  0.800000
Starting point x0[1]                  0.600000
Solution x1                       0.7071067812
Solution x2                       0.7071067812
Expected (1/√2)                   0.7071067812
Residual f1(x)                        2.22e-16
Residual f2(x)                        0.00e+00
|f(x)| (2-norm)                       2.22e-16
Function evals                              10
Converged                                 True
-------------------------------------------------------