GuidesCompare formulas

Compare formulas

Use equiv-profile normalization for cheap spelling comparison, and a CAS for true mathematical equivalence.

Comparing two LaTeX formulas usually means one of two different questions, and it helps to separate them. The cheap question is whether two formulas are the same up to spelling — \cfrac{a}{b} and \frac{a}{b} denote the same fraction written two ways. The expensive question is whether two formulas are mathematically equal — x + x and 2x are equal but structurally different. TeXForm answers the first directly and is deliberately not in the business of answering the second.

Layer 1 — spelling equality via the equiv profile. Normalize both formulas under the equiv profile and compare the resulting strings. This profile canonicalizes equivalent spellings hard enough that surface differences collapse, so a string comparison resolves them with no extra dependencies.

import texform

equiv = texform.TransformEngine(profile="equiv")
corpus = texform.TransformEngine(profile="corpus")


def same_form(engine, a, b):
    return engine.normalize(a) == engine.normalize(b)


for a, b in [(r"\cfrac{a}{b}", r"\frac{a}{b}"), (r"\widehat{x}", r"\hat{x}"), (r"x + x", r"2x")]:
    print(f"{a!r:16} vs {b!r:14} equiv={same_form(equiv, a, b)} corpus={same_form(corpus, a, b)}")
Output
'\\cfrac{a}{b}'  vs '\\frac{a}{b}' equiv=True corpus=False
'\\widehat{x}'   vs '\\hat{x}'     equiv=True corpus=False
'x + x'          vs '2x'           equiv=False corpus=False
import { TransformEngine } from 'texform';

const equiv = new TransformEngine({ profile: 'equiv' });
const corpus = new TransformEngine({ profile: 'corpus' });

const sameForm = (engine, a, b) =>
  engine.normalize(a) === engine.normalize(b);

for (const [a, b] of [['\\cfrac{a}{b}', '\\frac{a}{b}'], ['\\widehat{x}', '\\hat{x}'], ['x + x', '2x']]) {
  console.log(a, 'vs', b, 'equiv =', sameForm(equiv, a, b), 'corpus =', sameForm(corpus, a, b));
}

The corpus column is why equiv exists. \cfrac and \widehat are notation choices a training label should keep — corpus preserves the continued-fraction styling and the wide hat, so the two spellings stay distinct there. equiv deliberately discards that distinction (ams/cfrac-to-frac and base/widehat-to-hat are the rules doing it) because a comparison key should not care how wide the hat was.

The third row states the other boundary just as plainly: x + x and 2x do not compare equal, because equiv normalizes form, not meaning. It never performs algebra — it will not factor, expand, or evaluate. Treat layer 1 as a fast, zero-dependency filter that resolves spelling differences and nothing more, and reach for equiv output as a comparison key or fingerprint rather than as a label you would show a reader.

Layer 2 — mathematical equivalence via a CAS. For the cases layer 1 reports as different, hand the normalized strings to a computer algebra system. The snippet below sketches the hookup with SymPy; a math-recognition evaluation harness such as Math-Verify wraps the same idea with parsing and timeout handling built in.

Python
# Illustrative — requires `pip install sympy antlr4-python3-runtime`.
import texform
from sympy.parsing.latex import parse_latex

engine = texform.TransformEngine(profile="equiv")


def math_equal(a, b):
    expr_a = parse_latex(engine.normalize(a))
    expr_b = parse_latex(engine.normalize(b))
    return (expr_a - expr_b).simplify() == 0


# math_equal(r"x + x", r"2x")  ->  True

Normalizing with equiv first does real work for the CAS: it removes the spelling variation a LaTeX parser would otherwise choke on, so the CAS only has to reason about genuine mathematical difference. For how the equiv profile canonicalizes and why it stops short of mathematical reasoning, see Transforms. For how this compares with string normalizers used in answer grading, see How TeXForm compares.

Next steps#

Last updated