Jacobian Conjecture: the missing context
Why I wrote this post
The Jacobian Conjecture was recently proven to be wrong for the case of dimension 3 and higher by Fable (and then an internal OpenAI model IIRC). Most of the posts I came across were either about the sequence of events leading to the announcement or an interpretation of the example used to disprove it. This kind of discourse makes sense for someone who is well familiar with it and working in a related area.
This article is written for someone who is not, yet is curious and knowledgeable enough to understand it. It takes the reader on a journey to understand what the conjecture is, what motivated it, and a couple of other related statements which were believed to be true but were later disproven, much like the conjecture itself.
Polynomials and invertibility in and
Consider a function that maps (or ) to in the same space, where every output coordinate is a polynomial in the input coordinates:
Now add some constraints by making these assumptions:
- Assume it is invertible, with being the inverse function.
- Assume the inverse is a polynomial function.
Under this assumption the Jacobian determinant has to be a non-zero constant.
Why?
Since undoes , composing them returns the input unchanged. Differentiating that identity with the chain rule turns the composition into a matrix product, and taking determinants turns the matrix product into an ordinary product of numbers:
Each partial derivative in and is a polynomial function, because differentiating a polynomial gives a polynomial:
A determinant is a sum of products of entries, so the determinant of and is also a polynomial function. That leaves us with two polynomials whose product is the constant :
The product of two polynomial functions can be a constant only if they both are constants, because degrees add under multiplication and neither degree can be negative:
Both terms are non-negative and they sum to zero, so both are zero. Thus is a constant, and it is non-zero since its product with something equals .
Thus, what people had was this:
If a polynomial map (or ) has an inverse polynomial map , then the Jacobian determinant of is a non-zero constant.
Implication: local and global invertibility
To understand the implication of it, it is important to understand local and global invertibility.
- A function can be invertible locally i.e. in the neighborhood of a given point.
- If the inverse of a function exists, it implies that is invertible everywhere. Thus global invertibility implies local invertibility everywhere. Two unique inputs always map to two unique outputs.
Now one can ask:
- If a function is locally invertible everywhere, does it make it globally invertible? A handwavy but useful way to frame it is:
If nearby points don't share an output, does it guarantee that distant points also don't share one?
Plainly said, does local invertibility imply global invertibility?
Time for some detours
Before getting to the conjecture, let's spend some more time on the implication and the behavior it leads to.
A detour from polynomial functions
import cmath, math
pie.clear()
pie.scalar(50, out='unit')
pie.scalar(0, out='gridOpacity')
C = dict(ink='#141414', gray='#75756f', muted='#5f5f5c', blue='#1d4ed8', pink='#be185d',
amber='#b45309', green='#047857', rose='#be123c')
def P(x, y):
"""A point that only places other shapes, so it isn't drawn."""
return pie.point(x, y, hidden=True)
def segment(x1, y1, x2, y2, color):
return pie.line(P(x1, y1), P(x2, y2), stroke=color)
# Where 0 sits in each plane, on the canvas.
Z_ORIGIN, W_ORIGIN = (-3.2, -3.4), (3.0, 0.0)
def exp_value(z):
"""exp of the z-plane point z, as (real part, imaginary part)."""
w = cmath.exp(complex(z[0] - Z_ORIGIN[0], z[1] - Z_ORIGIN[1]))
return (w.real, w.imag)
def exp_point(z):
"""Where exp of the z-plane point z sits in the w-plane."""
re, im = exp_value(z)
return (W_ORIGIN[0] + re, W_ORIGIN[1] + im)
def plus_2pi_i(z):
return (z[0], z[1] + 2 * math.pi)
def x_plus(p, d):
return p[0] + d
def y_plus(p, d):
return p[1] + d
x_of, y_of = pie.lift(x_plus, pie.Scalar), pie.lift(y_plus, pie.Scalar)
def label(text, p, dx, dy, color):
"""Text at (dx, dy) from point p. It moves with p."""
return pie.text(text, x_of(p, dx), y_of(p, dy), 14, stroke=color)
# small enough neighborhood: the two planes, z₀, and a disc around it on which exp is injective
axes = [
segment(-5.9, -3.4, -0.7, -3.4, C['gray']),
segment(-3.2, -4.3, -3.2, 4.7, C['gray']),
segment(0.7, 0.0, 5.9, 0.0, C['gray']),
segment(3.0, -2.4, 3.0, 2.6, C['gray']),
]
def plus(v, d):
return v + d
z0_im = pie.scalar(0.9, out='z0_im') # the imaginary part of z₀
z0 = pie.point(-2.8, pie.lift(plus, pie.Scalar)(z0_im, Z_ORIGIN[1]), out='z0', fill=C['ink'])
disc = pie.circle(z0, 0.35, stroke=C['amber'])
# not globally invertible: the second preimage z₀ + 2πi, and the one image point it shares
z1 = pie.lift(plus_2pi_i, pie.Point)(z0, fill=C['ink'])
column = pie.line(z0, z1, stroke=C['gray'])
w = pie.lift(exp_point, pie.Point)(z0, fill=C['rose'])
ring = pie.circle(w, 0.18, stroke=C['rose'])
# map to the same point: the arrows, the names, and the 2πi period
collapse = [
pie.curved_arrow(z0, w, P(0.4, -3.2), 0.07, 'exp', stroke=C['rose']),
pie.curved_arrow(z1, w, P(0.4, 4.2), 0.07, 'exp', stroke=C['rose']),
pie.text('z-plane', -3.2, 5.4, 14, stroke=C['gray']),
pie.text('w-plane', 3.0, 5.4, 14, stroke=C['gray']),
label('z₀', z0, 0.65, 0.75, C['ink']),
label('z₀ + 2πi', z1, -1.5, 0, C['ink']),
label('exp(z₀)', w, 0.82, 0, C['rose']),
label('injective here', z0, -2.05, 0, C['amber']),
pie.dim_line(z0, z1, '2πi', stroke=C['rose']),
pie.text('z₀ and z₀ + 2πi are far apart, yet exp sends both to the same point. '
'Injective on the amber disc, never on all of ℂ.',
2.9, -4.3, 14, 5.4, -1, stroke=C['ink']),
]
# Moving z₀ moves z₀ + 2πi with it, and the two values below stay equal.
pie.ui.value_slider(z0_im, min=-0.9, max=1.8, step=0.01, label='Im(z₀)')
def complex_latex(re, im):
"""re + im·i as LaTeX, to 4 decimals: 0.2829 + 1.4648i, or 0.2829 - 1.4648i."""
sign = '+' if im >= 0 else '-'
return f'{re:.4f} {sign} {abs(im):.4f}i'
def exp_both_latex(z, z_shifted):
return (rf'\exp(z_0) = {complex_latex(*exp_value(z))} \qquad '
rf'\exp(z_0 + 2\pi i) = {complex_latex(*exp_value(z_shifted))}')
pie.ui.latex(pie.lift(exp_both_latex, pie.Text)(z0, z1))
Local invertibility does not imply global invertibility in general. A simple example is . In every small enough neighborhood, this function is invertible. However it is obviously not globally invertible since the points and map to the same point.
A detour from constant Jacobians
Note that for a function to be (globally) invertible, its Jacobian determinant doesn't need to be a non-zero constant. It just needs to be non-zero everywhere.
- In the field , they are the same thing. The only polynomial in that never vanishes is a non-zero constant . This is due to the fundamental theorem of algebra: a non-constant polynomial always has a root.
- It implies the only invertible polynomial in is of the form with . This is only true for single dimension.
A common gotcha
pie.clear()
pie.scalar(50, out='unit')
pie.scalar(0, out='gridOpacity')
C = dict(ink='#141414', gray='#75756f', muted='#5f5f5c', blue='#1d4ed8', pink='#be185d',
amber='#b45309', green='#047857', rose='#be123c')
def P(x, y):
"""A point that only places other shapes, so it isn't drawn."""
return pie.point(x, y, hidden=True)
def segment(x1, y1, x2, y2, color):
return pie.line(P(x1, y1), P(x2, y2), stroke=color)
xs, xi = -4.4, 2.1 # where the domain and the image sit
y_levels = [-1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5]
x_levels = [-1.0, 0.0, 1.0]
# higher degree polynomials: the domain's grid, and the unit square in it
domain = [segment(xs - 1, y, xs + 1, y, C['pink']) for y in y_levels]
domain += [segment(xs + x, -1.5, xs + x, 1.5, C['blue']) for x in x_levels]
sq_bl, sq_br, sq_tr, sq_tl = P(xs, 0.5), P(xs + 1, 0.5), P(xs + 1, 1.5), P(xs, 1.5)
domain += [pie.line(a, b, stroke=C['amber'])
for a, b in [(sq_bl, sq_br), (sq_br, sq_tr), (sq_tr, sq_tl), (sq_tl, sq_bl)]]
# constant Jacobian determinant: pink lines only slide, blue lines bend, and the square comes along
image = [segment(xi - 1 + y * y, y, xi + 1 + y * y, y, C['pink']) for y in y_levels]
# x = c + y² for y in [-1.5, 1.5] is exactly the quadratic Bézier with control point (c - 2.25, 0)
image += [pie.bezier_quadratic(P(xi + x + 2.25, -1.5), P(xi + x - 2.25, 0.0), P(xi + x + 2.25, 1.5),
stroke=C['blue'])
for x in x_levels]
im_bl, im_br, im_tr, im_tl = P(xi + 0.25, 0.5), P(xi + 1.25, 0.5), P(xi + 3.25, 1.5), P(xi + 2.25, 1.5)
image += [
pie.line(im_bl, im_br, stroke=C['amber']),
pie.bezier_quadratic(im_br, P(xi + 1.75, 1.0), im_tr, stroke=C['amber']),
pie.line(im_tr, im_tl, stroke=C['amber']),
pie.bezier_quadratic(im_tl, P(xi + 0.75, 1.0), im_bl, stroke=C['amber']),
pie.text('domain', xs, 2.7, 14, stroke=C['gray']),
pie.text('image under f', 3.225, 2.7, 14, stroke=C['gray']),
pie.text('det J = 1 everywhere', -1.15, 1.5, 14, stroke=C['amber']),
pie.arrow(P(-2.9, 0.0), P(0.6, 0.0), 0.05, 'f(x, y) = (x + y², y)', stroke=C['gray']),
]
# never multiplies it: every horizontal slice keeps its length
area = [
pie.dim_line(sq_tr, sq_tl, stroke=C['amber']),
pie.dim_line(im_tr, im_tl, stroke=C['amber']),
pie.text('Every horizontal slice keeps its length and only slides right by y², '
'so the bent image has exactly the area of the square. That is det J = 1.',
0, -2.9, 14, 8, -1, stroke=C['ink']),
]
In higher dimensions, there can be higher degree polynomials with a constant Jacobian determinant. Eg
Even though the first coordinate is quadratic, the that would carry the degree sits in the upper right of the Jacobian, where the determinant never multiplies it against anything but a zero:
Therefore, for polynomial maps over complex numbers, saying "the Jacobian is a non-zero constant" and saying "the map is locally invertible everywhere" are mathematically equivalent statements.
- However, in the field , they are not the same thing. E.g. is always positive but not constant.
- For a long time, this was the OG Jacobian conjecture (the strong real Jacobian conjecture): it was believed that in the field of real numbers, if a polynomial map had a non-zero Jacobian determinant everywhere (implying local invertibility everywhere), it had a global inverse.
Note that the strong real conjecture did not require the inverse to be a polynomial. It only conjectured the existence of an inverse, that's it.
This conjecture was disproven by a counterexample in 1994.
A detour to constant Jacobian real polynomial maps
Another interesting development in this area was related to the case of inverse polynomial maps for constant Jacobian functions i.e. polynomial functions with a constant Jacobian that also had an inverse polynomial function. Every example could be decomposed to one of these three patterns:
1. An affine map , whose Jacobian is the constant matrix itself:
pie.clear()
pie.scalar(56, out='unit')
pie.scalar(0, out='gridOpacity')
C = dict(ink='#141414', gray='#75756f', muted='#5f5f5c', blue='#1d4ed8', pink='#be185d',
amber='#b45309', green='#047857', rose='#be123c')
def P(x, y):
"""A point that only places other shapes, so it isn't drawn."""
return pie.point(x, y, hidden=True)
def segment(x1, y1, x2, y2, color):
return pie.line(P(x1, y1), P(x2, y2), stroke=color)
a, a3 = 0.9, 0.729 # half the width of each panel, and a³
x1, x2, x3 = -3.55, 0.0, 3.55 # the panels' centers
above, below = 2.2, -2.2 # the rows of text just clear of the bent panel, which reaches a + a³ = 1.63
pie.scalar(1.0, out='originY') # centers the figure, from the captions down to the note
def panel(ox, color):
"""A square of side 2a centered at (ox, 0), with its two middle lines."""
ends = [(-a, -a, a, -a), (a, -a, a, a), (a, a, -a, a), (-a, a, -a, -a), (-a, 0, a, 0), (0, -a, 0, a)]
return [segment(ox + u1, v1, ox + u2, v2, color) for u1, v1, u2, v2 in ends]
def cubic_row(y):
"""The curve y + x³ for x in [-a, a], exactly, as a cubic Bézier."""
return pie.bezier_cubic(P(x2 - a, y - a3), P(x2 - a / 3, y + a3), P(x2 + a / 3, y - a3), P(x2 + a, y + a3),
stroke=C['amber'])
def f(p):
"""A point of the left panel -> f of it, in the middle panel."""
x, y = p[0] - x1, p[1]
return (x2 + x, y + x ** 3)
def f_inverse(q):
"""A point of the middle panel -> f⁻¹ of it, in the right panel."""
u, v = q[0] - x2, q[1]
return (x3 + u, v - u ** 3)
def before_rise(p):
"""A point of the left panel, moved to the middle panel without adding x³."""
return (x2 + p[0] - x1, p[1])
# its own variable: the square we start with
start = panel(x1, C['blue'])
# plus a polynomial: after f, horizontal lines bend into y = c + x³, vertical lines slide up
bend = [
cubic_row(-a), cubic_row(0.0), cubic_row(a),
segment(x2 + a, -a + a3, x2 + a, a + a3, C['amber']),
segment(x2 - a, a - a3, x2 - a, -a - a3, C['amber']),
segment(x2, -a, x2, a, C['amber']),
]
# read off by back-substitution: after f⁻¹, exactly the square we started with. Drag the left dot.
dot_a = pie.point(x1 + a, 0.45, out='dot_a', fill=C['pink'])
dot_b = pie.lift(f, pie.Point)(dot_a, fill=C['pink'])
dot_c = pie.lift(f_inverse, pie.Point)(dot_b, fill=C['pink'])
undo = panel(x3, C['green']) + [
dot_a, dot_b, dot_c,
pie.text('(x, y)', x1, above, 14, stroke=C['blue']),
pie.text('(x, y + x³)', x2, above, 14, stroke=C['amber']),
pie.text('f⁻¹(f(x, y)) = (x, y)', x3, above, 14, stroke=C['green']),
pie.text('back to the start', x3, below, 14, stroke=C['green']),
# Level with the panels' middle lines, in the gaps between the panels
pie.arrow(P(x1 + 1.1, 0.0), P(x2 - 1.1, 0.0), 0.08, 'f', stroke=C['amber']),
pie.arrow(P(x2 + 1.1, 0.0), P(x3 - 1.1, 0.0), 0.08, 'f⁻¹', stroke=C['green']),
pie.dim_line(pie.lift(before_rise, pie.Point)(dot_a, hidden=True), dot_b, 'x³', stroke=C['amber']),
pie.text('Each output coordinate is its own variable plus a polynomial in the earlier ones, '
'so back-substitution inverts f exactly: subtract x³ and the square snaps back.',
0, -3.6, 14, 8, -1, stroke=C['ink']),
]
2. A triangular map, where each coordinate is its own variable plus a polynomial in the earlier variables only. The Jacobian is then triangular with s on the diagonal, so its determinant is for free, and the inverse can be read off by back-substitution:
The same trick stacks up in any dimension, one variable at a time:
pie.clear()
pie.scalar(34, out='unit')
pie.scalar(0, out='gridOpacity')
C = dict(ink='#141414', gray='#75756f', muted='#5f5f5c', blue='#1d4ed8', pink='#be185d',
amber='#b45309', green='#047857', rose='#be123c')
def P(x, y):
"""A point that only places other shapes, so it isn't drawn."""
return pie.point(x, y, hidden=True)
def box(text, x, y, size, color, w=0, h=0):
"""Text centered at (x, y). With a width `w`, it wraps inside a box."""
return pie.text(text, x, y, size, w, h, stroke=color)
# affine coordinate swap: the box that defines "tame", and the two kinds of map inside it
frame = [
box('Constant-Jacobian polynomial maps', 0, 7.5, 13, C['muted'], 8, -1),
pie.rectangle(P(-8.0, 0.0), 16.0, 6.6, stroke=C['green']),
box('TAME', -6.2, 6.6, 14, C['green'], 1.6, -1),
box('affine: f(x, y) = (2x + y + 3, x + y - 1)', -4.2, 4.6, 12, C['green'], 6.0, -1),
box('triangular: f(x, y) = (x, y + x³)', 4.2, 4.6, 12, C['green'], 6.0, -1),
]
# built from both: they compose into something that looks like neither, with a constant det J too
compose = [
pie.arrow(P(-2.4, 3.6), P(-1.0, 2.3), 0.05, stroke=C['green']),
pie.arrow(P(2.4, 3.6), P(1.0, 2.3), 0.05, stroke=C['green']),
box('composition A∘T: f(x, y) = (y + x², x)', 0.0, 1.3, 12, C['green'], 6.0, -1),
box('det J = 1', -4.2, 2.95, 11, C['amber']),
box('det J = 1', 4.2, 2.95, 11, C['amber']),
box('det J = -1', 5.2, 1.3, 11, C['amber']),
]
# "wild" map: a second box, outside the first, with the same det J = 1
wild = [
pie.rectangle(P(-8.0, -4.85), 16.0, 3.6, stroke=C['rose']),
box('WILD', -6.2, -1.25, 14, C['rose'], 1.6, -1),
box('Nagata (1972): N(x, y, z) = (x - 2yΔ - zΔ², y + zΔ, z) with Δ = xz + y²', 1.3, -2.5, 12, C['rose'], 11.0, -1),
box('det J = 1', -4.2, -4.15, 11, C['amber']),
box('proven wild in 2003', 1.5, -4.15, 11, C['rose']),
]
# what we know: what decides which box a map lands in
rule = [
box('n ≤ 2: every such map is tame', -4.1, -6.05, 12, C['green'], 7.0, -1),
box('n ≥ 3: wild maps exist', 4.1, -6.05, 12, C['rose'], 7.0, -1),
box("Nagata's map has the same det J = 1 as the tame generators, yet is not built from them.",
0, -7.85, 12, C['muted'], 14, -1),
]
3. A composition of the above maps, which is where it stops being obvious. Compose the triangular with the affine coordinate swap :
It looks like neither building block, yet it is built from both, and its inverse is polynomial for the same reason: .
It was believed that all such polynomial functions were decomposable i.e. they were "tame" maps. In 1972, Japanese mathematician Nagata proposed a polynomial map which did not seem to be decomposable - a "wild" map:
This fact was proven to be true only in 2003. This led to the fall of yet another conjecture related to constant Jacobian polynomial maps.
As of today, this is what we know:
- all such polynomial automorphisms are tame for dimension .
- for dimension , such an automorphism can be a wild one.
The Jacobian Conjecture
Now it is a good time to state the conjecture that was proven to be wrong just a few days ago:
Given a polynomial map with a non-zero constant Jacobian, it has an inverse. If such an inverse exists, it is guaranteed to be polynomial.
Why is it guaranteed to be polynomial?
The proof follows these arguments:
- the polynomial inverse, if it exists, has to be of the form i.e. it is a fraction of two polynomials.
- Since it exists everywhere, at every point, which is possible only if is a constant. Thus is a polynomial for all .
Take the map from the gotcha above, now written in indexed coordinates:
Solving for the inputs one at a time is pure back-substitution:
Written in the form the argument above predicts, both denominators come out as the constant :
So nothing rational survives, and the inverse is an honest polynomial map:
Implications of this conjecture
pie.clear()
pie.scalar(38, out='unit')
pie.scalar(0, out='gridOpacity')
C = dict(ink='#141414', gray='#75756f', muted='#5f5f5c', blue='#1d4ed8', pink='#be185d',
amber='#b45309', green='#047857', rose='#be123c')
def P(x, y):
"""A point that only places other shapes, so it isn't drawn."""
return pie.point(x, y, hidden=True)
def box(text, x, y, size, color, w=0, h=0):
"""Text centered at (x, y). With a width `w`, it wraps inside a box."""
return pie.text(text, x, y, size, w, h, stroke=color)
# Each row: its height, the hypothesis, the conclusion it hoped for, and what became of it
rows = [
(3.4, "any holomorphic f: f'(z) ≠ 0 everywhere", 'f is injective on ℂ',
'false - counterexample: exp(z)'),
(0.0, 'strong real Jacobian conjecture: det J ≠ 0 everywhere on ℝⁿ', 'f has a global inverse',
'false - disproven in 1994'),
(-3.4, 'Jacobian Conjecture: det J = c ≠ 0 on ℂⁿ', 'f⁻¹ exists and is a polynomial map',
'false for n ≥ 3, 2026 - stood 90 years'),
]
# implies global invertibility: the question, the column headers, and the hypotheses, which always hold
table = [
box('Does local invertibility imply global invertibility?', 0, 6.4, 15, C['muted'], 12, -1),
box('hypothesis', -4.3, 5.0, 12, C['amber']),
box('conclusion', 4.3, 5.0, 12, C['green']),
]
table += [box(hyp, -4.3, y, 13, C['amber'], 4.8, -1) for y, hyp, _, _ in rows]
# massive claim: the implication each setting would like to make, and what it hoped to conclude
conclusion = [pie.arrow(P(-1.45, y), P(1.45, y), 0.04, stroke=C['rose']) for y, _, _, _ in rows]
conclusion += [box(con, 4.3, y, 13, C['green'], 4.8, -1) for y, _, con, _ in rows]
# does not guarantee: the same verdict three times over
verdict = [box('verdict', 0.0, 5.0, 12, C['rose'])]
verdict += [box('✗', 0.0, y, 18, C['rose'], 0.8, -1) for y, _, _, _ in rows]
verdict += [box(note, 4.3, y - 1.55, 11, C['rose']) for y, _, _, note in rows]
verdict += [
pie.rectangle(P(-7.3, -5.45), 14.6, 3.3, stroke=C['rose']),
box('In every row the hypothesis holds and the conclusion fails: '
'local invertibility never buys global invertibility.', 0, -6.75, 13, C['muted'], 13, -1),
]
The conjecture essentially claims that for polynomials function, local invertibility implies global invertibility.
This was a massive claim because, outside of complex polynomials, local invertibility does not guarantee global invertibility. We saw the example of earlier.
It was disproved only recently (July 2026) for dimension . It still remains an open conjecture for .