Skip to Content

ROOT

Overview

The ROOT function solves square systems of nonlinear equations by calling scipy.optimize.root. Given nn equations in nn unknowns, the solver iteratively searches for a vector xx such that F(x)=0F(x) = 0, where FF collects the residuals of each equation. This is useful for fixed-point problems, chemical equilibria, and engineering design conditions. This example function is provided as-is without any representation of accuracy.

Usage

To evaluate the function in Excel:

=ROOT(equations, variables)
  • equations (2D list of string, required): Each entry is an equation expressed in terms of variables such as x and y that should evaluate to zero at the solution.
  • variables (2D list of float, required): Initial guess for each variable; must provide the same number of values as there are equations.

The function returns a single-row 2D list containing the solution vector, or a string error message if validation fails or the solver does not converge.

Examples

Example 1: Unit Circle Intersection

Inputs:

equationsvariables
x2 + y2 - 10.50.5
x - y

Excel formula:

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

Expected output:

xy
0.7070.707

Example 2: Coupled Cubic System

Inputs:

equationsvariables
x**3 + y - 10.70.7
x + y**3 - 1

Excel formula:

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

Expected output:

xy
0.6830.683

Example 3: Alternate Initial Guess on the Unit Circle

Inputs:

equationsvariables
x2 + y2 - 1-0.5-0.5
x - y

Excel formula:

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

Expected output:

xy
-0.707-0.707

Example 4: Cubic System with Different Start

Inputs:

equationsvariables
x**3 + y - 10.20.2
x + y**3 - 1

Excel formula:

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

Expected output:

xy
0.6820.682

Python Code

import math from typing import List, Union import numpy as np from scipy.optimize import root as scipy_root Number = Union[int, float] def root(equations: List[List[str]], variables: List[List[Number]]) -> Union[List[List[float]], str]: """Solve a square nonlinear system using SciPy's ``root`` solver. Args: equations: 2D list of equations written in terms of variable names such as ``x`` and ``y``. Each entry must be a string expression that evaluates to zero at the solution. variables: 2D list providing the initial guess for each variable. The number of variables must match the number of equations. Returns: Single-row 2D list containing the solution vector, or an error message string if validation fails or the solver does not converge. This example function is provided as-is without any representation of accuracy. """ 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: List[str] = [] 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 def normalize_to_2d_list(value): if not isinstance(value, list): return [[value]] return value variables = normalize_to_2d_list(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})." ) # Generate readable variable names (x, y, z, x3, x4, ...) base_names = ["x", "y", "z"] variable_names: List[str] = [] for index in range(equation_count): if index < len(base_names): variable_names.append(base_names[index]) else: variable_names.append(f"x{index}") safe_globals = { name: getattr(math, name) for name in dir(math) if not name.startswith("_") } safe_globals.update({"np": np, "numpy": np}) def _system(vector: np.ndarray) -> np.ndarray: local_context = {name: vector[i] for i, name in enumerate(variable_names)} residuals: List[float] = [] for expression in flattened_equations: try: value = eval(expression, safe_globals, local_context) except Exception: return np.full(equation_count, np.nan, dtype=float) try: numeric_value = float(value) except (TypeError, ValueError): return np.full(equation_count, np.nan, dtype=float) if not math.isfinite(numeric_value): return np.full(equation_count, np.nan, dtype=float) 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]

Example Workbook

Link to Workbook 

Last updated on