简体中文 | English
HilbertBalance computes all indecomposable nonnegative-integer balances of a single- or multi-stage chemical reaction. The runtime is pure Python plus the open-source Normaliz executable.
Version 1.5 presents constrained and nonunique balancing as the main Web workflow. The redesigned responsive interface keeps the coefficient lower bound at 0 by default, lets users raise selected minima to 1 or more, and provides separate collapsible libraries with 21 balancing examples and 4 reaction- discovery examples. It also improves formula typesetting, ionic charge display, charge-list guidance, and discovery-filter layout in both Chinese and English.
Reaction discovery introduced in v1.4 remains available: the same species pool is placed on both sides internally; identity transfers are removed, reverse directions are merged, and every candidate reports fixed-support coefficient uniqueness. Arbitrary per-compound lower bounds and exact positive integer ratios remain available for explicitly specified reactions.
The default equations method sends the conservation matrix directly to
Normaliz and computes
{x in nonnegative integers : M x = 0}
in one Normaliz invocation. The package also retains the one-call saturation
method and the two-step two_stage decomposition for independent regression checks.
All rational linear algebra outside Normaliz uses Python's exact Fraction
type; no floating-point null-space computation is involved.
Python 3.9 or newer and Normaliz are required. Normaliz 3.11.1 is recommended; the regression suite also covers 3.9.4.
cd python
python3 -m venv .venv
.venv/bin/pip install -e '.[web]'
export HILBERTBALANCE_NORMALIZ_PATH=/full/path/to/normalizIf the environment variable is omitted, the package resolves normaliz from
PATH.
from hilbertbalance import balance_reaction
result = balance_reaction(
"ClO3+Cl+H->ClO2+Cl2+H2O",
charges=[-1, -1, 1, 0, 0, 0],
)
print(result.equations)
print(result.hilbert_basis.basis)Require every initial reactant:
result = balance_reaction(
"H2+O2+N2->H2O+NH3",
require_all_reactants=True,
)
assert result.hilbert_basis.solution_type == "module_generators"
assert result.hilbert_basis.basis == [[5, 1, 1, 2, 2]]Set arbitrary zero-based coefficient minima and exact ratios:
result = balance_reaction(
"H2+O2+N2->H2O+NH3",
coefficient_lower_bounds={0: 5},
coefficient_ratios=[([1, 2], [2, 2])],
)
assert result.hilbert_basis.basis == [[5, 1, 1, 2, 2]]
assert result.hilbert_basis.coefficient_ratios == [([1, 2], [1, 1])]For a conservation matrix that is already available:
from hilbertbalance import hilbert_basis_from_matrix
result = hilbert_basis_from_matrix(matrix, method="equations")Supported methods are equations, saturation, and two_stage.
Discover stoichiometric transformations without preassigning reactants and products:
from hilbertbalance import discover_reactions
discovery = discover_reactions(["H2", "O2", "H2O", "CO2", "C"])
assert [candidate.equation for candidate in discovery.candidates] == [
"O2 + C <-> CO2",
"2H2 + CO2 <-> 2H2O + C",
"2H2 + O2 <-> 2H2O",
]
assert discovery.identity_transfer_count == 5
assert discovery.reverse_duplicate_count == 3Optional zero-based required_species and forbidden_species filters refer to
the original pool regardless of which side a species occupies. The discovery
API also accepts maximum_coefficient, unique_coefficients_only, and a charge
list containing one integer per pool entry. reaction_candidates(result) is
the lower-level API for an already partitioned, unconstrained two-stage
BalanceResult.
hilbertbalance \
'ClO3+Cl+H->ClO2+Cl2+H2O' \
--charges=-1,-1,1,0,0,0 \
--jsonAdd --require-all-reactants to request the all-reactant shortcut. General
constraints use one-based CLI columns:
hilbertbalance 'H2+O2+N2->H2O+NH3' \
--lower-bound 1=5 \
--ratio 2,3=1,1 \
--jsonReaction discovery uses a comma-separated pool; CLI filter indices are one-based:
hilbertbalance \
--discover 'H2,O2,H2O,CO2,C' \
--required-species 2,4 \
--maximum-coefficient 20 \
--unique-coefficients-only \
--jsonHILBERTBALANCE_NORMALIZ_PATH=/full/path/to/normaliz \
.venv/bin/uvicorn hilbertbalance.web.app:app \
--host 127.0.0.1 --port 8000Open http://127.0.0.1:8000. Interactive API documentation is available at http://127.0.0.1:8000/api/docs. The Web interface can switch between Chinese and English and remembers the selected language in the browser. The solver card switches between equation balancing and species-pool discovery. Discovery mode provides per-species required/forbidden selectors, a maximum-coefficient limit, and coefficient-uniqueness filtering; candidate equations use chemical subscripts, charge superscripts, and a direction-neutral double arrow.
Environment variables:
HILBERTBALANCE_NORMALIZ_PATH: executable path; otherwise usePATH.HILBERTBALANCE_TIMEOUT: per-request Normaliz timeout in seconds; default 30.
The balance API accepts at most 500 reaction characters, 64 compounds, and 64 charge values. The discovery API accepts up to 32 pool species. Normaliz is invoked with an argument list rather than a shell command, and every request receives a private temporary directory.
HILBERTBALANCE_NORMALIZ_PATH=/full/path/to/normaliz \
.venv/bin/python -m unittest discover -s tests -vThe regression suite compares all three methods on both paper matrices, including the complex example with 17 Hilbert-basis elements, arbitrary lower bounds, exact ratios, species-pool discovery, identity cancellation, reverse merging, charged candidates, and both uniqueness levels.
The first release intentionally matches the Mathematica package's formula
syntax: element symbols followed by optional positive integer subscripts, such
as H2SO4 or CH3COOH. Parentheses, hydration dots, isotope notation, and
inline ionic charge notation are not parsed yet. Charges are supplied as a
separate integer list.
If HilbertBalance supports your research, please cite:
Zeying Zhang, Guifu Su, Xueqin Zhang, Yuxin Zhao, Zhenghang Zhang, and Shengyuan A. Yang, “Balancing Chemical Equations: From the Perspective of Hilbert Basis,” MATCH Communications in Mathematical and in Computer Chemistry 95 (2026), 589–601. doi:10.46793/match.95-3.24225 · arXiv:2410.06023
@article{Zhang2026HilbertBasis,
author = {Zhang, Zeying and Su, Guifu and Zhang, Xueqin and Zhao, Yuxin and Zhang, Zhenghang and Yang, Shengyuan A.},
title = {Balancing Chemical Equations: From the Perspective of Hilbert Basis},
journal = {MATCH Communications in Mathematical and in Computer Chemistry},
year = {2026},
volume = {95},
number = {3},
pages = {589--601},
doi = {10.46793/match.95-3.24225},
eprint = {2410.06023},
archivePrefix = {arXiv},
primaryClass = {physics.chem-ph}
}HilbertBalance and Normaliz are distributed under GPL-3.0-or-later. The Web
application calls Normaliz as a separate command-line process. See
THIRD_PARTY_NOTICES.md before distributing a Docker image or binary bundle.