ROOT

Overview

The ROOT function finds solutions to systems of nonlinear equations using SciPy’s scipy.optimize.root solver. Given a set of equations and initial guess values, it determines the variable values where all equations equal zero simultaneously—a fundamental problem in numerical analysis, engineering, and scientific computing.

Root-finding for systems of equations extends the concept of finding zeros of a single function to multiple dimensions. For a system of n equations with n unknowns, the solver seeks a vector \mathbf{x} such that:

\mathbf{F}(\mathbf{x}) = \begin{pmatrix} f_1(x_1, x_2, \ldots, x_n) \\ f_2(x_1, x_2, \ldots, x_n) \\ \vdots \\ f_n(x_1, x_2, \ldots, x_n) \end{pmatrix} = \mathbf{0}

The implementation uses the hybrid Powell method (hybr) by default, which combines the robustness of the steepest descent method with the fast convergence of Newton’s method near the solution. This algorithm, originally implemented in MINPACK, iteratively refines the initial guess using a modified trust-region approach. For more details, see the SciPy optimization documentation.

This function accepts equations as string expressions using standard mathematical notation, including support for common functions like sin, cos, exp, log, and sqrt. The ^ operator is automatically converted to Python’s exponentiation syntax. Variables are named sequentially as x, y, z, x3, x4, and so on, corresponding to the order of initial guess values provided.

Applications include solving equilibrium conditions in chemical systems, intersection of curves and surfaces in computational geometry, steady-state analysis in control systems, and calibration problems in financial modeling.

This example function is provided as-is without any representation of accuracy.

Excel Usage

=ROOT(equations, variables)
  • equations (list[list], required): 2D list of equation strings expressed in terms of variables (x, y, z, etc.) that should evaluate to zero at the solution.
  • variables (list[list], required): 2D list providing initial guess values for each variable. The number of values must match the number of equations.

Returns (list[list]): 2D list [[x1, x2, …]], or error message string.

Examples

Example 1: Circle and line intersection

Inputs:

equations variables
x^2 + y^2 - 1 0.5 0.5
x - y

Excel formula:

=ROOT({"x^2 + y^2 - 1";"x - y"}, {0.5,0.5})

Expected output:

Result
0.70710678 0.70710678

Example 2: Coupled cubic system

Inputs:

equations variables
x^3 + y - 1 0.7 0.7
x + y^3 - 1

Excel formula:

=ROOT({"x^3 + y - 1";"x + y^3 - 1"}, {0.7,0.7})

Expected output:

Result
0.68260619 0.68260619

Example 3: Circle and line negative solution

Inputs:

equations variables
x^2 + y^2 - 1 -0.5 -0.5
x - y

Excel formula:

=ROOT({"x^2 + y^2 - 1";"x - y"}, {-0.5,-0.5})

Expected output:

Result
-0.70710678 -0.70710678

Example 4: Coupled cubic with different initial guess

Inputs:

equations variables
x^3 + y - 1 0.2 0.2
x + y^3 - 1

Excel formula:

=ROOT({"x^3 + y - 1";"x + y^3 - 1"}, {0.2,0.2})

Expected output:

Result
0.68260619 0.68260619

Python Code

import math
import re

import numpy as np
from scipy.optimize import root as scipy_root

def root(equations, variables):
    """
    Solve a square nonlinear system using SciPy's ``root`` solver.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.root.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        equations (list[list]): 2D list of equation strings expressed in terms of variables (x, y, z, etc.) that should evaluate to zero at the solution.
        variables (list[list]): 2D list providing initial guess values for each variable. The number of values must match the number of equations.

    Returns:
        list[list]: 2D list [[x1, x2, ...]], or error message string.
    """
    def to2d(x):
        return [[x]] if not isinstance(x, list) else x

    equations = to2d(equations)

    if not isinstance(equations, list) or len(equations) == 0:
        return "Invalid input: equations must be a 2D list of strings."

    # Flatten nested equation lists while retaining only string entries.
    flattened_equations = []
    for row in equations:
        if not isinstance(row, list) or len(row) == 0:
            return "Invalid input: equations must be a 2D list of strings."
        for item in row:
            if isinstance(item, str) and item.strip() != "":
                flattened_equations.append(item)
    if len(flattened_equations) == 0:
        return "Invalid input: equations must contain at least one string expression."

    # Normalize variables which may be passed as a scalar by the Excel add-in
    variables = to2d(variables)
    if not isinstance(variables, list) or len(variables) == 0 or not isinstance(variables[0], list):
        return "Invalid input: variables must be a 2D list of numeric values."

    initial_guess = variables[0]
    if len(initial_guess) == 0:
        return "Invalid input: variables must contain at least one initial guess value."

    try:
        x0 = np.asarray([float(value) for value in initial_guess], dtype=float)
    except (TypeError, ValueError):
        return "Invalid input: variables must contain numeric values."

    equation_count = len(flattened_equations)
    if x0.size != equation_count:
        return (
            f"Invalid input: number of variables ({x0.size}) must match number of equations ({equation_count})."
        )

    # Convert caret to exponentiation once before any processing
    flattened_equations = [re.sub(r'\^', '**', eq) for eq in flattened_equations]

    # Generate readable variable names (x, y, z, x3, x4, ...)
    base_names = ["x", "y", "z"]
    variable_names = []
    for index in range(equation_count):
        if index < len(base_names):
            variable_names.append(base_names[index])
        else:
            variable_names.append(f"x{index}")

    # Complete safe_globals dictionary with all math functions and aliases
    safe_globals = {
        "math": math,
        "np": np,
        "numpy": np,
        "__builtins__": {},
    }
    safe_globals.update({
        name: getattr(math, name)
        for name in dir(math)
        if not name.startswith("_")
    })
    safe_globals.update({
        "sin": np.sin,
        "cos": np.cos,
        "tan": np.tan,
        "asin": np.arcsin,
        "arcsin": np.arcsin,
        "acos": np.arccos,
        "arccos": np.arccos,
        "atan": np.arctan,
        "arctan": np.arctan,
        "sinh": np.sinh,
        "cosh": np.cosh,
        "tanh": np.tanh,
        "exp": np.exp,
        "log": np.log,
        "ln": np.log,
        "log10": np.log10,
        "sqrt": np.sqrt,
        "abs": np.abs,
        "pow": np.power,
        "pi": math.pi,
        "e": math.e,
        "inf": math.inf,
        "nan": math.nan,
    })

    def _system(vector):
        local_context = {name: vector[i] for i, name in enumerate(variable_names)}
        residuals = []
        for expression in flattened_equations:
            try:
                value = eval(expression, safe_globals, local_context)
            except Exception as exc:
                raise ValueError(f"Error evaluating equation: {exc}")
            try:
                numeric_value = float(value)
            except (TypeError, ValueError) as exc:
                raise ValueError(f"Equation did not return a numeric value: {exc}")
            if not math.isfinite(numeric_value):
                raise ValueError("Equation evaluation produced a non-finite value.")
            residuals.append(numeric_value)
        return np.asarray(residuals, dtype=float)

    try:
        result = scipy_root(_system, x0=x0)
    except ValueError as exc:
        return f"root error: {exc}"
    except Exception as exc:
        return f"root error: {exc}"

    if not result.success or result.x is None:
        message = result.message if hasattr(result, "message") else "Solver did not converge."
        return f"root failed: {message}"

    if not np.all(np.isfinite(result.x)):
        return "root failed: solution contains non-finite values."

    solution = [float(value) for value in result.x]
    return [solution]

Online Calculator