jax-ml/jax · error · ValueError
Invalid equality constraint: {e1} == {e2}. The left-hand-sid
Error message
Invalid equality constraint: {e1} == {e2}. The left-hand-side must be of the form `term * coefficient`. What it means
For equality constraints used as normalization rules (solving one variable in terms of others), the left-hand side must be a single term times a coefficient (e.g. 'a * 2 == b'). If the LHS is not a symbolic expression at all (a bare constant), the constraint cannot define a normalization and is rejected.
Source
Thrown at jax/_src/export/shape_poly.py:1076
e2, = _Parser(e2_str, None, repr(e2_str), self).parse()
if cmp == Comparator.GEQ and not is_geq:
e1, e2 = e2, e1
# Compute e1 - e2 before we add to normalization rules
constr = _SymbolicConstraint(debug_str=c_str, cmp=cmp, e1=e1, e2=e2,
diff=e1 - e2)
self._process_explicit_constraint(constr)
def _process_explicit_constraint(self, constr: _SymbolicConstraint):
if (diff_const := _DimExpr._to_constant(constr.diff)) is not None:
if ((constr.cmp == Comparator.EQ and diff_const != 0) or
(constr.cmp == Comparator.GEQ and diff_const < 0)):
raise ValueError(f"Unsatisfiable explicit constraint: {constr.debug_str}")
return
if constr.cmp == Comparator.EQ:
if not isinstance(constr.e1, _DimExpr):
raise ValueError("Invalid equality constraint: {e1} == {e2}. "
"The left-hand-side must be of the form `term * coefficient`.")
(before, before_k), *rest = constr.e1._sorted_terms
if rest:
raise ValueError("Invalid equality constraint: {e1} == {e2}. "
"The left-hand-side must be of the form `term * coefficient`.")
after = _ensure_poly(constr.e2, "parse_constraint", constr.e1.scope)
if before in self._normalization_rules:
raise NotImplementedError(
f"Found multiple equality constraints with the same left-hand-side: {before}")
self._normalization_rules[before] = (after, before_k)
# Look for constraints of the form mod(before_e1, before_k2) * 1 == 0
if (before_k == 1 and
isinstance(constr.e2, int) and constr.e2 == 0 and
(before_f := before.to_factor()) and
before_f.operation == _DimFactor.MOD and
(before_k2 := _DimExpr._to_constant(before_f.operands[1])) is not None):
# Add before_k2*floordiv(before_e1, before_k2) == before_e1View on GitHub (pinned to 1e1c6a8fc0)
Solutions
- Put the symbolic term on the left-hand side: 'n * 2 == 8' instead of '8 == n * 2'
- If the constraint is a bound, use '>=' or '<=' instead of '=='
- Ensure at least one dim variable appears on the LHS
Example fix
# before
constraints = ('8 == n * 2',)
# after
constraints = ('n * 2 == 8',) Defensive patterns
Strategy: validation
Validate before calling
for c in eq_constraints:
lhs = c.split('==')[0]
assert has_dim_var(lhs), f'LHS must contain a variable: {c}' Type guard
def lhs_is_symbolic(c: str) -> bool:
return any(ch.isalpha() for ch in c.split('==')[0]) Prevention
- Put the variable term on the left of every equality
- Use >=/<= for pure bounds
When it happens
Trigger: Constraints like '4 == b', '8 == n*2', or '2 == a' where the LHS contains no dimension variable.
Common situations: Writing the constant on the left by habit; constraints intended as range checks mistakenly written with '==' and constant LHS.
Related errors
- The symbolic constraints should be a sequence of strings. Go
- SymbolicScope constraint must be a string: got {repr(c_str)}
- Constraint parsing error: must contain one of '==' or '>=' o
- Unsatisfiable explicit constraint: {constr.debug_str}
- Found multiple equality constraints with the same left-hand-
AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27).
Data as JSON: /api/errors/47f1ad1bca369ea5.
Report an issue: GitHub.